@thi.ng/geom-sdf 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -1
- package/README.md +91 -2
- package/api.d.ts +94 -4
- package/as-polygons.d.ts +26 -0
- package/as-polygons.js +39 -0
- package/as-sdf.d.ts +8 -0
- package/as-sdf.js +53 -18
- package/bounds.d.ts +30 -0
- package/bounds.js +31 -0
- package/dist.d.ts +90 -3
- package/dist.js +143 -56
- package/domain.d.ts +34 -0
- package/domain.js +63 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/ops.d.ts +19 -7
- package/ops.js +74 -22
- package/package.json +21 -4
- package/sample.d.ts +15 -5
- package/sample.js +14 -19
- package/shapes.d.ts +25 -1
- package/shapes.js +59 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2022-06-
|
|
3
|
+
- **Last updated**: 2022-06-23T12:16:18Z
|
|
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,29 @@ 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.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/geom-sdf@0.2.0) (2022-06-23)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- major update: combinators, modifiers, shape support ([4ffbc86](https://github.com/thi-ng/umbrella/commit/4ffbc86))
|
|
17
|
+
- support more shapes (and conversions) in asSDF()
|
|
18
|
+
- update/extend SDFAttribs
|
|
19
|
+
- add new SDF combinators (chamfer, round, step)
|
|
20
|
+
- add higher order combinators defOp(), defParamOp()
|
|
21
|
+
- add support for combinator params to be spatial
|
|
22
|
+
- update asSDF() to support more shape types and auto-convert to poly/line
|
|
23
|
+
- add domain modifiers, update `sample2d()` to support domain mods
|
|
24
|
+
- update various distance functions (incl. uniform arg order, minimize allocs)
|
|
25
|
+
- add docstrings
|
|
26
|
+
- add bounds pre-checks, update SDFAttribs, ops ([ddf0a6e](https://github.com/thi-ng/umbrella/commit/ddf0a6e))
|
|
27
|
+
- update `SDFn` signature, add opt. min dist param
|
|
28
|
+
- add `withBoundingCircle/Rect()` SDF wrappers
|
|
29
|
+
- update shape fns (points2, polygon2, polyline2)
|
|
30
|
+
- update SDF combinators (union, isec, diff etc.)
|
|
31
|
+
- update `asSDF()` group impl
|
|
32
|
+
- update `SDFAttribs`, allow `round` & `smooth` opts to be field based
|
|
33
|
+
- add docstrings
|
|
34
|
+
|
|
12
35
|
### [0.1.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/geom-sdf@0.1.1) (2022-06-20)
|
|
13
36
|
|
|
14
37
|
#### 🩹 Bug fixes
|
package/README.md
CHANGED
|
@@ -10,7 +10,11 @@ This project is part of the
|
|
|
10
10
|
[@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo.
|
|
11
11
|
|
|
12
12
|
- [About](#about)
|
|
13
|
+
- [SDF creation](#sdf-creation)
|
|
14
|
+
- [SDF combinators](#sdf-combinators)
|
|
15
|
+
- [SDF discretization, sampling & domain modifiers](#sdf-discretization-sampling--domain-modifiers)
|
|
13
16
|
- [Status](#status)
|
|
17
|
+
- [Related packages](#related-packages)
|
|
14
18
|
- [Installation](#installation)
|
|
15
19
|
- [Dependencies](#dependencies)
|
|
16
20
|
- [API](#api)
|
|
@@ -21,12 +25,94 @@ This project is part of the
|
|
|
21
25
|
|
|
22
26
|
2D Signed Distance Field creation from [@thi.ng/geom](https://github.com/thi-ng/umbrella/tree/develop/packages/geom) shapes, conversions, sampling, combinators.
|
|
23
27
|
|
|
28
|
+
Includes several distance functions and SDF operators ported from GLSL
|
|
29
|
+
implementations by:
|
|
30
|
+
|
|
31
|
+
- Inigo Quilez ([Article](https://iquilezles.org/articles/distfunctions2d/))
|
|
32
|
+
- Mercury demogroup ([HG_SDF](https://mercury.sexy/hg_sdf/))
|
|
33
|
+
|
|
34
|
+
### SDF creation
|
|
35
|
+
|
|
36
|
+
SDFs can be directly defined/composed via provided shape primitive functions and
|
|
37
|
+
combinators OR via automatic conversion from @thi.ng/geom geometry
|
|
38
|
+
types/hierarchies. In the latter case various
|
|
39
|
+
[attributes](https://docs.thi.ng/umbrella/geom-sdf/interfaces/SDFAttribs.html)
|
|
40
|
+
can be used to control the conversion process. Regardless of approach, the
|
|
41
|
+
result will be a single distance function which accepts a world position and
|
|
42
|
+
returns the signed distance to the encoded scene.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// via direct SDF composition
|
|
46
|
+
import { circle2, union } from "@thi.ng/geom-sdf";
|
|
47
|
+
|
|
48
|
+
const f = union([circle2([-50, 0], 100), circle2([50, 0], 100)]);
|
|
49
|
+
|
|
50
|
+
// via conversion
|
|
51
|
+
import { circle, group } from "@thi.ng/geom";
|
|
52
|
+
import { asSDF } from "@thi.ng/geom-sdf";
|
|
53
|
+
|
|
54
|
+
const f = asSDF(group({}, [circle([-50, 0], 100), circle([50, 0], 100)]));
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### SDF combinators
|
|
58
|
+
|
|
59
|
+
The following table illustrates various options how SDFs can be combined. When
|
|
60
|
+
using the [`asSDF()`](https://docs.thi.ng/umbrella/geom-sdf/modules.html#asSDF)
|
|
61
|
+
geometry converter, these operators can be specified and configured (most are
|
|
62
|
+
parametric) via a shape `group()`'s
|
|
63
|
+
[attributes](https://docs.thi.ng/umbrella/geom-sdf/interfaces/SDFAttribs.html),
|
|
64
|
+
e.g.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
group({ __sdf: { combine: "diff", chamfer: 50 }}, [
|
|
68
|
+
rectFromCentroid([-50,-50], 200),
|
|
69
|
+
rectFromCentroid([50,50], 200),
|
|
70
|
+
])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Operator | Union | Difference | Intersection |
|
|
74
|
+
|----------|----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
|
75
|
+
| default |  |  |  |
|
|
76
|
+
| chamfer |  |  |  |
|
|
77
|
+
| round |  |  |  |
|
|
78
|
+
| smooth |  |  |  |
|
|
79
|
+
| steps |  |  |  |
|
|
80
|
+
|
|
81
|
+
### SDF discretization, sampling & domain modifiers
|
|
82
|
+
|
|
83
|
+
The package provides the
|
|
84
|
+
[`sample2d()`](https://docs.thi.ng/umbrella/geom-sdf/modules.html#sample2d) and
|
|
85
|
+
[`asPolygons()`](https://docs.thi.ng/umbrella/geom-sdf/modules.html#asPolygons)
|
|
86
|
+
functions to discretize an SDF and cache results in a buffer (image) and then
|
|
87
|
+
extract contour polygons from it, i.e. convert the 2D back into geometry (see
|
|
88
|
+
example further below). The SDF will be sampled in a user defined bounding
|
|
89
|
+
rectangle (with customizable resolution) and the sampling positions can be
|
|
90
|
+
modulated via several provided domain modifiers to create various axial/spatial
|
|
91
|
+
repetions, symmetries etc. Modifiers are nestable/composable via standard
|
|
92
|
+
functional composition (e.g. using
|
|
93
|
+
[`compL()`](https://docs.thi.ng/umbrella/compose/modules.html#compL)) and also
|
|
94
|
+
support custom modfifiers. The table below illustrates a few examples effects:
|
|
95
|
+
|
|
96
|
+
| Modifier | | | |
|
|
97
|
+
|-------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
|
|
98
|
+
| `repeat2()` |  |  |  |
|
|
99
|
+
| `repeatGrid2()` |  |  |  |
|
|
100
|
+
| `repeatMirror2()` |  |  |  |
|
|
101
|
+
| `repeatPolar2()` |  |  |  |
|
|
102
|
+
|
|
24
103
|
### Status
|
|
25
104
|
|
|
26
105
|
**ALPHA** - bleeding edge / work-in-progress
|
|
27
106
|
|
|
28
107
|
[Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bgeom-sdf%5D+in%3Atitle)
|
|
29
108
|
|
|
109
|
+
### Related packages
|
|
110
|
+
|
|
111
|
+
- [@thi.ng/distance-transform](https://github.com/thi-ng/umbrella/tree/develop/packages/distance-transform) - Binary image to Distance Field transformation
|
|
112
|
+
- [@thi.ng/geom-isoline](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-isoline) - Fast 2D contour line extraction / generation
|
|
113
|
+
- [@thi.ng/pixel](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel) - Typedarray integer & float pixel buffers w/ customizable formats, blitting, drawing, convolution
|
|
114
|
+
- [@thi.ng/shader-ast-stdlib](https://github.com/thi-ng/umbrella/tree/develop/packages/shader-ast-stdlib) - Function collection for modular GPGPU / shader programming with [@thi.ng/shader-ast](https://github.com/thi-ng/umbrella/tree/develop/packages/shader-ast)
|
|
115
|
+
|
|
30
116
|
## Installation
|
|
31
117
|
|
|
32
118
|
```bash
|
|
@@ -50,7 +136,7 @@ node --experimental-repl-await
|
|
|
50
136
|
> const geomSdf = await import("@thi.ng/geom-sdf");
|
|
51
137
|
```
|
|
52
138
|
|
|
53
|
-
Package sizes (gzipped, pre-treeshake): ESM:
|
|
139
|
+
Package sizes (gzipped, pre-treeshake): ESM: 3.87 KB
|
|
54
140
|
|
|
55
141
|
## Dependencies
|
|
56
142
|
|
|
@@ -61,6 +147,8 @@ Package sizes (gzipped, pre-treeshake): ESM: 2.63 KB
|
|
|
61
147
|
- [@thi.ng/geom](https://github.com/thi-ng/umbrella/tree/develop/packages/geom)
|
|
62
148
|
- [@thi.ng/geom-api](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-api)
|
|
63
149
|
- [@thi.ng/geom-isoline](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-isoline)
|
|
150
|
+
- [@thi.ng/geom-poly-utils](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-poly-utils)
|
|
151
|
+
- [@thi.ng/geom-resample](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-resample)
|
|
64
152
|
- [@thi.ng/math](https://github.com/thi-ng/umbrella/tree/develop/packages/math)
|
|
65
153
|
- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/develop/packages/transducers)
|
|
66
154
|
- [@thi.ng/vectors](https://github.com/thi-ng/umbrella/tree/develop/packages/vectors)
|
|
@@ -94,7 +182,8 @@ const scene = group({ stroke: "red", __sdf: { smooth: 20 } }, [
|
|
|
94
182
|
]);
|
|
95
183
|
|
|
96
184
|
// compute bounding box + some extra margin
|
|
97
|
-
// the margin is to ensure
|
|
185
|
+
// the extra margin is to ensure the SDF can be fully sampled
|
|
186
|
+
// at some distance from the original boundary (see further below)
|
|
98
187
|
const sceneBounds = bounds(scene, 40);
|
|
99
188
|
|
|
100
189
|
// convert to an SDF distance function
|
package/api.d.ts
CHANGED
|
@@ -1,12 +1,102 @@
|
|
|
1
1
|
import type { Fn } from "@thi.ng/api";
|
|
2
2
|
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Signed Distance Field function. Computes distance from given point `p`,
|
|
5
|
+
* optionally taking into account an already computed `minD` min distance (e.g.
|
|
6
|
+
* used for bounding hierarchies). `minD` defaults to positive ∞.
|
|
7
|
+
*/
|
|
8
|
+
export declare type SDFn = (p: ReadonlyVec, minD?: number) => number;
|
|
4
9
|
export declare type SDFCombineOp = "union" | "isec" | "diff";
|
|
10
|
+
export declare type FieldCoeff<T = number> = Fn<ReadonlyVec, T>;
|
|
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
|
+
* ```
|
|
19
|
+
*/
|
|
5
20
|
export interface SDFAttribs {
|
|
21
|
+
/**
|
|
22
|
+
* If true, only the absolute (unsigned) distance will be used. For closed
|
|
23
|
+
* shapes the default is false, for lines/curves the default is true (since
|
|
24
|
+
* there's no real interior).
|
|
25
|
+
*
|
|
26
|
+
* @defaultValue false
|
|
27
|
+
*/
|
|
28
|
+
abs: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Advanced usage only. If true (default: false), the SDF will be wrapped
|
|
31
|
+
* with a bounding box pre-check.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* Currently only supported by some shape types and only usable in some
|
|
35
|
+
* circumstances, hence disabled by default.
|
|
36
|
+
*/
|
|
37
|
+
bounds: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Only used for `groups()`. Specifies the type of operation used for
|
|
40
|
+
* combining child SDFs. If {@link SDFAttribs.smooth} is != zero, smoothed
|
|
41
|
+
* versions of the operators will be used.
|
|
42
|
+
*
|
|
43
|
+
* @defaultValue "union"
|
|
44
|
+
*/
|
|
6
45
|
combine: SDFCombineOp;
|
|
7
|
-
|
|
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
|
+
*/
|
|
8
52
|
flip: boolean;
|
|
9
|
-
|
|
10
|
-
|
|
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
|
+
/**
|
|
62
|
+
* Coefficient for smooth union, intersection, difference ops (only
|
|
63
|
+
* supported for `group()` shapes). If given as function, it will be called
|
|
64
|
+
* with the current SDF query point and the return value will be used as
|
|
65
|
+
* param. Ignored if zero (default).
|
|
66
|
+
*
|
|
67
|
+
* @defaultValue 0
|
|
68
|
+
*/
|
|
69
|
+
smooth: number | FieldCoeff;
|
|
70
|
+
/**
|
|
71
|
+
* Radius coefficient for chamfered union, intersection, difference ops
|
|
72
|
+
* (only supported for `group()` shapes). If given as function, it will be
|
|
73
|
+
* called with the current SDF query point and the return value will be used
|
|
74
|
+
* as param. Ignored if zero (default).
|
|
75
|
+
*
|
|
76
|
+
* @defaultValue 0
|
|
77
|
+
*/
|
|
78
|
+
chamfer: number | FieldCoeff;
|
|
79
|
+
/**
|
|
80
|
+
* Radius coefficient for rounded union, intersection, difference ops (only
|
|
81
|
+
* supported for `group()` shapes). If given as function, it will be called
|
|
82
|
+
* with the current SDF query point and the return value will be used as
|
|
83
|
+
* param. Ignored if zero (default).
|
|
84
|
+
*
|
|
85
|
+
* @defaultValue 0
|
|
86
|
+
*/
|
|
87
|
+
round: number | FieldCoeff;
|
|
88
|
+
/**
|
|
89
|
+
* Coefficient tuple of `[radius, num]` used for stepped union,
|
|
90
|
+
* intersection, difference ops (only supported for `group()` shapes). If
|
|
91
|
+
* given as function, it will be called with the current SDF query point and
|
|
92
|
+
* the return value will be used as param. Ignored if zero (default).
|
|
93
|
+
*/
|
|
94
|
+
steps?: [number, number] | FieldCoeff<[number, number]>;
|
|
95
|
+
/**
|
|
96
|
+
* If given, this value is used to control the number of samples used for
|
|
97
|
+
* converting the original geometry to a polygon or polyline. See
|
|
98
|
+
* {@link asSDF} for more details.
|
|
99
|
+
*/
|
|
100
|
+
samples?: number;
|
|
11
101
|
}
|
|
12
102
|
//# sourceMappingURL=api.d.ts.map
|
package/as-polygons.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NumericArray } from "@thi.ng/api";
|
|
2
|
+
import type { Polygon } from "@thi.ng/geom";
|
|
3
|
+
import type { AABBLike } from "@thi.ng/geom-api";
|
|
4
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
5
|
+
import type { SDFn } from "./api.js";
|
|
6
|
+
/**
|
|
7
|
+
* Extract contour polygons from given SDF at specified `distances`. The SDF can
|
|
8
|
+
* be either given as distance function or as pre-discretized image (e.g. as
|
|
9
|
+
* result of {@link sample2d}). The SDF will be sampled in the given bounding
|
|
10
|
+
* rect and resolution (if SDF was given as image, resolution MUST be the
|
|
11
|
+
* same!).
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* If `distances` are not given, only the original boundary (i.e. distance=0)
|
|
15
|
+
* will be extracted. By default all resulting polygons will be simplified using
|
|
16
|
+
* Douglas-Peucker with a threshold of `eps`. By default this will only remove
|
|
17
|
+
* co-linear vertices, but more agressive settings are possible/recommended.
|
|
18
|
+
*
|
|
19
|
+
* @param sdf
|
|
20
|
+
* @param bounds
|
|
21
|
+
* @param res
|
|
22
|
+
* @param distances
|
|
23
|
+
* @param eps
|
|
24
|
+
*/
|
|
25
|
+
export declare const asPolygons: (sdf: NumericArray | SDFn, bounds: AABBLike, res: ReadonlyVec, distances?: Iterable<number>, eps?: number) => Polygon[];
|
|
26
|
+
//# sourceMappingURL=as-polygons.d.ts.map
|
package/as-polygons.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isFunction } from "@thi.ng/checks/is-function";
|
|
2
|
+
import { isolines, setBorder } from "@thi.ng/geom-isoline";
|
|
3
|
+
import { simplify } from "@thi.ng/geom-resample/simplify";
|
|
4
|
+
import { polygon } from "@thi.ng/geom/polygon";
|
|
5
|
+
import { comp } from "@thi.ng/transducers/comp";
|
|
6
|
+
import { map } from "@thi.ng/transducers/map";
|
|
7
|
+
import { mapcat } from "@thi.ng/transducers/mapcat";
|
|
8
|
+
import { push } from "@thi.ng/transducers/push";
|
|
9
|
+
import { transduce } from "@thi.ng/transducers/transduce";
|
|
10
|
+
import { add2 } from "@thi.ng/vectors/add";
|
|
11
|
+
import { div2 } from "@thi.ng/vectors/div";
|
|
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);
|
|
39
|
+
};
|
package/as-sdf.d.ts
CHANGED
|
@@ -9,14 +9,22 @@ import type { SDFn } from "./api.js";
|
|
|
9
9
|
* Currently supported shape types:
|
|
10
10
|
*
|
|
11
11
|
* - circle
|
|
12
|
+
* - cubic (auto-converted to polyline)
|
|
12
13
|
* - ellipse
|
|
13
14
|
* - group
|
|
14
15
|
* - line
|
|
16
|
+
* - path (auto-converted to polygon/polyline)
|
|
15
17
|
* - points
|
|
16
18
|
* - polygon
|
|
17
19
|
* - polyline
|
|
18
20
|
* - quadratic bezier
|
|
19
21
|
* - rect
|
|
22
|
+
*
|
|
23
|
+
* For shapes which need to be converted to polygons/polylines, the
|
|
24
|
+
* {@link SDFAttribs.samples} attribute can be used to control the resulting
|
|
25
|
+
* number of vertices. If not specified {@link @thi.ng/geom-api#DEFAULT_SAMPLES}
|
|
26
|
+
* will be used (which can be globally set via
|
|
27
|
+
* {@link @thi.ng/geom-api#setDefaultSamples}).
|
|
20
28
|
*/
|
|
21
29
|
export declare const asSDF: MultiFn1<IShape, SDFn>;
|
|
22
30
|
//# sourceMappingURL=as-sdf.d.ts.map
|
package/as-sdf.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
|
|
2
|
+
import { assert } from "@thi.ng/errors/assert";
|
|
2
3
|
import { unsupported } from "@thi.ng/errors/unsupported";
|
|
4
|
+
import { asPolygon } from "@thi.ng/geom/as-polygon";
|
|
5
|
+
import { asPolyline } from "@thi.ng/geom/as-polyline";
|
|
6
|
+
import { simplify } from "@thi.ng/geom/simplify";
|
|
3
7
|
import { __dispatch } from "@thi.ng/geom/internal/dispatch";
|
|
4
8
|
import { add2 } from "@thi.ng/vectors/add";
|
|
5
9
|
import { mulN2 } from "@thi.ng/vectors/muln";
|
|
6
|
-
import {
|
|
10
|
+
import { chamferDiff, chamferIsec, chamferUnion, diff, isec, roundDiff, roundIsec, roundUnion, smoothDiff, smoothIsec, smoothUnion, stepDiff, stepIsec, stepUnion, union, } from "./ops.js";
|
|
7
11
|
import { box2, circle2, DEFAULT_ATTRIBS, ellipse2, line2, points2, polygon2, polyline2, quadratic2, withSDFAttribs, } from "./shapes.js";
|
|
8
12
|
/**
|
|
9
13
|
* Takes an {@link @thi.ng/geom-api#IShape} instance (possibly a tree, e.g. via
|
|
@@ -13,14 +17,22 @@ import { box2, circle2, DEFAULT_ATTRIBS, ellipse2, line2, points2, polygon2, pol
|
|
|
13
17
|
* Currently supported shape types:
|
|
14
18
|
*
|
|
15
19
|
* - circle
|
|
20
|
+
* - cubic (auto-converted to polyline)
|
|
16
21
|
* - ellipse
|
|
17
22
|
* - group
|
|
18
23
|
* - line
|
|
24
|
+
* - path (auto-converted to polygon/polyline)
|
|
19
25
|
* - points
|
|
20
26
|
* - polygon
|
|
21
27
|
* - polyline
|
|
22
28
|
* - quadratic bezier
|
|
23
29
|
* - rect
|
|
30
|
+
*
|
|
31
|
+
* For shapes which need to be converted to polygons/polylines, the
|
|
32
|
+
* {@link SDFAttribs.samples} attribute can be used to control the resulting
|
|
33
|
+
* number of vertices. If not specified {@link @thi.ng/geom-api#DEFAULT_SAMPLES}
|
|
34
|
+
* will be used (which can be globally set via
|
|
35
|
+
* {@link @thi.ng/geom-api#setDefaultSamples}).
|
|
24
36
|
*/
|
|
25
37
|
export const asSDF = defmulti(__dispatch, {
|
|
26
38
|
quad: "poly",
|
|
@@ -28,31 +40,40 @@ export const asSDF = defmulti(__dispatch, {
|
|
|
28
40
|
}, {
|
|
29
41
|
[DEFAULT]: ($) => unsupported(`shape type: ${$.type}`),
|
|
30
42
|
circle: ($) => circle2($.pos, $.r, __sdfAttribs($.attribs)),
|
|
43
|
+
cubic: ($) => asSDF(simplify(asPolyline($, (__sdfAttribs($.attribs) || {}).samples), 0)),
|
|
31
44
|
ellipse: ($) => ellipse2($.pos, $.r, __sdfAttribs($.attribs)),
|
|
32
|
-
group: (
|
|
33
|
-
const
|
|
45
|
+
group: ($) => {
|
|
46
|
+
const { attribs, children } = $;
|
|
47
|
+
const attr = { ...DEFAULT_ATTRIBS, ...__sdfAttribs(attribs) };
|
|
48
|
+
__validateAttribs(attr);
|
|
34
49
|
const $children = children.map(asSDF);
|
|
35
50
|
let res;
|
|
36
51
|
if ($children.length > 1) {
|
|
37
|
-
switch (
|
|
52
|
+
switch (attr.combine) {
|
|
38
53
|
case "diff":
|
|
39
|
-
res =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
res = __selectCombineOp(attr, $children, diff, {
|
|
55
|
+
chamfer: chamferDiff,
|
|
56
|
+
round: roundDiff,
|
|
57
|
+
smooth: smoothDiff,
|
|
58
|
+
steps: stepDiff,
|
|
59
|
+
});
|
|
43
60
|
break;
|
|
44
61
|
case "isec":
|
|
45
|
-
res =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
res = __selectCombineOp(attr, $children, isec, {
|
|
63
|
+
chamfer: chamferIsec,
|
|
64
|
+
round: roundIsec,
|
|
65
|
+
smooth: smoothIsec,
|
|
66
|
+
steps: stepIsec,
|
|
67
|
+
});
|
|
49
68
|
break;
|
|
50
69
|
case "union":
|
|
51
70
|
default: {
|
|
52
|
-
res =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
res = __selectCombineOp(attr, $children, union, {
|
|
72
|
+
chamfer: chamferUnion,
|
|
73
|
+
round: roundUnion,
|
|
74
|
+
smooth: smoothUnion,
|
|
75
|
+
steps: stepUnion,
|
|
76
|
+
});
|
|
56
77
|
}
|
|
57
78
|
}
|
|
58
79
|
}
|
|
@@ -60,11 +81,15 @@ export const asSDF = defmulti(__dispatch, {
|
|
|
60
81
|
res = $children[0];
|
|
61
82
|
}
|
|
62
83
|
else {
|
|
63
|
-
return
|
|
84
|
+
return attr.flip ? () => -Infinity : () => Infinity;
|
|
64
85
|
}
|
|
65
|
-
return withSDFAttribs(res,
|
|
86
|
+
return withSDFAttribs(res, attr);
|
|
66
87
|
},
|
|
67
88
|
line: ({ points: [a, b], attribs }) => line2(a, b, __sdfAttribs(attribs)),
|
|
89
|
+
path: ($) => {
|
|
90
|
+
const n = (__sdfAttribs($.attribs) || {}).samples;
|
|
91
|
+
return asSDF(simplify($.closed ? asPolygon($, n) : asPolyline($, n), 0));
|
|
92
|
+
},
|
|
68
93
|
points: ($) => points2($.points, __sdfAttribs($.attribs)),
|
|
69
94
|
poly: ($) => polygon2($.points, __sdfAttribs($.attribs)),
|
|
70
95
|
polyline: ($) => polyline2($.points, __sdfAttribs($.attribs)),
|
|
@@ -76,3 +101,13 @@ export const asSDF = defmulti(__dispatch, {
|
|
|
76
101
|
});
|
|
77
102
|
/** @internal */
|
|
78
103
|
const __sdfAttribs = (attribs) => attribs ? attribs.__sdf : null;
|
|
104
|
+
const OPS = ["chamfer", "round", "smooth", "steps"];
|
|
105
|
+
const __validateAttribs = (attribs) => assert(OPS.filter((x) => attribs[x]).length < 2, "only 1 of these options can be used at once: chamfer, round, smooth");
|
|
106
|
+
const __selectCombineOp = (attribs, children, op, paramOps) => {
|
|
107
|
+
for (let k of OPS) {
|
|
108
|
+
if (attribs[k]) {
|
|
109
|
+
return paramOps[k](attribs[k], children);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return op(children);
|
|
113
|
+
};
|
package/bounds.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
+
import type { SDFn } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Augments given distance function with a bounding circle pre-check. The circle
|
|
5
|
+
* can be either given as `[centroid, radius]` tuple or can be auto-computed
|
|
6
|
+
* from given array of points. When the returned function is called it first
|
|
7
|
+
* computes the distance to the bounding circle and *only* if that is less then
|
|
8
|
+
* the current (already computed) min distance (default: positive ∞), the
|
|
9
|
+
* original (wrapped) `sdf` function is called. If the distance to the bounding
|
|
10
|
+
* circle is > `minD`, the presumably more costly `sdf` is skipped and `minD`
|
|
11
|
+
* returned.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Currently used for {@link polygon2}, {@link polyline2}, {@link points2}.
|
|
15
|
+
*
|
|
16
|
+
* @param sdf
|
|
17
|
+
* @param pts
|
|
18
|
+
*/
|
|
19
|
+
export declare function withBoundingCircle(sdf: SDFn, pts: ReadonlyVec[]): SDFn;
|
|
20
|
+
export declare function withBoundingCircle(sdf: SDFn, centroid: ReadonlyVec, r: number): SDFn;
|
|
21
|
+
/**
|
|
22
|
+
* Similar to {@link withBoundingCircle}, but using a bounding rect (defined via
|
|
23
|
+
* `min`/`max` or computed from an array of points).
|
|
24
|
+
*
|
|
25
|
+
* @param sdf
|
|
26
|
+
* @param pts
|
|
27
|
+
*/
|
|
28
|
+
export declare function withBoundingRect(sdf: SDFn, pts: ReadonlyVec[]): SDFn;
|
|
29
|
+
export declare function withBoundingRect(sdf: SDFn, min: ReadonlyVec, max: ReadonlyVec): SDFn;
|
|
30
|
+
//# sourceMappingURL=bounds.d.ts.map
|
package/bounds.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { boundingCircle, bounds2 } from "@thi.ng/geom-poly-utils/bounds";
|
|
2
|
+
import { addmN2 } from "@thi.ng/vectors/addmn";
|
|
3
|
+
import { sub2 } from "@thi.ng/vectors/sub";
|
|
4
|
+
import { submN2 } from "@thi.ng/vectors/submn";
|
|
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
|
+
};
|
|
18
|
+
}
|
|
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
|
+
};
|
|
31
|
+
}
|
package/dist.d.ts
CHANGED
|
@@ -1,10 +1,97 @@
|
|
|
1
1
|
import { ReadonlyVec } from "@thi.ng/vectors/api";
|
|
2
|
+
/**
|
|
3
|
+
* Computes signed distance to centered 2D circle of given `radius`.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Ported from code by Inigo Quilez:
|
|
7
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
8
|
+
*
|
|
9
|
+
* @param p
|
|
10
|
+
* @param radius
|
|
11
|
+
*/
|
|
2
12
|
export declare const distCircle2: (p: ReadonlyVec, radius: number) => number;
|
|
13
|
+
/**
|
|
14
|
+
* Computes signed distance to centered 2D box of given `halfSize`.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Ported from code by Inigo Quilez:
|
|
18
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
19
|
+
*
|
|
20
|
+
* @param p
|
|
21
|
+
* @param halfSize
|
|
22
|
+
*/
|
|
3
23
|
export declare const distBox2: (p: ReadonlyVec, halfSize: ReadonlyVec) => number;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Computes signed distance to the 2D polygon defined by given point array.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Ported from code by Inigo Quilez:
|
|
29
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
30
|
+
*
|
|
31
|
+
* @param p
|
|
32
|
+
* @param pts
|
|
33
|
+
*/
|
|
34
|
+
export declare const distPolygon2: (p: ReadonlyVec, pts: ReadonlyVec[]) => number;
|
|
35
|
+
/**
|
|
36
|
+
* Computes distance to 2D line segment defined by endpoints `a` and `b`.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* Ported from code by Inigo Quilez:
|
|
40
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
41
|
+
*
|
|
42
|
+
* @param p
|
|
43
|
+
* @param a
|
|
44
|
+
* @param b
|
|
45
|
+
*/
|
|
46
|
+
export declare const distSegment2: (p: ReadonlyVec, a: ReadonlyVec, b: ReadonlyVec) => number;
|
|
47
|
+
/**
|
|
48
|
+
* Computes distance to 2D polyline defined by given point array (i.e. the union
|
|
49
|
+
* of pairwise results of {@link distSegment2}).
|
|
50
|
+
*
|
|
51
|
+
* @param p
|
|
52
|
+
* @param pts
|
|
53
|
+
*/
|
|
54
|
+
export declare const distPolyline2: (p: ReadonlyVec, pts: ReadonlyVec[]) => number;
|
|
55
|
+
/**
|
|
56
|
+
* Computes signed distance to circular arc with given aperture, radius and
|
|
57
|
+
* radius offset (thickness). The aperture is symmetric along the Y-axis and has
|
|
58
|
+
* its origin in +Y.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* Ported from code by Inigo Quilez:
|
|
62
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
63
|
+
*
|
|
64
|
+
* @param p
|
|
65
|
+
* @param apert - pre-computed vec2 of [sin, cos] of aperture angle
|
|
66
|
+
* @param ra - inner radius
|
|
67
|
+
* @param rb - outer radius offset (thickness)
|
|
68
|
+
*/
|
|
7
69
|
export declare const distArc2: (p: ReadonlyVec, apert: ReadonlyVec, ra: number, rb: number) => number;
|
|
70
|
+
/**
|
|
71
|
+
* Computes (unsigned, by default) distance to given 2D quadratic bezier
|
|
72
|
+
* segment. If `signed` is set to true, points in the curve's interior region
|
|
73
|
+
* will yield negative distances.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* Based on code by Inigo Quilez:
|
|
77
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
78
|
+
*
|
|
79
|
+
* @param pos
|
|
80
|
+
* @param a
|
|
81
|
+
* @param b
|
|
82
|
+
* @param c
|
|
83
|
+
* @param signed
|
|
84
|
+
*/
|
|
8
85
|
export declare const distQuadratic2: (pos: ReadonlyVec, a: ReadonlyVec, b: ReadonlyVec, c: ReadonlyVec, signed?: boolean) => number;
|
|
86
|
+
/**
|
|
87
|
+
* Computes signed distance to centered 2D ellipse.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* Based on code by Inigo Quilez:
|
|
91
|
+
* https://iquilezles.org/articles/distfunctions2d/
|
|
92
|
+
*
|
|
93
|
+
* @param p
|
|
94
|
+
* @param radii
|
|
95
|
+
*/
|
|
9
96
|
export declare const distEllipse2: ([px, py]: ReadonlyVec, [abx, aby]: ReadonlyVec) => number;
|
|
10
97
|
//# sourceMappingURL=dist.d.ts.map
|