/** Make a path from a d2 sbasis.
 \param p the d2 Symmetric basis polynomial
 \returns a Path

  If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves.
 TODO: some of this logic should be lifted into svg-path
*/
std::vector<Geom::Path>
path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol, bool only_cubicbeziers) {
    Geom::PathBuilder pb;
    if(B.size() == 0) return pb.peek();
    Geom::Point start = B[0].at0();
    pb.moveTo(start);
    for(unsigned i = 0; ; i++) {
        if ( (i+1 == B.size()) 
             || !are_near(B[i+1].at0(), B[i].at1(), tol) )
        {
            //start of a new path
            if (are_near(start, B[i].at1()) && sbasis_size(B[i]) <= 1) {
                pb.closePath();
                //last line seg already there (because of .closePath())
                goto no_add;
            }
            build_from_sbasis(pb, B[i], tol, only_cubicbeziers);
            if (are_near(start, B[i].at1())) {
                //it's closed, the last closing segment was not a straight line so it needed to be added, but still make it closed here with degenerate straight line.
                pb.closePath();
            }
          no_add:
            if (i+1 >= B.size()) {
                break;
            }
            start = B[i+1].at0();
            pb.moveTo(start);
        } else {
            build_from_sbasis(pb, B[i], tol, only_cubicbeziers);
        }
    }
    pb.flush();
    return pb.peek();
}
Esempio n. 2
0
Curve* EllipticalArc::portion(double f, double t) const
{
    // fix input arguments
    if (f < 0) f = 0;
    if (f > 1) f = 1;
    if (t < 0) t = 0;
    if (t > 1) t = 1;

    if ( are_near(f, t) )
    {
        EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate());
        arc->_center = arc->_initial_point = arc->_final_point = pointAt(f);
        arc->_start_angle = arc->_end_angle = _start_angle;
        arc->_rot_angle = _rot_angle;
        arc->_sweep = _sweep;
        arc->_large_arc = _large_arc;
        return arc;
    }

    EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate());
    arc->_initial_point = pointAt(f);
    arc->_final_point = pointAt(t);
    if ( f > t ) arc->_sweep = !_sweep;
    if ( _large_arc && fabs(sweepAngle() * (t-f)) < M_PI)
        arc->_large_arc = false;
    arc->_updateCenterAndAngles(arc->isSVGCompliant()); //TODO: be more clever
    return arc;
}
bool make_elliptical_arc::make_elliptiarc()
{
    const NL::Vector & coeff = fitter.result();
    Ellipse e;
    try
    {
        e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
    }
    catch(LogicalError const &exc)
    {
        return false;
    }

    Point inner_point = curve(0.5);

#ifdef CPP11
    std::unique_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point) );
#else
    std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point) );
#endif
    ea = *arc;

    if ( !are_near( e.center(),
                    ea.center(),
                    tol_at_center * std::min(e.ray(X),e.ray(Y))
                  )
       )
    {
        return false;
    }
    return true;
}
Esempio n. 4
0
void Affine::setExpansionY(double val) {
    double exp_y = expansionY();
    if(!are_near(exp_y, 0.0)) {  //TODO: best way to deal with it is to skip op?
        double coef = val / expansionY();
        for(unsigned i=2; i<4; i++) _c[i] *= coef;
    }
}
Esempio n. 5
0
/** @brief Check whether this matrix represents pure, nonzero uniform scaling.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           a_1 & 0 & 0 \\
           0 & a_2 & 0 \\
           0 & 0 & 1 \end{array}\right]\f$ where \f$|a_1| = |a_2|\f$
 * and \f$a_1, a_2 \neq 1\f$. */
bool Affine::isNonzeroUniformScale(Coord eps) const {
    if (isSingular(eps)) return false;
    // we need to test both c0 and c3 to handle the case of flips,
    // which should be treated as nonzero uniform scales
    return !(are_near(_c[0], 1.0, eps) && are_near(_c[3], 1.0, eps)) &&
           are_near(fabs(_c[0]), fabs(_c[3]), eps) &&
           are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&  
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
Esempio n. 6
0
Point Curve::unitTangentAt(Coord t, unsigned n) const
{
    std::vector<Point> derivs = pointAndDerivatives(t, n);
    for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) {
        Coord length = derivs[deriv_n].length();
        if ( ! are_near(length, 0) ) {
            // length of derivative is non-zero, so return unit vector
            return derivs[deriv_n] / length;
        }
    }
    return Point (0,0);
};
Esempio n. 7
0
void SVGPathWriter::lineTo(Point const &p)
{
    // The weird setting of _current is to avoid drift with many almost-aligned segments
    // The additional conditions ensure that the smaller dimension is rounded to zero
    bool written = false;
    if (_use_shorthands) {
        Point r = _current - p;
        if (are_near(p[X], _current[X], _epsilon) && std::abs(r[X]) < std::abs(r[Y])) {
            // emit vlineto
            _setCommand('V');
            _current_pars.push_back(p[Y]);
            _current[Y] = p[Y];
            written = true;
        } else if (are_near(p[Y], _current[Y], _epsilon) && std::abs(r[Y]) < std::abs(r[X])) {
            // emit hlineto
            _setCommand('H');
            _current_pars.push_back(p[X]);
            _current[X] = p[X];
            written = true;
        }
    }

    if (!written) {
        // emit normal lineto
        if (_command != 'M' && _command != 'L') {
            _setCommand('L');
        }
        _current_pars.push_back(p[X]);
        _current_pars.push_back(p[Y]);
        _current = p;
    }

    _cubic_tangent = _quad_tangent = _current;
    if (!_optimize) {
        flush();
    }
}
Esempio n. 8
0
void SVGPathWriter::quadTo(Point const &c, Point const &p)
{
    bool shorthand = _use_shorthands && are_near(c, _quad_tangent, _epsilon);

    _setCommand(shorthand ? 'T' : 'Q');
    if (!shorthand) {
        _current_pars.push_back(c[X]);
        _current_pars.push_back(c[Y]);
    }
    _current_pars.push_back(p[X]);
    _current_pars.push_back(p[Y]);

    _current = _cubic_tangent = p;
    _quad_tangent = p + (p - c);
    if (!_optimize) {
        flush();
    }
}
Esempio n. 9
0
void SVGPathWriter::curveTo(Point const &p1, Point const &p2, Point const &p3)
{
    bool shorthand = _use_shorthands && are_near(p1, _cubic_tangent, _epsilon);

    _setCommand(shorthand ? 'S' : 'C');
    if (!shorthand) {
        _current_pars.push_back(p1[X]);
        _current_pars.push_back(p1[Y]);
    }
    _current_pars.push_back(p2[X]);
    _current_pars.push_back(p2[Y]);
    _current_pars.push_back(p3[X]);
    _current_pars.push_back(p3[Y]);

    _current = _quad_tangent = p3;
    _cubic_tangent = p3 + (p3 - p2);
    if (!_optimize) {
        flush();
    }
}
Esempio n. 10
0
/** @brief Check whether this matrix represents pure scaling.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           a & 0 & 0 \\
           0 & b & 0 \\
           0 & 0 & 1 \end{array}\right]\f$. */
bool Affine::isScale(Coord eps) const {
    if (isSingular(eps)) return false;
    return are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) && 
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
Esempio n. 11
0
/** @brief Check whether this matrix represents pure, nonzero scaling.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           a & 0 & 0 \\
           0 & b & 0 \\
           0 & 0 & 1 \end{array}\right]\f$ and \f$a, b \neq 1\f$. */
bool Affine::isNonzeroScale(Coord eps) const {
    if (isSingular(eps)) return false;
    return (!are_near(_c[0], 1.0, eps) || !are_near(_c[3], 1.0, eps)) &&  //NOTE: these are the diags, and the next line opposite diags
           are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) && 
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
Esempio n. 12
0
/*
 * NOTE: this implementation follows Standard SVG 1.1 implementation guidelines
 * for elliptical arc curves. See Appendix F.6.
 */
void EllipticalArc::_updateCenterAndAngles(bool svg)
{
    Point d = initialPoint() - finalPoint();

    // TODO move this to SVGElipticalArc?
    if (svg)
    {
        if ( initialPoint() == finalPoint() )
        {
            _rot_angle = _start_angle = _end_angle = 0;
            _center = initialPoint();
            _rays = Geom::Point(0,0);
            _large_arc = _sweep = false;
            return;
        }

        _rays[X] = std::fabs(_rays[X]);
        _rays[Y] = std::fabs(_rays[Y]);

        if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
        {
            _rays[X] = L2(d) / 2;
            _rays[Y] = 0;
            _rot_angle = std::atan2(d[Y], d[X]);
            _start_angle = 0;
            _end_angle = M_PI;
            _center = middle_point(initialPoint(), finalPoint());
            _large_arc = false;
            _sweep = false;
            return;
        }
    }
    else
    {
        if ( are_near(initialPoint(), finalPoint()) )
        {
            if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
            {
                _start_angle = _end_angle = 0;
                _center = initialPoint();
                return;
            }
            else
            {
                THROW_RANGEERROR("initial and final point are the same");
            }
        }
        if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
        { // but initialPoint != finalPoint
            THROW_RANGEERROR(
                "there is no ellipse that satisfies the given constraints: "
                "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
            );
        }
        if ( are_near(ray(Y), 0) )
        {
            Point v = initialPoint() - finalPoint();
            if ( are_near(L2sq(v), 4*ray(X)*ray(X)) )
            {
                Angle angle(v);
                if ( are_near( angle, _rot_angle ) )
                {
                    _start_angle = 0;
                    _end_angle = M_PI;
                    _center = v/2 + finalPoint();
                    return;
                }
                angle -= M_PI;
                if ( are_near( angle, _rot_angle ) )
                {
                    _start_angle = M_PI;
                    _end_angle = 0;
                    _center = v/2 + finalPoint();
                    return;
                }
                THROW_RANGEERROR(
                    "there is no ellipse that satisfies the given constraints: "
                    "ray(Y) == 0 "
                    "and slope(initialPoint - finalPoint) != rotation_angle "
                    "and != rotation_angle + PI"
                );
            }
            if ( L2sq(v) > 4*ray(X)*ray(X) )
            {
                THROW_RANGEERROR(
                    "there is no ellipse that satisfies the given constraints: "
                    "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)"
                );
            }
            else
            {
                THROW_RANGEERROR(
                    "there is infinite ellipses that satisfy the given constraints: "
                    "ray(Y) == 0  and distance(initialPoint, finalPoint) < 2*ray(X)"
                );
            }

        }

        if ( are_near(ray(X), 0) )
        {
            Point v = initialPoint() - finalPoint();
            if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) )
            {
                double angle = std::atan2(v[Y], v[X]);
                if (angle < 0) angle += 2*M_PI;
                double rot_angle = _rot_angle.radians() + M_PI/2;
                if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI;
                if ( are_near( angle, rot_angle ) )
                {
                    _start_angle = M_PI/2;
                    _end_angle = 3*M_PI/2;
                    _center = v/2 + finalPoint();
                    return;
                }
                angle -= M_PI;
                if ( angle < 0 ) angle += 2*M_PI;
                if ( are_near( angle, rot_angle ) )
                {
                    _start_angle = 3*M_PI/2;
                    _end_angle = M_PI/2;
                    _center = v/2 + finalPoint();
                    return;
                }
                THROW_RANGEERROR(
                    "there is no ellipse that satisfies the given constraints: "
                    "ray(X) == 0 "
                    "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 "
                    "and != rotation_angle + (3/2)*PI"
                );
            }
            if ( L2sq(v) > 4*ray(Y)*ray(Y) )
            {
                THROW_RANGEERROR(
                    "there is no ellipse that satisfies the given constraints: "
                    "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)"
                );
            }
            else
            {
                THROW_RANGEERROR(
                    "there is infinite ellipses that satisfy the given constraints: "
                    "ray(X) == 0  and distance(initialPoint, finalPoint) < 2*ray(Y)"
                );
            }

        }

    }

    Rotate rm(_rot_angle);
    Affine m(rm);
    m[1] = -m[1];
    m[2] = -m[2];

    Point p = (d / 2) * m;
    double rx2 = _rays[X] * _rays[X];
    double ry2 = _rays[Y] * _rays[Y];
    double rxpy = _rays[X] * p[Y];
    double rypx = _rays[Y] * p[X];
    double rx2py2 = rxpy * rxpy;
    double ry2px2 = rypx * rypx;
    double num = rx2 * ry2;
    double den = rx2py2 + ry2px2;
    assert(den != 0);
    double rad = num / den;
    Point c(0,0);
    if (rad > 1)
    {
        rad -= 1;
        rad = std::sqrt(rad);

        if (_large_arc == _sweep) rad = -rad;
        c = rad * Point(rxpy / ray(Y), -rypx / ray(X));

        _center = c * rm + middle_point(initialPoint(), finalPoint());
    }
    else if (rad == 1 || svg)
    {
        double lamda = std::sqrt(1 / rad);
        _rays[X] *= lamda;
        _rays[Y] *= lamda;
        _center = middle_point(initialPoint(), finalPoint());
    }
    else
    {
        THROW_RANGEERROR(
            "there is no ellipse that satisfies the given constraints"
        );
    }

    Point sp((p[X] - c[X]) / ray(X), (p[Y] - c[Y]) / ray(Y));
    Point ep((-p[X] - c[X]) / ray(X), (-p[Y] - c[Y]) / ray(Y));
    Point v(1, 0);
    _start_angle = angle_between(v, sp);
    double sweep_angle = angle_between(sp, ep);
    if (!_sweep && sweep_angle > 0) sweep_angle -= 2*M_PI;
    if (_sweep && sweep_angle < 0) sweep_angle += 2*M_PI;

    _end_angle = _start_angle;
    _end_angle += sweep_angle;
}
Esempio n. 13
0
std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from, double to ) const
{
    std::vector<double> result;

    if ( from > to ) std::swap(from, to);
    if ( from < 0 || to > 1 )
    {
        THROW_RANGEERROR("[from,to] interval out of range");
    }

    if ( ( are_near(ray(X), 0) && are_near(ray(Y), 0) )  || are_near(from, to) )
    {
        result.push_back(from);
        return result;
    }
    else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
    {
        LineSegment seg(pointAt(from), pointAt(to));
        Point np = seg.pointAt( seg.nearestPoint(p) );
        if ( are_near(ray(Y), 0) )
        {
            if ( are_near(_rot_angle, M_PI/2)
                 || are_near(_rot_angle, 3*M_PI/2) )
            {
                result = roots(np[Y], Y);
            }
            else
            {
                result = roots(np[X], X);
            }
        }
        else
        {
            if ( are_near(_rot_angle, M_PI/2)
                 || are_near(_rot_angle, 3*M_PI/2) )
            {
                result = roots(np[X], X);
            }
            else
            {
                result = roots(np[Y], Y);
            }
        }
        return result;
    }
    else if ( are_near(ray(X), ray(Y)) )
    {
        Point r = p - center();
        if ( are_near(r, Point(0,0)) )
        {
            THROW_INFINITESOLUTIONS(0);
        }
        // TODO: implement case r != 0
//      Point np = ray(X) * unit_vector(r);
//      std::vector<double> solX = roots(np[X],X);
//      std::vector<double> solY = roots(np[Y],Y);
//      double t;
//      if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1]))
//      {
//          t = solX[0];
//      }
//      else
//      {
//          t = solX[1];
//      }
//      if ( !(t < from || t > to) )
//      {
//          result.push_back(t);
//      }
//      else
//      {
//
//      }
    }

    // solve the equation <D(E(t),t)|E(t)-p> == 0
    // that provides min and max distance points
    // on the ellipse E wrt the point p
    // after the substitutions:
    // cos(t) = (1 - s^2) / (1 + s^2)
    // sin(t) = 2t / (1 + s^2)
    // where s = tan(t/2)
    // we get a 4th degree equation in s
    /*
     *  ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) +
     *  ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) +
     *  2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) +
     *  2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi])
     */

    Point p_c = p - center();
    double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y));
    double sinrot, cosrot;
    sincos(_rot_angle, sinrot, cosrot);
    double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot);
    Poly coeff;
    coeff.resize(5);
    coeff[4] = ray(Y) * ( p_c[Y] * cosrot - p_c[X] * sinrot );
    coeff[3] = 2 * ( rx2_ry2 + expr1 );
    coeff[2] = 0;
    coeff[1] = 2 * ( -rx2_ry2 + expr1 );
    coeff[0] = -coeff[4];

//  for ( unsigned int i = 0; i < 5; ++i )
//      std::cerr << "c[" << i << "] = " << coeff[i] << std::endl;

    std::vector<double> real_sol;
    // gsl_poly_complex_solve raises an error
    // if the leading coefficient is zero
    if ( are_near(coeff[4], 0) )
    {
        real_sol.push_back(0);
        if ( !are_near(coeff[3], 0) )
        {
            double sq = -coeff[1] / coeff[3];
            if ( sq > 0 )
            {
                double s = std::sqrt(sq);
                real_sol.push_back(s);
                real_sol.push_back(-s);
            }
        }
    }
    else
    {
        real_sol = solve_reals(coeff);
    }

    for ( unsigned int i = 0; i < real_sol.size(); ++i )
    {
        real_sol[i] = 2 * std::atan(real_sol[i]);
        if ( real_sol[i] < 0 ) real_sol[i] += 2*M_PI;
    }
    // when s -> Infinity then <D(E)|E-p> -> 0 iff coeff[4] == 0
    // so we add M_PI to the solutions being lim arctan(s) = PI when s->Infinity
    if ( (real_sol.size() % 2) != 0 )
    {
        real_sol.push_back(M_PI);
    }

    double mindistsq1 = std::numeric_limits<double>::max();
    double mindistsq2 = std::numeric_limits<double>::max();
    double dsq;
    unsigned int mi1, mi2;
    for ( unsigned int i = 0; i < real_sol.size(); ++i )
    {
        dsq = distanceSq(p, pointAtAngle(real_sol[i]));
        if ( mindistsq1 > dsq )
        {
            mindistsq2 = mindistsq1;
            mi2 = mi1;
            mindistsq1 = dsq;
            mi1 = i;
        }
        else if ( mindistsq2 > dsq )
        {
            mindistsq2 = dsq;
            mi2 = i;
        }
    }

    double t = map_to_01( real_sol[mi1] );
    if ( !(t < from || t > to) )
    {
        result.push_back(t);
    }

    bool second_sol = false;
    t = map_to_01( real_sol[mi2] );
    if ( real_sol.size() == 4 && !(t < from || t > to) )
    {
        if ( result.empty() || are_near(mindistsq1, mindistsq2) )
        {
            result.push_back(t);
            second_sol = true;
        }
    }

    // we need to test extreme points too
    double dsq1 = distanceSq(p, pointAt(from));
    double dsq2 = distanceSq(p, pointAt(to));
    if ( second_sol )
    {
        if ( mindistsq2 > dsq1 )
        {
            result.clear();
            result.push_back(from);
            mindistsq2 = dsq1;
        }
        else if ( are_near(mindistsq2, dsq) )
        {
            result.push_back(from);
        }
        if ( mindistsq2 > dsq2 )
        {
            result.clear();
            result.push_back(to);
        }
        else if ( are_near(mindistsq2, dsq2) )
        {
            result.push_back(to);
        }

    }
    else
    {
        if ( result.empty() )
        {
            if ( are_near(dsq1, dsq2) )
            {
                result.push_back(from);
                result.push_back(to);
            }
            else if ( dsq2 > dsq1 )
            {
                result.push_back(from);
            }
            else
            {
                result.push_back(to);
            }
        }
    }

    return result;
}
Esempio n. 14
0
/** @brief Check whether this matrix represents pure uniform scaling.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           a_1 & 0 & 0 \\
           0 & a_2 & 0 \\
           0 & 0 & 1 \end{array}\right]\f$ where \f$|a_1| = |a_2|\f$. */
bool Affine::isUniformScale(Coord eps) const {
    if (isSingular(eps)) return false;
    return are_near(fabs(_c[0]), fabs(_c[3]), eps) &&
           are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&  
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
Esempio n. 15
0
/** @brief Check whether this matrix represents zooming.
 * Zooming is any combination of translation and uniform non-flipping scaling.
 * It preserves angles, ratios of distances between arbitrary points
 * and unit vectors of line segments.
 * @param eps Numerical tolerance
 * @return True iff the matrix is invertible and of the form
 *         \f$\left[\begin{array}{ccc}
           a & 0 & 0 \\
           0 & a & 0 \\
           b & c & 1 \end{array}\right]\f$. */
bool Affine::isZoom(Coord eps) const {
    if (isSingular(eps)) return false;
    return are_near(_c[0], _c[3], eps) && are_near(_c[1], 0, eps) && are_near(_c[2], 0, eps);
}
Esempio n. 16
0
/** @brief Check whether this matrix represents a pure nonzero translation.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           1 & 0 & 0 \\
           0 & 1 & 0 \\
           a & b & 1 \end{array}\right]\f$ and \f$a, b \neq 0\f$ */
bool Affine::isNonzeroTranslation(Coord eps) const {
    return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
           are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
           (!are_near(_c[4], 0.0, eps) || !are_near(_c[5], 0.0, eps));
}
Esempio n. 17
0
/** @brief Check whether this matrix represents a pure, nonzero rotation.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           a & b & 0 \\
           -b & a & 0 \\
           0 & 0 & 1 \end{array}\right]\f$, \f$a^2 + b^2 = 1\f$ and \f$a \neq 1\f$. */
bool Affine::isNonzeroRotation(Coord eps) const {
    return !are_near(_c[0], 1.0, eps) &&
           are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps) &&
           are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
}
Esempio n. 18
0
/** @brief Check whether this matrix represents a pure translation.
 * Will return true for the identity matrix, which represents a zero translation.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           1 & 0 & 0 \\
           0 & 1 & 0 \\
           a & b & 1 \end{array}\right]\f$ */
bool Affine::isTranslation(Coord eps) const {
    return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
           are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps);
}
Esempio n. 19
0
/** @brief Check whether the transformation preserves distances between points.
 * This means that the transformation can be any combination of translation,
 * rotation and flipping.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           a & b & 0 \\
           -b & a & 0 \\
           c & d & 1 \end{array}\right]\f$ or
           \f$\left[\begin{array}{ccc}
           -a & b & 0 \\
           b & a & 0 \\
           c & d & 1 \end{array}\right]\f$ and \f$a^2 + b^2 = 1\f$. */
bool Affine::preservesDistances(Coord eps) const
{
    return ((are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) ||
            (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps))) &&
           are_near(_c[0] * _c[0] + _c[1] * _c[1], 1.0, eps);
}
Esempio n. 20
0
/** @brief Check whether the transformation preserves angles between lines.
 * This means that the transformation can be any combination of translation, uniform scaling,
 * rotation and flipping.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
             a & b & 0 \\
             -b & a & 0 \\
             c & d & 1 \end{array}\right]\f$ or
           \f$\left[\begin{array}{ccc}
             -a & b & 0 \\
             b & a & 0 \\
             c & d & 1 \end{array}\right]\f$. */
bool Affine::preservesAngles(Coord eps) const
{
    if (isSingular(eps)) return false;
    return (are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) ||
           (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps));
}
Esempio n. 21
0
/** @brief Check whether the transformation preserves areas of polygons.
 * This means that the transformation can be any combination of translation, rotation,
 * shearing and squeezing (non-uniform scaling such that the absolute value of the product
 * of Y-scale and X-scale is 1).
 * @param eps Numerical tolerance
 * @return True iff \f$|\det A| = 1\f$. */
bool Affine::preservesArea(Coord eps) const
{
    return are_near(descrim2(), 1.0, eps);
}
Esempio n. 22
0
TEST(AffineTest, Nearness) {
    Affine a1(1, 0, 1, 2, 1e-8, 1e-8);
    Affine a2(1+1e-8, 0, 1, 2-1e-8, -1e-8, -1e-8);
    EXPECT_TRUE(are_near(a1, a2, 1e-7));
    EXPECT_FALSE(are_near(a1, a2, 1e-9));
}
Esempio n. 23
0
void
LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result)
{
    if(steps < 1) {
        return;
    }
    Geom::PathVector tmp_path;
    Geom::CubicBezier const *cubic = NULL;
    for (Geom::PathVector::iterator path_it = result.begin(); path_it != result.end(); ++path_it) {
        if (path_it->empty()) {
            continue;
        }

        Geom::Path::iterator curve_it1 = path_it->begin(); // incoming curve
        Geom::Path::iterator curve_it2 = ++(path_it->begin());// outgoing curve
        Geom::Path::iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
        SPCurve *nCurve = new SPCurve();
        if (path_it->closed()) {
            // if the path is closed, maybe we have to stop a bit earlier because the
            // closing line segment has zerolength.
            const Geom::Curve &closingline =
                path_it->back_closed(); // the closing line segment is always of type
            // Geom::LineSegment.
            if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
                // closingline.isDegenerate() did not work, because it only checks for
                // *exact* zero length, which goes wrong for relative coordinates and
                // rounding errors...
                // the closing line segment has zero-length. So stop before that one!
                curve_endit = path_it->end_open();
            }
        }
        if(helper_size > 0) {
            drawNode(curve_it1->initialPoint());
        }
        nCurve->moveto(curve_it1->initialPoint());
        Geom::Point start = Geom::Point(0,0);
        while (curve_it1 != curve_endit) {
            cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
            Geom::Point point_at1 = curve_it1->initialPoint();
            Geom::Point point_at2 = curve_it1->finalPoint();
            Geom::Point point_at3 = curve_it1->finalPoint();
            Geom::Point point_at4 = curve_it1->finalPoint();

            if(start == Geom::Point(0,0)) {
                start = point_at1;
            }

            if (cubic) {
                point_at1 = (*cubic)[1];
                point_at2 = (*cubic)[2];
            }

            if(path_it->closed() && curve_it2 == curve_endit) {
                point_at4 = start;
            }
            if(curve_it2 != curve_endit) {
                cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2);
                if (cubic) {
                    point_at4 = (*cubic)[1];
                }
            }
            Geom::Ray ray1(point_at2, point_at3);
            Geom::Ray ray2(point_at3, point_at4);
            double angle1 = Geom::deg_from_rad(ray1.angle());
            double angle2 = Geom::deg_from_rad(ray2.angle());
            if((smooth_angles  >= std::abs(angle2 - angle1)) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) {
                double dist = Geom::distance(point_at2,point_at3);
                Geom::Angle angleFixed = ray2.angle();
                angleFixed -= Geom::Angle::from_degrees(180.0);
                point_at2 =  Geom::Point::polar(angleFixed, dist) + point_at3;
            }
            nCurve->curveto(point_at1, point_at2, curve_it1->finalPoint());
            cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment());
            if (cubic) {
                point_at1 = (*cubic)[1];
                point_at2 = (*cubic)[2];
                if(helper_size > 0) {
                    if(!are_near((*cubic)[0],(*cubic)[1])) {
                        drawHandle((*cubic)[1]);
                        drawHandleLine((*cubic)[0],(*cubic)[1]);
                    }
                    if(!are_near((*cubic)[3],(*cubic)[2])) {
                        drawHandle((*cubic)[2]);
                        drawHandleLine((*cubic)[3],(*cubic)[2]);
                    }
                }
            }
            if(helper_size > 0) {
                drawNode(curve_it1->finalPoint());
            }
            ++curve_it1;
            ++curve_it2;
        }
        if (path_it->closed()) {
            nCurve->closepath_current();
        }
        tmp_path.push_back(nCurve->get_pathvector()[0]);
        nCurve->reset();
        delete nCurve;
    }
    result = tmp_path;
}
Esempio n. 24
0
TEST(AffineTest, Classification) {
    {
        Affine a; // identity
        EXPECT_TRUE(a.isIdentity());
        EXPECT_TRUE(a.isTranslation());
        EXPECT_TRUE(a.isScale());
        EXPECT_TRUE(a.isUniformScale());
        EXPECT_TRUE(a.isRotation());
        EXPECT_TRUE(a.isHShear());
        EXPECT_TRUE(a.isVShear());
        EXPECT_TRUE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_TRUE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Translate(10, 15); // pure translation
        EXPECT_FALSE(a.isIdentity());
        EXPECT_TRUE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_TRUE(a.isZoom());
        EXPECT_TRUE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_TRUE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Scale(-1.0, 1.0); // flip on the X axis
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_TRUE(a.isScale());
        EXPECT_TRUE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom()); // zoom must be non-flipping
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_TRUE(a.isNonzeroScale());
        EXPECT_TRUE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_TRUE(a.preservesDistances());
        EXPECT_TRUE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Scale(0.5, 0.5); // pure uniform scale
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_TRUE(a.isScale());
        EXPECT_TRUE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_TRUE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_TRUE(a.isNonzeroScale());
        EXPECT_TRUE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_FALSE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Scale(0.5, -0.5); // pure uniform flipping scale
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_TRUE(a.isScale());
        EXPECT_TRUE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom()); // zoom must be non-flipping
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_TRUE(a.isNonzeroScale());
        EXPECT_TRUE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_FALSE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_TRUE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Scale(0.5, 0.7); // pure non-uniform scale
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_TRUE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_TRUE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_FALSE(a.preservesArea());
        EXPECT_FALSE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Scale(0.5, 2.0); // "squeeze" transform (non-uniform scale with det=1)
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_TRUE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_TRUE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_FALSE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Rotate(0.7); // pure rotation
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_TRUE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_TRUE(a.isNonzeroRotation());
        EXPECT_TRUE(a.isNonzeroNonpureRotation());
        EXPECT_EQ(a.rotationCenter(), Point(0.0,0.0));
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_TRUE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Point rotation_center(1.23,4.56);
        Affine a = Translate(-rotation_center) * Rotate(0.7) * Translate(rotation_center); // rotation around (1.23,4.56)
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_TRUE(a.isNonzeroNonpureRotation());
        EXPECT_TRUE(are_near(a.rotationCenter(), rotation_center, 1e-7));
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_TRUE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = HShear(0.5); // pure horizontal shear
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_TRUE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_TRUE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_FALSE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = VShear(0.5); // pure vertical shear
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_TRUE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_TRUE(a.isNonzeroVShear());
        EXPECT_TRUE(a.preservesArea());
        EXPECT_FALSE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
    }
    {
        Affine a = Zoom(3.0, Translate(10, 15)); // zoom
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_TRUE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_FALSE(a.preservesArea());
        EXPECT_TRUE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_FALSE(a.isSingular());
        
        EXPECT_TRUE(a.withoutTranslation().isUniformScale());
        EXPECT_TRUE(a.withoutTranslation().isNonzeroUniformScale());
    }
    {
        Affine a(0, 0, 0, 0, 0, 0); // zero matrix (singular)
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_FALSE(a.preservesArea());
        EXPECT_FALSE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_TRUE(a.isSingular());
    }
    {
        Affine a(0, 1, 0, 1, 10, 10); // another singular matrix
        EXPECT_FALSE(a.isIdentity());
        EXPECT_FALSE(a.isTranslation());
        EXPECT_FALSE(a.isScale());
        EXPECT_FALSE(a.isUniformScale());
        EXPECT_FALSE(a.isRotation());
        EXPECT_FALSE(a.isHShear());
        EXPECT_FALSE(a.isVShear());
        EXPECT_FALSE(a.isZoom());
        EXPECT_FALSE(a.isNonzeroTranslation());
        EXPECT_FALSE(a.isNonzeroScale());
        EXPECT_FALSE(a.isNonzeroUniformScale());
        EXPECT_FALSE(a.isNonzeroRotation());
        EXPECT_FALSE(a.isNonzeroNonpureRotation());
        EXPECT_FALSE(a.isNonzeroHShear());
        EXPECT_FALSE(a.isNonzeroVShear());
        EXPECT_FALSE(a.preservesArea());
        EXPECT_FALSE(a.preservesAngles());
        EXPECT_FALSE(a.preservesDistances());
        EXPECT_FALSE(a.flips());
        EXPECT_TRUE(a.isSingular());
    }
}
Esempio n. 25
0
std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
{
    std::vector<Coord> sol;

    if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) {
        if ( center(d) == v )
            sol.push_back(0);
        return sol;
    }

    static const char* msg[2][2] =
    {
        { "d == X; ray(X) == 0; "
          "s = (v - center(X)) / ( -ray(Y) * std::sin(_rot_angle) ); "
          "s should be contained in [-1,1]",
          "d == X; ray(Y) == 0; "
          "s = (v - center(X)) / ( ray(X) * std::cos(_rot_angle) ); "
          "s should be contained in [-1,1]"
        },
        { "d == Y; ray(X) == 0; "
          "s = (v - center(X)) / ( ray(Y) * std::cos(_rot_angle) ); "
          "s should be contained in [-1,1]",
          "d == Y; ray(Y) == 0; "
          "s = (v - center(X)) / ( ray(X) * std::sin(_rot_angle) ); "
          "s should be contained in [-1,1]"
        },
    };

    for ( unsigned int dim = 0; dim < 2; ++dim )
    {
        if ( are_near(ray((Dim2) dim), 0) )
        {
            if ( initialPoint()[d] == v && finalPoint()[d] == v )
            {
                THROW_INFINITESOLUTIONS(0);
            }
            if ( (initialPoint()[d] < finalPoint()[d])
                 && (initialPoint()[d] > v || finalPoint()[d] < v) )
            {
                return sol;
            }
            if ( (initialPoint()[d] > finalPoint()[d])
                 && (finalPoint()[d] > v || initialPoint()[d] < v) )
            {
                return sol;
            }
            double ray_prj = 0.0;
            switch(d)
            {
                case X:
                    switch(dim)
                    {
                        case X: ray_prj = -ray(Y) * std::sin(_rot_angle);
                                break;
                        case Y: ray_prj = ray(X) * std::cos(_rot_angle);
                                break;
                    }
                    break;
                case Y:
                    switch(dim)
                    {
                        case X: ray_prj = ray(Y) * std::cos(_rot_angle);
                                break;
                        case Y: ray_prj = ray(X) * std::sin(_rot_angle);
                                break;
                    }
                    break;
            }

            double s = (v - center(d)) / ray_prj;
            if ( s < -1 || s > 1 )
            {
                THROW_LOGICALERROR(msg[d][dim]);
            }
            switch(dim)
            {
                case X:
                    s = std::asin(s); // return a value in [-PI/2,PI/2]
                    if ( logical_xor( _sweep, are_near(initialAngle(), M_PI/2) )  )
                    {
                        if ( s < 0 ) s += 2*M_PI;
                    }
                    else
                    {
                        s = M_PI - s;
                        if (!(s < 2*M_PI) ) s -= 2*M_PI;
                    }
                    break;
                case Y:
                    s = std::acos(s); // return a value in [0,PI]
                    if ( logical_xor( _sweep, are_near(initialAngle(), 0) ) )
                    {
                        s = 2*M_PI - s;
                        if ( !(s < 2*M_PI) ) s -= 2*M_PI;
                    }
                    break;
            }

            //std::cerr << "s = " << rad_to_deg(s);
            s = map_to_01(s);
            //std::cerr << " -> t: " << s << std::endl;
            if ( !(s < 0 || s > 1) )
                sol.push_back(s);
            return sol;
        }
    }

    double rotx, roty;
    sincos(_rot_angle, roty, rotx);
    if (d == X) roty = -roty;

    double rxrotx = ray(X) * rotx;
    double c_v = center(d) - v;

    double a = -rxrotx + c_v;
    double b = ray(Y) * roty;
    double c = rxrotx + c_v;
    //std::cerr << "a = " << a << std::endl;
    //std::cerr << "b = " << b << std::endl;
    //std::cerr << "c = " << c << std::endl;

    if ( are_near(a,0) )
    {
        sol.push_back(M_PI);
        if ( !are_near(b,0) )
        {
            double s = 2 * std::atan(-c/(2*b));
            if ( s < 0 ) s += 2*M_PI;
            sol.push_back(s);
        }
    }
    else
    {
        double delta = b * b - a * c;
        //std::cerr << "delta = " << delta << std::endl;
        if ( are_near(delta, 0) )
        {
            double s = 2 * std::atan(-b/a);
            if ( s < 0 ) s += 2*M_PI;
            sol.push_back(s);
        }
        else if ( delta > 0 )
        {
            double sq = std::sqrt(delta);
            double s = 2 * std::atan( (-b - sq) / a );
            if ( s < 0 ) s += 2*M_PI;
            sol.push_back(s);
            s = 2 * std::atan( (-b + sq) / a );
            if ( s < 0 ) s += 2*M_PI;
            sol.push_back(s);
        }
    }

    std::vector<double> arc_sol;
    for (unsigned int i = 0; i < sol.size(); ++i )
    {
        //std::cerr << "s = " << rad_to_deg(sol[i]);
        sol[i] = map_to_01(sol[i]);
        //std::cerr << " -> t: " << sol[i] << std::endl;
        if ( !(sol[i] < 0 || sol[i] > 1) )
            arc_sol.push_back(sol[i]);
    }
    return arc_sol;
}
Esempio n. 26
0
/** @brief Check whether this matrix is singular.
 * Singular matrices have no inverse, which means that applying them to a set of points
 * results in a loss of information.
 * @param eps Numerical tolerance
 * @return True iff the determinant is near zero. */
bool Affine::isSingular(Coord eps) const {
    return are_near(det(), 0.0, eps);
}
Esempio n. 27
0
/** @brief Check whether this matrix represents pure, nonzero vertical shearing.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           1 & k & 0 \\
           0 & 1 & 0 \\
           0 & 0 & 1 \end{array}\right]\f$ and \f$k \neq 0\f$. */
bool Affine::isNonzeroVShear(Coord eps) const {
    return are_near(_c[0], 1.0, eps) && !are_near(_c[1], 0.0, eps) &&
           are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
Esempio n. 28
0
/** @brief Nearness predicate for affine transforms
 * @returns True if all entries of matrices are within eps of each other */
bool are_near(Affine const &a, Affine const &b, Coord eps)
{
    return are_near(a[0], b[0], eps) && are_near(a[1], b[1], eps) &&
           are_near(a[2], b[2], eps) && are_near(a[3], b[3], eps) &&
           are_near(a[4], b[4], eps) && are_near(a[5], b[5], eps);
}
Esempio n. 29
0
/** @brief Check whether this matrix is an identity matrix.
 * @param eps Numerical tolerance
 * @return True iff the matrix is of the form
 *         \f$\left[\begin{array}{ccc}
           1 & 0 & 0 \\
           0 & 1 & 0 \\
           0 & 0 & 1 \end{array}\right]\f$ */
bool Affine::isIdentity(Coord eps) const {
    return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
           are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
           are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}