| |
| |
| #include "tcurveutil.h" |
| #include "tcurves.h" |
| #include "tmathutil.h" |
| #include "tbezier.h" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int intersect(const TSegment &first, |
| const TSegment &second, |
| std::vector<DoublePair> &intersections) |
| { |
| return intersect(first.getP0(), first.getP1(), second.getP0(), second.getP1(), intersections); |
| } |
| |
| int intersect(const TPointD &p1, const TPointD &p2, const TPointD &p3, const TPointD &p4, |
| std::vector<DoublePair> &intersections) |
| { |
| |
| |
| static double Ax, Bx, Ay, By, Cx, Cy, d, f, e; |
| static double x1lo, x1hi, y1lo, y1hi; |
| |
| Ax = p2.x - p1.x; |
| Bx = p3.x - p4.x; |
| |
| |
| if (Ax < 0.0) { |
| x1lo = p2.x; |
| x1hi = p1.x; |
| } else { |
| x1lo = p1.x; |
| x1hi = p2.x; |
| } |
| |
| if (Bx > 0.0) { |
| if (x1hi < p4.x || x1lo > p3.x) |
| return 0; |
| } else if (x1hi < p3.x || x1lo > p4.x) |
| return 0; |
| |
| Ay = p2.y - p1.y; |
| By = p3.y - p4.y; |
| |
| if (Ay < 0) { |
| y1lo = p2.y; |
| y1hi = p1.y; |
| } else { |
| y1lo = p1.y; |
| y1hi = p2.y; |
| } |
| |
| if (By > 0) { |
| if (y1hi < p4.y || y1lo > p3.y) |
| return 0; |
| } else if (y1hi < p3.y || y1lo > p4.y) |
| return 0; |
| |
| Cx = p1.x - p3.x; |
| Cy = p1.y - p3.y; |
| |
| d = By * Cx - Bx * Cy; |
| f = Ay * Bx - Ax * By; |
| e = Ax * Cy - Ay * Cx; |
| |
| if (f > 0) { |
| if (d < 0) |
| return 0; |
| |
| if (!areAlmostEqual(d, f)) |
| if (d > f) |
| return 0; |
| |
| if (e < 0) |
| return 0; |
| if (!areAlmostEqual(e, f)) |
| if (e > f) |
| return 0; |
| } else if (f < 0) { |
| if (d > 0) |
| return 0; |
| |
| if (!areAlmostEqual(d, f)) |
| if (d < f) |
| return 0; |
| |
| if (e > 0) |
| return 0; |
| if (!areAlmostEqual(e, f)) |
| if (e < f) |
| return 0; |
| } else { |
| if (d < 0 || d > 1 || e < 0 || e > 1) |
| return 0; |
| |
| if (p1 == p2 && p3 == p4) { |
| intersections.push_back(DoublePair(0, 0)); |
| return 1; |
| } |
| |
| |
| if (!cross(p2 - p1, p4 - p1)) { |
| |
| double distp2p1 = norm2(p2 - p1); |
| double distp3p4 = norm2(p3 - p4); |
| |
| double dist2_p3p1 = norm2(p3 - p1); |
| double dist2_p4p1 = norm2(p4 - p1); |
| double dist2_p3p2 = norm2(p3 - p2); |
| double dist2_p4p2 = norm2(p4 - p2); |
| |
| int intersection = 0; |
| |
| |
| double vol1; |
| |
| if (distp3p4) { |
| distp3p4 = sqrt(distp3p4); |
| |
| vol1 = (p1 - p3) * normalize(p4 - p3); |
| |
| if (vol1 >= 0 && vol1 <= distp3p4) |
| { |
| intersections.push_back(DoublePair(0.0, vol1 / distp3p4)); |
| ++intersection; |
| } |
| |
| vol1 = (p2 - p3) * normalize(p4 - p3); |
| |
| if (vol1 >= 0 && vol1 <= distp3p4) { |
| intersections.push_back(DoublePair(1.0, vol1 / distp3p4)); |
| ++intersection; |
| } |
| } |
| |
| if (distp2p1) { |
| distp2p1 = sqrt(distp2p1); |
| |
| vol1 = (p3 - p1) * normalize(p2 - p1); |
| |
| if (dist2_p3p2 && dist2_p3p1) |
| if (vol1 >= 0 && vol1 <= distp2p1) { |
| intersections.push_back(DoublePair(vol1 / distp2p1, 0.0)); |
| ++intersection; |
| } |
| |
| vol1 = (p4 - p1) * normalize(p2 - p1); |
| |
| if (dist2_p4p2 && dist2_p4p1) |
| if (vol1 >= 0 && vol1 <= distp2p1) { |
| intersections.push_back(DoublePair(vol1 / distp2p1, 1.0)); |
| ++intersection; |
| } |
| } |
| return intersection; |
| } |
| return -1; |
| } |
| |
| double par_s = d / f; |
| double par_t = e / f; |
| |
| intersections.push_back(DoublePair(par_s, par_t)); |
| return 1; |
| } |
| |
| |
| int intersectCloseControlPoints(const TQuadratic &c0, |
| const TQuadratic &c1, |
| std::vector<DoublePair> &intersections); |
| |
| int intersect(const TQuadratic &c0, |
| const TQuadratic &c1, |
| std::vector<DoublePair> &intersections, bool checksegments) |
| { |
| int ret; |
| |
| |
| 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)) |
| { |
| |
| TSegment s = TSegment(c0.getP0(), c0.getP2()); |
| ret = intersect(s, c1, intersections); |
| if (a == 0 && d == 0) |
| 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)) |
| { |
| TSegment s = TSegment(c1.getP0(), c1.getP2()); |
| ret = intersect(c0, s, intersections); |
| if (g == 0 && m == 0) |
| 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); |
| |
| |
| 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(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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) |
| { |
| 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); |
| } |
| |
| } else if (val0 > 1000000) |
| { |
| 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); |
| |
| } else if (val1 > 1000000) |
| { |
| 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; |
| } |
| |
| |
| |
| int intersect(const TQuadratic &q, |
| const TSegment &s, |
| std::vector<DoublePair> &intersections, |
| bool firstIsQuad) |
| { |
| int solutionNumber = 0; |
| |
| |
| |
| double |
| a = s.getP0().y - s.getP1().y, |
| b = s.getP1().x - s.getP0().x, |
| c = -(a * s.getP0().x + b * s.getP0().y); |
| |
| |
| 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()) { |
| 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; |
| } |
| |
| |
| |
| bool isCloseToSegment(const TPointD &point, const TSegment &segment, double distance) |
| { |
| TPointD a = segment.getP0(); |
| TPointD b = segment.getP1(); |
| double lenght2 = tdistance2(a, b); |
| if (lenght2 < tdistance2(a, point) || lenght2 < tdistance2(point, b)) |
| return false; |
| if (a.x == b.x) |
| return fabs(point.x - a.x) <= distance; |
| if (a.y == b.y) |
| return fabs(point.y - a.y) <= distance; |
| |
| |
| double m = (a.y - b.y) / (a.x - b.x); |
| double q = a.y - (m * a.x); |
| |
| double d2 = pow(fabs(point.y - (m * point.x) - q), 2) / (1 + (m * m)); |
| return d2 <= distance * distance; |
| } |
| |
| |
| |
| double tdistance(const TSegment &segment, const TPointD &point) |
| { |
| TPointD v1 = segment.getP1() - segment.getP0(); |
| TPointD v2 = point - segment.getP0(); |
| TPointD v3 = point - segment.getP1(); |
| |
| if (v2 * v1 <= 0) |
| return tdistance(point, segment.getP0()); |
| else if (v3 * v1 >= 0) |
| return tdistance(point, segment.getP1()); |
| |
| return fabs(v2 * rotate90(normalize(v1))); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| double computeStep(const TQuadratic &quad, double pixelSize) |
| { |
| double step = 2; |
| |
| TPointD A = quad.getP0() - 2.0 * quad.getP1() + quad.getP2(); |
| |
| double A_len = norm(A); |
| |
| |
| |
| |
| |
| |
| |
| if (A_len > 0) |
| step = sqrt(2 * pixelSize / A_len); |
| |
| return step; |
| } |
| |
| |
| |
| double computeStep(const TThickQuadratic &quad, double pixelSize) |
| { |
| TThickPoint |
| cp0 = quad.getThickP0(), |
| cp1 = quad.getThickP1(), |
| cp2 = quad.getThickP2(); |
| |
| TQuadratic |
| q1(TPointD(cp0.x, cp0.y), TPointD(cp1.x, cp1.y), TPointD(cp2.x, cp2.y)), |
| q2(TPointD(cp0.y, cp0.thick), TPointD(cp1.y, cp1.thick), TPointD(cp2.y, cp2.thick)), |
| q3(TPointD(cp0.x, cp0.thick), TPointD(cp1.x, cp1.thick), TPointD(cp2.x, cp2.thick)); |
| |
| return std::min({computeStep(q1, pixelSize), computeStep(q2, pixelSize), computeStep(q3, pixelSize)}); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| if (m_constantSpeed) { |
| m_c = sqrt(m_c); |
| return; |
| } |
| |
| m_sqrt_a_div_2 = 0.5 * sqrt(a); |
| |
| m_noSpeed0 = isAlmostZero(m_c); |
| 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 TQuadraticLengthEvaluator::getLengthAt(double t) const |
| { |
| if (m_constantSpeed) |
| return m_c * t; |
| |
| if (m_noSpeed0) |
| return m_sqrt_a_div_2 * sq(t); |
| |
| if (m_squareIntegrand) { |
| double t_plus_tRef = t + m_tRef; |
| return m_sqrt_a_div_2 * (m_f + ((t_plus_tRef > 0) ? sq(t_plus_tRef) : -sq(t_plus_tRef))); |
| } |
| |
| double y = t + m_tRef; |
| double sqrt_part = sqrt(sq(y) + m_e); |
| double log_arg = y + sqrt_part; |
| |
| return m_sqrt_a_div_2 * (y * sqrt_part + m_e * log(log_arg)) - m_primitive_0; |
| } |
| |
| |
| |
| |
| |