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