@@ -28,9 +28,18 @@ struct CubicData {
28
28
vec2 p2;
29
29
};
30
30
31
- struct Position {
32
- uint index;
33
- uint count;
31
+ struct QuadDecomposition {
32
+ float a0;
33
+ float a2;
34
+ float u0;
35
+ float u_scale;
36
+ uint line_count;
37
+ float steps;
38
+ };
39
+
40
+ struct PathComponent {
41
+ uint index; // Location in buffer
42
+ uint count; // Number of points. 4 = cubic, 3 = quad, 2 = line.
34
43
};
35
44
36
45
// / Solve for point on a quadratic Bezier curve defined by starting point `p1`,
@@ -65,4 +74,92 @@ float Cross(vec2 p1, vec2 p2) {
65
74
return p1.x * p2.y - p1.y * p2.x;
66
75
}
67
76
77
+ QuadData GenerateQuadraticFromCubic(CubicData cubic,
78
+ uint index,
79
+ float quad_count) {
80
+ float t0 = index / quad_count;
81
+ float t1 = (index + 1 ) / quad_count;
82
+
83
+ // calculate the subsegment
84
+ vec2 sub_p1 = CubicSolve(cubic, t0);
85
+ vec2 sub_p2 = CubicSolve(cubic, t1);
86
+ QuadData quad = QuadData(3.0 * (cubic.cp1 - cubic.p1), //
87
+ 3.0 * (cubic.cp2 - cubic.cp1), //
88
+ 3.0 * (cubic.p2 - cubic.cp2));
89
+ float sub_scale = (t1 - t0) * (1.0 / 3.0 );
90
+ vec2 sub_cp1 = sub_p1 + sub_scale * QuadraticSolve(quad, t0);
91
+ vec2 sub_cp2 = sub_p2 - sub_scale * QuadraticSolve(quad, t1);
92
+
93
+ vec2 quad_p1x2 = 3.0 * sub_cp1 - sub_p1;
94
+ vec2 quad_p2x2 = 3.0 * sub_cp2 - sub_p2;
95
+
96
+ return QuadData(sub_p1, //
97
+ ((quad_p1x2 + quad_p2x2) / 4.0 ), //
98
+ sub_p2);
99
+ }
100
+
101
+ uint EstimateQuadraticCount(CubicData cubic, float accuracy) {
102
+ // The maximum error, as a vector from the cubic to the best approximating
103
+ // quadratic, is proportional to the third derivative, which is constant
104
+ // across the segment. Thus, the error scales down as the third power of
105
+ // the number of subdivisions. Our strategy then is to subdivide `t` evenly.
106
+ //
107
+ // This is an overestimate of the error because only the component
108
+ // perpendicular to the first derivative is important. But the simplicity is
109
+ // appealing.
110
+
111
+ // This magic number is the square of 36 / sqrt(3).
112
+ // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
113
+ float max_hypot2 = 432.0 * accuracy * accuracy;
114
+
115
+ vec2 err_v = (3.0 * cubic.cp2 - cubic.p2) - (3.0 * cubic.cp1 - cubic.p1);
116
+ float err = dot (err_v, err_v);
117
+ return uint (max (1 ., ceil (pow (err * (1.0 / max_hypot2), 1 . / 6.0 ))));
118
+ }
119
+
120
+ QuadDecomposition DecomposeQuad(QuadData quad, float tolerance) {
121
+ float sqrt_tolerance = sqrt (tolerance);
122
+
123
+ vec2 d01 = quad.cp - quad.p1;
124
+ vec2 d12 = quad.p2 - quad.cp;
125
+ vec2 dd = d01 - d12;
126
+ float c = Cross(quad.p2 - quad.p1, dd);
127
+ float x0 = dot (d01, dd) * 1 . / c;
128
+ float x2 = dot (d12, dd) * 1 . / c;
129
+ float scale = abs (c / (sqrt (dd.x * dd.x + dd.y * dd.y) * (x2 - x0)));
130
+
131
+ float a0 = ApproximateParabolaIntegral(x0);
132
+ float a2 = ApproximateParabolaIntegral(x2);
133
+ float val = 0 .f;
134
+ if (isfinite(scale)) {
135
+ float da = abs (a2 - a0);
136
+ float sqrt_scale = sqrt (scale);
137
+ if ((x0 < 0 && x2 < 0 ) || (x0 >= 0 && x2 >= 0 )) {
138
+ val = da * sqrt_scale;
139
+ } else {
140
+ // cusp case
141
+ float xmin = sqrt_tolerance / sqrt_scale;
142
+ val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin);
143
+ }
144
+ }
145
+ float u0 = ApproximateParabolaIntegral(a0);
146
+ float u2 = ApproximateParabolaIntegral(a2);
147
+ float u_scale = 1 . / (u2 - u0);
148
+
149
+ uint line_count = uint (max (1 ., ceil (0.5 * val / sqrt_tolerance)) + 1 .);
150
+ float steps = 1 . / line_count;
151
+
152
+ return QuadDecomposition(a0, a2, u0, u_scale, line_count, steps);
153
+ }
154
+
155
+ vec2 GenerateLineFromQuad(QuadData quad,
156
+ uint index,
157
+ QuadDecomposition decomposition) {
158
+ float u = index * decomposition.steps;
159
+ float a = decomposition.a0 + (decomposition.a2 - decomposition.a0) * u;
160
+ float t = (ApproximateParabolaIntegral(a) - decomposition.u0) *
161
+ decomposition.u_scale;
162
+ return QuadraticSolve(quad, t);
163
+ }
164
+
68
165
#endif
0 commit comments