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