Skip to content

Commit e269bfe

Browse files
committed
feat(@typegpu/sdf): Add arc, capped cylinder, rhombus, and triangle shapes
1 parent 63ec92c commit e269bfe

3 files changed

Lines changed: 157 additions & 3 deletions

File tree

‎packages/typegpu-sdf/src/2d.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,25 @@ export const sdPie = tgpu.fn(
185185
const m = distance(p_w, sc * clamp(dot(p_w, sc), 0, radius));
186186
return max(l, m * sign(sc.y * p_w.x - sc.x * p_w.y));
187187
});
188+
189+
/**
190+
* Signed distance function for a circular arc.
191+
* The circle is centered at the origin; the arc is symmetric about the negative Y axis.
192+
*
193+
* @param position Point to evaluate, relative to the circle's center
194+
* @param sc Sine and cosine of the arc's half-angle: `vec2f(sin(angle / 2), cos(angle / 2))`,
195+
* where `angle` is the full sweep in radians, from 0 to 2 * PI
196+
* @param radius Radius of the arc's centerline (non-negative)
197+
* @returns Distance to the core of the arc (non-negative)
198+
*/
199+
export const sdArc = tgpu.fn(
200+
[vec2f, vec2f, f32],
201+
f32,
202+
)((position, sc, radius) => {
203+
'use gpu';
204+
const pos = vec2f(abs(position.x), -position.y);
205+
if (sc.y * pos.x > sc.x * pos.y) {
206+
return length(pos - sc * radius);
207+
}
208+
return abs(length(pos) - radius);
209+
});

‎packages/typegpu-sdf/src/3d.ts‎

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
import { tgpu } from 'typegpu';
2-
import { f32, vec3f } from 'typegpu/data';
3-
import { abs, distance, dot, length, max, min, saturate } from 'typegpu/std';
2+
import { f32, type v2f, type v3f, vec2f, vec3f } from 'typegpu/data';
3+
import {
4+
abs,
5+
clamp,
6+
cross,
7+
distance,
8+
dot,
9+
length,
10+
max,
11+
min,
12+
saturate,
13+
select,
14+
sign,
15+
sqrt,
16+
} from 'typegpu/std';
417

518
/**
619
* Signed distance function for a sphere
@@ -115,3 +128,110 @@ export const sdCapsule = tgpu.fn(
115128
const h = saturate(dot(pa, ba) / dot(ba, ba));
116129
return distance(pa, ba * h) - radius;
117130
});
131+
132+
/**
133+
* Squared length of a vector.
134+
* @param a Vector whose squared length to compute
135+
*/
136+
const dot2 = (a: v2f | v3f) => {
137+
'use gpu';
138+
return dot(a, a);
139+
};
140+
141+
/**
142+
* Unsigned distance function for a filled, zero-thickness triangle in 3D space.
143+
* The vertices must be distinct and non-collinear, and may use either winding order.
144+
*
145+
* @param p Point to evaluate, in the same coordinate space as the vertices
146+
* @param a First vertex of the triangle
147+
* @param b Second vertex of the triangle
148+
* @param c Third vertex of the triangle
149+
* @returns Distance to the nearest point on the triangle, including its edges;
150+
* zero on the triangle and positive on either side of its plane
151+
*/
152+
export const sdTriangle3d = (p: v3f, a: v3f, b: v3f, c: v3f) => {
153+
'use gpu';
154+
const ba = b - a;
155+
const pa = p - a;
156+
const cb = c - b;
157+
const pb = p - b;
158+
const ac = a - c;
159+
const pc = p - c;
160+
const nor = cross(ba, ac);
161+
162+
const cond =
163+
sign(dot(cross(ba, nor), pa)) + sign(dot(cross(cb, nor), pb)) + sign(dot(cross(ac, nor), pc)) <
164+
2;
165+
166+
return sqrt(
167+
select(
168+
// false
169+
(dot(nor, pa) * dot(nor, pa)) / dot2(nor),
170+
// true
171+
min(
172+
min(
173+
dot2(ba * saturate(dot(ba, pa) / dot2(ba)) - pa),
174+
dot2(cb * saturate(dot(cb, pb) / dot2(cb)) - pb),
175+
),
176+
dot2(ac * saturate(dot(ac, pc) / dot2(ac)) - pc),
177+
),
178+
cond,
179+
),
180+
);
181+
};
182+
183+
/**
184+
* Signed distance function for a solid cylinder centered at the origin, along the Y axis,
185+
* with flat caps at `y = -h` and `y = h`.
186+
*
187+
* @param p Point to evaluate, relative to the cylinder's center
188+
* @param r Radius of the circular cross-section in the XZ plane (non-negative)
189+
* @param h Half-height of the cylinder; its full height is `2 * h` (non-negative)
190+
* @returns Negative inside the cylinder, zero on its surface, positive outside
191+
*/
192+
export const sdCappedCylinder = tgpu.fn(
193+
[vec3f, f32, f32],
194+
f32,
195+
)((p, r, h) => {
196+
'use gpu';
197+
const dd = abs(vec2f(length(p.xz), p.y)) - vec2f(r, h);
198+
return min(max(dd.x, dd.y), 0.0) + length(max(dd, vec2f()));
199+
});
200+
201+
/**
202+
* Computes `a.x * b.x - a.y * b.y` for the rhombus edge projection.
203+
* @param a First vector
204+
* @param b Second vector
205+
*/
206+
function ndot(a: v2f, b: v2f) {
207+
'use gpu';
208+
return a.x * b.x - a.y * b.y;
209+
}
210+
211+
/**
212+
* Signed distance function for a rhombus in the XZ plane, extruded along Y and centered
213+
* at the origin. Rounding expands the 2D cross-section; the caps at `y = +/-h` stay flat.
214+
*
215+
* @param p Point to evaluate, relative to the shape's center
216+
* @param la Half-length of the unrounded rhombus's diagonal along X (positive)
217+
* @param lb Half-length of the unrounded rhombus's diagonal along Z (positive)
218+
* @param h Half-height of the extrusion along Y (non-negative)
219+
* @param ra Outward rounding radius in the XZ plane (non-negative). Zero gives a sharp
220+
* rhombus with vertices at `(x, z) = (+/-la, 0)` and `(0, +/-lb)`; rounding increases
221+
* the X and Z half-extents to `la + ra` and `lb + ra`
222+
* @returns Negative inside the solid, zero on its surface, positive outside
223+
*/
224+
export const sdRhombus = tgpu.fn(
225+
[vec3f, f32, f32, f32, f32],
226+
f32,
227+
)((p, la, lb, h, ra) => {
228+
'use gpu';
229+
const ap = abs(p);
230+
const b = vec2f(la, lb);
231+
const f = clamp(ndot(b, b - ap.xz * 2) / dot2(b), -1, 1);
232+
const q = vec2f(
233+
length(ap.xz - b * vec2f(1 - f, 1 + f) * 0.5) * sign(ap.x * b.y + ap.z * b.x - b.x * b.y) - ra,
234+
ap.y - h,
235+
);
236+
return min(max(q.x, q.y), 0.0) + length(max(q, vec2f()));
237+
});

‎packages/typegpu-sdf/src/index.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
export { sdBezier, sdBezierApprox, sdBox2d, sdDisk, sdLine, sdPie, sdRoundedBox2d } from './2d.ts';
1+
export {
2+
sdArc,
3+
sdBezier,
4+
sdBezierApprox,
5+
sdBox2d,
6+
sdDisk,
7+
sdLine,
8+
sdPie,
9+
sdRoundedBox2d,
10+
} from './2d.ts';
211

312
export {
413
sdBox3d,
514
sdBoxFrame3d,
15+
sdCappedCylinder,
616
sdCapsule,
717
sdLine3d,
818
sdPlane,
19+
sdRhombus,
920
sdRoundedBox3d,
1021
sdSphere,
22+
sdTriangle3d,
1123
} from './3d.ts';
1224

1325
export {

0 commit comments

Comments
 (0)