Important
Please participate in the survey here!
(open until end of February)
To achieve a better sample size, I'd highly appreciate if you could circulate the link to this survey in your own networks.
Note
This is one of 189 standalone projects, maintained as part of the @thi.ng/umbrella monorepo and anti-framework.
🚀 Help me to work full-time on these projects by sponsoring me on GitHub. Thank you! ❤️
2D Signed Distance Field creation from @thi.ng/geom shapes, conversions, sampling, combinators.
Includes several distance functions and SDF operators ported from GLSL implementations by:
SDFs can be directly defined/composed via provided shape primitive functions and combinators OR via automatic conversion from @thi.ng/geom geometry types/hierarchies. In the latter case various attributes can be used to control the conversion process. Regardless of approach, the result will be a single distance function which accepts a world position and returns the signed distance to the encoded scene.
// via direct SDF composition
import { circle2, union } from "@thi.ng/geom-sdf";
const f = union([circle2([-50, 0], 100), circle2([50, 0], 100)]);
// via conversion
import { circle, group } from "@thi.ng/geom";
import { asSDF } from "@thi.ng/geom-sdf";
const f = asSDF(group({}, [circle([-50, 0], 100), circle([50, 0], 100)]));
The following table illustrates various options how SDFs can be combined. When
using the asSDF()
geometry converter, these operators can be specified and configured (most are
parametric) via a shape group()
's
attributes,
e.g.
group({ __sdf: { combine: "diff", chamfer: 50 }}, [
rectFromCentroid([-50,-50], 200),
rectFromCentroid([50,50], 200),
])
Operator | Union | Difference | Intersection |
---|---|---|---|
default | ![]() |
![]() |
![]() |
chamfer | ![]() |
![]() |
![]() |
round | ![]() |
![]() |
![]() |
smooth | ![]() |
![]() |
![]() |
steps | ![]() |
![]() |
![]() |
The package provides the
sample2d()
and
asPolygons()
functions to discretize an SDF and cache results in a buffer (image) and then
extract contour polygons from it, i.e. convert the 2D back into geometry (see
example further below). The SDF will be sampled in a user defined bounding
rectangle (with customizable resolution) and the sampling positions can be
modulated via several provided domain modifiers to create various axial/spatial
repetions, symmetries etc. Modifiers are nestable/composable via standard
functional composition (e.g. using
compL()
) and also
support custom modfifiers. The table below illustrates a few examples effects:
Modifier | |||
---|---|---|---|
repeat2() |
![]() |
![]() |
![]() |
repeatGrid2() |
![]() |
![]() |
![]() |
repeatMirror2() |
![]() |
![]() |
![]() |
repeatPolar2() |
![]() |
![]() |
![]() |
ALPHA - bleeding edge / work-in-progress
Search or submit any issues for this package
- @thi.ng/distance-transform - Binary image to Distance Field transformation
- @thi.ng/geom-isoline - Fast 2D contour line extraction / generation
- @thi.ng/pixel - Typedarray integer & float pixel buffers w/ customizable formats, blitting, drawing, convolution
- @thi.ng/shader-ast-stdlib - Function collection for modular GPGPU / shader programming with @thi.ng/shader-ast
yarn add @thi.ng/geom-sdf
ES module import:
<script type="module" src="https://cdn.skypack.dev/@thi.ng/geom-sdf"></script>
For Node.js REPL:
const geomSdf = await import("@thi.ng/geom-sdf");
Package sizes (brotli'd, pre-treeshake): ESM: 3.65 KB
- @thi.ng/api
- @thi.ng/checks
- @thi.ng/defmulti
- @thi.ng/errors
- @thi.ng/geom
- @thi.ng/geom-api
- @thi.ng/geom-isoline
- @thi.ng/geom-poly-utils
- @thi.ng/geom-resample
- @thi.ng/math
- @thi.ng/transducers
- @thi.ng/vectors
One project in this repo's /examples directory is using this package:
Screenshot | Description | Live demo | Source |
---|---|---|---|
![]() |
(Re)Constructing the thi.ng logo using a 2D signed-distance field | Demo | Source |
import { asSvg, bounds, circle, group, svgDoc } from "@thi.ng/geom";
import { asPolygons, asSDF, sample2d } from "@thi.ng/geom-sdf";
import { range, repeatedly } from "@thi.ng/transducers";
import { randMinMax2 } from "@thi.ng/vectors";
import { writeFileSync } from "fs";
const RES = [256, 256];
// create a group of 20 random circle shapes
// the special `__sdf` attrib object is used to control the conversion later
// the `smooth` option will combine the circles using the `smoothUnion()` operator
// see: https://docs.thi.ng/umbrella/geom-sdf/interfaces/SDFAttribs.html
const scene = group({ stroke: "red", __sdf: { smooth: 20 } }, [
...repeatedly(
() =>
circle(
randMinMax2([], [-100, -100], [100, 100]),
5 + Math.random() * 15
),
20
),
]);
// compute bounding box + some extra margin
// the extra margin is to ensure the SDF can be fully sampled
// at some distance from the original boundary (see further below)
const sceneBounds = bounds(scene, 40);
// convert to an SDF distance function
// more information about supported shape types:
// https://docs.thi.ng/umbrella/geom-sdf/functions/asSDF.html
const sdf = asSDF(scene);
// sample SDF in given bounding rect and resolution
const image = sample2d(sdf, sceneBounds, RES);
// extract contour polygons from given image
// in this case the contours extracted are at distances in the [0..32) interval
// the function also simplifies the resulting polygons using the Douglas-Peucker algorithm
// with the given threshold (0.25) - the default setting only removes co-linear vertices...
// see: https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
const contours = asPolygons(image, sceneBounds, RES, range(0, 32, 4), 0.25);
// convert to SVG and output as file
writeFileSync(
"export/metaballs.svg",
asSvg(
svgDoc(
{ fill: "none" },
// contour polygons
group({ stroke: "#000" }, contours),
// original geometry
scene
)
)
);
Results:
circle() |
rect() |
---|---|
![]() |
![]() |
circle() (smooth) |
rect() (smooth) |
![]() |
![]() |
If this project contributes to an academic publication, please cite it as:
@misc{thing-geom-sdf,
title = "@thi.ng/geom-sdf",
author = "Karsten Schmidt",
note = "https://thi.ng/geom-sdf",
year = 2022
}
© 2022 - 2024 Karsten Schmidt // Apache License 2.0