Beispiel #1
0
void Path::appendPortionTo(Path &ret, double from, double to) const {
  if (!(from >= 0 && to >= 0)) {
    THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo");
  }
  if(to == 0) to = size()+0.999999;
  if(from == to) { return; }
  double fi, ti;
  double ff = modf(from, &fi), tf = modf(to, &ti);
  if(tf == 0) { ti--; tf = 1; }
  const_iterator fromi = inc(begin(), (unsigned)fi);
  if(fi == ti && from < to) {
    Curve *v = fromi->portion(ff, tf);
    ret.append(*v, STITCH_DISCONTINUOUS);
    delete v;
    return;
  }
  const_iterator toi = inc(begin(), (unsigned)ti);
  if(ff != 1.) {
    Curve *fromv = fromi->portion(ff, 1.);
    //fromv->setInitial(ret.finalPoint());
    ret.append(*fromv, STITCH_DISCONTINUOUS);
    delete fromv;
  }
  if(from >= to) {
    const_iterator ender = end();
    if(ender->initialPoint() == ender->finalPoint()) ++ender;
    ret.insert(ret.end(), ++fromi, ender, STITCH_DISCONTINUOUS);
    ret.insert(ret.end(), begin(), toi, STITCH_DISCONTINUOUS);
  } else {
    ret.insert(ret.end(), ++fromi, toi, STITCH_DISCONTINUOUS);
  }
  Curve *tov = toi->portion(0., tf);
  ret.append(*tov, STITCH_DISCONTINUOUS);
  delete tov;
}
/** Changes the basis of p to be bernstein.
 \param p the Symmetric basis polynomial
 \returns the Bernstein basis polynomial

 if the degree is even q is the order in the symmetrical power basis,
 if the degree is odd q is the order + 1
 n is always the polynomial degree, i. e. the Bezier order
 sz is the number of bezier handles.
*/
void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
{
    if (sb.size() == 0) {
        THROW_RANGEERROR("size of sb is too small");
    }

    size_t q, n;
    bool even;
    if (sz == 0)
    {
        q = sb.size();
        if (sb[q-1][0] == sb[q-1][1])
        {
            even = true;
            --q;
            n = 2*q;
        }
        else
        {
            even = false;
            n = 2*q-1;
        }
    }
    else
    {
        q = (sz > 2*sb.size()-1) ?  sb.size() : (sz+1)/2;
        n = sz-1;
        even = false;
    }
    bz.clear();
    bz.resize(n+1);
    double Tjk;
    for (size_t k = 0; k < q; ++k)
    {
        for (size_t j = k; j < n-k; ++j) // j <= n-k-1
        {
            Tjk = binomial(n-2*k-1, j-k);
            bz[j] += (Tjk * sb[k][0]);
            bz[n-j] += (Tjk * sb[k][1]); // n-k <-> [k][1]
        }
    }
    if (even)
    {
        bz[q] += sb[q][0];
    }
    // the resulting coefficients are with respect to the scaled Bernstein
    // basis so we need to divide them by (n, j) binomial coefficient
    for (size_t j = 1; j < n; ++j)
    {
        bz[j] /= binomial(n, j);
    }
    bz[0] = sb[0][0];
    bz[n] = sb[0][1];
}
Beispiel #3
0
double Path::nearestPoint(Point const &_point, double from, double to, double *distance_squared) const
{
	if ( from > to ) std::swap(from, to);
	const Path& _path = *this;
	unsigned int sz = _path.size();
	if ( _path.closed() ) ++sz;
	if ( from < 0 || to > sz )
	{
		THROW_RANGEERROR("[from,to] interval out of bounds");
	}
	double sif, st = modf(from, &sif);
	double eif, et = modf(to, &eif);
	unsigned int si = static_cast<unsigned int>(sif);
	unsigned int ei = static_cast<unsigned int>(eif);
        if(sz == 0) {// naked moveto
            if (distance_squared != NULL)
                *distance_squared = distanceSq(_point, _path.initialPoint());
            return 0;
        }
	if ( si == sz )
	{
		--si;
		st = 1;
	}
	if ( ei == sz )
	{
		--ei;
		et = 1;
	}
	if ( si == ei )
	{
		double nearest = _path[si].nearestPoint(_point, st, et);
		if (distance_squared != NULL)
		    *distance_squared = distanceSq(_point, _path[si].pointAt(nearest));
		return si + nearest;
	}

	double t;
	double nearest = _path[si].nearestPoint(_point, st);
	unsigned int ni = si;
	double dsq;
	double mindistsq = distanceSq(_point, _path[si].pointAt(nearest));
	for ( unsigned int i = si + 1; i < ei; ++i )
	{
            Rect bb = (_path[i].boundsFast());
		dsq = distanceSq(_point, bb);
		if ( mindistsq <= dsq ) continue;
		t = _path[i].nearestPoint(_point);
		dsq = distanceSq(_point, _path[i].pointAt(t));
		if ( mindistsq > dsq )
		{
			nearest = t;
			ni = i;
			mindistsq = dsq;
		}
	}
	Rect bb = (_path[ei].boundsFast());
	dsq = distanceSq(_point, bb);
	if ( mindistsq > dsq )
	{
		t = _path[ei].nearestPoint(_point, 0, et);
		dsq = distanceSq(_point, _path[ei].pointAt(t));
		if ( mindistsq > dsq )
		{
			nearest = t;
			ni = ei;
			mindistsq = dsq;
		}
	}

    if (distance_squared != NULL)
        *distance_squared = mindistsq;

	return ni + nearest;
}
Beispiel #4
0
std::vector<double>
Path::allNearestPoints(Point const& _point, double from, double to) const
{
	if ( from > to ) std::swap(from, to);
	const Path& _path = *this;
	unsigned int sz = _path.size();
	if ( _path.closed() ) ++sz;
	if ( from < 0 || to > sz )
	{
		THROW_RANGEERROR("[from,to] interval out of bounds");
	}
	double sif, st = modf(from, &sif);
	double eif, et = modf(to, &eif);
	unsigned int si = static_cast<unsigned int>(sif);
	unsigned int ei = static_cast<unsigned int>(eif);
	if ( si == sz )
	{
		--si;
		st = 1;
	}
	if ( ei == sz )
	{
		--ei;
		et = 1;
	}
	if ( si == ei )
	{
		std::vector<double>	all_nearest =
			_path[si].allNearestPoints(_point, st, et);
		for ( unsigned int i = 0; i < all_nearest.size(); ++i )
		{
			all_nearest[i] = si + all_nearest[i];
		}
		return all_nearest;
	}
	std::vector<double> all_t;
	std::vector< std::vector<double> > all_np;
	all_np.push_back( _path[si].allNearestPoints(_point, st) );
	std::vector<unsigned int> ni;
	ni.push_back(si);
	double dsq;
	double mindistsq
		= distanceSq( _point, _path[si].pointAt( all_np.front().front() ) );
	Rect bb(Geom::Point(0,0),Geom::Point(0,0));
	for ( unsigned int i = si + 1; i < ei; ++i )
	{
		bb = (_path[i].boundsFast());
		dsq = distanceSq(_point, bb);
		if ( mindistsq < dsq ) continue;
		all_t = _path[i].allNearestPoints(_point);
		dsq = distanceSq( _point, _path[i].pointAt( all_t.front() ) );
		if ( mindistsq > dsq )
		{
			all_np.clear();
			all_np.push_back(all_t);
			ni.clear();
			ni.push_back(i);
			mindistsq = dsq;
		}
		else if ( mindistsq == dsq )
		{
			all_np.push_back(all_t);
			ni.push_back(i);
		}
	}
	bb = (_path[ei].boundsFast());
	dsq = distanceSq(_point, bb);
	if ( mindistsq >= dsq )
	{
		all_t = _path[ei].allNearestPoints(_point, 0, et);
		dsq = distanceSq( _point, _path[ei].pointAt( all_t.front() ) );
		if ( mindistsq > dsq )
		{
			for ( unsigned int i = 0; i < all_t.size(); ++i )
			{
				all_t[i] = ei + all_t[i];
			}
			return all_t;
		}
		else if ( mindistsq == dsq )
		{
			all_np.push_back(all_t);
			ni.push_back(ei);
		}
	}
	std::vector<double> all_nearest;
	for ( unsigned int i = 0; i < all_np.size(); ++i )
	{
		for ( unsigned int j = 0; j < all_np[i].size(); ++j )
		{
			all_nearest.push_back( ni[i] + all_np[i][j] );
		}
	}
	all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()),
	                  all_nearest.end());
	return all_nearest;
}
/** Changes the basis of p to be Bernstein.
 \param p the D2 Symmetric basis polynomial
 \returns the D2 Bernstein basis cubic polynomial

Bezier is always cubic.
For general asymmetric case, fit the SBasis function value at midpoint
For parallel, symmetric case, find the point of closest approach to the midpoint
For parallel, anti-symmetric case, fit the SBasis slope at midpoint
*/
void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb)
{
    double delx[2], dely[2];
    double xprime[2], yprime[2];
    double midx = 0;
    double midy = 0;
    double numer;
    double denom;
    double div;

    if ((sb[X].size() == 0) || (sb[Y].size() == 0)) {
        THROW_RANGEERROR("size of sb is too small");
    }

    bz.resize(4, Point(0,0));
    bz[0][X] = sb[X][0][0];
    bz[0][Y] = sb[Y][0][0];
    bz[3][X] = sb[X][0][1];
    bz[3][Y] = sb[Y][0][1];

//  calculate first derivatives of x and y wrt t

    for (int i = 0; i < 2; ++i) {
        xprime[i] = sb[X][0][1] - sb[X][0][0];
        yprime[i] = sb[Y][0][1] - sb[Y][0][0];
    }
    if (sb[X].size() > 0) {
        xprime[0] += sb[X][1][0];
        xprime[1] -= sb[X][1][1];
    }
    if (sb[Y].size() > 0) {
        yprime[0] += sb[Y][1][0];
        yprime[1] -= sb[Y][1][1];
    }

//  calculate midpoint at t = 0.5

    div = 2;
    for (size_t i = 0; i < sb[X].size(); ++i) {
        midx += (sb[X][i][0] + sb[X][i][1])/div;
        div *= 4;
    }
    midx = 8*midx - 4*bz[0][X] - 4*bz[3][X];

    div = 2;
    for (size_t i = 0; i < sb[Y].size(); ++i) {
        midy += (sb[Y][i][0] + sb[Y][i][1])/div;
        div *= 4;
    }
    midy = 8*midy - 4*bz[0][Y] - 4*bz[3][Y];

//  calculate Bezier control arms

    if ((std::abs(xprime[0]) < EPSILON) && (std::abs(yprime[0]) < EPSILON)
    && ((std::abs(xprime[1]) > EPSILON) || (std::abs(yprime[1]) > EPSILON)))  { // degenerate handle at 0 : use distance of closest approach
        numer = midx*xprime[1] + midy*yprime[1];
        denom = 3.0*(xprime[1]*xprime[1] + yprime[1]*yprime[1]);
        delx[0] = 0;
        dely[0] = 0;
        delx[1] = -xprime[1]*numer/denom;
        dely[1] = -yprime[1]*numer/denom;
    } else if ((std::abs(xprime[1]) < EPSILON) && (std::abs(yprime[1]) < EPSILON)
           && ((std::abs(xprime[0]) > EPSILON) || (std::abs(yprime[0]) > EPSILON)))  { // degenerate handle at 1 : ditto
        numer = midx*xprime[0] + midy*yprime[0];
        denom = 3.0*(xprime[0]*xprime[0] + yprime[0]*yprime[0]);
        delx[0] = xprime[0]*numer/denom;
        dely[0] = yprime[0]*numer/denom;
        delx[1] = 0;
        dely[1] = 0;
    } else if (std::abs(xprime[1]*yprime[0] - yprime[1]*xprime[0]) > EPSILON) { // general case : fit mid fxn value
        denom = xprime[1]*yprime[0] - yprime[1]*xprime[0];
        for (int i = 0; i < 2; ++i) {
            numer = xprime[1 - i]*midy - yprime[1 - i]*midx;
            delx[i] = xprime[i]*numer/denom/3;
            dely[i] = yprime[i]*numer/denom/3;
        }
    } else if ((xprime[0]*xprime[1] < 0) || (yprime[0]*yprime[1] < 0)) { // symmetric case : use distance of closest approach
        numer = midx*xprime[0] + midy*yprime[0];
        denom = 6.0*(xprime[0]*xprime[0] + yprime[0]*yprime[0]);
        delx[0] = xprime[0]*numer/denom;
        dely[0] = yprime[0]*numer/denom;
        delx[1] = -delx[0];
        dely[1] = -dely[0];
    } else {                                    // anti-symmetric case : fit mid slope
                                                // calculate slope at t = 0.5
        midx = 0;
        div = 1;
        for (size_t i = 0; i < sb[X].size(); ++i) {
            midx += (sb[X][i][1] - sb[X][i][0])/div;
            div *= 4;
        }
        midy = 0;
        div = 1;
        for (size_t i = 0; i < sb[Y].size(); ++i) {
            midy += (sb[Y][i][1] - sb[Y][i][0])/div;
            div *= 4;
        }
        if (midx*yprime[0] != midy*xprime[0]) {
            denom = midx*yprime[0] - midy*xprime[0];
            numer = midx*(bz[3][Y] - bz[0][Y]) - midy*(bz[3][X] - bz[0][X]);
            for (int i = 0; i < 2; ++i) {
                delx[i] = xprime[0]*numer/denom;
                dely[i] = yprime[0]*numer/denom;
            }
        } else {                                // linear case
            for (int i = 0; i < 2; ++i) {
                delx[i] = (bz[3][X] - bz[0][X])/3;
                dely[i] = (bz[3][Y] - bz[0][Y])/3;
            }
        }
    }
    bz[1][X] = bz[0][X] + delx[0];
    bz[1][Y] = bz[0][Y] + dely[0];
    bz[2][X] = bz[3][X] - delx[1];
    bz[2][Y] = bz[3][Y] - dely[1];
}
Beispiel #6
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;
}
Beispiel #7
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;
}