2
0

convergent-curve-ordering.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #include "convergent-curve-ordering.h"
  2. #include "arithmetics.hpp"
  3. #include "Vector2.hpp"
  4. /*
  5. * For non-degenerate curves A(t), B(t) (ones where all control points are distinct) both originating at P = A(0) = B(0) = *corner,
  6. * we are computing the limit of
  7. *
  8. * sign(crossProduct( A(t / |A'(0)|) - P, B(t / |B'(0)|) - P ))
  9. *
  10. * for t -> 0 from 1. Of note is that the curves' parameter has to be normed by the first derivative at P,
  11. * which ensures that the limit approaches P at the same rate along both curves - omitting this was the main error of earlier versions of deconverge.
  12. *
  13. * For degenerate cubic curves (ones where the first control point equals the origin point), the denominator |A'(0)| is zero,
  14. * so to address that, we approach with the square root of t and use the derivative of A(sqrt(t)), which at t = 0 equals A''(0)/2
  15. * Therefore, in these cases, we replace one factor of the cross product with A(sqrt(2*t / |A''(0)|)) - P
  16. *
  17. * The cross product results in a polynomial (in respect to t or t^2 in the degenerate case),
  18. * the limit of sign of which at zero can be determined by the lowest order non-zero derivative,
  19. * which equals to the sign of the first non-zero polynomial coefficient in the order of increasing exponents.
  20. *
  21. * The polynomial's constant and linear terms are zero, so the first derivative is definitely zero as well.
  22. * The second derivative is assumed to be zero (or near zero) due to the curves being convergent - this is an input requirement
  23. * (otherwise the correct result is the sign of the cross product of their directions at t = 0).
  24. * Therefore, we skip the first and second derivatives.
  25. */
  26. namespace msdfgen {
  27. static void simplifyDegenerateCurve(Point2 *controlPoints, int &order) {
  28. if (order == 3 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[3]) && (controlPoints[2] == controlPoints[0] || controlPoints[2] == controlPoints[3])) {
  29. controlPoints[1] = controlPoints[3];
  30. order = 1;
  31. }
  32. if (order == 2 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[2])) {
  33. controlPoints[1] = controlPoints[2];
  34. order = 1;
  35. }
  36. if (order == 1 && controlPoints[0] == controlPoints[1])
  37. order = 0;
  38. }
  39. int convergentCurveOrdering(const Point2 *corner, int controlPointsBefore, int controlPointsAfter) {
  40. if (!(controlPointsBefore > 0 && controlPointsAfter > 0))
  41. return 0;
  42. Vector2 a1, a2, a3, b1, b2, b3;
  43. a1 = *(corner-1)-*corner;
  44. b1 = *(corner+1)-*corner;
  45. if (controlPointsBefore >= 2)
  46. a2 = *(corner-2)-*(corner-1)-a1;
  47. if (controlPointsAfter >= 2)
  48. b2 = *(corner+2)-*(corner+1)-b1;
  49. if (controlPointsBefore >= 3) {
  50. a3 = *(corner-3)-*(corner-2)-(*(corner-2)-*(corner-1))-a2;
  51. a2 *= 3;
  52. }
  53. if (controlPointsAfter >= 3) {
  54. b3 = *(corner+3)-*(corner+2)-(*(corner+2)-*(corner+1))-b2;
  55. b2 *= 3;
  56. }
  57. a1 *= controlPointsBefore;
  58. b1 *= controlPointsAfter;
  59. // Non-degenerate case
  60. if (a1 && b1) {
  61. double as = a1.length();
  62. double bs = b1.length();
  63. // Third derivative
  64. if (double d = as*crossProduct(a1, b2) + bs*crossProduct(a2, b1))
  65. return sign(d);
  66. // Fourth derivative
  67. if (double d = as*as*crossProduct(a1, b3) + as*bs*crossProduct(a2, b2) + bs*bs*crossProduct(a3, b1))
  68. return sign(d);
  69. // Fifth derivative
  70. if (double d = as*crossProduct(a2, b3) + bs*crossProduct(a3, b2))
  71. return sign(d);
  72. // Sixth derivative
  73. return sign(crossProduct(a3, b3));
  74. }
  75. // Degenerate curve after corner (control point after corner equals corner)
  76. int s = 1;
  77. if (a1) { // !b1
  78. // Swap aN <-> bN and handle in if (b1)
  79. b1 = a1;
  80. a1 = b2, b2 = a2, a2 = a1;
  81. a1 = b3, b3 = a3, a3 = a1;
  82. s = -1; // make sure to also flip output
  83. }
  84. // Degenerate curve before corner (control point before corner equals corner)
  85. if (b1) { // !a1
  86. // Two-and-a-half-th derivative
  87. if (double d = crossProduct(a3, b1))
  88. return s*sign(d);
  89. // Third derivative
  90. if (double d = crossProduct(a2, b2))
  91. return s*sign(d);
  92. // Three-and-a-half-th derivative
  93. if (double d = crossProduct(a3, b2))
  94. return s*sign(d);
  95. // Fourth derivative
  96. if (double d = crossProduct(a2, b3))
  97. return s*sign(d);
  98. // Four-and-a-half-th derivative
  99. return s*sign(crossProduct(a3, b3));
  100. }
  101. // Degenerate curves on both sides of the corner (control point before and after corner equals corner)
  102. { // !a1 && !b1
  103. // Two-and-a-half-th derivative
  104. if (double d = sqrt(a2.length())*crossProduct(a2, b3) + sqrt(b2.length())*crossProduct(a3, b2))
  105. return sign(d);
  106. // Third derivative
  107. return sign(crossProduct(a3, b3));
  108. }
  109. }
  110. int convergentCurveOrdering(const EdgeSegment *a, const EdgeSegment *b) {
  111. Point2 controlPoints[12];
  112. Point2 *corner = controlPoints+4;
  113. Point2 *aCpTmp = controlPoints+8;
  114. int aOrder = int(a->type());
  115. int bOrder = int(b->type());
  116. if (!(aOrder >= 1 && aOrder <= 3 && bOrder >= 1 && bOrder <= 3)) {
  117. // Not implemented - only linear, quadratic, and cubic curves supported
  118. return 0;
  119. }
  120. for (int i = 0; i <= aOrder; ++i)
  121. aCpTmp[i] = a->controlPoints()[i];
  122. for (int i = 0; i <= bOrder; ++i)
  123. corner[i] = b->controlPoints()[i];
  124. if (aCpTmp[aOrder] != *corner)
  125. return 0;
  126. simplifyDegenerateCurve(aCpTmp, aOrder);
  127. simplifyDegenerateCurve(corner, bOrder);
  128. for (int i = 0; i < aOrder; ++i)
  129. corner[i-aOrder] = aCpTmp[i];
  130. return convergentCurveOrdering(corner, aOrder, bOrder);
  131. }
  132. }