npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@thi.ng/geom-sdf

v1.0.65

Published

2D Signed Distance Field creation from @thi.ng/geom shapes, conversions, sampling, combinators

Downloads

391

Readme

@thi.ng/geom-sdf

npm version npm downloads Mastodon Follow

[!NOTE] This is one of 212 standalone projects, maintained as part of the @thi.ng/umbrella monorepo and anti-framework.

🚀 Please help me to work full-time on these projects by sponsoring me on GitHub. Thank you! ❤️

About

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:

SDF creation

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)]));

Supported thi.ng/geom shape types

(shape type descriptions)

  • circle
  • complexPoly (polygon w/ holes)
  • cubic
  • ellipse
  • group (of supported shapes)
  • line
  • path (w/ holes and/or sub-paths, multiple curves)
  • points
  • polygon
  • polyline
  • quad
  • quadratic
  • rect
  • triangle

SDF combinators

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.

import { group, rectFromCentroid } from "@thi.ng/geom";

group({ __sdf: { combine: "diff", chamfer: 50 }}, [
    rectFromCentroid([-50,-50], 200),
    rectFromCentroid([50,50], 200),
]);

| Operator | Union | Difference | Intersection | |----------|----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| | default | | | | | chamfer | | | | | round | | | | | smooth | | | | | steps | | | |

SDF discretization, sampling & domain modifiers

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() | | | |

Status

ALPHA - bleeding edge / work-in-progress

Search or submit any issues for this package

Related packages

Installation

yarn add @thi.ng/geom-sdf

ESM import:

import * as sdf from "@thi.ng/geom-sdf";

Browser ESM import:

<script type="module" src="https://esm.run/@thi.ng/geom-sdf"></script>

JSDelivr documentation

For Node.js REPL:

const sdf = await import("@thi.ng/geom-sdf");

Package sizes (brotli'd, pre-treeshake): ESM: 3.76 KB

Dependencies

Note: @thi.ng/api is in most cases a type-only import (not used at runtime)

Usage examples

Two projects in this repo's /examples directory are using this package:

| Screenshot | Description | Live demo | Source | |:---------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------|:----------------------------------------------------|:---------------------------------------------------------------------------------| | | (Re)Constructing the thi.ng logo using a 2D signed-distance field | Demo | Source | | | SVG path to SDF, applying deformation and converting back to SVG | Demo | Source |

API

Generated API docs

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 } "node: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() | |------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| | metaballs based on circles | metaballs based on rectangles | | circle() (smooth) | rect() (smooth) | | metaballs w/ smooth union | metaballs w/ smooth union |

Authors

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
}

License

© 2022 - 2025 Karsten Schmidt // Apache License 2.0