1 
2 
3 #include "tcurveutil.h"
4 #include "tcurves.h"
5 #include "tmathutil.h"
6 #include "tbezier.h"
7 
8 //=============================================================================
9 
10 /*
11 This function returns a vector of
12 pairs of double (DoublePair) which identifies the parameters
13 of the points of intersection.
14 
15   The integer returned is the number of intersections which
16   have been identified (for two segments).
17 
18     If the segments are parallel to the parameter it is set to -1.
19 */
20 
intersect(const TSegment & first,const TSegment & second,std::vector<DoublePair> & intersections)21 int intersect(const TSegment &first, const TSegment &second,
22               std::vector<DoublePair> &intersections) {
23   return intersect(first.getP0(), first.getP1(), second.getP0(), second.getP1(),
24                    intersections);
25 }
26 
intersect(const TPointD & p1,const TPointD & p2,const TPointD & p3,const TPointD & p4,std::vector<DoublePair> & intersections)27 int intersect(const TPointD &p1, const TPointD &p2, const TPointD &p3,
28               const TPointD &p4, std::vector<DoublePair> &intersections) {
29   // This algorithm is presented in Graphics Geems III pag 199
30 
31   static double Ax, Bx, Ay, By, Cx, Cy, d, f, e;
32   static double x1lo, x1hi, y1lo, y1hi;
33 
34   Ax = p2.x - p1.x;
35   Bx = p3.x - p4.x;
36 
37   // test delle BBox
38   if (Ax < 0.0) {
39     x1lo = p2.x;
40     x1hi = p1.x;
41   } else {
42     x1lo = p1.x;
43     x1hi = p2.x;
44   }
45 
46   if (Bx > 0.0) {
47     if (x1hi < p4.x || x1lo > p3.x) return 0;
48   } else if (x1hi < p3.x || x1lo > p4.x)
49     return 0;
50 
51   Ay = p2.y - p1.y;
52   By = p3.y - p4.y;
53 
54   if (Ay < 0) {
55     y1lo = p2.y;
56     y1hi = p1.y;
57   } else {
58     y1lo = p1.y;
59     y1hi = p2.y;
60   }
61 
62   if (By > 0) {
63     if (y1hi < p4.y || y1lo > p3.y) return 0;
64   } else if (y1hi < p3.y || y1lo > p4.y)
65     return 0;
66 
67   Cx = p1.x - p3.x;
68   Cy = p1.y - p3.y;
69 
70   d = By * Cx - Bx * Cy;
71   f = Ay * Bx - Ax * By;
72   e = Ax * Cy - Ay * Cx;
73 
74   if (f > 0) {
75     if (d < 0) return 0;
76 
77     if (!areAlmostEqual(d, f))
78       if (d > f) return 0;
79 
80     if (e < 0) return 0;
81     if (!areAlmostEqual(e, f))
82       if (e > f) return 0;
83   } else if (f < 0) {
84     if (d > 0) return 0;
85 
86     if (!areAlmostEqual(d, f))
87       if (d < f) return 0;
88 
89     if (e > 0) return 0;
90     if (!areAlmostEqual(e, f))
91       if (e < f) return 0;
92   } else {
93     if (d < 0 || d > 1 || e < 0 || e > 1) return 0;
94 
95     if (p1 == p2 && p3 == p4) {
96       intersections.push_back(DoublePair(0, 0));
97       return 1;
98     }
99 
100     // Check that the segments are not on the same line.
101     if (!cross(p2 - p1, p4 - p1)) {
102       // Calculation of Barycentric combinations.
103       double distp2p1 = norm2(p2 - p1);
104       double distp3p4 = norm2(p3 - p4);
105 
106       double dist2_p3p1 = norm2(p3 - p1);
107       double dist2_p4p1 = norm2(p4 - p1);
108       double dist2_p3p2 = norm2(p3 - p2);
109       double dist2_p4p2 = norm2(p4 - p2);
110 
111       int intersection = 0;
112 
113       // Calculation of the first two solutions.
114       double vol1;
115 
116       if (distp3p4) {
117         distp3p4 = sqrt(distp3p4);
118 
119         vol1 = (p1 - p3) * normalize(p4 - p3);
120 
121         if (vol1 >= 0 && vol1 <= distp3p4)  // Barycentric combinations valid
122         {
123           intersections.push_back(DoublePair(0.0, vol1 / distp3p4));
124           ++intersection;
125         }
126 
127         vol1 = (p2 - p3) * normalize(p4 - p3);
128 
129         if (vol1 >= 0 && vol1 <= distp3p4) {
130           intersections.push_back(DoublePair(1.0, vol1 / distp3p4));
131           ++intersection;
132         }
133       }
134 
135       if (distp2p1) {
136         distp2p1 = sqrt(distp2p1);
137 
138         vol1 = (p3 - p1) * normalize(p2 - p1);
139 
140         if (dist2_p3p2 && dist2_p3p1)
141           if (vol1 >= 0 && vol1 <= distp2p1) {
142             intersections.push_back(DoublePair(vol1 / distp2p1, 0.0));
143             ++intersection;
144           }
145 
146         vol1 = (p4 - p1) * normalize(p2 - p1);
147 
148         if (dist2_p4p2 && dist2_p4p1)
149           if (vol1 >= 0 && vol1 <= distp2p1) {
150             intersections.push_back(DoublePair(vol1 / distp2p1, 1.0));
151             ++intersection;
152           }
153       }
154       return intersection;
155     }
156     return -1;
157   }
158 
159   double par_s = d / f;
160   double par_t = e / f;
161 
162   intersections.push_back(DoublePair(par_s, par_t));
163   return 1;
164 }
165 
166 //------------------------------------------------------------------------------------------------------------
167 int intersectCloseControlPoints(const TQuadratic &c0, const TQuadratic &c1,
168                                 std::vector<DoublePair> &intersections);
169 
intersect(const TQuadratic & c0,const TQuadratic & c1,std::vector<DoublePair> & intersections,bool checksegments)170 int intersect(const TQuadratic &c0, const TQuadratic &c1,
171               std::vector<DoublePair> &intersections, bool checksegments) {
172   int ret;
173 
174   // Works baddly, sometimes patch intersections...
175   if (checksegments) {
176     ret = intersectCloseControlPoints(c0, c1, intersections);
177     if (ret != -2) return ret;
178   }
179 
180   double a = c0.getP0().x - 2 * c0.getP1().x + c0.getP2().x;
181   double b = 2 * (c0.getP1().x - c0.getP0().x);
182   double d = c0.getP0().y - 2 * c0.getP1().y + c0.getP2().y;
183   double e = 2 * (c0.getP1().y - c0.getP0().y);
184 
185   double coeff = b * d - a * e;
186   int i        = 0;
187 
188   if (areAlmostEqual(coeff, 0.0))  // c0 is a Segment, or a single point!!!
189   {
190     TSegment s = TSegment(c0.getP0(), c0.getP2());
191     ret        = intersect(s, c1, intersections);
192     if (a == 0 && d == 0)  // values of t in s coincide with values of t in c0
193       return ret;
194 
195     for (i = intersections.size() - ret; i < (int)intersections.size(); i++) {
196       intersections[i].first = c0.getT(s.getPoint(intersections[i].first));
197     }
198     return ret;
199   }
200 
201   double c = c0.getP0().x;
202   double f = c0.getP0().y;
203 
204   double g = c1.getP0().x - 2 * c1.getP1().x + c1.getP2().x;
205   double h = 2 * (c1.getP1().x - c1.getP0().x);
206   double k = c1.getP0().x;
207 
208   double m = c1.getP0().y - 2 * c1.getP1().y + c1.getP2().y;
209   double p = 2 * (c1.getP1().y - c1.getP0().y);
210   double q = c1.getP0().y;
211 
212   if (areAlmostEqual(h * m - g * p,
213                      0.0))  // c1 is a Segment, or a single point!!!
214   {
215     TSegment s = TSegment(c1.getP0(), c1.getP2());
216     ret        = intersect(c0, s, intersections);
217     if (g == 0 && m == 0)  // values of t in s coincide with values of t in c0
218       return ret;
219 
220     for (i = intersections.size() - ret; i < (int)intersections.size(); i++) {
221       intersections[i].second = c1.getT(s.getPoint(intersections[i].second));
222     }
223     return ret;
224   }
225 
226   double a2 = (g * d - a * m);
227   double b2 = (h * d - a * p);
228   double c2 = ((k - c) * d + (f - q) * a);
229 
230   coeff = 1.0 / coeff;
231 
232   double A   = (a * a + d * d) * coeff * coeff;
233   double aux = A * c2 + (a * b + d * e) * coeff;
234 
235   std::vector<double> t;
236   std::vector<double> solutions;
237 
238   t.push_back(aux * c2 + a * c + d * f - k * a - d * q);
239   aux += A * c2;
240   t.push_back(aux * b2 - h * a - d * p);
241   t.push_back(aux * a2 + A * b2 * b2 - g * a - d * m);
242   t.push_back(2 * A * a2 * b2);
243   t.push_back(A * a2 * a2);
244 
245   rootFinding(t, solutions);
246   //  solutions.push_back(0.0); //per convenzione; un valore vale l'altro....
247 
248   for (i = 0; i < (int)solutions.size(); i++) {
249     if (solutions[i] < 0) {
250       if (areAlmostEqual(solutions[i], 0, 1e-6))
251         solutions[i] = 0;
252       else
253         continue;
254     } else if (solutions[i] > 1) {
255       if (areAlmostEqual(solutions[i], 1, 1e-6))
256         solutions[i] = 1;
257       else
258         continue;
259     }
260 
261     DoublePair tt;
262     tt.second = solutions[i];
263     tt.first  = coeff * (tt.second * (a2 * tt.second + b2) + c2);
264     if (tt.first < 0) {
265       if (areAlmostEqual(tt.first, 0, 1e-6))
266         tt.first = 0;
267       else
268         continue;
269     } else if (tt.first > 1) {
270       if (areAlmostEqual(tt.first, 1, 1e-6))
271         tt.first = 1;
272       else
273         continue;
274     }
275 
276     intersections.push_back(tt);
277 
278     assert(areAlmostEqual(c0.getPoint(tt.first), c1.getPoint(tt.second), 1e-1));
279   }
280   return intersections.size();
281 }
282 
283 //=============================================================================
284 // This function checks whether the control point
285 // p1 is very close to p0 or p2.
286 // In this case, we are approximated to the quadratic p0-p2 segment.
287 // If p1 is near p0, the relationship between the original and the quadratic
288 // segment:
289 //     tq = sqrt(ts),
290 // If p1 is near p2, instead it's:
291 //     tq = 1-sqrt(1-ts).
292 
intersectCloseControlPoints(const TQuadratic & c0,const TQuadratic & c1,std::vector<DoublePair> & intersections)293 int intersectCloseControlPoints(const TQuadratic &c0, const TQuadratic &c1,
294                                 std::vector<DoublePair> &intersections) {
295   int ret = -2;
296 
297   double dist1          = tdistance2(c0.getP0(), c0.getP1());
298   if (dist1 == 0) dist1 = 1e-20;
299   double dist2          = tdistance2(c0.getP1(), c0.getP2());
300   if (dist2 == 0) dist2 = 1e-20;
301   double val0           = std::max(dist1, dist2) / std::min(dist1, dist2);
302   double dist3          = tdistance2(c1.getP0(), c1.getP1());
303   if (dist3 == 0) dist3 = 1e-20;
304   double dist4          = tdistance2(c1.getP1(), c1.getP2());
305   if (dist4 == 0) dist4 = 1e-20;
306   double val1           = std::max(dist3, dist4) / std::min(dist3, dist4);
307 
308   if (val0 > 1000000 &&
309       val1 > 1000000)  // both c0 and c1 approximated by segments
310   {
311     TSegment s0 = TSegment(c0.getP0(), c0.getP2());
312     TSegment s1 = TSegment(c1.getP0(), c1.getP2());
313     ret         = intersect(s0, s1, intersections);
314     for (UINT i = intersections.size() - ret; i < (int)intersections.size();
315          i++) {
316       intersections[i].first = (dist1 < dist2)
317                                    ? sqrt(intersections[i].first)
318                                    : 1 - sqrt(1 - intersections[i].first);
319       intersections[i].second = (dist3 < dist4)
320                                     ? sqrt(intersections[i].second)
321                                     : 1 - sqrt(1 - intersections[i].second);
322     }
323     // return ret;
324   } else if (val0 > 1000000)  // c0 only approximated segment
325   {
326     TSegment s0 = TSegment(c0.getP0(), c0.getP2());
327     ret         = intersect(s0, c1, intersections);
328     for (UINT i = intersections.size() - ret; i < (int)intersections.size();
329          i++)
330       intersections[i].first = (dist1 < dist2)
331                                    ? sqrt(intersections[i].first)
332                                    : 1 - sqrt(1 - intersections[i].first);
333     // return ret;
334   } else if (val1 > 1000000)  // only c1 approximated segment
335   {
336     TSegment s1 = TSegment(c1.getP0(), c1.getP2());
337     ret         = intersect(c0, s1, intersections);
338     for (UINT i = intersections.size() - ret; i < (int)intersections.size();
339          i++)
340       intersections[i].second = (dist3 < dist4)
341                                     ? sqrt(intersections[i].second)
342                                     : 1 - sqrt(1 - intersections[i].second);
343     // return ret;
344   }
345 
346   /*
347 if (ret!=-2)
348 {
349 std::vector<DoublePair> intersections1;
350 int ret1 = intersect(c0, c1, intersections1, false);
351 if (ret1>ret)
352 {
353 intersections = intersections1;
354 return ret1;
355 }
356 }
357 */
358 
359   return ret;
360 }
361 
362 //=============================================================================
363 
intersect(const TQuadratic & q,const TSegment & s,std::vector<DoublePair> & intersections,bool firstIsQuad)364 int intersect(const TQuadratic &q, const TSegment &s,
365               std::vector<DoublePair> &intersections, bool firstIsQuad) {
366   int solutionNumber = 0;
367 
368   // Note the line `a*x+b*y+c = 0` we search for solutions
369   //  di a*x(t)+b*y(t)+c=0 in [0,1]
370   double a = s.getP0().y - s.getP1().y, b = s.getP1().x - s.getP0().x,
371          c = -(a * s.getP0().x + b * s.getP0().y);
372 
373   // se il segmento e' un punto
374   if (0.0 == a && 0.0 == b) {
375     double outParForQuad = q.getT(s.getP0());
376 
377     if (areAlmostEqual(q.getPoint(outParForQuad), s.getP0())) {
378       if (firstIsQuad)
379         intersections.push_back(DoublePair(outParForQuad, 0));
380       else
381         intersections.push_back(DoublePair(0, outParForQuad));
382       return 1;
383     }
384     return 0;
385   }
386 
387   if (q.getP2() - q.getP1() ==
388       q.getP1() - q.getP0()) {  // the second is a segment....
389     if (firstIsQuad)
390       return intersect(TSegment(q.getP0(), q.getP2()), s, intersections);
391     else
392       return intersect(s, TSegment(q.getP0(), q.getP2()), intersections);
393   }
394 
395   std::vector<TPointD> bez, pol;
396   bez.push_back(q.getP0());
397   bez.push_back(q.getP1());
398   bez.push_back(q.getP2());
399 
400   bezier2poly(bez, pol);
401 
402   std::vector<double> poly_1(3, 0), sol;
403 
404   poly_1[0] = a * pol[0].x + b * pol[0].y + c;
405   poly_1[1] = a * pol[1].x + b * pol[1].y;
406   poly_1[2] = a * pol[2].x + b * pol[2].y;
407 
408   if (!(rootFinding(poly_1, sol))) return 0;
409 
410   double segmentPar, solution;
411 
412   TPointD v10(s.getP1() - s.getP0());
413   for (UINT i = 0; i < sol.size(); ++i) {
414     solution = sol[i];
415     if ((0.0 <= solution && solution <= 1.0) ||
416         areAlmostEqual(solution, 0.0, 1e-6) ||
417         areAlmostEqual(solution, 1.0, 1e-6)) {
418       segmentPar = (q.getPoint(solution) - s.getP0()) * v10 / (v10 * v10);
419       if ((0.0 <= segmentPar && segmentPar <= 1.0) ||
420           areAlmostEqual(segmentPar, 0.0, 1e-6) ||
421           areAlmostEqual(segmentPar, 1.0, 1e-6)) {
422         TPointD p1 = q.getPoint(solution);
423         TPointD p2 = s.getPoint(segmentPar);
424         assert(areAlmostEqual(p1, p2, 1e-1));
425 
426         if (firstIsQuad)
427           intersections.push_back(DoublePair(solution, segmentPar));
428         else
429           intersections.push_back(DoublePair(segmentPar, solution));
430         solutionNumber++;
431       }
432     }
433   }
434 
435   return solutionNumber;
436 }
437 
438 //=============================================================================
439 
isCloseToSegment(const TPointD & point,const TSegment & segment,double distance)440 bool isCloseToSegment(const TPointD &point, const TSegment &segment,
441                       double distance) {
442   TPointD a      = segment.getP0();
443   TPointD b      = segment.getP1();
444   double length2 = tdistance2(a, b);
445   if (length2 < tdistance2(a, point) || length2 < tdistance2(point, b))
446     return false;
447   if (a.x == b.x) return fabs(point.x - a.x) <= distance;
448   if (a.y == b.y) return fabs(point.y - a.y) <= distance;
449 
450   // y=mx+q
451   double m = (a.y - b.y) / (a.x - b.x);
452   double q = a.y - (m * a.x);
453 
454   double d2 = pow(fabs(point.y - (m * point.x) - q), 2) / (1 + (m * m));
455   return d2 <= distance * distance;
456 }
457 
458 //=============================================================================
459 
tdistance(const TSegment & segment,const TPointD & point)460 double tdistance(const TSegment &segment, const TPointD &point) {
461   TPointD v1 = segment.getP1() - segment.getP0();
462   TPointD v2 = point - segment.getP0();
463   TPointD v3 = point - segment.getP1();
464 
465   if (v2 * v1 <= 0)
466     return tdistance(point, segment.getP0());
467   else if (v3 * v1 >= 0)
468     return tdistance(point, segment.getP1());
469 
470   return fabs(v2 * rotate90(normalize(v1)));
471 }
472 
473 //-----------------------------------------------------------------------------
474 /*
475 This formule is derived from Graphic Gems pag. 600
476 
477   e = h^2 |a|/8
478 
479     e = pixel size
480     h = step
481     a = acceleration of curve (for a quadratic is a costant value)
482 */
483 
computeStep(const TQuadratic & quad,double pixelSize)484 double computeStep(const TQuadratic &quad, double pixelSize) {
485   double step = 2;
486 
487   TPointD A = quad.getP0() - 2.0 * quad.getP1() +
488               quad.getP2();  // 2*A is the acceleration of the curve
489 
490   double A_len = norm(A);
491 
492   /*
493 A_len is equal to 2*norm(a)
494 pixelSize will be 0.5*pixelSize
495 now h is equal to sqrt( 8 * 0.5 * pixelSize / (2*norm(a)) ) = sqrt(2) * sqrt(
496 pixelSize/A_len )
497 */
498 
499   if (A_len > 0) step = sqrt(2 * pixelSize / A_len);
500 
501   return step;
502 }
503 
504 //-----------------------------------------------------------------------------
505 
computeStep(const TThickQuadratic & quad,double pixelSize)506 double computeStep(const TThickQuadratic &quad, double pixelSize) {
507   TThickPoint cp0 = quad.getThickP0(), cp1 = quad.getThickP1(),
508               cp2 = quad.getThickP2();
509 
510   TQuadratic q1(TPointD(cp0.x, cp0.y), TPointD(cp1.x, cp1.y),
511                 TPointD(cp2.x, cp2.y)),
512       q2(TPointD(cp0.y, cp0.thick), TPointD(cp1.y, cp1.thick),
513          TPointD(cp2.y, cp2.thick)),
514       q3(TPointD(cp0.x, cp0.thick), TPointD(cp1.x, cp1.thick),
515          TPointD(cp2.x, cp2.thick));
516 
517   return std::min({computeStep(q1, pixelSize), computeStep(q2, pixelSize),
518                    computeStep(q3, pixelSize)});
519 }
520 
521 //=============================================================================
522 
523 /*
524   Explanation: The length of a Bezier quadratic can be calculated explicitly.
525 
526   Let Q be the quadratic. The tricks to explicitly integrate | Q'(t) | are:
527 
528     - The integrand can be reformulated as:  | Q'(t) | = sqrt(at^2 + bt + c);
529     - Complete the square beneath the sqrt (add/subtract sq(b) / 4a)
530       and perform a linear variable change. We reduce the integrand to:
531   sqrt(kx^2 + k),
532       where k can be taken outside => sqrt(x^2 + 1)
533     - Use x = tan y. The integrand will yield sec^3 y.
534     - Integrate by parts. In short, the resulting primitive of sqrt(x^2 + 1) is:
535 
536         F(x) = ( x * sqrt(x^2 + 1) + log(x + sqrt(x^2 + 1)) ) / 2;
537 */
538 
setQuad(const TQuadratic & quad)539 void TQuadraticLengthEvaluator::setQuad(const TQuadratic &quad) {
540   const TPointD &p0 = quad.getP0();
541   const TPointD &p1 = quad.getP1();
542   const TPointD &p2 = quad.getP2();
543 
544   TPointD speed0(2.0 * (p1 - p0));
545   TPointD accel(2.0 * (p2 - p1) - speed0);
546 
547   double a = accel * accel;
548   double b = 2.0 * accel * speed0;
549   m_c      = speed0 * speed0;
550 
551   m_constantSpeed = isAlmostZero(a);  // => b isAlmostZero, too
552   if (m_constantSpeed) {
553     m_c = sqrt(m_c);
554     return;
555   }
556 
557   m_sqrt_a_div_2 = 0.5 * sqrt(a);
558 
559   m_noSpeed0 = isAlmostZero(m_c);  // => b isAlmostZero, too
560   if (m_noSpeed0) return;
561 
562   m_tRef   = 0.5 * b / a;
563   double d = m_c - 0.5 * b * m_tRef;
564 
565   m_squareIntegrand = (d < TConsts::epsilon);
566   if (m_squareIntegrand) {
567     m_f = (b > 0) ? -sq(m_tRef) : sq(m_tRef);
568     return;
569   }
570 
571   m_e = d / a;
572 
573   double sqrt_part = sqrt(sq(m_tRef) + m_e);
574   double log_arg   = m_tRef + sqrt_part;
575 
576   m_squareIntegrand = (log_arg < TConsts::epsilon);
577   if (m_squareIntegrand) {
578     m_f = (b > 0) ? -sq(m_tRef) : sq(m_tRef);
579     return;
580   }
581 
582   m_primitive_0 = m_sqrt_a_div_2 * (m_tRef * sqrt_part + m_e * log(log_arg));
583 }
584 
585 //-----------------------------------------------------------------------------
586 
getLengthAt(double t) const587 double TQuadraticLengthEvaluator::getLengthAt(double t) const {
588   if (m_constantSpeed) return m_c * t;
589 
590   if (m_noSpeed0) return m_sqrt_a_div_2 * sq(t);
591 
592   if (m_squareIntegrand) {
593     double t_plus_tRef = t + m_tRef;
594     return m_sqrt_a_div_2 *
595            (m_f + ((t_plus_tRef > 0) ? sq(t_plus_tRef) : -sq(t_plus_tRef)));
596   }
597 
598   double y         = t + m_tRef;
599   double sqrt_part = sqrt(sq(y) + m_e);
600   double log_arg =
601       y + sqrt_part;  // NOTE: log_arg >= log_arg0 >= TConsts::epsilon
602 
603   return m_sqrt_a_div_2 * (y * sqrt_part + m_e * log(log_arg)) - m_primitive_0;
604 }
605 
606 //-----------------------------------------------------------------------------
607 //  End Of File
608 //-----------------------------------------------------------------------------
609