@thi.ng/geom-sdf 0.2.85 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-03T12:13:31Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
package/README.md CHANGED
@@ -135,7 +135,7 @@ For Node.js REPL:
135
135
  const geomSdf = await import("@thi.ng/geom-sdf");
136
136
  ```
137
137
 
138
- Package sizes (brotli'd, pre-treeshake): ESM: 3.53 KB
138
+ Package sizes (brotli'd, pre-treeshake): ESM: 3.55 KB
139
139
 
140
140
  ## Dependencies
141
141
 
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
- * Extract contour polygons from given SDF at specified `distances`. The SDF can
15
- * be either given as distance function or as pre-discretized image (e.g. as
16
- * result of {@link sample2d}). The SDF will be sampled in the given bounding
17
- * rect and resolution (if SDF was given as image, resolution MUST be the
18
- * same!).
19
- *
20
- * @remarks
21
- * If `distances` are not given, only the original boundary (i.e. distance=0)
22
- * will be extracted. By default all resulting polygons will be simplified using
23
- * Douglas-Peucker with a threshold of `eps`. By default this will only remove
24
- * co-linear vertices, but more agressive settings are possible/recommended.
25
- *
26
- * @param sdf
27
- * @param bounds
28
- * @param res
29
- * @param distances
30
- * @param eps
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 { chamferDiff, chamferIsec, chamferUnion, diff, isec, roundDiff, roundIsec, roundUnion, smoothDiff, smoothIsec, smoothUnion, stepDiff, stepIsec, stepUnion, union, } from "./ops.js";
11
- import { box2, circle2, DEFAULT_ATTRIBS, ellipse2, line2, points2, polygon2, polyline2, quadratic2, withSDFAttribs, } from "./shapes.js";
12
- /**
13
- * Takes an
14
- * [`IShape`](https://docs.thi.ng/umbrella/geom-api/interfaces/IShape.html)
15
- * instance (possibly a tree, e.g. via
16
- * [`group()`](https://docs.thi.ng/umbrella/geom/functions/group.html)) and
17
- * converts it into a {@link SDFn}.
18
- *
19
- * @remarks
20
- * Currently supported shape types:
21
- *
22
- * - circle
23
- * - cubic (auto-converted to polyline)
24
- * - ellipse
25
- * - group
26
- * - line
27
- * - path (auto-converted to polygon/polyline)
28
- * - points
29
- * - polygon
30
- * - polyline
31
- * - quadratic bezier
32
- * - rect
33
- *
34
- * For shapes which need to be converted to polygons/polylines, the
35
- * {@link SDFAttribs.samples} attribute can be used to control the resulting
36
- * number of vertices. If not specified
37
- * [`DEFAULT_SAMPLES`](https://docs.thi.ng/umbrella/geom-api/variables/DEFAULT_SAMPLES.html)
38
- * will be used (which can be globally set via
39
- * [`setDefaultSamples()`](https://docs.thi.ng/umbrella/geom-api/functions/setDefaultSamples.html)).
40
- */
41
- export const asSDF = defmulti(__dispatch, {
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(simplify(asPolyline($, (__sdfAttribs($.attribs) || {}).samples), 0)),
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
- const { attribs, children } = $;
51
- const attr = { ...DEFAULT_ATTRIBS, ...__sdfAttribs(attribs) };
52
- __validateAttribs(attr);
53
- const $children = children.map(asSDF);
54
- let res;
55
- if ($children.length > 1) {
56
- switch (attr.combine) {
57
- case "diff":
58
- res = __selectCombineOp(attr, $children, diff, {
59
- chamfer: chamferDiff,
60
- round: roundDiff,
61
- smooth: smoothDiff,
62
- steps: stepDiff,
63
- });
64
- break;
65
- case "isec":
66
- res = __selectCombineOp(attr, $children, isec, {
67
- chamfer: chamferIsec,
68
- round: roundIsec,
69
- smooth: smoothIsec,
70
- steps: stepIsec,
71
- });
72
- break;
73
- case "union":
74
- default: {
75
- res = __selectCombineOp(attr, $children, union, {
76
- chamfer: chamferUnion,
77
- round: roundUnion,
78
- smooth: smoothUnion,
79
- steps: stepUnion,
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
- else if ($children.length) {
85
- res = $children[0];
86
- }
87
- else {
88
- return attr.flip ? () => -Infinity : () => Infinity;
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
- const n = (__sdfAttribs($.attribs) || {}).samples;
95
- return asSDF(simplify($.closed ? asPolygon($, n) : asPolyline($, n), 0));
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
- const s = mulN2([], size, 0.5);
103
- return box2(add2([], s, pos), s, __sdfAttribs(attribs));
104
- },
105
- });
106
- /** @internal */
107
- const __sdfAttribs = (attribs) => attribs ? attribs.__sdf : undefined;
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(OPS.filter((x) => attribs[x]).length < 2, "only 1 of these options can be used at once: chamfer, round, smooth");
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
- for (let k of OPS) {
112
- if (attribs[k]) {
113
- return paramOps[k](attribs[k], children);
114
- }
120
+ for (let k of OPS) {
121
+ if (attribs[k]) {
122
+ return paramOps[k](attribs[k], children);
115
123
  }
116
- return op(children);
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
- export function withBoundingCircle(sdf, ...args) {
7
- let [[cx, cy], r] = args.length === 1
8
- ? boundingCircle(args[0])
9
- : args;
10
- r *= r;
11
- return (p, minD = Infinity) => {
12
- if (minD === Infinity)
13
- return sdf(p, minD);
14
- const dx = p[0] - cx;
15
- const dy = p[1] - cy;
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
- export function withBoundingRect(sdf, ...args) {
20
- const [min, max] = args.length === 1 ? bounds2(args[0]) : args;
21
- const centroid = addmN2([], min, max, 0.5);
22
- const hSize = submN2([], max, min, 0.5);
23
- const t = [0, 0];
24
- return (p, minD = Infinity) => {
25
- if (minD === Infinity)
26
- return sdf(p, minD);
27
- return distBox2(sub2(t, p, centroid), hSize) < minD
28
- ? sdf(p, minD)
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
- * 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
- */
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
- * 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) => {
62
- let d = distSq2(p, pts[0]);
63
- let s = 1;
64
- const py = p[1];
65
- for (let n = pts.length, i = 0, j = n - 1; i < n; j = i, i++) {
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
- * 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)))));
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
- * 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) => {
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
- * 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
- */
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
- * 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);
148
- const kk = 1 / magSq2(bb);
149
- const kx = kk * dot2(aa, bb);
150
- const ky = (kk * (2 * magSq2(aa) + dot2(dd, bb))) / 3;
151
- const kz = kk * dot2(dd, aa);
152
- const p = ky - kx * kx;
153
- const q = kx * (2 * kx * kx - 3 * ky) + kz;
154
- const p3 = p * p * p;
155
- const q2 = q * q;
156
- let h = q2 + 4 * p3;
157
- if (h >= 0) {
158
- h = sqrt(h);
159
- const x = [(h - q) / 2, (-h - q) / 2];
160
- const uv = mul2(null, sign2(t1, x), powN2(null, abs2(null, x), 1 / 3));
161
- const t = clamp01(uv[0] + uv[1] - kx);
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
- * 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
- */
194
- export const distEllipse2 = ([px, py], [abx, aby]) => {
195
- px = abs(px);
196
- py = abs(py);
197
- if (px > py) {
198
- let t = px;
199
- px = py;
200
- py = t;
201
- t = abx;
202
- abx = aby;
203
- aby = t;
204
- }
205
- const l = aby * aby - abx * abx;
206
- const m = (abx * px) / l;
207
- const m2 = m * m;
208
- const n = (aby * py) / l;
209
- const n2 = n * n;
210
- const c = (m2 + n2 - 1) / 3;
211
- const c3 = c * c * c;
212
- const mn2 = m2 * n2;
213
- const d = c3 + mn2;
214
- const q = c3 + mn2 * 2;
215
- const g = m + m * n2;
216
- let co;
217
- if (d < 0) {
218
- const h = Math.acos(q / c3) / 3;
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;
224
- }
225
- else {
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));
229
- const rx = -s - u - c * 4 + 2 * m2;
230
- const ry = (s - u) * SQRT3;
231
- const rm = Math.hypot(rx, ry);
232
- co = (ry / sqrt(rm - rx) + (2 * g) / rm - m) / 2;
233
- }
234
- const rx = abx * co;
235
- const ry = aby * sqrt(1 - co * co);
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
- * @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);
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
- * @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));
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
- * @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
- };
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
- * @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));
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) => (isFunction(k) ? k : () => k);
5
- const __ensureChildren = (children, min = 1) => assert(children.length >= 1, `require at least ${min} SDF(s)`);
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
- 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
- };
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
- 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
- };
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
- 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);
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
- 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);
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
- 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);
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
- 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)));
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
- 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)));
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.85",
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 clean && tsc --declaration",
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,22 +35,22 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/api": "^8.9.10",
37
- "@thi.ng/checks": "^3.4.10",
38
- "@thi.ng/defmulti": "^3.0.8",
39
- "@thi.ng/errors": "^2.4.4",
40
- "@thi.ng/geom": "^6.0.6",
41
- "@thi.ng/geom-api": "^3.4.48",
42
- "@thi.ng/geom-isoline": "^2.1.86",
43
- "@thi.ng/geom-poly-utils": "^2.3.74",
44
- "@thi.ng/geom-resample": "^2.3.12",
45
- "@thi.ng/math": "^5.7.5",
46
- "@thi.ng/transducers": "^8.8.13",
47
- "@thi.ng/vectors": "^7.8.7"
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",
51
- "@thi.ng/testament": "^0.4.3",
53
+ "esbuild": "^0.19.8",
52
54
  "rimraf": "^5.0.5",
53
55
  "tools": "^0.0.1",
54
56
  "typedoc": "^0.25.4",
@@ -126,5 +128,5 @@
126
128
  "status": "alpha",
127
129
  "year": 2022
128
130
  },
129
- "gitHead": "04d1de79f256d7a53c6b5fd157b37f49bc88e11d\n"
131
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
130
132
  }
package/sample.js CHANGED
@@ -1,32 +1,22 @@
1
1
  import { assert } from "@thi.ng/errors/assert";
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) => {
15
- if (buf) {
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
- else {
19
- buf = new Float32Array(resX * resY);
20
- }
21
- const dx = width / (resX - 1);
22
- const dy = height / (resY - 1);
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 { distArc2, distBox2, distCircle2, distEllipse2, distPolygon2, distPolyline2, distQuadratic2, distSegment2, } from "./dist.js";
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
- /** @internal */
8
- export const DEFAULT_ATTRIBS = {
9
- abs: false,
10
- bounds: false,
11
- chamfer: 0,
12
- combine: "union",
13
- flip: false,
14
- offset: 0,
15
- round: 0,
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
- * Applies any SDF modifiers specified via {@link SDFAttribs} to the given
20
- * distance function. Returns a possibly updated distance function.
21
- *
22
- * @remarks
23
- * Order of application is: abs, offset, flip
24
- *
25
- * @param fn
26
- * @param attribs
27
- */
28
- export const withSDFAttribs = (fn, attribs) => {
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
- export const arc2 = (center, apert, ra, rb, attribs = {}) => {
41
- const sdf = withSDFAttribs((p) => distArc2(sub2([], p, center), apert, ra, rb), attribs);
42
- return attribs.bounds ? withBoundingCircle(sdf, center, ra + rb) : sdf;
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
- export const box2 = (center, size, attribs) => withSDFAttribs((p) => distBox2(sub2([], p, center), size), attribs);
45
- export const circle2 = (center, radius, attribs) => withSDFAttribs((p) => distCircle2(sub2([], p, center), radius), attribs);
46
- export const ellipse2 = (center, radii, attribs = {}) => {
47
- const sdf = withSDFAttribs((p) => distEllipse2(sub2([], p, center), radii), attribs);
48
- return attribs.bounds
49
- ? withBoundingCircle(sdf, center, Math.max(...radii))
50
- : sdf;
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
- export const line2 = (a, b, attribs) => withSDFAttribs((p) => distSegment2(p, a, b), attribs);
53
- export const points2 = (pts, attribs = {}) => {
54
- const sdf = withSDFAttribs((p) => pts.reduce((acc, q) => Math.min(acc, distSq2(p, q)), Infinity), attribs);
55
- return attribs.bounds ? withBoundingCircle(sdf, pts) : sdf;
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
- export const polygon2 = (pts, attribs = {}) => {
58
- const sdf = withSDFAttribs((p) => distPolygon2(p, pts), attribs);
59
- return attribs.bounds ? withBoundingCircle(sdf, pts) : sdf;
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
- export const polyline2 = (pts, attribs = {}) => {
62
- const sdf = withSDFAttribs((p) => distPolyline2(p, pts), attribs);
63
- return attribs.bounds ? withBoundingCircle(sdf, pts) : sdf;
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 });