@thi.ng/geom-sdf 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -1
- package/README.md +91 -2
- package/api.d.ts +94 -4
- package/as-polygons.d.ts +26 -0
- package/as-polygons.js +39 -0
- package/as-sdf.d.ts +8 -0
- package/as-sdf.js +53 -18
- package/bounds.d.ts +30 -0
- package/bounds.js +31 -0
- package/dist.d.ts +90 -3
- package/dist.js +143 -56
- package/domain.d.ts +34 -0
- package/domain.js +63 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/ops.d.ts +19 -7
- package/ops.js +74 -22
- package/package.json +21 -4
- package/sample.d.ts +15 -5
- package/sample.js +14 -19
- package/shapes.d.ts +25 -1
- package/shapes.js +59 -11
package/dist.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SQRT3 } from "@thi.ng/math";
|
|
1
|
+
import { SQRT3 } from "@thi.ng/math/api";
|
|
2
2
|
import { clamp01 } from "@thi.ng/math/interval";
|
|
3
3
|
import { abs2 } from "@thi.ng/vectors/abs";
|
|
4
4
|
import { add2 } from "@thi.ng/vectors/add";
|
|
@@ -16,55 +16,135 @@ import { powN2 } from "@thi.ng/vectors/pown";
|
|
|
16
16
|
import { sign2 } from "@thi.ng/vectors/sign";
|
|
17
17
|
import { some3 } from "@thi.ng/vectors/some";
|
|
18
18
|
import { sub2 } from "@thi.ng/vectors/sub";
|
|
19
|
-
const
|
|
19
|
+
const t1 = [];
|
|
20
|
+
const t2 = [];
|
|
21
|
+
const t3 = [];
|
|
22
|
+
const t4 = [];
|
|
23
|
+
const t5 = [];
|
|
24
|
+
const { abs, cos, min, max, sign, sin, sqrt } = Math;
|
|
25
|
+
const mag2 = (v) => sqrt(magSq2(v));
|
|
26
|
+
/**
|
|
27
|
+
* Computes signed distance to centered 2D circle of given `radius`.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* Ported from code by Inigo Quilez:
|
|
31
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
32
|
+
*
|
|
33
|
+
* @param p
|
|
34
|
+
* @param radius
|
|
35
|
+
*/
|
|
20
36
|
export const distCircle2 = (p, radius) => mag2(p) - radius;
|
|
37
|
+
/**
|
|
38
|
+
* Computes signed distance to centered 2D box of given `halfSize`.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Ported from code by Inigo Quilez:
|
|
42
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
43
|
+
*
|
|
44
|
+
* @param p
|
|
45
|
+
* @param halfSize
|
|
46
|
+
*/
|
|
21
47
|
export const distBox2 = (p, halfSize) => {
|
|
22
|
-
const d = sub2(null, abs2(
|
|
23
|
-
return
|
|
48
|
+
const d = sub2(null, abs2(t1, p), halfSize);
|
|
49
|
+
return min(max(d[0], d[1]), 0) + mag2(max2(null, d, ZERO2));
|
|
24
50
|
};
|
|
25
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Computes signed distance to the 2D polygon defined by given point array.
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* Ported from code by Inigo Quilez:
|
|
56
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
57
|
+
*
|
|
58
|
+
* @param p
|
|
59
|
+
* @param pts
|
|
60
|
+
*/
|
|
61
|
+
export const distPolygon2 = (p, pts) => {
|
|
26
62
|
let d = distSq2(p, pts[0]);
|
|
27
63
|
let s = 1;
|
|
28
64
|
const py = p[1];
|
|
29
|
-
const t1 = [];
|
|
30
|
-
const t2 = [];
|
|
31
|
-
const t3 = [];
|
|
32
65
|
for (let n = pts.length, i = 0, j = n - 1; i < n; j = i, i++) {
|
|
33
66
|
const pi = pts[i];
|
|
34
67
|
const pj = pts[j];
|
|
35
68
|
const e = sub2(t1, pj, pi);
|
|
36
69
|
const w = sub2(t2, p, pi);
|
|
37
|
-
d =
|
|
70
|
+
d = min(d, distSq2(w, mulN2(t3, e, clamp01(dot2(w, e) / magSq2(e)))));
|
|
38
71
|
const c = [py >= pi[1], py < pj[1], e[0] * w[1] > e[1] * w[0]];
|
|
39
72
|
if (every3(c) || !some3(c))
|
|
40
73
|
s *= -1;
|
|
41
74
|
}
|
|
42
|
-
return s *
|
|
75
|
+
return s * sqrt(d);
|
|
43
76
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Computes distance to 2D line segment defined by endpoints `a` and `b`.
|
|
79
|
+
*
|
|
80
|
+
* @remarks
|
|
81
|
+
* Ported from code by Inigo Quilez:
|
|
82
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
83
|
+
*
|
|
84
|
+
* @param p
|
|
85
|
+
* @param a
|
|
86
|
+
* @param b
|
|
87
|
+
*/
|
|
88
|
+
export const distSegment2 = (p, a, b) => {
|
|
89
|
+
const pa = sub2(t1, p, a);
|
|
90
|
+
const ba = sub2(t2, b, a);
|
|
91
|
+
return mag2(sub2(null, pa, mulN2(null, ba, clamp01(dot2(pa, ba) / magSq2(ba)))));
|
|
49
92
|
};
|
|
50
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Computes distance to 2D polyline defined by given point array (i.e. the union
|
|
95
|
+
* of pairwise results of {@link distSegment2}).
|
|
96
|
+
*
|
|
97
|
+
* @param p
|
|
98
|
+
* @param pts
|
|
99
|
+
*/
|
|
100
|
+
export const distPolyline2 = (p, pts) => {
|
|
51
101
|
let d = Infinity;
|
|
52
102
|
for (let i = 1, n = pts.length; i < n; i++) {
|
|
53
|
-
d =
|
|
103
|
+
d = min(d, distSegment2(p, pts[i - 1], pts[i]));
|
|
54
104
|
}
|
|
55
105
|
return d;
|
|
56
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Computes signed distance to circular arc with given aperture, radius and
|
|
109
|
+
* radius offset (thickness). The aperture is symmetric along the Y-axis and has
|
|
110
|
+
* its origin in +Y.
|
|
111
|
+
*
|
|
112
|
+
* @remarks
|
|
113
|
+
* Ported from code by Inigo Quilez:
|
|
114
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
115
|
+
*
|
|
116
|
+
* @param p
|
|
117
|
+
* @param apert - pre-computed vec2 of [sin, cos] of aperture angle
|
|
118
|
+
* @param ra - inner radius
|
|
119
|
+
* @param rb - outer radius offset (thickness)
|
|
120
|
+
*/
|
|
57
121
|
export const distArc2 = (p, apert, ra, rb) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
122
|
+
t1[0] = abs(p[0]);
|
|
123
|
+
t1[1] = p[1];
|
|
124
|
+
return ((apert[1] * t1[0] > apert[0] * t1[1]
|
|
125
|
+
? mag2(maddN2(t1, apert, -ra, t1))
|
|
126
|
+
: abs(mag2(t1) - ra)) - rb);
|
|
62
127
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Computes (unsigned, by default) distance to given 2D quadratic bezier
|
|
130
|
+
* segment. If `signed` is set to true, points in the curve's interior region
|
|
131
|
+
* will yield negative distances.
|
|
132
|
+
*
|
|
133
|
+
* @remarks
|
|
134
|
+
* Based on code by Inigo Quilez:
|
|
135
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
136
|
+
*
|
|
137
|
+
* @param pos
|
|
138
|
+
* @param a
|
|
139
|
+
* @param b
|
|
140
|
+
* @param c
|
|
141
|
+
* @param signed
|
|
142
|
+
*/
|
|
143
|
+
export const distQuadratic2 = (pos, a, b, c, signed = false) => {
|
|
144
|
+
const aa = sub2(t1, b, a);
|
|
145
|
+
const bb = add2(null, maddN2(t2, b, -2, a), c);
|
|
146
|
+
const cc = mulN2(t3, aa, 2);
|
|
147
|
+
const dd = sub2(t4, a, pos);
|
|
68
148
|
const kk = 1 / magSq2(bb);
|
|
69
149
|
const kx = kk * dot2(aa, bb);
|
|
70
150
|
const ky = (kk * (2 * magSq2(aa) + dot2(dd, bb))) / 3;
|
|
@@ -75,38 +155,45 @@ export const distQuadratic2 = (pos, a, b, c, signed = true) => {
|
|
|
75
155
|
const q2 = q * q;
|
|
76
156
|
let h = q2 + 4 * p3;
|
|
77
157
|
if (h >= 0) {
|
|
78
|
-
h =
|
|
158
|
+
h = sqrt(h);
|
|
79
159
|
const x = [(h - q) / 2, (-h - q) / 2];
|
|
80
|
-
const uv = mul2(null, sign2(
|
|
160
|
+
const uv = mul2(null, sign2(t1, x), powN2(null, abs2(null, x), 1 / 3));
|
|
81
161
|
const t = clamp01(uv[0] + uv[1] - kx);
|
|
82
|
-
const qq = maddN2(null, maddN2(
|
|
162
|
+
const qq = maddN2(null, maddN2(t1, bb, t, cc), t, dd);
|
|
83
163
|
return (mag2(qq) *
|
|
84
|
-
(signed ?
|
|
164
|
+
(signed ? sign(cross2(maddN2(bb, bb, 2 * t, cc), qq)) : 1));
|
|
85
165
|
}
|
|
86
166
|
else {
|
|
87
|
-
const z =
|
|
167
|
+
const z = sqrt(-p);
|
|
88
168
|
const v = Math.acos(q / (p * z * 2)) / 3;
|
|
89
|
-
const m =
|
|
90
|
-
const n =
|
|
169
|
+
const m = cos(v);
|
|
170
|
+
const n = sin(v) * SQRT3;
|
|
91
171
|
const tx = clamp01((m + m) * z - kx);
|
|
92
172
|
const ty = clamp01((-n - m) * z - kx);
|
|
93
|
-
const qx = maddN2(null, maddN2(
|
|
173
|
+
const qx = maddN2(null, maddN2(t1, bb, tx, cc), tx, dd);
|
|
94
174
|
const dx = magSq2(qx);
|
|
95
|
-
const qy = maddN2(null, maddN2(
|
|
175
|
+
const qy = maddN2(null, maddN2(t5, bb, ty, cc), ty, dd);
|
|
96
176
|
const dy = magSq2(qy);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(signed ?
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return (Math.sqrt(dy) *
|
|
103
|
-
(signed ? Math.sign(cross2(maddN2([], bb, 2 * ty, cc), qy)) : 1));
|
|
104
|
-
}
|
|
177
|
+
return dx < dy
|
|
178
|
+
? sqrt(dx) *
|
|
179
|
+
(signed ? sign(cross2(maddN2(bb, bb, 2 * tx, cc), qx)) : 1)
|
|
180
|
+
: sqrt(dy) *
|
|
181
|
+
(signed ? sign(cross2(maddN2(bb, bb, 2 * ty, cc), qy)) : 1);
|
|
105
182
|
}
|
|
106
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Computes signed distance to centered 2D ellipse.
|
|
186
|
+
*
|
|
187
|
+
* @remarks
|
|
188
|
+
* Based on code by Inigo Quilez:
|
|
189
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
190
|
+
*
|
|
191
|
+
* @param p
|
|
192
|
+
* @param radii
|
|
193
|
+
*/
|
|
107
194
|
export const distEllipse2 = ([px, py], [abx, aby]) => {
|
|
108
|
-
px =
|
|
109
|
-
py =
|
|
195
|
+
px = abs(px);
|
|
196
|
+
py = abs(py);
|
|
110
197
|
if (px > py) {
|
|
111
198
|
let t = px;
|
|
112
199
|
px = py;
|
|
@@ -129,22 +216,22 @@ export const distEllipse2 = ([px, py], [abx, aby]) => {
|
|
|
129
216
|
let co;
|
|
130
217
|
if (d < 0) {
|
|
131
218
|
const h = Math.acos(q / c3) / 3;
|
|
132
|
-
const s =
|
|
133
|
-
const t =
|
|
134
|
-
const rx =
|
|
135
|
-
const ry =
|
|
136
|
-
co = (ry +
|
|
219
|
+
const s = cos(h);
|
|
220
|
+
const t = sin(h) * SQRT3;
|
|
221
|
+
const rx = sqrt(-c * (s + t + 2) + m2);
|
|
222
|
+
const ry = sqrt(-c * (s - t + 2) + m2);
|
|
223
|
+
co = (ry + sign(l) * rx + abs(g) / (rx * ry) - m) / 2;
|
|
137
224
|
}
|
|
138
225
|
else {
|
|
139
|
-
const h = 2 * m * n *
|
|
140
|
-
const s =
|
|
141
|
-
const u =
|
|
226
|
+
const h = 2 * m * n * sqrt(d);
|
|
227
|
+
const s = sign(q + h) * Math.cbrt(abs(q + h));
|
|
228
|
+
const u = sign(q - h) * Math.cbrt(abs(q - h));
|
|
142
229
|
const rx = -s - u - c * 4 + 2 * m2;
|
|
143
230
|
const ry = (s - u) * SQRT3;
|
|
144
231
|
const rm = Math.hypot(rx, ry);
|
|
145
|
-
co = (ry /
|
|
232
|
+
co = (ry / sqrt(rm - rx) + (2 * g) / rm - m) / 2;
|
|
146
233
|
}
|
|
147
234
|
const rx = abx * co;
|
|
148
|
-
const ry = aby *
|
|
149
|
-
return Math.hypot(rx - px, ry - py) *
|
|
235
|
+
const ry = aby * sqrt(1 - co * co);
|
|
236
|
+
return Math.hypot(rx - px, ry - py) * sign(py - ry);
|
|
150
237
|
};
|
package/domain.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReadonlyVec, Vec } from "@thi.ng/vectors/api";
|
|
2
|
+
/**
|
|
3
|
+
* @remarks
|
|
4
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
5
|
+
* https://mercury.sexy/hg_sdf/
|
|
6
|
+
*
|
|
7
|
+
* @param size
|
|
8
|
+
*/
|
|
9
|
+
export declare const repeat2: (size: ReadonlyVec) => (p: ReadonlyVec) => Vec;
|
|
10
|
+
/**
|
|
11
|
+
* @remarks
|
|
12
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
13
|
+
* https://mercury.sexy/hg_sdf/
|
|
14
|
+
*
|
|
15
|
+
* @param size
|
|
16
|
+
*/
|
|
17
|
+
export declare const repeatMirror2: (size: ReadonlyVec) => (p: ReadonlyVec) => Vec;
|
|
18
|
+
/**
|
|
19
|
+
* @remarks
|
|
20
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
21
|
+
* https://mercury.sexy/hg_sdf/
|
|
22
|
+
*
|
|
23
|
+
* @param size
|
|
24
|
+
*/
|
|
25
|
+
export declare const repeatGrid2: (size: ReadonlyVec) => (p: ReadonlyVec) => ReadonlyVec;
|
|
26
|
+
/**
|
|
27
|
+
* @remarks
|
|
28
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
29
|
+
* https://mercury.sexy/hg_sdf/
|
|
30
|
+
*
|
|
31
|
+
* @param n - number of repetition
|
|
32
|
+
*/
|
|
33
|
+
export declare const repeatPolar2: (n: number) => (p: ReadonlyVec) => number[];
|
|
34
|
+
//# sourceMappingURL=domain.d.ts.map
|
package/domain.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { cossin } from "@thi.ng/math";
|
|
2
|
+
import { TAU } from "@thi.ng/math/api";
|
|
3
|
+
import { mod } from "@thi.ng/math/prec";
|
|
4
|
+
import { add2 } from "@thi.ng/vectors/add";
|
|
5
|
+
import { div2 } from "@thi.ng/vectors/div";
|
|
6
|
+
import { floor2 } from "@thi.ng/vectors/floor";
|
|
7
|
+
import { mag } from "@thi.ng/vectors/mag";
|
|
8
|
+
import { mod2 } from "@thi.ng/vectors/mod";
|
|
9
|
+
import { modN2 } from "@thi.ng/vectors/modn";
|
|
10
|
+
import { mul2 } from "@thi.ng/vectors/mul";
|
|
11
|
+
import { mulN2 } from "@thi.ng/vectors/muln";
|
|
12
|
+
import { sub2 } from "@thi.ng/vectors/sub";
|
|
13
|
+
import { subN2 } from "@thi.ng/vectors/subn";
|
|
14
|
+
/**
|
|
15
|
+
* @remarks
|
|
16
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
17
|
+
* https://mercury.sexy/hg_sdf/
|
|
18
|
+
*
|
|
19
|
+
* @param size
|
|
20
|
+
*/
|
|
21
|
+
export const repeat2 = (size) => {
|
|
22
|
+
const s2 = mulN2([], size, 0.5);
|
|
23
|
+
return (p) => sub2(null, mod2(null, add2([], s2, p), size), s2);
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* @remarks
|
|
27
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
28
|
+
* https://mercury.sexy/hg_sdf/
|
|
29
|
+
*
|
|
30
|
+
* @param size
|
|
31
|
+
*/
|
|
32
|
+
export const repeatMirror2 = (size) => {
|
|
33
|
+
const c = [];
|
|
34
|
+
const s2 = mulN2([], size, 0.5);
|
|
35
|
+
return (p) => mul2(null, sub2(null, mod2(null, add2([], s2, p), size), s2), subN2(null, mulN2(null, modN2(null, floor2(null, div2(null, add2(c, s2, p), size)), 2), 2), 1));
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* @remarks
|
|
39
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
40
|
+
* https://mercury.sexy/hg_sdf/
|
|
41
|
+
*
|
|
42
|
+
* @param size
|
|
43
|
+
*/
|
|
44
|
+
export const repeatGrid2 = (size) => {
|
|
45
|
+
const s2 = mulN2([], size, 0.5);
|
|
46
|
+
const rm = repeatMirror2(size);
|
|
47
|
+
return (p) => {
|
|
48
|
+
p = sub2(null, rm(p), s2);
|
|
49
|
+
return p[0] > p[1] ? [p[1], p[0]] : p;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* @remarks
|
|
54
|
+
* Ported from: HG_SDF by Mercury Demogroup
|
|
55
|
+
* https://mercury.sexy/hg_sdf/
|
|
56
|
+
*
|
|
57
|
+
* @param n - number of repetition
|
|
58
|
+
*/
|
|
59
|
+
export const repeatPolar2 = (n) => {
|
|
60
|
+
const theta = TAU / n;
|
|
61
|
+
const halfTheta = theta / 2;
|
|
62
|
+
return (p) => cossin(mod(Math.atan2(p[1], p[0]) + halfTheta, theta) - halfTheta, mag(p));
|
|
63
|
+
};
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/ops.d.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Fn3, FnN2 } from "@thi.ng/api";
|
|
2
|
+
import type { FieldCoeff, SDFn } from "./api.js";
|
|
2
3
|
export declare const abs: (sdf: SDFn) => SDFn;
|
|
3
4
|
export declare const flip: (sdf: SDFn) => SDFn;
|
|
4
|
-
export declare const
|
|
5
|
+
export declare const offset: (sdf: SDFn, r: number | FieldCoeff) => SDFn;
|
|
6
|
+
export declare const defOp: (op: FnN2) => (children: SDFn[]) => SDFn;
|
|
5
7
|
export declare const union: (children: SDFn[]) => SDFn;
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
8
|
-
export declare const
|
|
9
|
-
export declare const
|
|
10
|
-
export declare const
|
|
8
|
+
export declare const isec: (children: SDFn[]) => SDFn;
|
|
9
|
+
export declare const diff: (children: SDFn[]) => SDFn;
|
|
10
|
+
export declare const defParamOp: <T = number>(op: Fn3<number, number, T, number>) => (k: T | FieldCoeff<T>, children: SDFn[]) => SDFn;
|
|
11
|
+
export declare const smoothUnion: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
12
|
+
export declare const smoothIsec: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
13
|
+
export declare const smoothDiff: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
14
|
+
export declare const chamferUnion: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
15
|
+
export declare const chamferIsec: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
16
|
+
export declare const chamferDiff: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
17
|
+
export declare const roundUnion: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
18
|
+
export declare const roundIsec: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
19
|
+
export declare const roundDiff: (k: number | FieldCoeff<number>, children: SDFn[]) => SDFn;
|
|
20
|
+
export declare const stepUnion: (k: [number, number] | FieldCoeff<[number, number]>, children: SDFn[]) => SDFn;
|
|
21
|
+
export declare const stepIsec: (k: [number, number] | FieldCoeff<[number, number]>, children: SDFn[]) => SDFn;
|
|
22
|
+
export declare const stepDiff: (k: [number, number] | FieldCoeff<[number, number]>, children: SDFn[]) => SDFn;
|
|
11
23
|
//# sourceMappingURL=ops.d.ts.map
|
package/ops.js
CHANGED
|
@@ -1,22 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const
|
|
8
|
-
export const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import { isFunction } from "@thi.ng/checks/is-function";
|
|
2
|
+
import { assert } from "@thi.ng/errors/assert";
|
|
3
|
+
import { clamp01, mix, mod, SQRT2_2 } from "@thi.ng/math";
|
|
4
|
+
const __asField = (k) => (isFunction(k) ? k : () => k);
|
|
5
|
+
const __ensureChildren = (children, min = 1) => assert(children.length >= 1, `require at least ${min} SDF(s)`);
|
|
6
|
+
const { min, max } = Math;
|
|
7
|
+
export const abs = (sdf) => (p, minD) => Math.abs(sdf(p, minD));
|
|
8
|
+
export const flip = (sdf) => (p, minD) => -sdf(p, minD);
|
|
9
|
+
export const offset = (sdf, r) => isFunction(r)
|
|
10
|
+
? (p, minD) => sdf(p, minD) - r(p)
|
|
11
|
+
: (p, minD) => sdf(p, minD) - r;
|
|
12
|
+
export const defOp = (op) => (children) => {
|
|
13
|
+
__ensureChildren(children);
|
|
14
|
+
const n = children.length;
|
|
15
|
+
return (p, minD = Infinity) => {
|
|
16
|
+
let res = children[0](p, minD);
|
|
17
|
+
if (res < minD)
|
|
18
|
+
minD = res;
|
|
19
|
+
for (let i = 1; i < n; i++) {
|
|
20
|
+
const d = children[i](p, minD);
|
|
21
|
+
if (d < minD)
|
|
22
|
+
minD = d;
|
|
23
|
+
res = op(res, d);
|
|
24
|
+
}
|
|
25
|
+
return res;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export const union = defOp(min);
|
|
29
|
+
export const isec = defOp(max);
|
|
30
|
+
export const diff = defOp((a, b) => max(a, -b));
|
|
31
|
+
export const defParamOp = (op) => (k, children) => {
|
|
32
|
+
__ensureChildren(children);
|
|
33
|
+
const kfield = __asField(k);
|
|
34
|
+
const n = children.length;
|
|
35
|
+
return (p, minD = Infinity) => {
|
|
36
|
+
const $k = kfield(p);
|
|
37
|
+
let res = children[0](p, minD);
|
|
38
|
+
if (res < minD)
|
|
39
|
+
minD = res;
|
|
40
|
+
for (let i = 1; i < n; i++) {
|
|
41
|
+
const d = children[i](p, minD);
|
|
42
|
+
if (d < minD)
|
|
43
|
+
minD = d;
|
|
44
|
+
res = op(res, d, $k);
|
|
45
|
+
}
|
|
46
|
+
return res;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export const smoothUnion = defParamOp((a, b, k) => {
|
|
50
|
+
const h = clamp01(0.5 + (0.5 * (b - a)) / k);
|
|
51
|
+
return mix(b, a, h) - k * h * (1 - h);
|
|
52
|
+
});
|
|
53
|
+
export const smoothIsec = defParamOp((a, b, k) => {
|
|
54
|
+
const h = clamp01(0.5 - (0.5 * (b - a)) / k);
|
|
55
|
+
return mix(b, a, h) + k * h * (1 - h);
|
|
56
|
+
});
|
|
57
|
+
export const smoothDiff = defParamOp((a, b, k) => {
|
|
58
|
+
const h = clamp01(0.5 - (0.5 * (a + b)) / k);
|
|
59
|
+
return mix(a, -b, h) + k * h * (1 - h);
|
|
60
|
+
});
|
|
61
|
+
export const chamferUnion = defParamOp((a, b, k) => min(min(a, b), (a - k + b) * SQRT2_2));
|
|
62
|
+
export const chamferIsec = defParamOp((a, b, k) => max(max(a, b), (a + k + b) * SQRT2_2));
|
|
63
|
+
export const chamferDiff = defParamOp((a, b, k) => max(max(a, -b), (a + k - b) * SQRT2_2));
|
|
64
|
+
export const roundUnion = defParamOp((a, b, k) => max(k, min(a, b)) - Math.hypot(max(k - a, 0), max(k - b, 0)));
|
|
65
|
+
export const roundIsec = defParamOp((a, b, k) => min(-k, max(a, b)) + Math.hypot(max(k + a, 0), max(k + b, 0)));
|
|
66
|
+
export const roundDiff = defParamOp((a, b, k) => min(-k, max(a, -b)) + Math.hypot(max(k + a, 0), max(k - b, 0)));
|
|
67
|
+
const __steps = (a, b, [r, n]) => {
|
|
68
|
+
const s = r / n;
|
|
69
|
+
const u = b - r;
|
|
70
|
+
return min(min(a, b), 0.5 * (u + a + Math.abs(mod(u - a + s, 2 * s) - s)));
|
|
71
|
+
};
|
|
72
|
+
export const stepUnion = defParamOp(__steps);
|
|
73
|
+
export const stepIsec = defParamOp((a, b, k) => -__steps(-a, -b, k));
|
|
74
|
+
export const stepDiff = defParamOp((a, b, k) => -__steps(-a, b, k));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/geom-sdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "2D Signed Distance Field creation from @thi.ng/geom shapes, conversions, sampling, combinators",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -38,9 +38,11 @@
|
|
|
38
38
|
"@thi.ng/checks": "^3.2.1",
|
|
39
39
|
"@thi.ng/defmulti": "^2.1.7",
|
|
40
40
|
"@thi.ng/errors": "^2.1.7",
|
|
41
|
-
"@thi.ng/geom": "^3.3.
|
|
42
|
-
"@thi.ng/geom-api": "^3.2.
|
|
41
|
+
"@thi.ng/geom": "^3.3.1",
|
|
42
|
+
"@thi.ng/geom-api": "^3.2.1",
|
|
43
43
|
"@thi.ng/geom-isoline": "^2.1.14",
|
|
44
|
+
"@thi.ng/geom-poly-utils": "^2.3.0",
|
|
45
|
+
"@thi.ng/geom-resample": "^2.1.16",
|
|
44
46
|
"@thi.ng/math": "^5.3.3",
|
|
45
47
|
"@thi.ng/transducers": "^8.3.5",
|
|
46
48
|
"@thi.ng/vectors": "^7.5.6"
|
|
@@ -89,12 +91,21 @@
|
|
|
89
91
|
"./api": {
|
|
90
92
|
"default": "./api.js"
|
|
91
93
|
},
|
|
94
|
+
"./as-polygons": {
|
|
95
|
+
"default": "./as-polygons.js"
|
|
96
|
+
},
|
|
92
97
|
"./as-sdf": {
|
|
93
98
|
"default": "./as-sdf.js"
|
|
94
99
|
},
|
|
100
|
+
"./bounds": {
|
|
101
|
+
"default": "./bounds.js"
|
|
102
|
+
},
|
|
95
103
|
"./dist": {
|
|
96
104
|
"default": "./dist.js"
|
|
97
105
|
},
|
|
106
|
+
"./domain": {
|
|
107
|
+
"default": "./domain.js"
|
|
108
|
+
},
|
|
98
109
|
"./ops": {
|
|
99
110
|
"default": "./ops.js"
|
|
100
111
|
},
|
|
@@ -107,8 +118,14 @@
|
|
|
107
118
|
},
|
|
108
119
|
"thi.ng": {
|
|
109
120
|
"parent": "@thi.ng/geom",
|
|
121
|
+
"related": [
|
|
122
|
+
"distance-transform",
|
|
123
|
+
"geom-isoline",
|
|
124
|
+
"pixel",
|
|
125
|
+
"shader-ast-stdlib"
|
|
126
|
+
],
|
|
110
127
|
"status": "alpha",
|
|
111
128
|
"year": 2022
|
|
112
129
|
},
|
|
113
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "73139849c7b46c0451693d994e14a34bc2434280\n"
|
|
114
131
|
}
|
package/sample.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import type { NumericArray } from "@thi.ng/api";
|
|
2
|
-
import type { Polygon } from "@thi.ng/geom";
|
|
1
|
+
import type { Fn, NumericArray } from "@thi.ng/api";
|
|
3
2
|
import type { AABBLike } from "@thi.ng/geom-api";
|
|
4
|
-
import { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
+
import type { ReadonlyVec, Vec } from "@thi.ng/vectors";
|
|
5
4
|
import type { SDFn } from "./api.js";
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Samples and discretizes the given SDF within the given bounding rect and
|
|
7
|
+
* resolution. The optional `domain` fn can be used to apply modifiers to each
|
|
8
|
+
* sample position. If `buf` is provided, writes results into it, else creates a
|
|
9
|
+
* new buffer automatically. Returns buffer.
|
|
10
|
+
*
|
|
11
|
+
* @param sdf
|
|
12
|
+
* @param bounds
|
|
13
|
+
* @param res
|
|
14
|
+
* @param domain
|
|
15
|
+
* @param buf
|
|
16
|
+
*/
|
|
17
|
+
export declare const sample2d: (sdf: SDFn, { pos: [px, py], size: [width, height] }: AABBLike, [resX, resY]: ReadonlyVec, domain?: Fn<ReadonlyVec, Vec>, buf?: NumericArray) => NumericArray;
|
|
8
18
|
//# sourceMappingURL=sample.d.ts.map
|
package/sample.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { isFunction } from "@thi.ng/checks/is-function";
|
|
2
1
|
import { assert } from "@thi.ng/errors/assert";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Samples and discretizes the given SDF within the given bounding rect and
|
|
4
|
+
* resolution. The optional `domain` fn can be used to apply modifiers to each
|
|
5
|
+
* sample position. If `buf` is provided, writes results into it, else creates a
|
|
6
|
+
* new buffer automatically. Returns buffer.
|
|
7
|
+
*
|
|
8
|
+
* @param sdf
|
|
9
|
+
* @param bounds
|
|
10
|
+
* @param res
|
|
11
|
+
* @param domain
|
|
12
|
+
* @param buf
|
|
13
|
+
*/
|
|
14
|
+
export const sample2d = (sdf, { pos: [px, py], size: [width, height] }, [resX, resY], domain, buf) => {
|
|
12
15
|
if (buf) {
|
|
13
16
|
assert(buf.length >= resX * resY, "insufficient buffer size");
|
|
14
17
|
}
|
|
@@ -22,16 +25,8 @@ export const sample2d = (sdf, { pos: [px, py], size: [width, height] }, [resX, r
|
|
|
22
25
|
p[1] = py + y * dy;
|
|
23
26
|
for (let x = 0; x < resX; x++, i++) {
|
|
24
27
|
p[0] = px + x * dx;
|
|
25
|
-
buf[i] = sdf(p);
|
|
28
|
+
buf[i] = sdf(domain ? domain(p) : p);
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
return buf;
|
|
29
32
|
};
|
|
30
|
-
export const asPolygons = (sdf, bounds, res, isoLevels = [0]) => {
|
|
31
|
-
const $sdf = isFunction(sdf) ? sample2d(sdf, bounds, res) : sdf;
|
|
32
|
-
const { pos, size } = bounds;
|
|
33
|
-
const [resX, resY] = res;
|
|
34
|
-
setBorder($sdf, resX, resY, 1e6);
|
|
35
|
-
const scale = div2([], size, [resX - 1, resY - 1]);
|
|
36
|
-
return transduce(comp(mapcat((iso) => isolines($sdf, resX, resY, iso, scale)), map((pts) => polygon(pts.map((p) => add2(null, p, pos))))), push(), isoLevels);
|
|
37
|
-
};
|