bool areInYRange(const TQuadratic &q, double t0, double t1, int(&solIdx)[2]) const { assert(0.0 <= t0 && t0 <= 1.0), assert(0.0 <= t1 && t1 <= 1.0); const TPointD &p0 = q.getP0(), &p1 = q.getP1(), &p2 = q.getP2(); double der[2] = {y(p1) - y(p0), y(p0) - y(p1) + y(p2) - y(p1)}, s; double y0 = getY(q, t0), y1 = getY(q, t1); if (tcg::poly_ops::solve_1(der, &s, m_tol)) { if (t0 <= s && s < t1) { double ys = getY(q, s); solIdx[0] = (ys < m_y && m_y <= y0 || y0 <= m_y && m_y < ys) ? 0 : -1; solIdx[1] = (ys < m_y && m_y < y1 || y1 < m_y && m_y < ys) ? 1 : -1; } else if (t1 < s && s <= t0) { double ys = getY(q, s); solIdx[0] = (ys < m_y && m_y <= y0 || y0 <= m_y && m_y < ys) ? 1 : -1; solIdx[1] = (ys < m_y && m_y < y1 || y1 < m_y && m_y < ys) ? 0 : -1; } else { solIdx[0] = isInYRange(y0, y1) ? (t0 < s) ? 0 : 1 : -1; solIdx[1] = -1; } } else solIdx[1] = solIdx[0] = -1; return (solIdx[0] >= 0 || solIdx[1] >= 0); }
int leftScanlineIntersections(const TQuadratic &q, double t0, double t1, bool &ascending) { const TPointD &p0 = q.getP0(), &p1 = q.getP1(), &p2 = q.getP2(); double y1_y0 = y(p1) - y(p0), accel = y(p2) - y(p1) - y1_y0; // Fallback to segment case whenever we have too flat quads if (std::fabs(accel) < m_tol) return leftScanlineIntersections(TSegment(q.getPoint(t0), q.getPoint(t1)), ascending); // Calculate new ascension int ascends = isAscending(q, t1, t0 < t1); bool wasAscending = ascending; ascending = (ascends > 0) ? true : (ascends < 0) ? false : (wasAscending = !ascending, ascending); // Couples with the cusps check below // In case the y coords are not in range, quit int solIdx[2]; if (!areInYRange(q, t0, t1, solIdx)) return 0; // Identify coordinates for which q(t) == y double poly[3] = {y(p0) - m_y, 2.0 * y1_y0, accel}, s[2]; int sCount = tcg::poly_ops::solve_2(poly, s); // Tolerance dealt at the first bailout above if (sCount == 2) { // Calculate result int result = 0; if (solIdx[0] >= 0) { result += int(getX(q, s[solIdx[0]]) < m_x && (getY(q, t0) != m_y || ascending == wasAscending)); // Cusp check } if (solIdx[1] >= 0) result += int(getX(q, s[solIdx[1]]) < m_x); return result; } return (assert(sCount == 0), 0); // Should never happen, since m_y is in range. If it ever happens, // it must be close to the extremal - so quit with no intersections. }
void TQuadraticLengthEvaluator::setQuad(const TQuadratic &quad) { const TPointD &p0 = quad.getP0(); const TPointD &p1 = quad.getP1(); const TPointD &p2 = quad.getP2(); TPointD speed0(2.0 * (p1 - p0)); TPointD accel(2.0 * (p2 - p1) - speed0); double a = accel * accel; double b = 2.0 * accel * speed0; m_c = speed0 * speed0; m_constantSpeed = isAlmostZero(a); // => b isAlmostZero, too if (m_constantSpeed) { m_c = sqrt(m_c); return; } m_sqrt_a_div_2 = 0.5 * sqrt(a); m_noSpeed0 = isAlmostZero(m_c); // => b isAlmostZero, too if (m_noSpeed0) return; m_tRef = 0.5 * b / a; double d = m_c - 0.5 * b * m_tRef; m_squareIntegrand = (d < TConsts::epsilon); if (m_squareIntegrand) { m_f = (b > 0) ? -sq(m_tRef) : sq(m_tRef); return; } m_e = d / a; double sqrt_part = sqrt(sq(m_tRef) + m_e); double log_arg = m_tRef + sqrt_part; m_squareIntegrand = (log_arg < TConsts::epsilon); if (m_squareIntegrand) { m_f = (b > 0) ? -sq(m_tRef) : sq(m_tRef); return; } m_primitive_0 = m_sqrt_a_div_2 * (m_tRef * sqrt_part + m_e * log(log_arg)); }
double computeStep(const TQuadratic &quad, double pixelSize) { double step = 2; TPointD A = quad.getP0() - 2.0 * quad.getP1() + quad.getP2(); // 2*A is the acceleration of the curve double A_len = norm(A); /* A_len is equal to 2*norm(a) pixelSize will be 0.5*pixelSize now h is equal to sqrt( 8 * 0.5 * pixelSize / (2*norm(a)) ) = sqrt(2) * sqrt( pixelSize/A_len ) */ if (A_len > 0) step = sqrt(2 * pixelSize / A_len); return step; }
void makeOutline(const TStroke *stroke, int startQuad, int endQuad, outlineBoundary &ob, double error2) { //std::ofstream cout("c:\\temp\\outline.txt"); assert(stroke); assert(startQuad >= 0); assert(endQuad < stroke->getChunkCount()); assert(startQuad <= endQuad); TThickQuadratic *tq; std::vector<TQuadratic *> arrayUp, arrayDown; TQuadratic arc; if (!stroke->getChunkCount()) return; //if (startQuad==0) { const TThickQuadratic *tq = stroke->getChunk(startQuad); // trova i punti sul cerchio che corrispondono // a due fette di 90 gradi. // Ritorna una quadratica invece di tre singoli punti solo per compattezza. TQuadratic arc = getCircleQuarter(tq, QUARTER_BEGIN); // estrae le quadratiche che corrispondono ad i due archi... splitCircularArcIntoQuadraticCurves(tq->getP0(), arc.getP0(), arc.getP1(), arrayUp); // e le ordina in modo che l'outline sia composta sempre da // una curva superiore ed una inferiore corrispondente changeDirection(arrayUp); splitCircularArcIntoQuadraticCurves(tq->getP0(), arc.getP1(), arc.getP2(), arrayDown); changeDirection(arrayDown, true); // copia le curve nell'outline; se gli array non hanno la stessa dimensione // quello con meno curve viene riempito con curve improprie // che hanno i punti di controllo coincidente con l'ultimo estremo valido //cout<<"quads del semicerchio left:"<<std::endl; copy(/*cout, */ arrayUp, arrayDown, ob); } for (int i = startQuad; i <= endQuad; ++i) { tq = (TThickQuadratic *)stroke->getChunk(i); TThickPoint p0 = tq->getThickP0(); TThickPoint p1 = tq->getThickP1(); TThickPoint p2 = tq->getThickP2(); if (p0.x == p1.x) { if (p1.x == p2.x && ((p1.y > p0.y && p1.y > p2.y) || (p1.y < p0.y && p1.y < p2.y))) tq = new TThickQuadratic(p0, 0.5 * (p0 + p1), p1); } else if (p0.y == p1.y) { if (p0.y == p2.y && ((p1.x > p0.x && p1.x > p2.x) || (p1.x < p0.x && p1.x < p2.x))) tq = new TThickQuadratic(p0, 0.5 * (p0 + p1), p1); } else { double fac1 = 1.0 / (p0.x - p1.x); double fac2 = 1.0 / (p0.y - p1.y); double aux1 = fac1 * (p2.x - p1.x); double aux2 = fac2 * (p2.y - p1.y); double aux3 = fac1 * (p0.x - p2.x); double aux4 = fac2 * (p0.y - p2.y); if ((areAlmostEqual(aux1, aux2) && aux1 >= 0) || (areAlmostEqual(aux3, aux4) && aux3 >= 0 && aux3 <= 1)) tq = new TThickQuadratic(p0, 0.5 * (p0 + p1), p1); } //cout<<"quad# "<<i<<":" <<*tq<<std::endl; makeOutline(/*cout, */ ob, *tq, error2); if (tq != stroke->getChunk(i)) delete tq; } arrayUp.clear(); arrayDown.clear(); // come sopra ultimo pezzo di arco // if (endQuad==stroke->getChunkCount()-1) { arc = getCircleQuarter(tq, QUARTER_END); splitCircularArcIntoQuadraticCurves(tq->getP2(), arc.getP1(), arc.getP0(), arrayUp); changeDirection(arrayUp); splitCircularArcIntoQuadraticCurves(tq->getP2(), arc.getP2(), arc.getP1(), arrayDown); changeDirection(arrayDown, true); //cout<<"quads del semicerchio right:"<<std::endl; copy(/*cout,*/ arrayUp, arrayDown, ob); } }
int intersect(const TQuadratic &q, const TSegment &s, std::vector<DoublePair> &intersections, bool firstIsQuad) { int solutionNumber = 0; // Note the line `a*x+b*y+c = 0` we search for solutions // di a*x(t)+b*y(t)+c=0 in [0,1] double a = s.getP0().y - s.getP1().y, b = s.getP1().x - s.getP0().x, c = -(a * s.getP0().x + b * s.getP0().y); // se il segmento e' un punto if (0.0 == a && 0.0 == b) { double outParForQuad = q.getT(s.getP0()); if (areAlmostEqual(q.getPoint(outParForQuad), s.getP0())) { if (firstIsQuad) intersections.push_back(DoublePair(outParForQuad, 0)); else intersections.push_back(DoublePair(0, outParForQuad)); return 1; } return 0; } if (q.getP2() - q.getP1() == q.getP1() - q.getP0()) { // the second is a segment.... if (firstIsQuad) return intersect(TSegment(q.getP0(), q.getP2()), s, intersections); else return intersect(s, TSegment(q.getP0(), q.getP2()), intersections); } std::vector<TPointD> bez, pol; bez.push_back(q.getP0()); bez.push_back(q.getP1()); bez.push_back(q.getP2()); bezier2poly(bez, pol); std::vector<double> poly_1(3, 0), sol; poly_1[0] = a * pol[0].x + b * pol[0].y + c; poly_1[1] = a * pol[1].x + b * pol[1].y; poly_1[2] = a * pol[2].x + b * pol[2].y; if (!(rootFinding(poly_1, sol))) return 0; double segmentPar, solution; TPointD v10(s.getP1() - s.getP0()); for (UINT i = 0; i < sol.size(); ++i) { solution = sol[i]; if ((0.0 <= solution && solution <= 1.0) || areAlmostEqual(solution, 0.0, 1e-6) || areAlmostEqual(solution, 1.0, 1e-6)) { segmentPar = (q.getPoint(solution) - s.getP0()) * v10 / (v10 * v10); if ((0.0 <= segmentPar && segmentPar <= 1.0) || areAlmostEqual(segmentPar, 0.0, 1e-6) || areAlmostEqual(segmentPar, 1.0, 1e-6)) { TPointD p1 = q.getPoint(solution); TPointD p2 = s.getPoint(segmentPar); assert(areAlmostEqual(p1, p2, 1e-1)); if (firstIsQuad) intersections.push_back(DoublePair(solution, segmentPar)); else intersections.push_back(DoublePair(segmentPar, solution)); solutionNumber++; } } } return solutionNumber; }
int intersectCloseControlPoints(const TQuadratic &c0, const TQuadratic &c1, std::vector<DoublePair> &intersections) { int ret = -2; double dist1 = tdistance2(c0.getP0(), c0.getP1()); if (dist1 == 0) dist1 = 1e-20; double dist2 = tdistance2(c0.getP1(), c0.getP2()); if (dist2 == 0) dist2 = 1e-20; double val0 = std::max(dist1, dist2) / std::min(dist1, dist2); double dist3 = tdistance2(c1.getP0(), c1.getP1()); if (dist3 == 0) dist3 = 1e-20; double dist4 = tdistance2(c1.getP1(), c1.getP2()); if (dist4 == 0) dist4 = 1e-20; double val1 = std::max(dist3, dist4) / std::min(dist3, dist4); if (val0 > 1000000 && val1 > 1000000) // both c0 and c1 approximated by segments { TSegment s0 = TSegment(c0.getP0(), c0.getP2()); TSegment s1 = TSegment(c1.getP0(), c1.getP2()); ret = intersect(s0, s1, intersections); for (UINT i = intersections.size() - ret; i < (int)intersections.size(); i++) { intersections[i].first = (dist1 < dist2) ? sqrt(intersections[i].first) : 1 - sqrt(1 - intersections[i].first); intersections[i].second = (dist3 < dist4) ? sqrt(intersections[i].second) : 1 - sqrt(1 - intersections[i].second); } // return ret; } else if (val0 > 1000000) // c0 only approximated segment { TSegment s0 = TSegment(c0.getP0(), c0.getP2()); ret = intersect(s0, c1, intersections); for (UINT i = intersections.size() - ret; i < (int)intersections.size(); i++) intersections[i].first = (dist1 < dist2) ? sqrt(intersections[i].first) : 1 - sqrt(1 - intersections[i].first); // return ret; } else if (val1 > 1000000) // only c1 approximated segment { TSegment s1 = TSegment(c1.getP0(), c1.getP2()); ret = intersect(c0, s1, intersections); for (UINT i = intersections.size() - ret; i < (int)intersections.size(); i++) intersections[i].second = (dist3 < dist4) ? sqrt(intersections[i].second) : 1 - sqrt(1 - intersections[i].second); // return ret; } /* if (ret!=-2) { std::vector<DoublePair> intersections1; int ret1 = intersect(c0, c1, intersections1, false); if (ret1>ret) { intersections = intersections1; return ret1; } } */ return ret; }
int intersect(const TQuadratic &c0, const TQuadratic &c1, std::vector<DoublePair> &intersections, bool checksegments) { int ret; // Works baddly, sometimes patch intersections... if (checksegments) { ret = intersectCloseControlPoints(c0, c1, intersections); if (ret != -2) return ret; } double a = c0.getP0().x - 2 * c0.getP1().x + c0.getP2().x; double b = 2 * (c0.getP1().x - c0.getP0().x); double d = c0.getP0().y - 2 * c0.getP1().y + c0.getP2().y; double e = 2 * (c0.getP1().y - c0.getP0().y); double coeff = b * d - a * e; int i = 0; if (areAlmostEqual(coeff, 0.0)) // c0 is a Segment, or a single point!!! { TSegment s = TSegment(c0.getP0(), c0.getP2()); ret = intersect(s, c1, intersections); if (a == 0 && d == 0) // values of t in s coincide with values of t in c0 return ret; for (i = intersections.size() - ret; i < (int)intersections.size(); i++) { intersections[i].first = c0.getT(s.getPoint(intersections[i].first)); } return ret; } double c = c0.getP0().x; double f = c0.getP0().y; double g = c1.getP0().x - 2 * c1.getP1().x + c1.getP2().x; double h = 2 * (c1.getP1().x - c1.getP0().x); double k = c1.getP0().x; double m = c1.getP0().y - 2 * c1.getP1().y + c1.getP2().y; double p = 2 * (c1.getP1().y - c1.getP0().y); double q = c1.getP0().y; if (areAlmostEqual(h * m - g * p, 0.0)) // c1 is a Segment, or a single point!!! { TSegment s = TSegment(c1.getP0(), c1.getP2()); ret = intersect(c0, s, intersections); if (g == 0 && m == 0) // values of t in s coincide with values of t in c0 return ret; for (i = intersections.size() - ret; i < (int)intersections.size(); i++) { intersections[i].second = c1.getT(s.getPoint(intersections[i].second)); } return ret; } double a2 = (g * d - a * m); double b2 = (h * d - a * p); double c2 = ((k - c) * d + (f - q) * a); coeff = 1.0 / coeff; double A = (a * a + d * d) * coeff * coeff; double aux = A * c2 + (a * b + d * e) * coeff; std::vector<double> t; std::vector<double> solutions; t.push_back(aux * c2 + a * c + d * f - k * a - d * q); aux += A * c2; t.push_back(aux * b2 - h * a - d * p); t.push_back(aux * a2 + A * b2 * b2 - g * a - d * m); t.push_back(2 * A * a2 * b2); t.push_back(A * a2 * a2); rootFinding(t, solutions); // solutions.push_back(0.0); //per convenzione; un valore vale l'altro.... for (i = 0; i < (int)solutions.size(); i++) { if (solutions[i] < 0) { if (areAlmostEqual(solutions[i], 0, 1e-6)) solutions[i] = 0; else continue; } else if (solutions[i] > 1) { if (areAlmostEqual(solutions[i], 1, 1e-6)) solutions[i] = 1; else continue; } DoublePair tt; tt.second = solutions[i]; tt.first = coeff * (tt.second * (a2 * tt.second + b2) + c2); if (tt.first < 0) { if (areAlmostEqual(tt.first, 0, 1e-6)) tt.first = 0; else continue; } else if (tt.first > 1) { if (areAlmostEqual(tt.first, 1, 1e-6)) tt.first = 1; else continue; } intersections.push_back(tt); assert(areAlmostEqual(c0.getPoint(tt.first), c1.getPoint(tt.second), 1e-1)); } return intersections.size(); }
inline double get(const TQuadratic &q, double t, double(TPointD::*val)) const { double one_t = 1.0 - t; return one_t * (one_t * q.getP0().*val + t * q.getP1().*val) + t * (one_t * q.getP1().*val + t * q.getP2().*val); }