Skip to content

Commit bdfd02a

Browse files
committed
#16 add sdBezier and unit test
1 parent 631b3e5 commit bdfd02a

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

Sources/vger/sdf.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,59 @@ inline float udBezier(float2 pos, float2 A, float2 B, float2 C )
118118
return sqrt( res );
119119
}
120120

121+
inline float3 solveCubic(float a, float b, float c)
122+
{
123+
float p = b - a*a / 3.0, p3 = p*p*p;
124+
float q = a * (2.0*a*a - 9.0*b) / 27.0 + c;
125+
float d = q*q + 4.0*p3 / 27.0;
126+
float offset = -a / 3.0;
127+
if(d >= 0.0) {
128+
float z = sqrt(d);
129+
float2 x = (float2{z, -z} - q) / 2.0;
130+
float2 uv = sign(x)*pow(abs(x), float2(1.0/3.0));
131+
return float3(offset + uv.x + uv.y);
132+
}
133+
float v = acos(-sqrt(-27.0 / p3) * q / 2.0) / 3.0;
134+
float m = cos(v), n = sin(v)*1.732050808;
135+
return float3{m + m, -n - m, n - m} * sqrt(-p / 3.0) + offset;
136+
}
137+
138+
inline float sdBezier(float2 A, float2 B, float2 C, float2 p)
139+
{
140+
// Offset to avoid degeneracy when B == midpoint(A, C)
141+
B = mix(B + float2(1e-4), B, step(float2(1e-6f), abs(B * 2.0 - A - C)));
142+
143+
float2 a = B - A;
144+
float2 b = A - 2.0 * B + C;
145+
float2 c = 2.0 * a;
146+
float2 d = A - p;
147+
148+
float3 k = float3{3.0f * dot(a, b),
149+
2.0f * dot(a, a) + dot(d, b),
150+
dot(d, a)} / dot(b, b);
151+
152+
float3 t = clamp(solveCubic(k.x, k.y, k.z), 0.0, 1.0);
153+
154+
float minDist = 1e10;
155+
float bestT = 0.0;
156+
for (int i = 0; i < 3; ++i) {
157+
float ti = i == 0 ? t.x : (i == 1 ? t.y : t.z);
158+
float2 qi = A + (c + b * ti) * ti; // Evaluate position on curve
159+
float di = length(qi - p);
160+
if (di < minDist) {
161+
minDist = di;
162+
bestT = ti;
163+
}
164+
}
165+
166+
// Evaluate curve and tangent at bestT
167+
float2 pos = A + (c + b * bestT) * bestT;
168+
float2 tangent = normalize(2.0 * mix(B - A, C - B, bestT));
169+
float2 diff = normalize(pos - p);
170+
171+
return minDist * sign(tangent.x * diff.y - tangent.y * diff.x);
172+
}
173+
121174
inline float sdSubtract(float d1, float d2)
122175
{
123176
return max(-d1, d2);

Tests/vgerTests/sdfTests.mm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,15 @@ - (void) testBezierCollinear {
100100
XCTAssertEqualWithAccuracy(d, 1.0, 0.001);
101101
}
102102

103+
- (void) testSdBezier {
104+
105+
float2 a{0,0};
106+
float2 b{1,1};
107+
float2 c{2,0};
108+
109+
XCTAssertEqualWithAccuracy(sdBezier(a, b, c, float2{0,0}), 0.0, 0.001);
110+
XCTAssertEqualWithAccuracy(sdBezier(a, b, c, float2{1,0}), 0.5, 0.001);
111+
XCTAssertEqualWithAccuracy(sdBezier(a, b, c, float2{1,1}), -0.5, 0.001);
112+
}
113+
103114
@end

0 commit comments

Comments
 (0)