@thi.ng/geom-sdf 0.2.95 → 0.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-28T23:24:38Z
3
+ - **Last updated**: 2024-01-23T15:58:27Z
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.
@@ -9,6 +9,17 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/geom-sdf@0.3.0) (2024-01-23)
13
+
14
+ #### 🚀 Features
15
+
16
+ - update SDFAttribs/SDFModifiers ([e1e51ff](https://github.com/thi-ng/umbrella/commit/e1e51ff))
17
+ - extract/update SDFModifiers
18
+ - add min/max/clamp() mods
19
+ - add withSDFModifiers()
20
+ - update withSDFAttribs()
21
+ - add/update docstrings
22
+
12
23
  ### [0.2.81](https://github.com/thi-ng/umbrella/tree/@thi.ng/geom-sdf@0.2.81) (2023-11-09)
13
24
 
14
25
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -7,8 +7,9 @@
7
7
  ![npm downloads](https://img.shields.io/npm/dm/@thi.ng/geom-sdf.svg)
8
8
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
9
9
 
10
- This project is part of the
11
- [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo and anti-framework.
10
+ This is a standalone project, maintained as part of the
11
+ [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo and
12
+ anti-framework.
12
13
 
13
14
  - [About](#about)
14
15
  - [SDF creation](#sdf-creation)
@@ -135,7 +136,7 @@ For Node.js REPL:
135
136
  const geomSdf = await import("@thi.ng/geom-sdf");
136
137
  ```
137
138
 
138
- Package sizes (brotli'd, pre-treeshake): ESM: 3.55 KB
139
+ Package sizes (brotli'd, pre-treeshake): ESM: 3.65 KB
139
140
 
140
141
  ## Dependencies
141
142
 
@@ -250,4 +251,4 @@ If this project contributes to an academic publication, please cite it as:
250
251
 
251
252
  ## License
252
253
 
253
- © 2022 - 2023 Karsten Schmidt // Apache License 2.0
254
+ © 2022 - 2024 Karsten Schmidt // Apache License 2.0
package/api.d.ts CHANGED
@@ -9,23 +9,74 @@ export type SDFn = (p: ReadonlyVec, minD?: number) => number;
9
9
  export type SDFCombineOp = "union" | "isec" | "diff";
10
10
  export type FieldCoeff<T = number> = Fn<ReadonlyVec, T>;
11
11
  /**
12
- * Options object to customize geometry -> SDF conversions. Given as value to
13
- * the special `__sdf` shape attribute.
14
- *
15
- * @example
16
- * ```ts
17
- * const sdf = asSDF(circle(100, { __sdf: { abs: true } }));
18
- * ```
12
+ * Modifier options for {@link withSDFModifiers} and basis for
13
+ * {@link SDFAttribs}.
19
14
  */
20
- export interface SDFAttribs {
15
+ export interface SDFModifiers {
21
16
  /**
22
17
  * If true, only the absolute (unsigned) distance will be used. For closed
23
18
  * shapes the default is false, for lines/curves the default is true (since
24
19
  * there's no real interior).
25
20
  *
21
+ * @remarks
22
+ * See {@link withSDFModifiers} for order of application.
23
+ *
26
24
  * @defaultValue false
27
25
  */
28
26
  abs: boolean;
27
+ /**
28
+ * If true (default: false), the sign of the resulting distance will be
29
+ * flipped. Useful for boolean operations.
30
+ *
31
+ * @remarks
32
+ * See {@link withSDFModifiers} for order of application.
33
+ *
34
+ * @defaultValue false
35
+ */
36
+ flip: boolean;
37
+ /**
38
+ * Subtracts given value from actual distance, thereby creating an
39
+ * offsetting effect. If given as function, it will be called with the
40
+ * current SDF query point and the return value will be used as param.
41
+ *
42
+ * @remarks
43
+ * See {@link withSDFModifiers} for order of application.
44
+ *
45
+ * @defaultValue 0
46
+ */
47
+ offset: number | FieldCoeff;
48
+ /**
49
+ * If given, the actual distance will be clamped to this value as lower
50
+ * bound.
51
+ *
52
+ * @remarks
53
+ * See {@link withSDFModifiers} for order of application.
54
+ *
55
+ * @defaultValue -Infinity
56
+ */
57
+ min: number;
58
+ /**
59
+ * If given, the actual distance will be clamped to this value as upper
60
+ * bound.
61
+ *
62
+ * @remarks
63
+ * See {@link withSDFModifiers} for order of application.
64
+ *
65
+ * @defaultValue Infinity
66
+ */
67
+ max: number;
68
+ }
69
+ /**
70
+ * Options object to customize geometry -> SDF conversions. Given as value to
71
+ * the special `__sdf` shape attribute. Also see {@link asSDF},
72
+ * {@link withSDFAttribs}.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const sdf = asSDF(circle(100, { __sdf: { abs: true } }));
77
+ * ```
78
+ */
79
+ export interface SDFAttribs extends SDFModifiers {
29
80
  /**
30
81
  * Advanced usage only. If true (default: false), the SDF will be wrapped
31
82
  * with a bounding box pre-check.
@@ -43,21 +94,6 @@ export interface SDFAttribs {
43
94
  * @defaultValue "union"
44
95
  */
45
96
  combine: SDFCombineOp;
46
- /**
47
- * If true (default: false), the sign of the resulting distance will be
48
- * flipped. Useful for boolean operations.
49
- *
50
- * @defaultValue false
51
- */
52
- flip: boolean;
53
- /**
54
- * Subtracts given value from actual distance, thereby creating an
55
- * offsetting effect. If given as function, it will be called with the
56
- * current SDF query point and the return value will be used as param.
57
- *
58
- * @defaultValue 0
59
- */
60
- offset: number | FieldCoeff;
61
97
  /**
62
98
  * Coefficient for smooth union, intersection, difference ops (only
63
99
  * supported for `group()` shapes). If given as function, it will be called
package/ops.d.ts CHANGED
@@ -1,7 +1,66 @@
1
1
  import type { Fn3, FnN2 } from "@thi.ng/api";
2
- import type { FieldCoeff, SDFn } from "./api.js";
2
+ import type { FieldCoeff, SDFModifiers, SDFn } from "./api.js";
3
+ /** @internal */
4
+ export declare const DEFAULT_MODS: SDFModifiers;
5
+ /**
6
+ * Applies any SDF modifiers specified via {@link SDFModifiers} to the given
7
+ * distance function. Returns a possibly updated distance function.
8
+ *
9
+ * @remarks
10
+ * Order of application is: abs, offset, flip, min, max
11
+ *
12
+ * @param fn
13
+ * @param mods
14
+ */
15
+ export declare const withSDFModifiers: (fn: SDFn, mods: Partial<SDFModifiers>) => SDFn;
16
+ /**
17
+ * SDF modifier. Augments given `fn` such that it always produces an unsigned
18
+ * (absolute) distance.
19
+ *
20
+ * @param sdf
21
+ */
3
22
  export declare const abs: (sdf: SDFn) => SDFn;
23
+ /**
24
+ * SDF modifier. Augments given `fn` such that the returned distance is <= given
25
+ * `maxD`.
26
+ *
27
+ * @param sdf
28
+ * @param maxD
29
+ */
30
+ export declare const min: (sdf: SDFn, maxD: number) => SDFn;
31
+ /**
32
+ * SDF modifier. Augments given `fn` such that the returned distance is >= given
33
+ * `minD`.
34
+ *
35
+ * @param sdf
36
+ * @param minD
37
+ */
38
+ export declare const max: (sdf: SDFn, minD: number) => SDFn;
39
+ /**
40
+ * SDF modifier. Augments given `fn` such that the returned distance is clamped
41
+ * to given `[minD, maxD]` interval.
42
+ *
43
+ * @param sdf
44
+ * @param minD
45
+ * @param maxD
46
+ */
47
+ export declare const clamp: (sdf: SDFn, minD: number, maxD: number) => SDFn;
48
+ /**
49
+ * SDF modifier. Augments given `fn` such that the sign of the returned distance
50
+ * is flipped (i.e. inside vs. outside).
51
+ *
52
+ * @param sdf
53
+ */
4
54
  export declare const flip: (sdf: SDFn) => SDFn;
55
+ /**
56
+ * SDF modifier. Augments given `fn` such that the returned distance will have
57
+ * given offset `r` (radius) subtracted. If `r` is a function it will be
58
+ * evaluated for each point the original SDF function is evaluated too and its
59
+ * return value will be subtracted, allowing for varying offsets.
60
+ *
61
+ * @param sdf
62
+ * @param r
63
+ */
5
64
  export declare const offset: (sdf: SDFn, r: number | FieldCoeff) => SDFn;
6
65
  export declare const defOp: (op: FnN2) => (children: SDFn[]) => SDFn;
7
66
  export declare const union: (children: SDFn[]) => SDFn;
package/ops.js CHANGED
@@ -1,10 +1,45 @@
1
1
  import { isFunction } from "@thi.ng/checks/is-function";
2
2
  import { assert } from "@thi.ng/errors/assert";
3
- import { clamp01, mix, mod, SQRT2_2 } from "@thi.ng/math";
3
+ import { SQRT2_2 } from "@thi.ng/math/api";
4
+ import { clamp as $clamp, clamp01 } from "@thi.ng/math/interval";
5
+ import { mix } from "@thi.ng/math/mix";
6
+ import { mod } from "@thi.ng/math/prec";
7
+ const { abs: $abs, min: $min, max: $max } = Math;
4
8
  const __asField = (k) => isFunction(k) ? k : () => k;
5
9
  const __ensureChildren = (children, min2 = 1) => assert(children.length >= 1, `require at least ${min2} SDF(s)`);
6
- const { min, max } = Math;
7
- const abs = (sdf) => (p, minD) => Math.abs(sdf(p, minD));
10
+ const DEFAULT_MODS = {
11
+ abs: false,
12
+ flip: false,
13
+ min: -Infinity,
14
+ max: Infinity,
15
+ offset: 0
16
+ };
17
+ const withSDFModifiers = (fn, mods) => {
18
+ const {
19
+ abs: $abs2,
20
+ flip: $flip,
21
+ offset: $offset,
22
+ min: $min2,
23
+ max: $max2
24
+ } = { ...DEFAULT_MODS, ...mods };
25
+ if ($abs2)
26
+ fn = abs(fn);
27
+ if (isFunction($offset) || $offset > 0)
28
+ fn = offset(fn, $offset);
29
+ if ($flip)
30
+ fn = flip(fn);
31
+ if ($min2 >= -Infinity) {
32
+ if ($max2 < Infinity)
33
+ return clamp(fn, $min2, $max2);
34
+ fn = max(fn, $min2);
35
+ } else if ($max2 < Infinity)
36
+ fn = min(fn, $max2);
37
+ return fn;
38
+ };
39
+ const abs = (sdf) => (p, minD) => $abs(sdf(p, minD));
40
+ const min = (sdf, maxD) => (p, minD) => $min(sdf(p, minD), maxD);
41
+ const max = (sdf, minD) => (p, currMin) => $max(sdf(p, currMin), minD);
42
+ const clamp = (sdf, minD, maxD) => (p, $minD) => $clamp(sdf(p, $minD), minD, maxD);
8
43
  const flip = (sdf) => (p, minD) => -sdf(p, minD);
9
44
  const offset = (sdf, r) => isFunction(r) ? (p, minD) => sdf(p, minD) - r(p) : (p, minD) => sdf(p, minD) - r;
10
45
  const defOp = (op) => (children) => {
@@ -23,9 +58,9 @@ const defOp = (op) => (children) => {
23
58
  return res;
24
59
  };
25
60
  };
26
- const union = defOp(min);
27
- const isec = defOp(max);
28
- const diff = defOp((a, b) => max(a, -b));
61
+ const union = defOp($min);
62
+ const isec = defOp($max);
63
+ const diff = defOp((a, b) => $max(a, -b));
29
64
  const defParamOp = (op) => (k, children) => {
30
65
  __ensureChildren(children);
31
66
  const kfield = __asField(k);
@@ -57,27 +92,27 @@ const smoothDiff = defParamOp((a, b, k) => {
57
92
  return mix(a, -b, h) + k * h * (1 - h);
58
93
  });
59
94
  const chamferUnion = defParamOp(
60
- (a, b, k) => min(min(a, b), (a - k + b) * SQRT2_2)
95
+ (a, b, k) => $min(a, b, (a - k + b) * SQRT2_2)
61
96
  );
62
97
  const chamferIsec = defParamOp(
63
- (a, b, k) => max(max(a, b), (a + k + b) * SQRT2_2)
98
+ (a, b, k) => $max(a, b, (a + k + b) * SQRT2_2)
64
99
  );
65
100
  const chamferDiff = defParamOp(
66
- (a, b, k) => max(max(a, -b), (a + k - b) * SQRT2_2)
101
+ (a, b, k) => $max(a, -b, (a + k - b) * SQRT2_2)
67
102
  );
68
103
  const roundUnion = defParamOp(
69
- (a, b, k) => max(k, min(a, b)) - Math.hypot(max(k - a, 0), max(k - b, 0))
104
+ (a, b, k) => $max(k, $min(a, b)) - Math.hypot($max(k - a, 0), $max(k - b, 0))
70
105
  );
71
106
  const roundIsec = defParamOp(
72
- (a, b, k) => min(-k, max(a, b)) + Math.hypot(max(k + a, 0), max(k + b, 0))
107
+ (a, b, k) => $min(-k, $max(a, b)) + Math.hypot($max(k + a, 0), $max(k + b, 0))
73
108
  );
74
109
  const roundDiff = defParamOp(
75
- (a, b, k) => min(-k, max(a, -b)) + Math.hypot(max(k + a, 0), max(k - b, 0))
110
+ (a, b, k) => $min(-k, $max(a, -b)) + Math.hypot($max(k + a, 0), $max(k - b, 0))
76
111
  );
77
112
  const __steps = (a, b, [r, n]) => {
78
113
  const s = r / n;
79
114
  const u = b - r;
80
- return min(min(a, b), 0.5 * (u + a + Math.abs(mod(u - a + s, 2 * s) - s)));
115
+ return $min(a, b, 0.5 * (u + a + Math.abs(mod(u - a + s, 2 * s) - s)));
81
116
  };
82
117
  const stepUnion = defParamOp(__steps);
83
118
  const stepIsec = defParamOp(
@@ -87,15 +122,19 @@ const stepDiff = defParamOp(
87
122
  (a, b, k) => -__steps(-a, b, k)
88
123
  );
89
124
  export {
125
+ DEFAULT_MODS,
90
126
  abs,
91
127
  chamferDiff,
92
128
  chamferIsec,
93
129
  chamferUnion,
130
+ clamp,
94
131
  defOp,
95
132
  defParamOp,
96
133
  diff,
97
134
  flip,
98
135
  isec,
136
+ max,
137
+ min,
99
138
  offset,
100
139
  roundDiff,
101
140
  roundIsec,
@@ -106,5 +145,6 @@ export {
106
145
  stepDiff,
107
146
  stepIsec,
108
147
  stepUnion,
109
- union
148
+ union,
149
+ withSDFModifiers
110
150
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/geom-sdf",
3
- "version": "0.2.95",
3
+ "version": "0.3.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",
@@ -35,18 +35,18 @@
35
35
  "test": "bun test"
36
36
  },
37
37
  "dependencies": {
38
- "@thi.ng/api": "^8.9.15",
39
- "@thi.ng/checks": "^3.4.15",
40
- "@thi.ng/defmulti": "^3.0.13",
41
- "@thi.ng/errors": "^2.4.9",
42
- "@thi.ng/geom": "^6.0.16",
43
- "@thi.ng/geom-api": "^3.4.55",
44
- "@thi.ng/geom-isoline": "^2.1.93",
45
- "@thi.ng/geom-poly-utils": "^2.3.81",
46
- "@thi.ng/geom-resample": "^2.3.19",
47
- "@thi.ng/math": "^5.7.10",
48
- "@thi.ng/transducers": "^8.8.19",
49
- "@thi.ng/vectors": "^7.8.14"
38
+ "@thi.ng/api": "^8.9.17",
39
+ "@thi.ng/checks": "^3.4.17",
40
+ "@thi.ng/defmulti": "^3.0.15",
41
+ "@thi.ng/errors": "^2.4.11",
42
+ "@thi.ng/geom": "^6.0.18",
43
+ "@thi.ng/geom-api": "^3.4.57",
44
+ "@thi.ng/geom-isoline": "^2.1.95",
45
+ "@thi.ng/geom-poly-utils": "^2.3.83",
46
+ "@thi.ng/geom-resample": "^2.3.21",
47
+ "@thi.ng/math": "^5.7.12",
48
+ "@thi.ng/transducers": "^8.8.21",
49
+ "@thi.ng/vectors": "^7.9.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@microsoft/api-extractor": "^7.39.0",
@@ -127,5 +127,5 @@
127
127
  "status": "alpha",
128
128
  "year": 2022
129
129
  },
130
- "gitHead": "775c664723cd87d6ac5909cedf91195317add287\n"
130
+ "gitHead": "417b5a7ea7bd54a3b4f086fe0fc2ce8e8933c9b2\n"
131
131
  }
package/shapes.d.ts CHANGED
@@ -3,11 +3,7 @@ import type { SDFAttribs, SDFn } from "./api.js";
3
3
  /** @internal */
4
4
  export declare const DEFAULT_ATTRIBS: SDFAttribs;
5
5
  /**
6
- * Applies any SDF modifiers specified via {@link SDFAttribs} to the given
7
- * distance function. Returns a possibly updated distance function.
8
- *
9
- * @remarks
10
- * Order of application is: abs, offset, flip
6
+ * Syntax sugar for {@link withSDFModifiers}.
11
7
  *
12
8
  * @param fn
13
9
  * @param attribs
package/shapes.js CHANGED
@@ -1,4 +1,3 @@
1
- import { isFunction } from "@thi.ng/checks/is-function";
2
1
  import { distSq2 } from "@thi.ng/vectors";
3
2
  import { sub2 } from "@thi.ng/vectors/sub";
4
3
  import { withBoundingCircle } from "./bounds.js";
@@ -12,29 +11,16 @@ import {
12
11
  distQuadratic2,
13
12
  distSegment2
14
13
  } from "./dist.js";
15
- import { abs as $abs, flip as $flip, offset as $offset } from "./ops.js";
14
+ import { DEFAULT_MODS, withSDFModifiers } from "./ops.js";
16
15
  const DEFAULT_ATTRIBS = {
17
- abs: false,
16
+ ...DEFAULT_MODS,
18
17
  bounds: false,
19
18
  chamfer: 0,
20
19
  combine: "union",
21
- flip: false,
22
- offset: 0,
23
20
  round: 0,
24
21
  smooth: 0
25
22
  };
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;
37
- };
23
+ const withSDFAttribs = (fn, attribs) => attribs ? withSDFModifiers(fn, attribs) : fn;
38
24
  const arc2 = (center, apert, ra, rb, attribs = {}) => {
39
25
  const sdf = withSDFAttribs(
40
26
  (p) => distArc2(sub2([], p, center), apert, ra, rb),