@thi.ng/geom-sdf 0.2.86 → 0.2.87
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 +1 -1
- package/README.md +1 -1
- package/api.js +0 -1
- package/as-polygons.js +18 -26
- package/as-sdf.js +101 -89
- package/bounds.js +24 -24
- package/dist.js +121 -205
- package/domain.js +43 -46
- package/ops.js +99 -63
- package/package.json +18 -15
- package/sample.js +19 -29
- package/shapes.js +74 -69
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/as-polygons.js
CHANGED
|
@@ -10,30 +10,22 @@ import { transduce } from "@thi.ng/transducers/transduce";
|
|
|
10
10
|
import { add2 } from "@thi.ng/vectors/add";
|
|
11
11
|
import { div2 } from "@thi.ng/vectors/div";
|
|
12
12
|
import { sample2d } from "./sample.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*/
|
|
32
|
-
export const asPolygons = (sdf, bounds, res, distances = [0], eps = 1e-6) => {
|
|
33
|
-
const $sdf = isFunction(sdf) ? sample2d(sdf, bounds, res) : sdf;
|
|
34
|
-
const { pos, size } = bounds;
|
|
35
|
-
const [resX, resY] = res;
|
|
36
|
-
setBorder($sdf, resX, resY, 1e6);
|
|
37
|
-
const scale = div2([], size, [resX - 1, resY - 1]);
|
|
38
|
-
return transduce(comp(mapcat((iso) => isolines($sdf, resX, resY, iso, scale)), map((pts) => pts.map((p) => add2(null, p, pos))), map((pts) => polygon(eps >= 0 ? simplify(pts, eps, true) : pts))), push(), distances);
|
|
13
|
+
const asPolygons = (sdf, bounds, res, distances = [0], eps = 1e-6) => {
|
|
14
|
+
const $sdf = isFunction(sdf) ? sample2d(sdf, bounds, res) : sdf;
|
|
15
|
+
const { pos, size } = bounds;
|
|
16
|
+
const [resX, resY] = res;
|
|
17
|
+
setBorder($sdf, resX, resY, 1e6);
|
|
18
|
+
const scale = div2([], size, [resX - 1, resY - 1]);
|
|
19
|
+
return transduce(
|
|
20
|
+
comp(
|
|
21
|
+
mapcat((iso) => isolines($sdf, resX, resY, iso, scale)),
|
|
22
|
+
map((pts) => pts.map((p) => add2(null, p, pos))),
|
|
23
|
+
map((pts) => polygon(eps >= 0 ? simplify(pts, eps, true) : pts))
|
|
24
|
+
),
|
|
25
|
+
push(),
|
|
26
|
+
distances
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
export {
|
|
30
|
+
asPolygons
|
|
39
31
|
};
|
package/as-sdf.js
CHANGED
|
@@ -7,111 +7,123 @@ import { simplify } from "@thi.ng/geom/simplify";
|
|
|
7
7
|
import { __dispatch } from "@thi.ng/geom/internal/dispatch";
|
|
8
8
|
import { add2 } from "@thi.ng/vectors/add";
|
|
9
9
|
import { mulN2 } from "@thi.ng/vectors/muln";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
10
|
+
import {
|
|
11
|
+
chamferDiff,
|
|
12
|
+
chamferIsec,
|
|
13
|
+
chamferUnion,
|
|
14
|
+
diff,
|
|
15
|
+
isec,
|
|
16
|
+
roundDiff,
|
|
17
|
+
roundIsec,
|
|
18
|
+
roundUnion,
|
|
19
|
+
smoothDiff,
|
|
20
|
+
smoothIsec,
|
|
21
|
+
smoothUnion,
|
|
22
|
+
stepDiff,
|
|
23
|
+
stepIsec,
|
|
24
|
+
stepUnion,
|
|
25
|
+
union
|
|
26
|
+
} from "./ops.js";
|
|
27
|
+
import {
|
|
28
|
+
box2,
|
|
29
|
+
circle2,
|
|
30
|
+
DEFAULT_ATTRIBS,
|
|
31
|
+
ellipse2,
|
|
32
|
+
line2,
|
|
33
|
+
points2,
|
|
34
|
+
polygon2,
|
|
35
|
+
polyline2,
|
|
36
|
+
quadratic2,
|
|
37
|
+
withSDFAttribs
|
|
38
|
+
} from "./shapes.js";
|
|
39
|
+
const asSDF = defmulti(
|
|
40
|
+
__dispatch,
|
|
41
|
+
{
|
|
42
42
|
quad: "poly",
|
|
43
|
-
tri: "poly"
|
|
44
|
-
},
|
|
43
|
+
tri: "poly"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
45
46
|
[DEFAULT]: ($) => unsupported(`shape type: ${$.type}`),
|
|
46
47
|
circle: ($) => circle2($.pos, $.r, __sdfAttribs($.attribs)),
|
|
47
|
-
cubic: ($) => asSDF(
|
|
48
|
+
cubic: ($) => asSDF(
|
|
49
|
+
simplify(
|
|
50
|
+
asPolyline($, (__sdfAttribs($.attribs) || {}).samples),
|
|
51
|
+
0
|
|
52
|
+
)
|
|
53
|
+
),
|
|
48
54
|
ellipse: ($) => ellipse2($.pos, $.r, __sdfAttribs($.attribs)),
|
|
49
55
|
group: ($) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
56
|
+
const { attribs, children } = $;
|
|
57
|
+
const attr = { ...DEFAULT_ATTRIBS, ...__sdfAttribs(attribs) };
|
|
58
|
+
__validateAttribs(attr);
|
|
59
|
+
const $children = children.map(asSDF);
|
|
60
|
+
let res;
|
|
61
|
+
if ($children.length > 1) {
|
|
62
|
+
switch (attr.combine) {
|
|
63
|
+
case "diff":
|
|
64
|
+
res = __selectCombineOp(attr, $children, diff, {
|
|
65
|
+
chamfer: chamferDiff,
|
|
66
|
+
round: roundDiff,
|
|
67
|
+
smooth: smoothDiff,
|
|
68
|
+
steps: stepDiff
|
|
69
|
+
});
|
|
70
|
+
break;
|
|
71
|
+
case "isec":
|
|
72
|
+
res = __selectCombineOp(attr, $children, isec, {
|
|
73
|
+
chamfer: chamferIsec,
|
|
74
|
+
round: roundIsec,
|
|
75
|
+
smooth: smoothIsec,
|
|
76
|
+
steps: stepIsec
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
case "union":
|
|
80
|
+
default: {
|
|
81
|
+
res = __selectCombineOp(attr, $children, union, {
|
|
82
|
+
chamfer: chamferUnion,
|
|
83
|
+
round: roundUnion,
|
|
84
|
+
smooth: smoothUnion,
|
|
85
|
+
steps: stepUnion
|
|
86
|
+
});
|
|
87
|
+
}
|
|
83
88
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return withSDFAttribs(res, attr);
|
|
89
|
+
} else if ($children.length) {
|
|
90
|
+
res = $children[0];
|
|
91
|
+
} else {
|
|
92
|
+
return attr.flip ? () => -Infinity : () => Infinity;
|
|
93
|
+
}
|
|
94
|
+
return withSDFAttribs(res, attr);
|
|
91
95
|
},
|
|
92
96
|
line: ({ points: [a, b], attribs }) => line2(a, b, __sdfAttribs(attribs)),
|
|
93
97
|
path: ($) => {
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
const n = (__sdfAttribs($.attribs) || {}).samples;
|
|
99
|
+
return asSDF(
|
|
100
|
+
simplify($.closed ? asPolygon($, n) : asPolyline($, n), 0)
|
|
101
|
+
);
|
|
96
102
|
},
|
|
97
103
|
points: ($) => points2($.points, __sdfAttribs($.attribs)),
|
|
98
104
|
poly: ($) => polygon2($.points, __sdfAttribs($.attribs)),
|
|
99
105
|
polyline: ($) => polyline2($.points, __sdfAttribs($.attribs)),
|
|
100
106
|
quadratic: ({ points: [a, b, c], attribs }) => quadratic2(a, b, c, __sdfAttribs(attribs)),
|
|
101
107
|
rect: ({ pos, size, attribs }) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const __sdfAttribs = (attribs) => attribs ? attribs.__sdf :
|
|
108
|
+
const s = mulN2([], size, 0.5);
|
|
109
|
+
return box2(add2([], s, pos), s, __sdfAttribs(attribs));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
const __sdfAttribs = (attribs) => attribs ? attribs.__sdf : void 0;
|
|
108
114
|
const OPS = ["chamfer", "round", "smooth", "steps"];
|
|
109
|
-
const __validateAttribs = (attribs) => assert(
|
|
115
|
+
const __validateAttribs = (attribs) => assert(
|
|
116
|
+
OPS.filter((x) => attribs[x]).length < 2,
|
|
117
|
+
"only 1 of these options can be used at once: chamfer, round, smooth"
|
|
118
|
+
);
|
|
110
119
|
const __selectCombineOp = (attribs, children, op, paramOps) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
120
|
+
for (let k of OPS) {
|
|
121
|
+
if (attribs[k]) {
|
|
122
|
+
return paramOps[k](attribs[k], children);
|
|
115
123
|
}
|
|
116
|
-
|
|
124
|
+
}
|
|
125
|
+
return op(children);
|
|
126
|
+
};
|
|
127
|
+
export {
|
|
128
|
+
asSDF
|
|
117
129
|
};
|
package/bounds.js
CHANGED
|
@@ -3,29 +3,29 @@ import { addmN2 } from "@thi.ng/vectors/addmn";
|
|
|
3
3
|
import { sub2 } from "@thi.ng/vectors/sub";
|
|
4
4
|
import { submN2 } from "@thi.ng/vectors/submn";
|
|
5
5
|
import { distBox2 } from "./dist.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return dx * dx + dy * dy - r < minD * minD ? sdf(p, minD) : minD;
|
|
17
|
-
};
|
|
6
|
+
function withBoundingCircle(sdf, ...args) {
|
|
7
|
+
let [[cx, cy], r] = args.length === 1 ? boundingCircle(args[0]) : args;
|
|
8
|
+
r *= r;
|
|
9
|
+
return (p, minD = Infinity) => {
|
|
10
|
+
if (minD === Infinity)
|
|
11
|
+
return sdf(p, minD);
|
|
12
|
+
const dx = p[0] - cx;
|
|
13
|
+
const dy = p[1] - cy;
|
|
14
|
+
return dx * dx + dy * dy - r < minD * minD ? sdf(p, minD) : minD;
|
|
15
|
+
};
|
|
18
16
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
: minD;
|
|
30
|
-
};
|
|
17
|
+
function withBoundingRect(sdf, ...args) {
|
|
18
|
+
const [min, max] = args.length === 1 ? bounds2(args[0]) : args;
|
|
19
|
+
const centroid = addmN2([], min, max, 0.5);
|
|
20
|
+
const hSize = submN2([], max, min, 0.5);
|
|
21
|
+
const t = [0, 0];
|
|
22
|
+
return (p, minD = Infinity) => {
|
|
23
|
+
if (minD === Infinity)
|
|
24
|
+
return sdf(p, minD);
|
|
25
|
+
return distBox2(sub2(t, p, centroid), hSize) < minD ? sdf(p, minD) : minD;
|
|
26
|
+
};
|
|
31
27
|
}
|
|
28
|
+
export {
|
|
29
|
+
withBoundingCircle,
|
|
30
|
+
withBoundingRect
|
|
31
|
+
};
|
package/dist.js
CHANGED
|
@@ -23,215 +23,131 @@ const t4 = [];
|
|
|
23
23
|
const t5 = [];
|
|
24
24
|
const { abs, cos, min, max, sign, sin, sqrt } = Math;
|
|
25
25
|
const mag2 = (v) => sqrt(magSq2(v));
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* Ported from code by Inigo Quilez:
|
|
31
|
-
* https://iquilezles.org/articles/distfunctions2d/
|
|
32
|
-
*
|
|
33
|
-
* @param p
|
|
34
|
-
* @param radius
|
|
35
|
-
*/
|
|
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
|
-
*/
|
|
47
|
-
export const distBox2 = (p, halfSize) => {
|
|
48
|
-
const d = sub2(null, abs2(t1, p), halfSize);
|
|
49
|
-
return min(max(d[0], d[1]), 0) + mag2(max2(null, d, ZERO2));
|
|
26
|
+
const distCircle2 = (p, radius) => mag2(p) - radius;
|
|
27
|
+
const distBox2 = (p, halfSize) => {
|
|
28
|
+
const d = sub2(null, abs2(t1, p), halfSize);
|
|
29
|
+
return min(max(d[0], d[1]), 0) + mag2(max2(null, d, ZERO2));
|
|
50
30
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const pi = pts[i];
|
|
67
|
-
const pj = pts[j];
|
|
68
|
-
const e = sub2(t1, pj, pi);
|
|
69
|
-
const w = sub2(t2, p, pi);
|
|
70
|
-
d = min(d, distSq2(w, mulN2(t3, e, clamp01(dot2(w, e) / magSq2(e)))));
|
|
71
|
-
const c = [py >= pi[1], py < pj[1], e[0] * w[1] > e[1] * w[0]];
|
|
72
|
-
if (every3(c) || !some3(c))
|
|
73
|
-
s *= -1;
|
|
74
|
-
}
|
|
75
|
-
return s * sqrt(d);
|
|
31
|
+
const distPolygon2 = (p, pts) => {
|
|
32
|
+
let d = distSq2(p, pts[0]);
|
|
33
|
+
let s = 1;
|
|
34
|
+
const py = p[1];
|
|
35
|
+
for (let n = pts.length, i = 0, j = n - 1; i < n; j = i, i++) {
|
|
36
|
+
const pi = pts[i];
|
|
37
|
+
const pj = pts[j];
|
|
38
|
+
const e = sub2(t1, pj, pi);
|
|
39
|
+
const w = sub2(t2, p, pi);
|
|
40
|
+
d = min(d, distSq2(w, mulN2(t3, e, clamp01(dot2(w, e) / magSq2(e)))));
|
|
41
|
+
const c = [py >= pi[1], py < pj[1], e[0] * w[1] > e[1] * w[0]];
|
|
42
|
+
if (every3(c) || !some3(c))
|
|
43
|
+
s *= -1;
|
|
44
|
+
}
|
|
45
|
+
return s * sqrt(d);
|
|
76
46
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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)))));
|
|
47
|
+
const distSegment2 = (p, a, b) => {
|
|
48
|
+
const pa = sub2(t1, p, a);
|
|
49
|
+
const ba = sub2(t2, b, a);
|
|
50
|
+
return mag2(
|
|
51
|
+
sub2(null, pa, mulN2(null, ba, clamp01(dot2(pa, ba) / magSq2(ba))))
|
|
52
|
+
);
|
|
92
53
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
*/
|
|
100
|
-
export const distPolyline2 = (p, pts) => {
|
|
101
|
-
let d = Infinity;
|
|
102
|
-
for (let i = 1, n = pts.length; i < n; i++) {
|
|
103
|
-
d = min(d, distSegment2(p, pts[i - 1], pts[i]));
|
|
104
|
-
}
|
|
105
|
-
return d;
|
|
54
|
+
const distPolyline2 = (p, pts) => {
|
|
55
|
+
let d = Infinity;
|
|
56
|
+
for (let i = 1, n = pts.length; i < n; i++) {
|
|
57
|
+
d = min(d, distSegment2(p, pts[i - 1], pts[i]));
|
|
58
|
+
}
|
|
59
|
+
return d;
|
|
106
60
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
*
|
|
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
|
-
*/
|
|
121
|
-
export const distArc2 = (p, apert, ra, rb) => {
|
|
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);
|
|
61
|
+
const distArc2 = (p, apert, ra, rb) => {
|
|
62
|
+
t1[0] = abs(p[0]);
|
|
63
|
+
t1[1] = p[1];
|
|
64
|
+
return (apert[1] * t1[0] > apert[0] * t1[1] ? mag2(maddN2(t1, apert, -ra, t1)) : abs(mag2(t1) - ra)) - rb;
|
|
127
65
|
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const qq = maddN2(null, maddN2(t1, bb, t, cc), t, dd);
|
|
163
|
-
return (mag2(qq) *
|
|
164
|
-
(signed ? sign(cross2(maddN2(bb, bb, 2 * t, cc), qq)) : 1));
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
const z = sqrt(-p);
|
|
168
|
-
const v = Math.acos(q / (p * z * 2)) / 3;
|
|
169
|
-
const m = cos(v);
|
|
170
|
-
const n = sin(v) * SQRT3;
|
|
171
|
-
const tx = clamp01((m + m) * z - kx);
|
|
172
|
-
const ty = clamp01((-n - m) * z - kx);
|
|
173
|
-
const qx = maddN2(null, maddN2(t1, bb, tx, cc), tx, dd);
|
|
174
|
-
const dx = magSq2(qx);
|
|
175
|
-
const qy = maddN2(null, maddN2(t5, bb, ty, cc), ty, dd);
|
|
176
|
-
const dy = magSq2(qy);
|
|
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);
|
|
182
|
-
}
|
|
66
|
+
const distQuadratic2 = (pos, a, b, c, signed = false) => {
|
|
67
|
+
const aa = sub2(t1, b, a);
|
|
68
|
+
const bb = add2(null, maddN2(t2, b, -2, a), c);
|
|
69
|
+
const cc = mulN2(t3, aa, 2);
|
|
70
|
+
const dd = sub2(t4, a, pos);
|
|
71
|
+
const kk = 1 / magSq2(bb);
|
|
72
|
+
const kx = kk * dot2(aa, bb);
|
|
73
|
+
const ky = kk * (2 * magSq2(aa) + dot2(dd, bb)) / 3;
|
|
74
|
+
const kz = kk * dot2(dd, aa);
|
|
75
|
+
const p = ky - kx * kx;
|
|
76
|
+
const q = kx * (2 * kx * kx - 3 * ky) + kz;
|
|
77
|
+
const p3 = p * p * p;
|
|
78
|
+
const q2 = q * q;
|
|
79
|
+
let h = q2 + 4 * p3;
|
|
80
|
+
if (h >= 0) {
|
|
81
|
+
h = sqrt(h);
|
|
82
|
+
const x = [(h - q) / 2, (-h - q) / 2];
|
|
83
|
+
const uv = mul2(null, sign2(t1, x), powN2(null, abs2(null, x), 1 / 3));
|
|
84
|
+
const t = clamp01(uv[0] + uv[1] - kx);
|
|
85
|
+
const qq = maddN2(null, maddN2(t1, bb, t, cc), t, dd);
|
|
86
|
+
return mag2(qq) * (signed ? sign(cross2(maddN2(bb, bb, 2 * t, cc), qq)) : 1);
|
|
87
|
+
} else {
|
|
88
|
+
const z = sqrt(-p);
|
|
89
|
+
const v = Math.acos(q / (p * z * 2)) / 3;
|
|
90
|
+
const m = cos(v);
|
|
91
|
+
const n = sin(v) * SQRT3;
|
|
92
|
+
const tx = clamp01((m + m) * z - kx);
|
|
93
|
+
const ty = clamp01((-n - m) * z - kx);
|
|
94
|
+
const qx = maddN2(null, maddN2(t1, bb, tx, cc), tx, dd);
|
|
95
|
+
const dx = magSq2(qx);
|
|
96
|
+
const qy = maddN2(null, maddN2(t5, bb, ty, cc), ty, dd);
|
|
97
|
+
const dy = magSq2(qy);
|
|
98
|
+
return dx < dy ? sqrt(dx) * (signed ? sign(cross2(maddN2(bb, bb, 2 * tx, cc), qx)) : 1) : sqrt(dy) * (signed ? sign(cross2(maddN2(bb, bb, 2 * ty, cc), qy)) : 1);
|
|
99
|
+
}
|
|
183
100
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return Math.hypot(rx - px, ry - py) * sign(py - ry);
|
|
101
|
+
const distEllipse2 = ([px, py], [abx, aby]) => {
|
|
102
|
+
px = abs(px);
|
|
103
|
+
py = abs(py);
|
|
104
|
+
if (px > py) {
|
|
105
|
+
let t = px;
|
|
106
|
+
px = py;
|
|
107
|
+
py = t;
|
|
108
|
+
t = abx;
|
|
109
|
+
abx = aby;
|
|
110
|
+
aby = t;
|
|
111
|
+
}
|
|
112
|
+
const l = aby * aby - abx * abx;
|
|
113
|
+
const m = abx * px / l;
|
|
114
|
+
const m2 = m * m;
|
|
115
|
+
const n = aby * py / l;
|
|
116
|
+
const n2 = n * n;
|
|
117
|
+
const c = (m2 + n2 - 1) / 3;
|
|
118
|
+
const c3 = c * c * c;
|
|
119
|
+
const mn2 = m2 * n2;
|
|
120
|
+
const d = c3 + mn2;
|
|
121
|
+
const q = c3 + mn2 * 2;
|
|
122
|
+
const g = m + m * n2;
|
|
123
|
+
let co;
|
|
124
|
+
if (d < 0) {
|
|
125
|
+
const h = Math.acos(q / c3) / 3;
|
|
126
|
+
const s = cos(h);
|
|
127
|
+
const t = sin(h) * SQRT3;
|
|
128
|
+
const rx2 = sqrt(-c * (s + t + 2) + m2);
|
|
129
|
+
const ry2 = sqrt(-c * (s - t + 2) + m2);
|
|
130
|
+
co = (ry2 + sign(l) * rx2 + abs(g) / (rx2 * ry2) - m) / 2;
|
|
131
|
+
} else {
|
|
132
|
+
const h = 2 * m * n * sqrt(d);
|
|
133
|
+
const s = sign(q + h) * Math.cbrt(abs(q + h));
|
|
134
|
+
const u = sign(q - h) * Math.cbrt(abs(q - h));
|
|
135
|
+
const rx2 = -s - u - c * 4 + 2 * m2;
|
|
136
|
+
const ry2 = (s - u) * SQRT3;
|
|
137
|
+
const rm = Math.hypot(rx2, ry2);
|
|
138
|
+
co = (ry2 / sqrt(rm - rx2) + 2 * g / rm - m) / 2;
|
|
139
|
+
}
|
|
140
|
+
const rx = abx * co;
|
|
141
|
+
const ry = aby * sqrt(1 - co * co);
|
|
142
|
+
return Math.hypot(rx - px, ry - py) * sign(py - ry);
|
|
143
|
+
};
|
|
144
|
+
export {
|
|
145
|
+
distArc2,
|
|
146
|
+
distBox2,
|
|
147
|
+
distCircle2,
|
|
148
|
+
distEllipse2,
|
|
149
|
+
distPolygon2,
|
|
150
|
+
distPolyline2,
|
|
151
|
+
distQuadratic2,
|
|
152
|
+
distSegment2
|
|
237
153
|
};
|
package/domain.js
CHANGED
|
@@ -11,53 +11,50 @@ import { mul2 } from "@thi.ng/vectors/mul";
|
|
|
11
11
|
import { mulN2 } from "@thi.ng/vectors/muln";
|
|
12
12
|
import { sub2 } from "@thi.ng/vectors/sub";
|
|
13
13
|
import { subN2 } from "@thi.ng/vectors/subn";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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);
|
|
14
|
+
const repeat2 = (size) => {
|
|
15
|
+
const s2 = mulN2([], size, 0.5);
|
|
16
|
+
return (p) => sub2(null, mod2(null, add2([], s2, p), size), s2);
|
|
24
17
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
18
|
+
const repeatMirror2 = (size) => {
|
|
19
|
+
const c = [];
|
|
20
|
+
const s2 = mulN2([], size, 0.5);
|
|
21
|
+
return (p) => mul2(
|
|
22
|
+
null,
|
|
23
|
+
sub2(null, mod2(null, add2([], s2, p), size), s2),
|
|
24
|
+
subN2(
|
|
25
|
+
null,
|
|
26
|
+
mulN2(
|
|
27
|
+
null,
|
|
28
|
+
modN2(
|
|
29
|
+
null,
|
|
30
|
+
floor2(null, div2(null, add2(c, s2, p), size)),
|
|
31
|
+
2
|
|
32
|
+
),
|
|
33
|
+
2
|
|
34
|
+
),
|
|
35
|
+
1
|
|
36
|
+
)
|
|
37
|
+
);
|
|
36
38
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
};
|
|
39
|
+
const repeatGrid2 = (size) => {
|
|
40
|
+
const s2 = mulN2([], size, 0.5);
|
|
41
|
+
const rm = repeatMirror2(size);
|
|
42
|
+
return (p) => {
|
|
43
|
+
p = sub2(null, rm(p), s2);
|
|
44
|
+
return p[0] > p[1] ? [p[1], p[0]] : p;
|
|
45
|
+
};
|
|
51
46
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
const repeatPolar2 = (n) => {
|
|
48
|
+
const theta = TAU / n;
|
|
49
|
+
const halfTheta = theta / 2;
|
|
50
|
+
return (p) => cossin(
|
|
51
|
+
mod(Math.atan2(p[1], p[0]) + halfTheta, theta) - halfTheta,
|
|
52
|
+
mag(p)
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
export {
|
|
56
|
+
repeat2,
|
|
57
|
+
repeatGrid2,
|
|
58
|
+
repeatMirror2,
|
|
59
|
+
repeatPolar2
|
|
63
60
|
};
|
package/ops.js
CHANGED
|
@@ -1,74 +1,110 @@
|
|
|
1
1
|
import { isFunction } from "@thi.ng/checks/is-function";
|
|
2
2
|
import { assert } from "@thi.ng/errors/assert";
|
|
3
3
|
import { clamp01, mix, mod, SQRT2_2 } from "@thi.ng/math";
|
|
4
|
-
const __asField = (k) =>
|
|
5
|
-
const __ensureChildren = (children,
|
|
4
|
+
const __asField = (k) => isFunction(k) ? k : () => k;
|
|
5
|
+
const __ensureChildren = (children, min2 = 1) => assert(children.length >= 1, `require at least ${min2} SDF(s)`);
|
|
6
6
|
const { min, max } = Math;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return res;
|
|
26
|
-
};
|
|
7
|
+
const abs = (sdf) => (p, minD) => Math.abs(sdf(p, minD));
|
|
8
|
+
const flip = (sdf) => (p, minD) => -sdf(p, minD);
|
|
9
|
+
const offset = (sdf, r) => isFunction(r) ? (p, minD) => sdf(p, minD) - r(p) : (p, minD) => sdf(p, minD) - r;
|
|
10
|
+
const defOp = (op) => (children) => {
|
|
11
|
+
__ensureChildren(children);
|
|
12
|
+
const n = children.length;
|
|
13
|
+
return (p, minD = Infinity) => {
|
|
14
|
+
let res = children[0](p, minD);
|
|
15
|
+
if (res < minD)
|
|
16
|
+
minD = res;
|
|
17
|
+
for (let i = 1; i < n; i++) {
|
|
18
|
+
const d = children[i](p, minD);
|
|
19
|
+
if (d < minD)
|
|
20
|
+
minD = d;
|
|
21
|
+
res = op(res, d);
|
|
22
|
+
}
|
|
23
|
+
return res;
|
|
24
|
+
};
|
|
27
25
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
26
|
+
const union = defOp(min);
|
|
27
|
+
const isec = defOp(max);
|
|
28
|
+
const diff = defOp((a, b) => max(a, -b));
|
|
29
|
+
const defParamOp = (op) => (k, children) => {
|
|
30
|
+
__ensureChildren(children);
|
|
31
|
+
const kfield = __asField(k);
|
|
32
|
+
const n = children.length;
|
|
33
|
+
return (p, minD = Infinity) => {
|
|
34
|
+
const $k = kfield(p);
|
|
35
|
+
let res = children[0](p, minD);
|
|
36
|
+
if (res < minD)
|
|
37
|
+
minD = res;
|
|
38
|
+
for (let i = 1; i < n; i++) {
|
|
39
|
+
const d = children[i](p, minD);
|
|
40
|
+
if (d < minD)
|
|
41
|
+
minD = d;
|
|
42
|
+
res = op(res, d, $k);
|
|
43
|
+
}
|
|
44
|
+
return res;
|
|
45
|
+
};
|
|
48
46
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
const smoothUnion = defParamOp((a, b, k) => {
|
|
48
|
+
const h = clamp01(0.5 + 0.5 * (b - a) / k);
|
|
49
|
+
return mix(b, a, h) - k * h * (1 - h);
|
|
52
50
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
const smoothIsec = defParamOp((a, b, k) => {
|
|
52
|
+
const h = clamp01(0.5 - 0.5 * (b - a) / k);
|
|
53
|
+
return mix(b, a, h) + k * h * (1 - h);
|
|
56
54
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
const smoothDiff = defParamOp((a, b, k) => {
|
|
56
|
+
const h = clamp01(0.5 - 0.5 * (a + b) / k);
|
|
57
|
+
return mix(a, -b, h) + k * h * (1 - h);
|
|
60
58
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
const chamferUnion = defParamOp(
|
|
60
|
+
(a, b, k) => min(min(a, b), (a - k + b) * SQRT2_2)
|
|
61
|
+
);
|
|
62
|
+
const chamferIsec = defParamOp(
|
|
63
|
+
(a, b, k) => max(max(a, b), (a + k + b) * SQRT2_2)
|
|
64
|
+
);
|
|
65
|
+
const chamferDiff = defParamOp(
|
|
66
|
+
(a, b, k) => max(max(a, -b), (a + k - b) * SQRT2_2)
|
|
67
|
+
);
|
|
68
|
+
const roundUnion = defParamOp(
|
|
69
|
+
(a, b, k) => max(k, min(a, b)) - Math.hypot(max(k - a, 0), max(k - b, 0))
|
|
70
|
+
);
|
|
71
|
+
const roundIsec = defParamOp(
|
|
72
|
+
(a, b, k) => min(-k, max(a, b)) + Math.hypot(max(k + a, 0), max(k + b, 0))
|
|
73
|
+
);
|
|
74
|
+
const roundDiff = defParamOp(
|
|
75
|
+
(a, b, k) => min(-k, max(a, -b)) + Math.hypot(max(k + a, 0), max(k - b, 0))
|
|
76
|
+
);
|
|
67
77
|
const __steps = (a, b, [r, n]) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
const s = r / n;
|
|
79
|
+
const u = b - r;
|
|
80
|
+
return min(min(a, b), 0.5 * (u + a + Math.abs(mod(u - a + s, 2 * s) - s)));
|
|
81
|
+
};
|
|
82
|
+
const stepUnion = defParamOp(__steps);
|
|
83
|
+
const stepIsec = defParamOp(
|
|
84
|
+
(a, b, k) => -__steps(-a, -b, k)
|
|
85
|
+
);
|
|
86
|
+
const stepDiff = defParamOp(
|
|
87
|
+
(a, b, k) => -__steps(-a, b, k)
|
|
88
|
+
);
|
|
89
|
+
export {
|
|
90
|
+
abs,
|
|
91
|
+
chamferDiff,
|
|
92
|
+
chamferIsec,
|
|
93
|
+
chamferUnion,
|
|
94
|
+
defOp,
|
|
95
|
+
defParamOp,
|
|
96
|
+
diff,
|
|
97
|
+
flip,
|
|
98
|
+
isec,
|
|
99
|
+
offset,
|
|
100
|
+
roundDiff,
|
|
101
|
+
roundIsec,
|
|
102
|
+
roundUnion,
|
|
103
|
+
smoothDiff,
|
|
104
|
+
smoothIsec,
|
|
105
|
+
smoothUnion,
|
|
106
|
+
stepDiff,
|
|
107
|
+
stepIsec,
|
|
108
|
+
stepUnion,
|
|
109
|
+
union
|
|
71
110
|
};
|
|
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.2.
|
|
3
|
+
"version": "0.2.87",
|
|
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",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,21 +35,22 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/checks": "^3.4.
|
|
38
|
-
"@thi.ng/defmulti": "^3.0.
|
|
39
|
-
"@thi.ng/errors": "^2.4.
|
|
40
|
-
"@thi.ng/geom": "^6.0.
|
|
41
|
-
"@thi.ng/geom-api": "^3.4.
|
|
42
|
-
"@thi.ng/geom-isoline": "^2.1.
|
|
43
|
-
"@thi.ng/geom-poly-utils": "^2.3.
|
|
44
|
-
"@thi.ng/geom-resample": "^2.3.
|
|
45
|
-
"@thi.ng/math": "^5.7.
|
|
46
|
-
"@thi.ng/transducers": "^8.8.
|
|
47
|
-
"@thi.ng/vectors": "^7.8.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/checks": "^3.4.12",
|
|
40
|
+
"@thi.ng/defmulti": "^3.0.10",
|
|
41
|
+
"@thi.ng/errors": "^2.4.6",
|
|
42
|
+
"@thi.ng/geom": "^6.0.8",
|
|
43
|
+
"@thi.ng/geom-api": "^3.4.50",
|
|
44
|
+
"@thi.ng/geom-isoline": "^2.1.88",
|
|
45
|
+
"@thi.ng/geom-poly-utils": "^2.3.76",
|
|
46
|
+
"@thi.ng/geom-resample": "^2.3.14",
|
|
47
|
+
"@thi.ng/math": "^5.7.7",
|
|
48
|
+
"@thi.ng/transducers": "^8.8.15",
|
|
49
|
+
"@thi.ng/vectors": "^7.8.9"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
52
|
"@microsoft/api-extractor": "^7.38.3",
|
|
53
|
+
"esbuild": "^0.19.8",
|
|
51
54
|
"rimraf": "^5.0.5",
|
|
52
55
|
"tools": "^0.0.1",
|
|
53
56
|
"typedoc": "^0.25.4",
|
|
@@ -125,5 +128,5 @@
|
|
|
125
128
|
"status": "alpha",
|
|
126
129
|
"year": 2022
|
|
127
130
|
},
|
|
128
|
-
"gitHead": "
|
|
131
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
129
132
|
}
|
package/sample.js
CHANGED
|
@@ -1,32 +1,22 @@
|
|
|
1
1
|
import { assert } from "@thi.ng/errors/assert";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
assert(buf.length >= resX * resY, "insufficient buffer size");
|
|
2
|
+
const sample2d = (sdf, { pos: [px, py], size: [width, height] }, [resX, resY], domain, buf) => {
|
|
3
|
+
if (buf) {
|
|
4
|
+
assert(buf.length >= resX * resY, "insufficient buffer size");
|
|
5
|
+
} else {
|
|
6
|
+
buf = new Float32Array(resX * resY);
|
|
7
|
+
}
|
|
8
|
+
const dx = width / (resX - 1);
|
|
9
|
+
const dy = height / (resY - 1);
|
|
10
|
+
const p = [0, 0];
|
|
11
|
+
for (let y = 0, i = 0; y < resY; y++) {
|
|
12
|
+
p[1] = py + y * dy;
|
|
13
|
+
for (let x = 0; x < resX; x++, i++) {
|
|
14
|
+
p[0] = px + x * dx;
|
|
15
|
+
buf[i] = sdf(domain ? domain(p) : p);
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const p = [0, 0];
|
|
24
|
-
for (let y = 0, i = 0; y < resY; y++) {
|
|
25
|
-
p[1] = py + y * dy;
|
|
26
|
-
for (let x = 0; x < resX; x++, i++) {
|
|
27
|
-
p[0] = px + x * dx;
|
|
28
|
-
buf[i] = sdf(domain ? domain(p) : p);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return buf;
|
|
17
|
+
}
|
|
18
|
+
return buf;
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
sample2d
|
|
32
22
|
};
|
package/shapes.js
CHANGED
|
@@ -2,80 +2,85 @@ import { isFunction } from "@thi.ng/checks/is-function";
|
|
|
2
2
|
import { distSq2 } from "@thi.ng/vectors";
|
|
3
3
|
import { sub2 } from "@thi.ng/vectors/sub";
|
|
4
4
|
import { withBoundingCircle } from "./bounds.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
distArc2,
|
|
7
|
+
distBox2,
|
|
8
|
+
distCircle2,
|
|
9
|
+
distEllipse2,
|
|
10
|
+
distPolygon2,
|
|
11
|
+
distPolyline2,
|
|
12
|
+
distQuadratic2,
|
|
13
|
+
distSegment2
|
|
14
|
+
} from "./dist.js";
|
|
6
15
|
import { abs as $abs, flip as $flip, offset as $offset } from "./ops.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
smooth: 0,
|
|
16
|
+
const DEFAULT_ATTRIBS = {
|
|
17
|
+
abs: false,
|
|
18
|
+
bounds: false,
|
|
19
|
+
chamfer: 0,
|
|
20
|
+
combine: "union",
|
|
21
|
+
flip: false,
|
|
22
|
+
offset: 0,
|
|
23
|
+
round: 0,
|
|
24
|
+
smooth: 0
|
|
17
25
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (attribs) {
|
|
30
|
-
const { abs, flip, offset } = { ...DEFAULT_ATTRIBS, ...attribs };
|
|
31
|
-
if (abs)
|
|
32
|
-
fn = $abs(fn);
|
|
33
|
-
if (isFunction(offset) || offset > 0)
|
|
34
|
-
fn = $offset(fn, offset);
|
|
35
|
-
if (flip)
|
|
36
|
-
fn = $flip(fn);
|
|
37
|
-
}
|
|
38
|
-
return fn;
|
|
26
|
+
const withSDFAttribs = (fn, attribs) => {
|
|
27
|
+
if (attribs) {
|
|
28
|
+
const { abs, flip, offset } = { ...DEFAULT_ATTRIBS, ...attribs };
|
|
29
|
+
if (abs)
|
|
30
|
+
fn = $abs(fn);
|
|
31
|
+
if (isFunction(offset) || offset > 0)
|
|
32
|
+
fn = $offset(fn, offset);
|
|
33
|
+
if (flip)
|
|
34
|
+
fn = $flip(fn);
|
|
35
|
+
}
|
|
36
|
+
return fn;
|
|
39
37
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
const arc2 = (center, apert, ra, rb, attribs = {}) => {
|
|
39
|
+
const sdf = withSDFAttribs(
|
|
40
|
+
(p) => distArc2(sub2([], p, center), apert, ra, rb),
|
|
41
|
+
attribs
|
|
42
|
+
);
|
|
43
|
+
return attribs.bounds ? withBoundingCircle(sdf, center, ra + rb) : sdf;
|
|
43
44
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
const box2 = (center, size, attribs) => withSDFAttribs((p) => distBox2(sub2([], p, center), size), attribs);
|
|
46
|
+
const circle2 = (center, radius, attribs) => withSDFAttribs((p) => distCircle2(sub2([], p, center), radius), attribs);
|
|
47
|
+
const ellipse2 = (center, radii, attribs = {}) => {
|
|
48
|
+
const sdf = withSDFAttribs(
|
|
49
|
+
(p) => distEllipse2(sub2([], p, center), radii),
|
|
50
|
+
attribs
|
|
51
|
+
);
|
|
52
|
+
return attribs.bounds ? withBoundingCircle(sdf, center, Math.max(...radii)) : sdf;
|
|
51
53
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const line2 = (a, b, attribs) => withSDFAttribs((p) => distSegment2(p, a, b), attribs);
|
|
55
|
+
const points2 = (pts, attribs = {}) => {
|
|
56
|
+
const sdf = withSDFAttribs(
|
|
57
|
+
(p) => pts.reduce((acc, q) => Math.min(acc, distSq2(p, q)), Infinity),
|
|
58
|
+
attribs
|
|
59
|
+
);
|
|
60
|
+
return attribs.bounds ? withBoundingCircle(sdf, pts) : sdf;
|
|
56
61
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const polygon2 = (pts, attribs = {}) => {
|
|
63
|
+
const sdf = withSDFAttribs((p) => distPolygon2(p, pts), attribs);
|
|
64
|
+
return attribs.bounds ? withBoundingCircle(sdf, pts) : sdf;
|
|
60
65
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
const polyline2 = (pts, attribs = {}) => {
|
|
67
|
+
const sdf = withSDFAttribs((p) => distPolyline2(p, pts), attribs);
|
|
68
|
+
return attribs.bounds ? withBoundingCircle(sdf, pts) : sdf;
|
|
69
|
+
};
|
|
70
|
+
const quadratic2 = (a, b, c, attribs) => withSDFAttribs(
|
|
71
|
+
!attribs || attribs.abs !== false ? (p) => distQuadratic2(p, a, b, c) : (p) => distQuadratic2(p, a, b, c, true),
|
|
72
|
+
{ ...attribs, abs: false }
|
|
73
|
+
);
|
|
74
|
+
export {
|
|
75
|
+
DEFAULT_ATTRIBS,
|
|
76
|
+
arc2,
|
|
77
|
+
box2,
|
|
78
|
+
circle2,
|
|
79
|
+
ellipse2,
|
|
80
|
+
line2,
|
|
81
|
+
points2,
|
|
82
|
+
polygon2,
|
|
83
|
+
polyline2,
|
|
84
|
+
quadratic2,
|
|
85
|
+
withSDFAttribs
|
|
64
86
|
};
|
|
65
|
-
/**
|
|
66
|
-
* SDF for a single quadratic bezier segment. Similar to {@link line2}, by
|
|
67
|
-
* default only computes the _unsigned_ distance to the segment. To obtain the
|
|
68
|
-
* signed distance (for the "interior" region, if any), set the `abs` attribute
|
|
69
|
-
* explicitly to `false`.
|
|
70
|
-
*
|
|
71
|
-
* @remarks
|
|
72
|
-
* See {@link SDFAttribs.abs} for details.
|
|
73
|
-
*
|
|
74
|
-
* @param a
|
|
75
|
-
* @param b
|
|
76
|
-
* @param c
|
|
77
|
-
* @param attribs
|
|
78
|
-
*/
|
|
79
|
-
export const quadratic2 = (a, b, c, attribs) => withSDFAttribs(!attribs || attribs.abs !== false
|
|
80
|
-
? (p) => distQuadratic2(p, a, b, c)
|
|
81
|
-
: (p) => distQuadratic2(p, a, b, c, true), { ...attribs, abs: false });
|