model2d

package
v0.4.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2024 License: BSD-2-Clause Imports: 24 Imported by: 9

Documentation

Overview

Package model2d provides various tools for loading, manipulating, saving, and using 2D shapes. It is also intended to aid in creating 3D models which incorporate 2D shapes in some way.

Index

Constants

View Source
const (
	DefaultBezierFitterNumIters  = 100
	DefaultBezierFitTolerance    = 1e-8
	DefaultBezierFitDelta        = 1e-6
	DefaultBezierFitMinStepScale = 1e-2
	DefaultBezierFitLineStep     = 2.0
	DefaultBezierFitLineGSS      = 8
)
View Source
const (
	DefaultMedialAxisIters = 32
	DefaultMedialAxisEps   = 1e-8
)
View Source
const (
	RasterizerDefaultSubsamples = 8
	RasterizerDefaultLineWidth  = 1.0
)
View Source
const (
	DefaultSurfaceEstimatorBisectCount         = 32
	DefaultSurfaceEstimatorNormalSamples       = 40
	DefaultSurfaceEstimatorNormalBisectEpsilon = 1e-4
	DefaultSurfaceEstimatorNormalNoiseEpsilon  = 1e-4
)
View Source
const DefaultBezierMaxSplits = 16

DefaultBezierMaxSplits determines the maximum number of subdivisions when computing Bezier arc lengths.

Variables

View Source
var Origin = Coord{}

Functions

func BoundsUnion added in v0.4.2

func BoundsUnion[B Bounder](bs []B) (min Coord, max Coord)

BoundsUnion computes the bounds of one or more bounder.

func BoundsValid added in v0.2.9

func BoundsValid(b Bounder) bool

BoundsValid checks for numerical issues with the bounds.

func CacheScalarFunc added in v0.2.13

func CacheScalarFunc(f func(float64) float64) func(float64) float64

CacheScalarFunc creates a scalar function that is equivalent to a deterministic function f, but caches results across calls in a concurrency-safe manner.

func ColliderContains

func ColliderContains(c Collider, coord Coord, margin float64) bool

ColliderContains checks if a point is within a Collider and at least margin away from the border.

If the margin is negative, points are also conatined if the point is less than -margin away from the surface.

func Colorize added in v0.2.13

func Colorize(g *image.Gray, co color.Color) *image.RGBA

Colorize turns a grayscale image into a color image with an alpha channel.

It is assumed that black (0) is "positive" while white (0xff) is negative.

func ColorizeOverlay added in v0.2.13

func ColorizeOverlay(gs []*image.Gray, cs []color.Color) *image.RGBA

ColorizeOverlay turns a series of grayscale images into corresponding colors and then overlays them, each on top of the last.

All images must have the same bounds.

func CurveEvalX added in v0.2.6

func CurveEvalX(c Curve, x float64) float64

CurveEvalX finds the y value that occurs at the given x value, assuming that the curve is monotonic in x.

If the y value cannot be found, NaN is returned.

func CurveInverseX added in v0.2.6

func CurveInverseX(c Curve, x float64) float64

CurveInverseX gets the t value between 0 and 1 where the x value is equal to some x, assuming the curve is monotonic in x.

If the t cannot be found, NaN is returned.

func EncodeCSV added in v0.2.13

func EncodeCSV(m *Mesh) []byte

EncodeCSV encodes the mesh as a CSV file.

func EncodeCustomPathSVG added in v0.4.2

func EncodeCustomPathSVG(meshes []*Mesh, fillColors []string, strokeColors []string,
	thicknesses []float64, bounds Bounder) []byte

EncodeCustomPathSVG encodes a collection of meshes each as path elements with individually specified parameters.

If bounds is not nil, it is used to determine the resulting bounds of the SVG. Otherwise, the bounds of the mesh are used.

func EncodeCustomSVG added in v0.2.11

func EncodeCustomSVG(meshes []*Mesh, colors []string, thicknesses []float64, bounds Bounder) []byte

EncodeCustomSVG encodes multiple meshes, each with a different color and line thickness.

If bounds is not nil, it is used to determine the resulting bounds of the SVG. Otherwise, the union of all meshes is used.

func EncodePathSVG added in v0.4.2

func EncodePathSVG(m *Mesh) []byte

EncodePathSVG encodes the mesh as a single filled path in an SVG file.

func EncodeSVG

func EncodeSVG(m *Mesh) []byte

EncodeSVG encodes the mesh as an SVG file.

func GroupBounders added in v0.2.11

func GroupBounders[B Bounder](objects []B)

GroupBounders sorts a slice of objects into a balanced bounding box hierarchy.

The sorted slice can be recursively cut in half, and each half will be spatially separated as well as possible along some axis. To cut a slice in half, divide the length by two, round down, and use the result as the start index for the second half.

func GroupSegments

func GroupSegments(faces []*Segment)

GroupSegments is like GroupBounders, but for segments in particular. This is now equivalent to GroupBounders(faces).

This can be used to prepare models for being turned into a collider efficiently, or for storing meshes in an order well-suited for file compression.

The resulting hierarchy can be passed directly to GroupedSegmentsToCollider().

func InBounds

func InBounds(b Bounder, c Coord) bool

InBounds returns true if c is contained within the bounding box of b.

func MarchingSquaresASCII added in v0.2.8

func MarchingSquaresASCII(s Solid, delta float64) string

MarchingSquaresASCII turns a Solid into an ASCII-art line-drawing using a 2D version of marching cubes.

The delta is used as the horizontal spacing, and an aspect ratio of 2.0 (height/width) is assumed.

func QuarticMetaballFalloffFunc added in v0.3.5

func QuarticMetaballFalloffFunc(r float64) float64

QuarticMetaballFalloffFunc implements 1/r^4 falloff.

func Rasterize added in v0.2.6

func Rasterize(path string, obj any, scale float64) error

Rasterize renders a Solid, Collider, or Mesh to an image file.

The bounds of the object being rendered are scaled by the provided scale factor to convert to pixel coordinates.

This uses the default rasterization settings, such as the default line width and anti-aliasing settings. To change this, use a Rasterizer object directly.

func RasterizeColor added in v0.2.13

func RasterizeColor(path string, objs []any, colors []color.Color, scale float64) error

RasterizeColor is like Rasterize, but it renders multiple objects in different colors.

func SaveImage added in v0.2.7

func SaveImage(path string, img image.Image) error

SaveImage saves a rasterized image to a file, inferring the file type from the extension.

func Triangulate

func Triangulate(polygon []Coord) [][3]Coord

Triangulate turns any simple polygon into a set of equivalent triangles.

The polygon is passed as a series of points, in order. The first point is re-used as the ending point, so no ending should be explicitly specified.

Unlike TriangulateMesh, the order of the coordinates needn't be clockwise, and the orientation of the resulting triangles is undefined.

This should only be used for polygons with several vertices. For more complex shapes, use TriangulateMesh.

func TriangulateMesh added in v0.2.10

func TriangulateMesh(m *Mesh) [][3]Coord

TriangulateMesh creates a minimal collection of triangles that cover the enclosed region of a mesh.

The mesh must be manifold, non-intersecting, and have the correct orientation (i.e. correct normals). The mesh may have holes, and is assumed to obey the even-odd rule for containment. If the mesh does not meet the expected criteria, the behavior of TriangulateMesh is undefined and may result in a panic.

The vertices of the resulting triangles are ordered clockwise (assuming a y-axis that points upward). This way, each triangle can itself be considered a minimal, correctly-oriented mesh.

Types

type Adder added in v0.4.0

type Adder interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 | ~complex64 | ~complex128
}

type BVH added in v0.2.11

type BVH[B Bounder] struct {
	// Leaf, if non-nil, is the final bounder.
	Leaf B

	// Branch, if Leaf is nil, points to two children.
	Branch []*BVH[B]
}

BVH represents a (possibly unbalanced) axis-aligned bounding box hierarchy.

A BVH can be used to accelerate collision detection. See BVHToCollider() for more details.

A BVH node is either a leaf (a single Bounder), or a branch with two or more children.

func NewBVHAreaDensity added in v0.2.11

func NewBVHAreaDensity[B Bounder](objects []B) *BVH[B]

NewBVHAreaDensity creates a BVH by minimizing the product of each bounding box's perimeter with the number of objects contained in the bounding box at each branch.

This is good for efficient ray collision detection.

type BezierCurve

type BezierCurve []Coord

BezierCurve implements an arbitrarily high-dimensional Bezier curve.

func (BezierCurve) CachedEvalX added in v0.2.13

func (b BezierCurve) CachedEvalX(x float64) func(float64) float64

CachedEvalX returns a function like EvalX that is cached between calls in a concurrency-safe manner.

func (BezierCurve) Eval

func (b BezierCurve) Eval(t float64) Coord

Eval evaluates the curve at time t, where 0 <= t <= 1.

func (BezierCurve) EvalX

func (b BezierCurve) EvalX(x float64) float64

EvalX finds the y value that occurs at the given x value, assuming that the curve is monotonic in x.

If the y value cannot be found, NaN is returned.

func (BezierCurve) InverseX

func (b BezierCurve) InverseX(x float64) float64

InverseX gets the t value between 0 and 1 where the x value is equal to some x, assuming the curve is monotonic in x.

If the t cannot be found, NaN is returned.

func (BezierCurve) Length added in v0.2.22

func (b BezierCurve) Length(tol float64, maxSplits int) float64

Length approximates the arclength of the curve within the given margin of error.

If maxSplits is specified, it determines the maximum number of sub-divisions to perform. Otherwise, DefaultBezierMaxSplits is used.

func (BezierCurve) Polynomials added in v0.2.22

func (b BezierCurve) Polynomials() [2]numerical.Polynomial

Polynomials converts the X and Y coordinates of the curve into polynomials of t.

func (BezierCurve) Split added in v0.2.22

Split creates two Bezier curves from b, where the first curve represents b in the range [0, t] and the second in the range [t, 1].

func (BezierCurve) Transpose

func (b BezierCurve) Transpose() BezierCurve

Transpose generates a BezierCurve where x and y are swapped.

type BezierFitter added in v0.2.15

type BezierFitter struct {
	// NumIters is the number of gradient steps to use to
	// fit each Bezier curve.
	// If 0, DefaultBezierFitterNumIters is used.
	NumIters int

	// Tolerance is the maximum mean-squared error for a
	// curve when fitting a chain of connected points.
	// It is relative to the area of the bounding box of
	// the points in the chain.
	//
	// If 0, DefaultBezierFitTolerance is used.
	Tolerance float64

	// AbsTolerance is similar to Tolerance, but not
	// relative to the size of a chain.
	// If both Tolerance and AbsTolerance are specified,
	// then the max of both are used.
	//
	// If 0, only the relative tolerance is used.
	AbsTolerance float64

	// Delta, if specified, controls the step size used
	// for finite differences, relative to the size of the
	// entire Bezier curve.
	// If 0, DefaultBezierFitDelta is used.
	Delta float64

	// L2Penalty, if specified, is a loss penalty imposed
	// on the squared distance between the control points
	// and their corresponding endpoints, scaled relative
	// to the squared distance between the endpoints.
	L2Penalty float64

	// PerimPenalty, if specified, is a loss penalty
	// imposed on the square of the length of the control
	// polygon, which upper-bounds the square of the arc
	// length.
	PerimPenalty float64

	// MinStepScale, if specified, is a scalar multiplied
	// by the finite-differences delta to decide the first
	// (and smallest) step to try for line search.
	// If 0, DefaultBezierFitMinStepScale is used.
	MinStepScale float64

	// LineStep, if specified, is the rate of increase for
	// each step of line search. Larger values make line
	// search faster, but can miss local minima.
	// If 0, DefaultBezierFitLineStep is used.
	LineStep float64

	// LineGSS, if specified, is the number of steps used
	// for golden section search at the end of line
	// search. Higher values yield more precise steps.
	// If 0, DefaultBezierFitLineGSS is used.
	LineGSS int

	// Momentum, if specified, is the momentum coefficient.
	// If 0, regular gradient descent is used.
	Momentum float64

	// AllowIntersections can be set to true to allow
	// Bezier curves to cross themselves.
	AllowIntersections bool

	// NoFirstGuess disables FirstGuess() approximations
	// during Fit() and FitChain() calls.
	NoFirstGuess bool
}

A BezierFitter fits Bezier curves to points.

func (*BezierFitter) FirstGuess added in v0.2.15

func (b *BezierFitter) FirstGuess(points []Coord) BezierCurve

FirstGuess attempts to quickly approximate some subset of the specified points with a cubic Bezier curve, allowing for potentially faster convergence when fitting all of the points.

This method assumes that all of the points are sorted along the curve from a start point to an end point, and no two points are exactly equal. This is a stronger assumption than FitCubic() makes.

func (*BezierFitter) FirstGuessConstrained added in v0.2.22

func (b *BezierFitter) FirstGuessConstrained(points []Coord, t1, t2 *Coord) BezierCurve

FirstGuessConstrained is like FirstGuess, but the control points are optionally constrained to given tangent directions.

func (*BezierFitter) Fit added in v0.2.15

func (b *BezierFitter) Fit(m *Mesh) []BezierCurve

Fit fits a collection of cubic Bezier curves to a manifold mesh.

func (*BezierFitter) FitChain added in v0.2.15

func (b *BezierFitter) FitChain(points []Coord, closed bool) []BezierCurve

FitChain fits a sequence of points along some curve using one or more cubic Bezier curves.

The closed argument indicates if the final point should be smoothly reconnected to the first point.

The points should be ordered along the desired curve, and no two points should be exactly equal.

func (*BezierFitter) FitCubic added in v0.2.15

func (b *BezierFitter) FitCubic(points []Coord, start BezierCurve) BezierCurve

FitCubic finds the cubic Bezier curve of best fit for the points.

The first and last points are used as start and end points, and all the other points may be in any order.

func (*BezierFitter) FitCubicConstrained added in v0.2.15

func (b *BezierFitter) FitCubicConstrained(points []Coord, t1, t2 *Coord,
	start BezierCurve) BezierCurve

FitCubicConstrained is like FitCubic, but constrains the tangent vectors at either the start or end point, or both.

If non-nil, t1 is the direction for the first control point from the first point, and t2 is the direction of the second control point from the last point.

func (*BezierFitter) MSE added in v0.2.15

func (b *BezierFitter) MSE(points []Coord, curve BezierCurve) float64

MSE computes the MSE of a cubic Bezier fit.

type Bitmap

type Bitmap struct {
	Data   []bool
	Width  int
	Height int
}

A Bitmap is a two-dimensional image with boolean values. The data is stored in row-major order.

func MustReadBitmap

func MustReadBitmap(path string, c ColorBitFunc) *Bitmap

MustReadBitmap is like ReadBitmap, except that it panics if the bitmap cannot be read.

func NewBitmap

func NewBitmap(width, height int) *Bitmap

NewBitmap creates an empty bitmap.

func NewBitmapImage

func NewBitmapImage(img image.Image, c ColorBitFunc) *Bitmap

NewBitmapImage creates a Bitmap from an image, by calling c for each pixel and using the result as the bit.

If c is nil, then the mean RGBA is computed, and pixels are considered true if they are closer to the mean in L2 distance than they are to the top-left pixel. For images with two dominant colors, this is equivalent to making the background false, and the foreground true, assuming that the first pixel is background.

func ReadBitmap

func ReadBitmap(path string, c ColorBitFunc) (*Bitmap, error)

ReadBitmap is like NewBitmapImage, except that it reads the image from a file.

func (*Bitmap) FlipX

func (b *Bitmap) FlipX() *Bitmap

FlipX reverses the x-axis.

func (*Bitmap) FlipY

func (b *Bitmap) FlipY() *Bitmap

FlipY reverses the y-axis.

func (*Bitmap) Get

func (b *Bitmap) Get(x, y int) bool

Get gets the bit at the coordinate.

If the coordinate is out of bounds, false is returned.

func (*Bitmap) Invert

func (b *Bitmap) Invert() *Bitmap

Invert creates a new bitmap with the opposite values.

func (*Bitmap) Mesh

func (b *Bitmap) Mesh() *Mesh

Mesh converts the bitmap to a mesh by creating boxes around every true pixel and deleting duplicate segments.

func (*Bitmap) Set

func (b *Bitmap) Set(x, y int, v bool)

Set sets the bit at the coordinate.

The coordinate must be in bounds.

type Bounder

type Bounder interface {
	// Get the corners of a bounding box.
	//
	// A point p satisfies p >= Min and p <= Max if it is
	// within the bounds.
	Min() Coord
	Max() Coord
}

A Bounder is an object contained in an axis-aligned bounding box.

type Capsule added in v0.3.0

type Capsule struct {
	P1     Coord
	P2     Coord
	Radius float64
}

A Capsule is a shape which contains all of the points within a given distance of a line segment.

func (*Capsule) CircleCollision added in v0.3.0

func (c *Capsule) CircleCollision(center Coord, r float64) bool

CircleCollision checks if the surface of c collides with a solid circle centered at c with radius r.

func (*Capsule) Contains added in v0.3.0

func (c *Capsule) Contains(coord Coord) bool

Contains checks if c is within the capsule.

func (*Capsule) FirstRayCollision added in v0.3.0

func (c *Capsule) FirstRayCollision(r *Ray) (RayCollision, bool)

FirstRayCollision gets the first ray collision with the capsule, if one occurs.

func (*Capsule) Max added in v0.3.0

func (c *Capsule) Max() Coord

Max gets the maximum point of the bounding box.

func (*Capsule) MetaballDistBound added in v0.3.5

func (c *Capsule) MetaballDistBound(d float64) float64

MetaballDistBound returns d always, since the metaball implemented by MetaballField() is defined in terms of standard Euclidean coordinates.

func (*Capsule) MetaballField added in v0.3.5

func (c *Capsule) MetaballField(coord Coord) float64

MetaballField returns positive values outside of the surface, and these values increase linearly with distance to the surface.

func (*Capsule) Min added in v0.3.0

func (c *Capsule) Min() Coord

Min gets the minimum point of the bounding box.

func (*Capsule) NormalSDF added in v0.3.0

func (c *Capsule) NormalSDF(coord Coord) (Coord, float64)

NormalSDF gets the signed distance to the capsule and the normal at the closest point on the surface.

func (*Capsule) PointSDF added in v0.3.0

func (c *Capsule) PointSDF(coord Coord) (Coord, float64)

PointSDF gets the nearest point on the surface of the capsule and the corresponding SDF.

func (*Capsule) RayCollisions added in v0.3.0

func (c *Capsule) RayCollisions(r *Ray, f func(RayCollision)) int

RayCollisions calls f (if non-nil) with every ray collision.

It returns the total number of collisions.

func (*Capsule) SDF added in v0.3.0

func (c *Capsule) SDF(coord Coord) float64

SDF gets the signed distance to the surface of the capsule.

type Circle added in v0.2.3

type Circle struct {
	Center Coord
	Radius float64
}

A Circle is a 2D perfect circle.

func (*Circle) CircleCollision added in v0.3.0

func (c *Circle) CircleCollision(center Coord, r float64) bool

CircleCollision checks if the surface of c collides with a solid circle centered at c with radius r.

func (*Circle) Contains added in v0.2.3

func (c *Circle) Contains(coord Coord) bool

Contains checks if a point c is inside the circle.

func (*Circle) FirstRayCollision added in v0.3.0

func (c *Circle) FirstRayCollision(r *Ray) (RayCollision, bool)

FirstRayCollision gets the first ray collision with the circle, if one occurs.

func (*Circle) Max added in v0.2.3

func (c *Circle) Max() Coord

Max gets the maximum point of the bounding box.

func (*Circle) MetaballDistBound added in v0.3.5

func (c *Circle) MetaballDistBound(d float64) float64

MetaballDistBound returns d always, since the metaball implemented by MetaballField() is defined in terms of standard Euclidean coordinates.

func (*Circle) MetaballField added in v0.3.5

func (c *Circle) MetaballField(coord Coord) float64

MetaballField returns positive values outside of the surface, and these values increase linearly with distance to the surface.

func (*Circle) Min added in v0.2.3

func (c *Circle) Min() Coord

Min gets the minimum point of the bounding box.

func (*Circle) NormalSDF added in v0.3.0

func (c *Circle) NormalSDF(coord Coord) (Coord, float64)

NormalSDF gets the signed distance function at coord and also returns the normal at the nearest point to it on the circle.

func (*Circle) PointSDF added in v0.3.0

func (c *Circle) PointSDF(coord Coord) (Coord, float64)

PointSDF gets the signed distance function at coord and also returns the nearest point to it on the circle.

func (*Circle) RayCollisions added in v0.3.0

func (c *Circle) RayCollisions(r *Ray, f func(RayCollision)) int

RayCollisions calls f (if non-nil) with every ray collision.

It returns the total number of collisions.

func (*Circle) SDF added in v0.3.0

func (c *Circle) SDF(coord Coord) float64

SDF gets the signed distance relative to the circle.

type Collider

type Collider interface {
	Bounder

	// RayCollisions enumerates the collisions with a ray.
	// It returns the total number of collisions.
	//
	// f may be nil, in which case this is simply used for
	// counting.
	RayCollisions(r *Ray, f func(RayCollision)) int

	// FirstRayCollision gets the ray collision with the
	// lowest scale.
	//
	// The second return value is false if no collisions
	// were found.
	FirstRayCollision(r *Ray) (collision RayCollision, collides bool)

	// CircleCollision checks if the collider touches a
	// circle with origin c and radius r.
	CircleCollision(c Coord, r float64) bool
}

A Collider is the outline of a 2-dimensional shape. It can count its intersections with a ray, and check if any part of the outline is inside a circle.

All methods of a Collider are safe for concurrency.

func TransformCollider added in v0.2.11

func TransformCollider(t DistTransform, c Collider) Collider

TransformCollider applies t to the Collider c to produce a new, transformed Collider.

type ColliderSolid

type ColliderSolid struct {
	// contains filtered or unexported fields
}

A ColliderSolid is a Solid that uses a Collider to check if points are in the solid.

There are two modes for a ColliderSolid. In the first, points are inside the solid if a ray passes through the surface of the Collider an odd number of times. In the second, points are inside the solid if a sphere of a pre-determined radius touches the surface of the Collider from the point. The second modality is equivalent to creating a thick but hollow solid.

func NewColliderSolid

func NewColliderSolid(c Collider) *ColliderSolid

NewColliderSolid creates a basic ColliderSolid.

func NewColliderSolidHollow

func NewColliderSolidHollow(c Collider, r float64) *ColliderSolid

NewColliderSolidHollow creates a ColliderSolid that only reports containment around the edges.

func NewColliderSolidInset

func NewColliderSolidInset(c Collider, inset float64) *ColliderSolid

NewColliderSolidInset creates a ColliderSolid that only reports containment at some distance from the surface.

If inset is negative, then the solid is outset from the collider.

func (*ColliderSolid) Contains

func (c *ColliderSolid) Contains(coord Coord) bool

Contains checks if coord is in the solid.

func (*ColliderSolid) Max

func (c *ColliderSolid) Max() Coord

Max gets the maximum of the bounding box.

func (*ColliderSolid) Min

func (c *ColliderSolid) Min() Coord

Min gets the minimum of the bounding box.

type ColorBitFunc

type ColorBitFunc func(c color.Color) bool

ColorBitFunc turns colors into single bits.

type ConvexPolytope added in v0.2.11

type ConvexPolytope []*LinearConstraint

A ConvexPolytope is the intersection of some linear constraints.

func NewConvexPolytopeRect added in v0.2.11

func NewConvexPolytopeRect(min, max Coord) ConvexPolytope

NewConvexPolytopeRect creates a rectangular convex polytope.

func (ConvexPolytope) Contains added in v0.2.11

func (c ConvexPolytope) Contains(coord Coord) bool

Contains checks that c satisfies the constraints.

func (ConvexPolytope) Mesh added in v0.2.11

func (c ConvexPolytope) Mesh() *Mesh

Mesh creates a mesh containing all of the finite faces of the polytope.

For complicated polytopes, this may take a long time to run, since it is O(n^2) in the constraints.

func (ConvexPolytope) Solid added in v0.2.11

func (c ConvexPolytope) Solid() Solid

Solid creates a solid out of the polytope.

This runs in O(n^2) in the constraints, so it may be unacceptable for large polytopes.

type Coord

type Coord struct {
	X float64
	Y float64
}

A Coord is a coordinate in 2-D Euclidean space.

func NewCoordArray

func NewCoordArray(a [2]float64) Coord

NewCoordArray creates a Coord from an array of x and y.

func NewCoordPolar added in v0.2.9

func NewCoordPolar(theta, radius float64) Coord

NewCoordPolar converts polar coordinates to a Coord.

func NewCoordRandBounds added in v0.2.8

func NewCoordRandBounds(min, max Coord) Coord

NewCoordRandBounds creates a random Coord uniformly inside the given rectangular boundary.

func NewCoordRandNorm

func NewCoordRandNorm() Coord

NewCoordRandNorm creates a random Coord with normally distributed components.

func NewCoordRandUniform added in v0.2.4

func NewCoordRandUniform() Coord

NewCoordRandUniform creates a random Coord with uniformly random coordinates in [0, 1).

func NewCoordRandUnit

func NewCoordRandUnit() Coord

NewCoordRandUnit creates a random Coord with magnitude 1.

func Ones added in v0.2.6

func Ones(a float64) Coord

Ones creates the unit vector scaled by a constant.

func ProjectMedialAxis added in v0.2.8

func ProjectMedialAxis(p PointSDF, c Coord, iters int, eps float64) Coord

ProjectMedialAxis projects the point c onto the medial axis of the shape defined by SDF p.

The iters argument specifies the number of search steps to perform to narrow down the medial axis. If 0, DefaultMedialAxisIters is used.

The eps argument specifies how close two points on the surface of p must be to be considered the same point. If 0, DefaultMedialAxisEps is used.

The bounds of p are used to aid computation. Thus, it is important to get tight bounds on the SDF for maximally accurate results.

func X added in v0.2.4

func X(x float64) Coord

X gets a coordinate in the X direction.

func XY added in v0.2.6

func XY(x, y float64) Coord

XY constructs a coordinate.

func Y added in v0.2.4

func Y(y float64) Coord

Y gets a coordinate in the Y direction.

func (Coord) Abs added in v0.3.4

func (c Coord) Abs() Coord

Abs computes the per-coordinate absolute value of c.

func (Coord) Add

func (c Coord) Add(c1 Coord) Coord

Add computes the sum of c and c1.

func (Coord) AddScalar added in v0.2.6

func (c Coord) AddScalar(s float64) Coord

AddScalar adds s to all of the coordinates and returns the new coordinate.

func (Coord) Array

func (c Coord) Array() [2]float64

Array gets an array of x and y.

func (Coord) Dist

func (c Coord) Dist(c1 Coord) float64

Dist computes the Euclidean distance to c1.

func (Coord) Div added in v0.2.2

func (c Coord) Div(c1 Coord) Coord

Div computes the element-wise quotient of c / c1.

func (Coord) Dot

func (c Coord) Dot(c1 Coord) float64

Dot computes the dot product of c and c1.

func (Coord) L1Dist added in v0.2.13

func (c Coord) L1Dist(c1 Coord) float64

L1Dist computes the L1 distance to c1.

func (Coord) Max

func (c Coord) Max(c1 Coord) Coord

Max gets the element-wise maximum of c and c1.

func (Coord) MaxCoord added in v0.3.4

func (c Coord) MaxCoord() float64

MaxCoord gets the maximum of c.X and c.Y.

func (Coord) Mid

func (c Coord) Mid(c1 Coord) Coord

Mid computes the midpoint between c and c1.

func (Coord) Min

func (c Coord) Min(c1 Coord) Coord

Min gets the element-wise minimum of c and c1.

func (Coord) Mul

func (c Coord) Mul(c1 Coord) Coord

Mul computes the element-wise product of c and c1.

func (Coord) Norm

func (c Coord) Norm() float64

Norm computes the vector L2 norm.

func (Coord) NormSquared added in v0.3.0

func (c Coord) NormSquared() float64

NormSquared computes the squared vector L2 norm.

func (Coord) Normalize

func (c Coord) Normalize() Coord

Normalize gets a unit vector from c.

func (Coord) ProjectOut

func (c Coord) ProjectOut(c1 Coord) Coord

ProjectOut projects the c1 direction out of c.

func (Coord) Recip added in v0.2.9

func (c Coord) Recip() Coord

Recip gets a coordinate as 1 / c.

func (Coord) Reflect

func (c Coord) Reflect(c1 Coord) Coord

Reflect reflects c1 around c.

func (Coord) Scale

func (c Coord) Scale(s float64) Coord

Scale scales all the coordinates by s and returns the new coordinate.

func (Coord) SquaredDist added in v0.2.13

func (c Coord) SquaredDist(c1 Coord) float64

SquaredDist gets the squared Euclidean distance to c1.

func (Coord) Sub

func (c Coord) Sub(c1 Coord) Coord

Sub computes c - c1.

func (Coord) Sum

func (c Coord) Sum() float64

Sum sums the elements of c.

type CoordMap added in v0.2.14

type CoordMap[T any] struct {
	// contains filtered or unexported fields
}

CoordMap implements a map-like interface for mapping Coord to T.

This can be more efficient than using a map directly, since it uses a special hash function for coordinates. The speed-up is variable, but was ~2x as of mid-2021.

func NewCoordMap added in v0.2.14

func NewCoordMap[T any]() *CoordMap[T]

NewCoordMap creates an empty map.

func (*CoordMap[T]) Delete added in v0.2.14

func (m *CoordMap[T]) Delete(key Coord)

Delete removes the key from the map if it exists, and does nothing otherwise.

func (*CoordMap[T]) KeyRange added in v0.2.14

func (m *CoordMap[T]) KeyRange(f func(key Coord) bool)

KeyRange is like Range, but only iterates over keys, not values.

func (*CoordMap[T]) Len added in v0.2.14

func (m *CoordMap[T]) Len() int

Len gets the number of elements in the map.

func (*CoordMap[T]) Load added in v0.2.14

func (m *CoordMap[T]) Load(key Coord) (T, bool)

Load gets the value for the given key.

If no value is present, the first return argument is a zero value, and the second is false. Otherwise, the second return value is true.

func (*CoordMap[T]) Range added in v0.2.14

func (m *CoordMap[T]) Range(f func(key Coord, value T) bool)

Range iterates over the map, calling f successively for each value until it returns false, or all entries are enumerated.

It is not safe to modify the map with Store or Delete during enumeration.

func (*CoordMap[T]) Store added in v0.2.14

func (m *CoordMap[T]) Store(key Coord, value T)

Store assigns the value to the given key, overwriting the previous value for the key if necessary.

func (*CoordMap[T]) Value added in v0.2.14

func (m *CoordMap[T]) Value(key Coord) T

Value is like Load(), but without a second return value.

func (*CoordMap[T]) ValueRange added in v0.2.14

func (m *CoordMap[T]) ValueRange(f func(value T) bool)

ValueRange is like Range, but only iterates over values only.

type CoordToNumber added in v0.4.0

type CoordToNumber[T Adder] struct {
	// contains filtered or unexported fields
}

CoordToNumber implements a map-like interface for mapping Coord to T.

This can be more efficient than using a map directly, since it uses a special hash function for coordinates. The speed-up is variable, but was ~2x as of mid-2021.

func NewCoordToNumber added in v0.4.0

func NewCoordToNumber[T Adder]() *CoordToNumber[T]

NewCoordToNumber creates an empty map.

func (*CoordToNumber[T]) Add added in v0.4.0

func (m *CoordToNumber[T]) Add(key Coord, x T) T

Add adds x to the value stored for the given key and returns the new value.

func (*CoordToNumber[T]) Delete added in v0.4.0

func (m *CoordToNumber[T]) Delete(key Coord)

Delete removes the key from the map if it exists, and does nothing otherwise.

func (*CoordToNumber[T]) KeyRange added in v0.4.0

func (m *CoordToNumber[T]) KeyRange(f func(key Coord) bool)

KeyRange is like Range, but only iterates over keys, not values.

func (*CoordToNumber[T]) Len added in v0.4.0

func (m *CoordToNumber[T]) Len() int

Len gets the number of elements in the map.

func (*CoordToNumber[T]) Load added in v0.4.0

func (m *CoordToNumber[T]) Load(key Coord) (T, bool)

Load gets the value for the given key.

If no value is present, the first return argument is a zero value, and the second is false. Otherwise, the second return value is true.

func (*CoordToNumber[T]) Range added in v0.4.0

func (m *CoordToNumber[T]) Range(f func(key Coord, value T) bool)

Range iterates over the map, calling f successively for each value until it returns false, or all entries are enumerated.

It is not safe to modify the map with Store or Delete during enumeration.

func (*CoordToNumber[T]) Store added in v0.4.0

func (m *CoordToNumber[T]) Store(key Coord, value T)

Store assigns the value to the given key, overwriting the previous value for the key if necessary.

func (*CoordToNumber[T]) Value added in v0.4.0

func (m *CoordToNumber[T]) Value(key Coord) T

Value is like Load(), but without a second return value.

func (*CoordToNumber[T]) ValueRange added in v0.4.0

func (m *CoordToNumber[T]) ValueRange(f func(value T) bool)

ValueRange is like Range, but only iterates over values only.

type CoordToSlice added in v0.4.0

type CoordToSlice[T any] struct {
	// contains filtered or unexported fields
}

CoordToSlice implements a map-like interface for mapping Coord to []T.

This can be more efficient than using a map directly, since it uses a special hash function for coordinates. The speed-up is variable, but was ~2x as of mid-2021.

func NewCoordToSlice added in v0.4.0

func NewCoordToSlice[T any]() *CoordToSlice[T]

NewCoordToSlice creates an empty map.

func (*CoordToSlice[T]) Append added in v0.4.0

func (m *CoordToSlice[T]) Append(key Coord, x T) []T

Append appends x to the value stored for the given key and returns the new value.

func (*CoordToSlice[T]) Delete added in v0.4.0

func (m *CoordToSlice[T]) Delete(key Coord)

Delete removes the key from the map if it exists, and does nothing otherwise.

func (*CoordToSlice[T]) KeyRange added in v0.4.0

func (m *CoordToSlice[T]) KeyRange(f func(key Coord) bool)

KeyRange is like Range, but only iterates over keys, not values.

func (*CoordToSlice[T]) Len added in v0.4.0

func (m *CoordToSlice[T]) Len() int

Len gets the number of elements in the map.

func (*CoordToSlice[T]) Load added in v0.4.0

func (m *CoordToSlice[T]) Load(key Coord) ([]T, bool)

Load gets the value for the given key.

If no value is present, the first return argument is a zero value, and the second is false. Otherwise, the second return value is true.

func (*CoordToSlice[T]) Range added in v0.4.0

func (m *CoordToSlice[T]) Range(f func(key Coord, value []T) bool)

Range iterates over the map, calling f successively for each value until it returns false, or all entries are enumerated.

It is not safe to modify the map with Store or Delete during enumeration.

func (*CoordToSlice[T]) Store added in v0.4.0

func (m *CoordToSlice[T]) Store(key Coord, value []T)

Store assigns the value to the given key, overwriting the previous value for the key if necessary.

func (*CoordToSlice[T]) Value added in v0.4.0

func (m *CoordToSlice[T]) Value(key Coord) []T

Value is like Load(), but without a second return value.

func (*CoordToSlice[T]) ValueRange added in v0.4.0

func (m *CoordToSlice[T]) ValueRange(f func(value []T) bool)

ValueRange is like Range, but only iterates over values only.

type CoordTree added in v0.2.13

type CoordTree struct {
	Coord Coord

	// SplitAxis is the dimension to split on for branches.
	SplitAxis int

	// At least one of these is non-nil for branches.
	LessThan     *CoordTree
	GreaterEqual *CoordTree
}

A CoordTree is a k-d tree over Coords.

A nil *CoordTree represents an empty tree.

func NewCoordTree added in v0.2.13

func NewCoordTree(points []Coord) *CoordTree

func (*CoordTree) Contains added in v0.2.13

func (c *CoordTree) Contains(p Coord) bool

Contains checks if any point in the tree is exactly equal to p.

func (*CoordTree) Dist added in v0.2.14

func (c *CoordTree) Dist(p Coord) float64

Dist gets the distance from a point p to its nearest neighbor in c.

func (*CoordTree) Empty added in v0.2.13

func (c *CoordTree) Empty() bool

Empty returns true if c contains no points.

func (*CoordTree) KNN added in v0.2.13

func (c *CoordTree) KNN(k int, p Coord) []Coord

KNN gets the closest K coordinates to p in the tree. The results are sorted by ascending distance.

If there are fewer than K coordinates in the tree, then fewer than K coordinates are returned.

func (*CoordTree) Leaf added in v0.2.13

func (c *CoordTree) Leaf() bool

Leaf returns true if this tree contains 1 or fewer points.

func (*CoordTree) NearestNeighbor added in v0.2.13

func (c *CoordTree) NearestNeighbor(p Coord) Coord

NearestNeighbor gets the closest coordinate to p in the tree.

This will panic() if c is empty.

func (*CoordTree) Slice added in v0.2.13

func (c *CoordTree) Slice() []Coord

Slice combines the points back into a slice.

The order will be from the first (less than) leaf to the final (greater than) leaf, with intermediate nodes interspersed throughout the middle.

func (*CoordTree) SphereCollision added in v0.2.13

func (c *CoordTree) SphereCollision(p Coord, r float64) bool

SphereCollision checks if the sphere centered at point p with radius r contains any points in the tree.

type Curve added in v0.2.6

type Curve interface {
	Eval(t float64) Coord
}

A Curve is a parametric curve that returns points for values of t in the range [0, 1].

func CurveTranspose added in v0.2.6

func CurveTranspose(c Curve) Curve

CurveTranspose generates a Curve where x and y are swapped from the original c.

type DistTransform added in v0.2.11

type DistTransform interface {
	Transform

	// ApplyDistance computes the distance between
	// t.Apply(c1) and t.Apply(c2) given the distance
	// between c1 and c2, where c1 and c2 are arbitrary
	// points.
	ApplyDistance(d float64) float64
}

DistTransform is a Transform that changes Euclidean distances in a coordinate-independent fashion.

The inverse of a DistTransform should also be a DistTransform.

func Rotation added in v0.2.11

func Rotation(theta float64) DistTransform

Rotation creates a rotation transformation using an angle in radians.

type EdgeMap added in v0.2.14

type EdgeMap[T any] struct {
	// contains filtered or unexported fields
}

EdgeMap implements a map-like interface for mapping [2]Coord to T.

This can be more efficient than using a map directly, since it uses a special hash function for coordinates. The speed-up is variable, but was ~2x as of mid-2021.

func NewEdgeMap added in v0.2.14

func NewEdgeMap[T any]() *EdgeMap[T]

NewEdgeMap creates an empty map.

func (*EdgeMap[T]) Delete added in v0.2.14

func (m *EdgeMap[T]) Delete(key [2]Coord)

Delete removes the key from the map if it exists, and does nothing otherwise.

func (*EdgeMap[T]) KeyRange added in v0.2.14

func (m *EdgeMap[T]) KeyRange(f func(key [2]Coord) bool)

KeyRange is like Range, but only iterates over keys, not values.

func (*EdgeMap[T]) Len added in v0.2.14

func (m *EdgeMap[T]) Len() int

Len gets the number of elements in the map.

func (*EdgeMap[T]) Load added in v0.2.14

func (m *EdgeMap[T]) Load(key [2]Coord) (T, bool)

Load gets the value for the given key.

If no value is present, the first return argument is a zero value, and the second is false. Otherwise, the second return value is true.

func (*EdgeMap[T]) Range added in v0.2.14

func (m *EdgeMap[T]) Range(f func(key [2]Coord, value T) bool)

Range iterates over the map, calling f successively for each value until it returns false, or all entries are enumerated.

It is not safe to modify the map with Store or Delete during enumeration.

func (*EdgeMap[T]) Store added in v0.2.14

func (m *EdgeMap[T]) Store(key [2]Coord, value T)

Store assigns the value to the given key, overwriting the previous value for the key if necessary.

func (*EdgeMap[T]) Value added in v0.2.14

func (m *EdgeMap[T]) Value(key [2]Coord) T

Value is like Load(), but without a second return value.

func (*EdgeMap[T]) ValueRange added in v0.2.14

func (m *EdgeMap[T]) ValueRange(f func(value T) bool)

ValueRange is like Range, but only iterates over values only.

type EdgeToNumber added in v0.4.0

type EdgeToNumber[T Adder] struct {
	// contains filtered or unexported fields
}

EdgeToNumber implements a map-like interface for mapping [2]Coord to T.

This can be more efficient than using a map directly, since it uses a special hash function for coordinates. The speed-up is variable, but was ~2x as of mid-2021.

func NewEdgeToNumber added in v0.4.0

func NewEdgeToNumber[T Adder]() *EdgeToNumber[T]

NewEdgeToNumber creates an empty map.

func (*EdgeToNumber[T]) Add added in v0.4.0

func (m *EdgeToNumber[T]) Add(key [2]Coord, x T) T

Add adds x to the value stored for the given key and returns the new value.

func (*EdgeToNumber[T]) Delete added in v0.4.0

func (m *EdgeToNumber[T]) Delete(key [2]Coord)

Delete removes the key from the map if it exists, and does nothing otherwise.

func (*EdgeToNumber[T]) KeyRange added in v0.4.0

func (m *EdgeToNumber[T]) KeyRange(f func(key [2]Coord) bool)

KeyRange is like Range, but only iterates over keys, not values.

func (*EdgeToNumber[T]) Len added in v0.4.0

func (m *EdgeToNumber[T]) Len() int

Len gets the number of elements in the map.

func (*EdgeToNumber[T]) Load added in v0.4.0

func (m *EdgeToNumber[T]) Load(key [2]Coord) (T, bool)

Load gets the value for the given key.

If no value is present, the first return argument is a zero value, and the second is false. Otherwise, the second return value is true.

func (*EdgeToNumber[T]) Range added in v0.4.0

func (m *EdgeToNumber[T]) Range(f func(key [2]Coord, value T) bool)

Range iterates over the map, calling f successively for each value until it returns false, or all entries are enumerated.

It is not safe to modify the map with Store or Delete during enumeration.

func (*EdgeToNumber[T]) Store added in v0.4.0

func (m *EdgeToNumber[T]) Store(key [2]Coord, value T)

Store assigns the value to the given key, overwriting the previous value for the key if necessary.

func (*EdgeToNumber[T]) Value added in v0.4.0

func (m *EdgeToNumber[T]) Value(key [2]Coord) T

Value is like Load(), but without a second return value.

func (*EdgeToNumber[T]) ValueRange added in v0.4.0

func (m *EdgeToNumber[T]) ValueRange(f func(value T) bool)

ValueRange is like Range, but only iterates over values only.

type EdgeToSlice added in v0.4.0

type EdgeToSlice[T any] struct {
	// contains filtered or unexported fields
}

EdgeToSlice implements a map-like interface for mapping [2]Coord to []T.

This can be more efficient than using a map directly, since it uses a special hash function for coordinates. The speed-up is variable, but was ~2x as of mid-2021.

func NewEdgeToSlice added in v0.4.0

func NewEdgeToSlice[T any]() *EdgeToSlice[T]

NewEdgeToSlice creates an empty map.

func (*EdgeToSlice[T]) Append added in v0.4.0

func (m *EdgeToSlice[T]) Append(key [2]Coord, x T) []T

Append appends x to the value stored for the given key and returns the new value.

func (*EdgeToSlice[T]) Delete added in v0.4.0

func (m *EdgeToSlice[T]) Delete(key [2]Coord)

Delete removes the key from the map if it exists, and does nothing otherwise.

func (*EdgeToSlice[T]) KeyRange added in v0.4.0

func (m *EdgeToSlice[T]) KeyRange(f func(key [2]Coord) bool)

KeyRange is like Range, but only iterates over keys, not values.

func (*EdgeToSlice[T]) Len added in v0.4.0

func (m *EdgeToSlice[T]) Len() int

Len gets the number of elements in the map.

func (*EdgeToSlice[T]) Load added in v0.4.0

func (m *EdgeToSlice[T]) Load(key [2]Coord) ([]T, bool)

Load gets the value for the given key.

If no value is present, the first return argument is a zero value, and the second is false. Otherwise, the second return value is true.

func (*EdgeToSlice[T]) Range added in v0.4.0

func (m *EdgeToSlice[T]) Range(f func(key [2]Coord, value []T) bool)

Range iterates over the map, calling f successively for each value until it returns false, or all entries are enumerated.

It is not safe to modify the map with Store or Delete during enumeration.

func (*EdgeToSlice[T]) Store added in v0.4.0

func (m *EdgeToSlice[T]) Store(key [2]Coord, value []T)

Store assigns the value to the given key, overwriting the previous value for the key if necessary.

func (*EdgeToSlice[T]) Value added in v0.4.0

func (m *EdgeToSlice[T]) Value(key [2]Coord) []T

Value is like Load(), but without a second return value.

func (*EdgeToSlice[T]) ValueRange added in v0.4.0

func (m *EdgeToSlice[T]) ValueRange(f func(value []T) bool)

ValueRange is like Range, but only iterates over values only.

type FaceSDF added in v0.2.14

type FaceSDF interface {
	PointSDF
	NormalSDF

	// FaceSDF gets the SDF at c and also returns the
	// nearest point and face to c on the surface.
	FaceSDF(c Coord) (*Segment, Coord, float64)
}

A FaceSDF is a PointSDF that can additionally get the segment containing the closest point.

func GroupedSegmentsToSDF added in v0.2.3

func GroupedSegmentsToSDF(faces []*Segment) FaceSDF

GroupedSegmentsToSDF creates a FaceSDF from a slice of segments. If the segments are not grouped by GroupSegments(), the resulting PointSDF is inefficient.

func MeshToSDF added in v0.2.3

func MeshToSDF(m *Mesh) FaceSDF

MeshToSDF turns a mesh into a FaceSDF.

type InterpBitmap

type InterpBitmap struct {
	Data   []color.RGBA
	Width  int
	Height int
	Model  color.Model
	F      ColorBitFunc

	// Interp is the interpolation function.
	// A zero value is Bicubic.
	Interp Interpolator
}

An InterpBitmap is a dynamic Bitmap backed by an image with a color interpolation scheme.

func MustReadInterpBitmap

func MustReadInterpBitmap(path string, c ColorBitFunc) *InterpBitmap

MustReadInterpBitmap is like ReadInterpBitmap, except that it panics if the InterpBitmap cannot be read.

func NewInterpBitmap

func NewInterpBitmap(img image.Image, c ColorBitFunc) *InterpBitmap

NewInterpBitmap creates a InterpBitmap from an image.

If c is nil, then the mean RGBA is computed, and pixels are considered true if they are closer to the mean in L2 distance than they are to the top-left pixel. For images with two dominant colors, this is equivalent to making the background false, and the foreground true, assuming that the first pixel is background.

func ReadInterpBitmap

func ReadInterpBitmap(path string, c ColorBitFunc) (*InterpBitmap, error)

ReadInterpBitmap is like NewInterpBitmap, except that it reads the image from a file.

func (*InterpBitmap) Bitmap

func (b *InterpBitmap) Bitmap() *Bitmap

Bitmap gets an uninterpolated bitmap from b.

func (*InterpBitmap) Contains

func (b *InterpBitmap) Contains(c Coord) bool

Contains gets the bit at the interpolated coordinate.

If the coordinate is out of bounds, false is returned.

func (*InterpBitmap) FlipX

func (b *InterpBitmap) FlipX() *InterpBitmap

FlipX reverses the x-axis.

func (*InterpBitmap) FlipY

func (b *InterpBitmap) FlipY() *InterpBitmap

FlipY reverses the y-axis.

func (*InterpBitmap) Get

func (b *InterpBitmap) Get(x, y int) color.RGBA

Get gets the color at the coordinate.

If the coordinate is out of bounds, a the edge of the image is extended.

func (*InterpBitmap) Invert

func (b *InterpBitmap) Invert() *InterpBitmap

Invert creates a new InterpBitmap with the opposite color bitmap values.

func (*InterpBitmap) Max

func (b *InterpBitmap) Max() Coord

Max gets the maximum of the pixel bounding box.

func (*InterpBitmap) Min

func (b *InterpBitmap) Min() Coord

Min gets the minimum of the pixel bounding box.

type Interpolator

type Interpolator int

Interpolator is a 1-dimensional interpolation kernel.

const (
	Bicubic Interpolator = iota
	Bilinear
)

func (Interpolator) Kernel

func (i Interpolator) Kernel(t float64) []float64

type IntersectedSolid added in v0.2.3

type IntersectedSolid []Solid

IntersectedSolid is a Solid containing the intersection of one or more Solids.

func (IntersectedSolid) Contains added in v0.2.3

func (i IntersectedSolid) Contains(c Coord) bool

func (IntersectedSolid) Max added in v0.2.3

func (i IntersectedSolid) Max() Coord

func (IntersectedSolid) Min added in v0.2.3

func (i IntersectedSolid) Min() Coord

type JoinedCollider

type JoinedCollider struct {
	// contains filtered or unexported fields
}

A JoinedCollider wraps multiple other Colliders and only passes along rays and circles that enter their combined bounding box.

func NewJoinedCollider

func NewJoinedCollider(other []Collider) *JoinedCollider

NewJoinedCollider creates a JoinedCollider which combines zero or more other colliders.

func (*JoinedCollider) CircleCollision

func (j *JoinedCollider) CircleCollision(center Coord, r float64) bool

func (*JoinedCollider) FirstRayCollision

func (j *JoinedCollider) FirstRayCollision(r *Ray) (RayCollision, bool)

func (*JoinedCollider) Max

func (j *JoinedCollider) Max() Coord

func (*JoinedCollider) Min

func (j *JoinedCollider) Min() Coord

func (*JoinedCollider) RayCollisions

func (j *JoinedCollider) RayCollisions(r *Ray, f func(RayCollision)) int

type JoinedCurve added in v0.2.6

type JoinedCurve []Curve

A JoinedCurve combines Curves into a single curve. Each curve should end where the next curve begins.

func SmoothBezier added in v0.2.6

func SmoothBezier(start1, c1, c2, end1 Coord, ctrlEnds ...Coord) JoinedCurve

SmoothBezier creates a joined cubic bezier curve where control points are reflected around end-points. The first four points define the first bezier curve. After that, each group of two points defines a control point and an endpoint.

func (JoinedCurve) Eval added in v0.2.6

func (j JoinedCurve) Eval(t float64) Coord

Eval evaluates the joint curve.

Each sub-curve consumes an equal fraction of t. For t outside of [0, 1], the first or last curve is used.

type JoinedSolid

type JoinedSolid []Solid

A JoinedSolid is a Solid composed of other solids.

func (JoinedSolid) Contains

func (j JoinedSolid) Contains(c Coord) bool

func (JoinedSolid) Max

func (j JoinedSolid) Max() Coord

func (JoinedSolid) Min

func (j JoinedSolid) Min() Coord

func (JoinedSolid) Optimize added in v0.2.11

func (j JoinedSolid) Optimize() Solid

Optimize creates a version of the solid that is faster when joining a large number of smaller solids.

type JoinedTransform added in v0.2.8

type JoinedTransform []Transform

A JoinedTransform composes transformations from left to right.

func (JoinedTransform) Apply added in v0.2.8

func (j JoinedTransform) Apply(c Coord) Coord

func (JoinedTransform) ApplyBounds added in v0.2.8

func (j JoinedTransform) ApplyBounds(min Coord, max Coord) (Coord, Coord)

func (JoinedTransform) ApplyDistance added in v0.2.11

func (j JoinedTransform) ApplyDistance(d float64) float64

ApplyDistance transforms a distance.

It panic()s if any transforms don't implement DistTransform.

func (JoinedTransform) Inverse added in v0.2.8

func (j JoinedTransform) Inverse() Transform

type LinearConstraint added in v0.2.11

type LinearConstraint struct {
	Normal Coord
	Max    float64
}

A LinearConstraint defines a half-space of all points c such that c.Dot(Normal) <= Max.

func (*LinearConstraint) Contains added in v0.2.11

func (l *LinearConstraint) Contains(c Coord) bool

Contains checks if the half-space contains c.

type Matrix2

type Matrix2 [4]float64

Matrix2 is a 2x2 matrix, stored in row-major order.

func NewMatrix2Columns

func NewMatrix2Columns(c1, c2 Coord) *Matrix2

NewMatrix2Columns creates a Matrix2 with the given coordinates as column entries.

func NewMatrix2Rotation

func NewMatrix2Rotation(theta float64) *Matrix2

NewMatrix2Rotation creates a rotation matrix that rotates column vectors by theta.

func (*Matrix2) Add added in v0.2.2

func (m *Matrix2) Add(m1 *Matrix2) *Matrix2

Add computes m+m1 and returns the sum.

func (*Matrix2) Det

func (m *Matrix2) Det() float64

Det computes the determinant of the matrix.

func (*Matrix2) Eigenvalues added in v0.2.2

func (m *Matrix2) Eigenvalues() [2]complex128

Eigenvalues computes the eigenvalues of the matrix.

There may be a repeated eigenvalue, but for numerical reasons two are always returned.

func (*Matrix2) Inverse

func (m *Matrix2) Inverse() *Matrix2

Inverse computes the inverse matrix.

func (*Matrix2) InvertInPlace

func (m *Matrix2) InvertInPlace()

InvertInPlace moves the inverse of m into m without causing any new allocations.

func (*Matrix2) InvertInPlaceDet added in v0.2.14

func (m *Matrix2) InvertInPlaceDet(det float64)

InvertInPlaceDet is an optimization for InvertInPlace when the determinant has been pre-computed.

func (*Matrix2) Mul

func (m *Matrix2) Mul(m1 *Matrix2) *Matrix2

Mul computes m*m1 and returns the product.

func (*Matrix2) MulColumn

func (m *Matrix2) MulColumn(c Coord) Coord

MulColumn multiplies the matrix m by a column vector represented by c.

func (*Matrix2) MulColumnInv added in v0.2.14

func (m *Matrix2) MulColumnInv(c Coord, det float64) Coord

MulColumnInv multiplies the inverse of m by the column c, given the determinant of m.

func (*Matrix2) SVD added in v0.2.2

func (m *Matrix2) SVD(u, s, v *Matrix2)

SVD computes the singular value decomposition of the matrix.

It populates matrices u, s, and v, such that

m = u*s*v.Transpose()

The singular values in s are sorted largest to smallest.

func (*Matrix2) Scale

func (m *Matrix2) Scale(s float64)

Scale scales m by a factor s.

func (*Matrix2) Transpose

func (m *Matrix2) Transpose() *Matrix2

Transpose computes the matrix transpose.

type Matrix2Transform added in v0.2.8

type Matrix2Transform struct {
	Matrix *Matrix2
}

Matrix2Transform is a Transform that applies a matrix to coordinates.

func (*Matrix2Transform) Apply added in v0.2.8

func (m *Matrix2Transform) Apply(c Coord) Coord

func (*Matrix2Transform) ApplyBounds added in v0.2.8

func (m *Matrix2Transform) ApplyBounds(min, max Coord) (Coord, Coord)

func (*Matrix2Transform) Inverse added in v0.2.8

func (m *Matrix2Transform) Inverse() Transform

type Mesh

type Mesh struct {
	// contains filtered or unexported fields
}

A Mesh is a collection of segments.

The segments are uniquely identified as pointers, not as values. This is important for methods which reference existing segments, such as Remove and Neighbors.

Segments in a mesh are "connected" when they contain exactly identical points. Thus, small rounding errors can cause segments to incorrectly be disassociated with each other.

A Mesh can be read safely from concurrent Goroutines, but modifications must not be performed concurrently with any mesh operations.

func CurveMesh added in v0.2.22

func CurveMesh(c Curve, n int) *Mesh

CurveMesh creates a mesh with n evenly-spaced segments along the curve.

func MarchingSquares

func MarchingSquares(s Solid, delta float64) *Mesh

MarchingSquares turns a Solid into a mesh using a 2D version of the marching cubes algorithm.

func MarchingSquaresC2F added in v0.3.0

func MarchingSquaresC2F(s Solid, bigDelta, smallDelta, extraSpace float64, iters int) *Mesh

MarchingSquaresC2F computes a coarse mesh for the solid, then uses that mesh to compute a fine mesh more efficiently.

The extraSpace argument, if non-zero, is extra space to consider around the coarse mesh. It can be increased in the case where the solid has fine details that are totally missed by the coarse mesh.

func MarchingSquaresConj added in v0.2.9

func MarchingSquaresConj(s Solid, delta float64, iters int, xforms ...Transform) *Mesh

MarchingSquaresConj is like MarchingSquaresSearch, but in a transformed space. In particular, it applies a series of transformations to the Solid, and then applies the inverse to the resulting mesh.

func MarchingSquaresFilter added in v0.3.0

func MarchingSquaresFilter(s Solid, f func(*Rect) bool, delta float64) *Mesh

MarchingSquaresFilter is like MarchingSquares, but does not scan rectangular areas for which f returns false. This can be much more efficient than MarchingSquares if the surface is sparse and f can tell when large regions don't intersect the mesh.

Note that the filter can be conservative and possibly report collisions that do not occur. However, it should never fail to report collisions, since this could cause segments to be missed.

func MarchingSquaresSearch

func MarchingSquaresSearch(s Solid, delta float64, iters int) *Mesh

MarchingSquaresSearch is like MarchingSquares, but applies an additional search step to move the vertices along the edges of each square.

The tightness of the triangulation will double for every iteration.

func MarchingSquaresSearchFilter added in v0.3.0

func MarchingSquaresSearchFilter(s Solid, f func(*Rect) bool, delta float64, iters int) *Mesh

MarchingSquaresSearchFilter combines the two methods MarchingSquaresSearch and MarchingSquaresFilter.

func NewMesh

func NewMesh() *Mesh

NewMesh creates an empty mesh.

func NewMeshPolar added in v0.2.9

func NewMeshPolar(radius func(theta float64) float64, stops int) *Mesh

NewMeshPolar creates a closed polar mesh.

The mesh will have correct normals if the radius function returns positive values when theta is in the range [0, 2*pi].

Even if the polar function does not reach its original value at 2*pi radians, the mesh will be closed by connecting the first point to the last.

func NewMeshRect added in v0.2.13

func NewMeshRect(min, max Coord) *Mesh

NewMeshRect creates a rectangle mesh.

func NewMeshSegments

func NewMeshSegments(faces []*Segment) *Mesh

NewMeshSegments creates a mesh with the given collection of segments.

func (*Mesh) Add

func (m *Mesh) Add(f *Segment)

Add adds the segment f to the mesh.

func (*Mesh) AddMesh

func (m *Mesh) AddMesh(m1 *Mesh)

AddMesh adds all the segments from m1 to m.

func (*Mesh) AllVertexNeighbors added in v0.4.0

func (m *Mesh) AllVertexNeighbors() *CoordToSlice[Coord]

AllVertexNeighbors returns a mapping between vertices and all of their directly connected neighbors.

func (*Mesh) Area added in v0.2.22

func (m *Mesh) Area() float64

Area computes the area inside of a manifold mesh.

func (*Mesh) Blur

func (m *Mesh) Blur(rate float64) *Mesh

Blur moves each vertex closer to the average of its neighbors.

The rate argument controls how much the vertices move. If it is 1, then the vertices become the average of their neighbors. If it is 0, then the vertices remain where they are.

func (*Mesh) Center added in v0.4.2

func (m *Mesh) Center() *Mesh

CenterBounds translates the mesh so that the midpoint of Min() and Max() is the origin.

func (*Mesh) Contains

func (m *Mesh) Contains(f *Segment) bool

Contains checks if f has been added to the mesh.

func (*Mesh) Copy added in v0.2.23

func (m *Mesh) Copy() *Mesh

Copy returns a shallow copy of m, where all of the segments are the same exact pointers.

func (*Mesh) Decimate added in v0.2.6

func (m *Mesh) Decimate(maxVertices int) *Mesh

Decimate repeatedly removes vertices from a mesh until it contains maxVertices or fewer vertices with two neighbors.

For manifold meshes, maxVertices is a hard-limit on the number of resulting vertices. For non-manifold meshes, more than maxVertices vertices will be retained if all of the remaining vertices are not part of exactly two segments.

func (*Mesh) DeepCopy added in v0.2.23

func (m *Mesh) DeepCopy() *Mesh

DeepCopy returns a deep copy of m, where all of the segments are copied individually.

func (*Mesh) EliminateColinear added in v0.2.13

func (m *Mesh) EliminateColinear(epsilon float64) *Mesh

EliminateColinear eliminates vertices that connect nearly co-linear edges.

The epsilon argument should be a small positive value that is used to approximate co-linearity. A good value for very precise results is 1e-8.

func (*Mesh) Find

func (m *Mesh) Find(ps ...Coord) []*Segment

Find gets all the segments that contain all of the passed points.

For example, to find all segments containing a line from from p1 to p2, you could do m.Find(p1, p2).

func (*Mesh) InconsistentVertices added in v0.4.2

func (m *Mesh) InconsistentVertices() []Coord

InconsistentVertices finds vertices which are used as either the first or second point in more than one edge, indicating an inconsistent mesh orientation in otherwise manifold meshes.

func (*Mesh) Invert added in v0.2.11

func (m *Mesh) Invert() *Mesh

Invert flips every segment in the mesh, effectively inverting all the normals.

func (*Mesh) InvertNormals added in v0.3.4

func (m *Mesh) InvertNormals() *Mesh

InvertNormals returns a new mesh with every segment oriented in the opposite way.

func (*Mesh) Iterate

func (m *Mesh) Iterate(f func(*Segment))

Iterate calls f for every segment in m in an arbitrary order.

If f adds or removes segments, they will not be visited.

func (*Mesh) IterateSorted

func (m *Mesh) IterateSorted(f func(*Segment), cmp func(f1, f2 *Segment) bool)

IterateSorted is like Iterate, but it first sorts all the segments according to a less than function, cmp.

func (*Mesh) IterateVertices added in v0.1.1

func (m *Mesh) IterateVertices(f func(c Coord))

IterateVertices calls f for every vertex in m in an arbitrary order.

If f adds or removes vertices, they will not be visited.

func (*Mesh) Manifold

func (m *Mesh) Manifold() bool

Manifold checks if the mesh is manifold, i.e. if every vertex has two segments.

func (*Mesh) MapCoords

func (m *Mesh) MapCoords(f func(Coord) Coord) *Mesh

MapCoords creates a new mesh by transforming all of the coordinates according to the function f.

func (*Mesh) Max

func (m *Mesh) Max() Coord

Max gets the component-wise maximum across all the vertices in the mesh.

func (*Mesh) Min

func (m *Mesh) Min() Coord

Min gets the component-wise minimum across all the vertices in the mesh.

func (*Mesh) Neighbors

func (m *Mesh) Neighbors(f *Segment) []*Segment

Neighbors gets all the segments with a side touching a given segment f.

The segment f itself is not included in the results.

The segment f needn't be in the mesh. However, if it is not in the mesh, but an equivalent segment is, then said equivalent segment will be in the results.

func (*Mesh) NumSegments added in v0.4.0

func (m *Mesh) NumSegments() int

NumSegments returns the number of segments in m.

func (*Mesh) Remove

func (m *Mesh) Remove(f *Segment)

Remove removes the segment f from the mesh.

It looks at f as a pointer, so the pointer must be exactly the same as one passed to Add.

func (*Mesh) Repair added in v0.2.6

func (m *Mesh) Repair(epsilon float64) *Mesh

Repair finds vertices that are close together and combines them into one.

The epsilon argument controls how close points have to be. In particular, it sets the approximate maximum distance across all dimensions.

func (*Mesh) RepairNormals added in v0.2.4

func (m *Mesh) RepairNormals(epsilon float64) (*Mesh, int)

RepairNormals flips normals when they point within the shape defined by the mesh, as determined by the even-odd rule.

The repaired mesh is returned, along with the number of modified segments.

The check is performed by adding the normal, scaled by epsilon, to the center of the segment, and then counting the number of ray collisions from this point in the direction of the normal.

func (*Mesh) Rotate added in v0.2.15

func (m *Mesh) Rotate(angle float64) *Mesh

Rotate returns a mesh with all coordinates rotated around the origin by a given angle (in radians).

func (*Mesh) SavePathSVG added in v0.4.2

func (m *Mesh) SavePathSVG(path string) error

SavePathSVG encodes the mesh as a path to an SVG file.

func (*Mesh) SaveSVG

func (m *Mesh) SaveSVG(path string) error

SaveSVG encodes the mesh to an SVG file.

func (*Mesh) Scale added in v0.2.2

func (m *Mesh) Scale(s float64) *Mesh

Scale creates a new mesh by scaling the coordinates by a factor s.

func (*Mesh) SegmentSlice added in v0.2.11

func (m *Mesh) SegmentSlice() []*Segment

SegmentSlice gets a snapshot of all the segments currently in the mesh. The resulting slice is a copy, and will not change as the mesh is updated.

func (*Mesh) SegmentsSlice

func (m *Mesh) SegmentsSlice() []*Segment

SegmentsSlice is exactly like SegmentSlice(), and is only implemented for backwards-compatibility.

func (*Mesh) Smooth

func (m *Mesh) Smooth(iters int) *Mesh

Smooth is similar to Blur, but it is less sensitive to differences in segment length.

func (*Mesh) SmoothSq

func (m *Mesh) SmoothSq(iters int) *Mesh

SmoothSq is like Smooth, but it minimizes the sum of squared segment lengths rather than the sum of lengths directly. Thus, SmoothSq produces more even segments than Smooth.

func (*Mesh) Subdivide

func (m *Mesh) Subdivide(iters int) *Mesh

Subdivide uses Chaikin subdivision to add segments between every vertex.

This can only be applied to manifold meshes. This can be checked with m.Manifold().

func (*Mesh) Transform added in v0.2.9

func (m *Mesh) Transform(t Transform) *Mesh

Transform applies t to the coordinates.

func (*Mesh) Translate added in v0.2.13

func (m *Mesh) Translate(v Coord) *Mesh

Translate returns a mesh with all coordinates added to a given vector.

func (*Mesh) VertexSlice added in v0.1.1

func (m *Mesh) VertexSlice() []Coord

VertexSlice gets a snapshot of all the vertices currently in the mesh.

The result is a copy and is in no way connected to the mesh in memory.

type MeshHierarchy added in v0.2.10

type MeshHierarchy struct {
	// Mesh is the root shape of this (sub-)hierarchy.
	Mesh *Mesh

	// MeshSolid is a solid indicating which points are
	// contained in the mesh.
	MeshSolid Solid

	Children []*MeshHierarchy
}

A MeshHierarchy is a tree structure where each node is a closed, simple polygon, and children are contained inside their parents.

Only manifold meshes with no self-intersections can be converted into a MeshHierarchy.

func MeshToHierarchy added in v0.2.10

func MeshToHierarchy(m *Mesh) []*MeshHierarchy

MeshToHierarchy creates a MeshHierarchy for each exterior mesh contained in m.

The mesh m must be manifold and have no self-intersections.

func (*MeshHierarchy) Contains added in v0.2.10

func (m *MeshHierarchy) Contains(c Coord) bool

Contains checks if c is inside the hierarchy using the even-odd rule.

func (*MeshHierarchy) FullMesh added in v0.2.10

func (m *MeshHierarchy) FullMesh() *Mesh

FullMesh re-combines the root mesh with all of its children.

func (*MeshHierarchy) MapCoords added in v0.2.10

func (m *MeshHierarchy) MapCoords(f func(Coord) Coord) *MeshHierarchy

MapCoords creates a new MeshHierarchy by applying f to every coordinate in every mesh.

func (*MeshHierarchy) Max added in v0.2.10

func (m *MeshHierarchy) Max() Coord

Max gets the maximum point of the outer mesh's bounding box.

func (*MeshHierarchy) Min added in v0.2.10

func (m *MeshHierarchy) Min() Coord

Min gets the minimum point of the outer mesh's bounding box.

type Metaball added in v0.3.5

type Metaball interface {
	// Bounder returns the bounds for the volume where
	// MetaballField() may return values <= 0.
	Bounder

	// MetaballField returns the distance, in some possibly
	// transformed space, of c to the metaball surface.
	//
	// Note that this is not an actual distance in
	// Euclidean coordinates, so for example one could have
	// ||f(c) - f(c1)|| > ||c - c1||.
	//
	// This can happen when scaling a metaball, which
	// effectively changes how fast the field increases as
	// points move away from the surface.
	MetaballField(c Coord) float64

	// MetaballDistBound gives, for a distance to the
	// underlying metaball surface, the minimum value that
	// may be returned by MetaballField.
	//
	// This function must be non-decreasing.
	// For any d and t such that MetaballDistBound(d) >= t,
	// it must be the case that MetaballDistBound(d1) >= t
	// for all d1 >= d.
	MetaballDistBound(d float64) float64
}

A Metaball implements a field f(c) where values greater than zero indicate points "outside" of some shape, and larger values indicate points "further" away.

The values of the field are related to distances from the ground truth shape in Euclidean space. This relationship is implemented by MetaballDistBound(), which provides an upper-bound on Euclidean distance given a field value. This makes it possible to bound a level set of the field in Euclidean space, provided the bounds of the coordinates where f(c) <= 0.

func RotateMetaball added in v0.3.5

func RotateMetaball(m Metaball, angle float64) Metaball

RotateMetaball creates a new Metaball by rotating a Metaball by a given angle (in radians).

func SDFToMetaball added in v0.4.0

func SDFToMetaball(s SDF) Metaball

SDFToMetaball creates a Metaball from the SDF. The resulting field is equal to the negative SDF.

func ScaleMetaball added in v0.3.5

func ScaleMetaball(m Metaball, s float64) Metaball

ScaleMetaball creates a new Metaball by scaling m by the factor s.

func TransformMetaball added in v0.3.5

func TransformMetaball(t DistTransform, m Metaball) Metaball

TransformMetaball applies t to the metaball m to produce a new, transformed metaball.

The inverse transform must also implement DistTransform.

func TranslateMetaball added in v0.3.5

func TranslateMetaball(m Metaball, offset Coord) Metaball

TranslateMetaball creates a new Metaball by translating a Metaball by a given offset.

func VecScaleMetaball added in v0.3.5

func VecScaleMetaball(m Metaball, scale Coord) Metaball

VecScaleMetaball transforms the metaball m by scaling each axis by a different factor.

type MetaballFalloffFunc added in v0.3.5

type MetaballFalloffFunc func(r float64) float64

MetaballFalloffFunc is a function that determines how the influence of metaballs falls off outside their surface.

type MultiCollider added in v0.2.6

type MultiCollider interface {
	Collider
	SegmentCollider
	RectCollider
}

func BVHToCollider added in v0.4.0

func BVHToCollider(b *BVH[*Segment]) MultiCollider

BVHToCollider converts a BVH into a MultiCollider in a hierarchical way.

func GroupedSegmentsToCollider

func GroupedSegmentsToCollider(segs []*Segment) MultiCollider

GroupedSegmentsToCollider converts pre-grouped segments into an efficient MultiCollider. If the segments were not grouped with GroupSegments, then the resulting collider may be highly inefficient.

func MeshToCollider

func MeshToCollider(m *Mesh) MultiCollider

MeshToCollider converts a mesh to an efficient MultiCollider.

type NormalSDF added in v0.3.0

type NormalSDF interface {
	SDF

	// NormalSDF gets the SDF at c and also returns the
	// normal at the nearest point to c on the surface.
	NormalSDF(c Coord) (Coord, float64)
}

A NormalSDF is an SDF that can additionally get the tangent normal for the nearest point on a surface.

type PointSDF added in v0.2.3

type PointSDF interface {
	SDF

	// PointSDF gets the SDF at c and also returns the
	// nearest point to c on the surface.
	PointSDF(c Coord) (Coord, float64)
}

A PointSDF is an SDF that can additionally get the nearest point on a surface.

func FuncPointSDF added in v0.2.13

func FuncPointSDF(min, max Coord, f func(Coord) (Coord, float64)) PointSDF

FuncPointSDF creates a PointSDF from a function.

If the bounds are invalid, FuncPointSDF() will panic(). In particular, max must be no less than min, and all floating-point values must be finite numbers.

type Rasterizer added in v0.2.6

type Rasterizer struct {
	// Scale determines how many pixels comprise a unit
	// distance in the model being rasterized.
	//
	// This determines how large output images are, given
	// the bounds of the model being rasterized.
	//
	// A value of 0 defaults to a value of 1.
	Scale float64

	// Subsamples indicates how many sub-samples to test
	// for each axis in each pixel.
	// A value of 1 means one sample is taken per pixel,
	// and values higher than one cause anti-aliasing.
	// If 0, RasterizerDefaultSubsamples is used.
	Subsamples int

	// LineWidth is the thickness of lines (in pixels)
	// when rendering a mesh or collider.
	//
	// If 0, RasterizerDefaultLineWidth is used.
	LineWidth float64

	// Bounds, if non-nil, is used to override the bounds
	// of any rasterized object.
	// This can be used to add padding, or have a
	// consistent canvas when drawing a moving scene.
	Bounds Bounder
}

A Rasterizer converts 2D models into raster images.

func (*Rasterizer) Rasterize added in v0.2.6

func (r *Rasterizer) Rasterize(obj any) *image.Gray

Rasterize rasterizes a Solid, Mesh, or Collider.

func (*Rasterizer) RasterizeCollider added in v0.2.6

func (r *Rasterizer) RasterizeCollider(c Collider) *image.Gray

RasterizeCollider rasterizes the collider as a line drawing.

func (*Rasterizer) RasterizeColliderSolid added in v0.2.7

func (r *Rasterizer) RasterizeColliderSolid(c Collider) *image.Gray

RasterizeColliderSolid rasterizes the collider as a filled in Solid using the even-odd test.

func (*Rasterizer) RasterizeSolid added in v0.2.6

func (r *Rasterizer) RasterizeSolid(s Solid) *image.Gray

RasterizeSolid rasterizes a Solid into an image.

func (*Rasterizer) RasterizeSolidFilter added in v0.2.6

func (r *Rasterizer) RasterizeSolidFilter(s Solid, f func(r *Rect) bool) *image.Gray

RasterizeSolidFilter rasterizes a Solid using a heuristic filter than can eliminate the need to render blank regions of the image.

If f returns false for a given rectangular region, it means that the solid is definitely uniform within the region (i.e. there is no boundary in the region). The exact pattern with which f is called will depend on the image and rasterization parameters.

type Ray

type Ray struct {
	Origin    Coord
	Direction Coord
}

A Ray is a line originating at a point and extending infinitely in some (positive) direction.

type RayCollision

type RayCollision struct {
	// The amount of the ray direction to add to the ray
	// origin to hit the point in question.
	//
	// The scale should be non-negative.
	Scale float64

	// The normal pointing outward from the outline at the
	// point of collision.
	Normal Coord

	// Extra contains additional, implementation-specific
	// information about the collision.
	Extra any
}

RayCollision is a point where a ray intersects a 2-dimensional outline.

type Rect added in v0.2.4

type Rect struct {
	MinVal Coord
	MaxVal Coord
}

A Rect is a 2D primitive that fills an axis-aligned rectangular space.

func BoundsRect added in v0.2.13

func BoundsRect(b Bounder) *Rect

BoundsRect creates a Rect from a Bounder's bounds.

func NewRect added in v0.2.13

func NewRect(min, max Coord) *Rect

NewRect creates a Rect with a min and a max value.

func (*Rect) CircleCollision added in v0.3.0

func (r *Rect) CircleCollision(c Coord, radius float64) bool

CircleCollision checks if a solid circle touches any part of the rectangular surface.

func (*Rect) Contains added in v0.2.4

func (r *Rect) Contains(c Coord) bool

Contains checks if c is inside of r.

func (*Rect) Expand added in v0.3.0

func (r *Rect) Expand(delta float64) *Rect

Expand returns a new Rect that is delta units further along in every direction, making it a total of 2*delta units longer along each axis.

func (*Rect) FirstRayCollision added in v0.3.0

func (r *Rect) FirstRayCollision(ray *Ray) (RayCollision, bool)

FirstRayCollision gets the first ray collision with the rectangular surface.

func (*Rect) Max added in v0.2.4

func (r *Rect) Max() Coord

Max yields r.MaxVal.

func (*Rect) MetaballDistBound added in v0.3.5

func (r *Rect) MetaballDistBound(d float64) float64

MetaballDistBound returns d always, since the metaball implemented by MetaballField() is defined in terms of standard Euclidean coordinates.

func (*Rect) MetaballField added in v0.3.5

func (r *Rect) MetaballField(coord Coord) float64

MetaballField returns positive values outside of the surface, and these values increase linearly with distance to the surface.

func (*Rect) Min added in v0.2.4

func (r *Rect) Min() Coord

Min yields r.MinVal.

func (*Rect) NormalSDF added in v0.3.0

func (r *Rect) NormalSDF(c Coord) (Coord, float64)

NormalSDF gets the signed distance to the rect and the normal at the closest point on the surface.

func (*Rect) PointSDF added in v0.3.0

func (r *Rect) PointSDF(c Coord) (Coord, float64)

PointSDF gets the nearest point on the surface of the rect and the corresponding SDF.

func (*Rect) RayCollisions added in v0.3.0

func (r *Rect) RayCollisions(ray *Ray, f func(RayCollision)) int

RayCollisions calls f (if non-nil) with each ray collision with the rectangular surface. It returns the number of collisions.

func (*Rect) SDF added in v0.3.0

func (r *Rect) SDF(c Coord) float64

SDF gets the signed distance to the surface of the rectangular volume.

type RectCollider added in v0.2.6

type RectCollider interface {
	// RectCollision returns true if any part of the
	// outline is inside the rect.
	RectCollision(r *Rect) bool
}

A RectCollider is a 2-dimensional outline which can detect if a 2D axis-aligned rectangular area collides with the outline.

type SDF added in v0.2.3

type SDF interface {
	Bounder

	SDF(c Coord) float64
}

An SDF is a signed distance function.

An SDF returns 0 on the boundary of some surface, positive values inside the surface, and negative values outside the surface. The magnitude is the distance to the surface.

All methods of an SDF are safe for concurrency.

func ColliderToSDF added in v0.2.3

func ColliderToSDF(c Collider, iterations int) SDF

ColliderToSDF generates an SDF that uses bisection search to approximate the SDF for any Collider.

The iterations argument controls the precision. If set to 0, a default of 32 is used.

func FuncSDF added in v0.2.13

func FuncSDF(min, max Coord, f func(Coord) float64) SDF

FuncSDF creates an SDF from a function.

If the bounds are invalid, FuncSDF() will panic(). In particular, max must be no less than min, and all floating-point values must be finite numbers.

func TransformSDF added in v0.2.11

func TransformSDF(t DistTransform, s SDF) SDF

TransformSDF applies t to the SDF s to produce a new, transformed SDF.

type Scale added in v0.2.11

type Scale struct {
	Scale float64
}

Scale is a transform that scales an object.

func (*Scale) Apply added in v0.2.11

func (s *Scale) Apply(c Coord) Coord

func (*Scale) ApplyBounds added in v0.2.11

func (s *Scale) ApplyBounds(min Coord, max Coord) (Coord, Coord)

func (*Scale) ApplyDistance added in v0.2.11

func (s *Scale) ApplyDistance(d float64) float64

func (*Scale) Inverse added in v0.2.11

func (s *Scale) Inverse() Transform

type Segment

type Segment [2]Coord

A Segment is a 2-dimensional line segment.

The order determines the normal direction.

In particular, if the segments in a polygon go in the clockwise direction, assuming the y-axis faces up, then the normals face outwards from the polygon.

func DecodeCSV added in v0.2.13

func DecodeCSV(data []byte) ([]*Segment, error)

DecodeCSV decodes the CSV format from EncodeCSV().

func (*Segment) CircleCollision

func (s *Segment) CircleCollision(c Coord, r float64) bool

CircleCollision checks if the circle intersects the segment s.

func (Segment) Closest added in v0.2.3

func (s Segment) Closest(c Coord) Coord

Closest gets the point on the segment closest to c.

func (Segment) Dist added in v0.2.3

func (s Segment) Dist(c Coord) float64

Dist gets the minimum distance from c to a point on the line segment.

func (*Segment) FirstRayCollision

func (s *Segment) FirstRayCollision(r *Ray) (RayCollision, bool)

FirstRayCollision gets the ray collision if there is one.

func (Segment) Length

func (s Segment) Length() float64

Length gets the length of the segment.

func (Segment) Max

func (s Segment) Max() Coord

Max gets the element-wise maximum of the endpoints.

func (Segment) Mid

func (s Segment) Mid() Coord

Mid gets the midpoint of the segment.

func (Segment) Min

func (s Segment) Min() Coord

Min gets the element-wise minimum of the endpoints.

func (*Segment) Normal

func (s *Segment) Normal() Coord

Normal computes the normal vector to the segment, facing outwards from the surface.

func (*Segment) RayCollisions

func (s *Segment) RayCollisions(r *Ray, f func(RayCollision)) int

RayCollisions calls f (if non-nil) with a collision (if applicable) and returns the collisions count (0 or 1).

func (*Segment) RectCollision added in v0.2.6

func (s *Segment) RectCollision(r *Rect) bool

RectCollision returns true if any part of the segment is inside of the rectangle.

func (*Segment) SegmentCollision added in v0.2.6

func (s *Segment) SegmentCollision(s1 *Segment) bool

SegmentCollision returns true if s intersects s1.

type SegmentCollider added in v0.2.6

type SegmentCollider interface {
	// SegmentCollision returns true if the segment
	// collides with the outline.
	SegmentCollision(s *Segment) bool
}

A SegmentCollider is a 2-dimensional outline which can detect if a line segment collides with the outline.

type Solid

type Solid interface {
	// Contains must always return false outside of the
	// boundaries of the solid.
	Bounder

	Contains(p Coord) bool
}

A Solid is a boolean function where a value of true indicates that a point is part of the solid, and false indicates that it is not.

All methods of a Solid are safe for concurrency.

func BitmapToSolid

func BitmapToSolid(b *Bitmap) Solid

func CacheSolidBounds added in v0.2.11

func CacheSolidBounds(s Solid) Solid

CacheSolidBounds creates a Solid that has a cached version of the solid's boundary coordinates.

The solid also explicitly checks that points are inside the boundary before passing them off to s.

func CheckedFuncSolid added in v0.2.11

func CheckedFuncSolid(min, max Coord, f func(Coord) bool) Solid

CheckedFuncSolid is like FuncSolid, but it does an automatic bounds check before calling f.

func ForceSolidBounds added in v0.2.7

func ForceSolidBounds(s Solid, min, max Coord) Solid

ForceSolidBounds creates a new solid that reports the exact bounds given by min and max.

Points outside of these bounds will be removed from s, but otherwise s is preserved.

func FuncSolid added in v0.2.11

func FuncSolid(min, max Coord, f func(Coord) bool) Solid

FuncSolid creates a Solid from a function.

If the bounds are invalid, FuncSolid() will panic(). In particular, max must be no less than min, and all floating-point values must be finite numbers.

func MetaballSolid added in v0.3.5

func MetaballSolid(f MetaballFalloffFunc, radiusThreshold float64, m ...Metaball) Solid

MetaballSolid creates a Solid by smoothly combining multiple metaballs.

The f argument determines how MetaballField() values are converted to values to be summed across multiple metaballs. If nil, QuarticMetaballFalloffFunc is used.

The radiusThreshold is passed through f to determine the field threshold. When converting a single metaball to a solid, radiusThreshold can be thought of as the max value of the metaball's field that is contained within the solid.

func RotateSolid added in v0.2.15

func RotateSolid(solid Solid, angle float64) Solid

RotateSolid creates a new Solid by rotating a Solid by a given angle (in radians).

func SDFToSolid added in v0.3.0

func SDFToSolid(s SDF, outset float64) Solid

SDFToSolid creates a Solid which is true inside the SDF.

If the outset argument is non-zero, it is the extra distance outside the SDF that is considered inside the solid. It can also be negative to inset the solid.

func ScaleSolid

func ScaleSolid(solid Solid, s float64) Solid

ScaleSolid creates a new Solid that scales incoming coordinates c by 1/s. Thus, the new solid is s times larger.

func SmoothJoin added in v0.2.11

func SmoothJoin(radius float64, sdfs ...SDF) Solid

SmoothJoin joins the SDFs into a union Solid and smooths the intersections using a given smoothing radius.

If the radius is 0, it is equivalent to turning the SDFs directly into solids and then joining them.

func SmoothJoinV2 added in v0.3.0

func SmoothJoinV2(radius float64, sdfs ...NormalSDF) Solid

SmoothJoinV2 is like SmoothJoin, but uses surface normals to improve results for SDFs that intersect at obtuse angles.

func TransformSolid added in v0.2.8

func TransformSolid(t Transform, s Solid) Solid

TransformSolid applies t to the solid s to produce a new, transformed solid.

func TranslateSolid added in v0.2.13

func TranslateSolid(solid Solid, offset Coord) Solid

TranslateSolid creates a new Solid by translating a Solid by a given offset.

func VecScaleSolid added in v0.3.5

func VecScaleSolid(solid Solid, v Coord) Solid

VecScaleSolid creates a new Solid that scales incoming coordinates c by 1/v, thus resizing the solid a variable amount along each axis.

type SolidMux added in v0.3.6

type SolidMux struct {
	// contains filtered or unexported fields
}

A SolidMux computes many solid values in parallel and returns a bitmap of containment for each solid.

This uses a BVH to efficiently check the containment of many solids without explicitly having to check every single solid's Contains() methods.

func NewSolidMux added in v0.3.6

func NewSolidMux(solids []Solid) *SolidMux

NewSolidMux creates a SolidMux using the ordered list of solids provided as arguments.

func (*SolidMux) AllContains added in v0.3.6

func (s *SolidMux) AllContains(c Coord) []bool

AllContains returns, for each solid in the mux, whether the coordinate c is contained in the solid.

func (*SolidMux) Contains added in v0.3.6

func (s *SolidMux) Contains(c Coord) bool

func (*SolidMux) IterContains added in v0.4.0

func (s *SolidMux) IterContains(c Coord, f func(i int)) int

IterContains calls f with the index of each solid that contains the coordinate.

Returns the number of solids which contain the coordinate.

The f argument may be nil, in which case this simply counts the number of containing solids.

The order in which f is called is arbitrary and subject to change. It will not necessarily be called in order of the solids, but it will always be called at most once per solid.

func (*SolidMux) Max added in v0.3.6

func (s *SolidMux) Max() Coord

func (*SolidMux) Min added in v0.3.6

func (s *SolidMux) Min() Coord

func (*SolidMux) Solids added in v0.4.0

func (s *SolidMux) Solids() []Solid

type SolidSurfaceEstimator added in v0.3.4

type SolidSurfaceEstimator struct {
	// The Solid to estimate the surface of.
	Solid Solid

	// BisectCount, if non-zero, specifies the number of
	// bisections to use in Bisect().
	// Default is DefaultSurfaceEstimatorBisectCount.
	BisectCount int

	// NormalSamples, if non-zero, specifies how many
	// samples to use to approximate normals.
	// Default is DefaultSurfaceEstimatorNormalSamples.
	NormalSamples int

	// RandomSearchNormals can be set to true to disable
	// the binary search to compute normals. Instead, an
	// evolution strategy is performed to estimate the
	// gradient by sampling random points at a distance
	// of NormalNoiseEpsilon.
	RandomSearchNormals bool

	// NormalBisectEpsilon, if non-zero, specifies a small
	// distance to use in a bisection-based method to
	// compute approximate normals.
	//
	// The value must be larger than the distance between
	// the surface and points passed to Normal().
	//
	// Default is DefaultSurfaceEstimatorNormalBisectionEpsilon.
	NormalBisectEpsilon float64

	// NormalNoiseEpsilon, if non-zero, specifies a small
	// distance to use in an evolution strategy when
	// RandomSearchNormals is true.
	//
	// The value must be larger than the distance between
	// the surface and points passed to Normal().
	//
	// Default is DefaultSurfaceEstimatorNormalNoiseEpsilon.
	NormalNoiseEpsilon float64
}

SolidSurfaceEstimator estimates collision points and normals on the surface of a solid using search.

func (*SolidSurfaceEstimator) Bisect added in v0.3.4

func (s *SolidSurfaceEstimator) Bisect(p1, p2 Coord) Coord

Bisect finds the point between p1 and p2 closest to the surface, provided that p1 and p2 are on different sides.

func (*SolidSurfaceEstimator) BisectInterior added in v0.4.2

func (s *SolidSurfaceEstimator) BisectInterior(p1, p2 Coord) Coord

BisectInterior is like Bisect, but it always returns a point contained within the solid as long as one of the two points is within the solid.

func (*SolidSurfaceEstimator) BisectInterp added in v0.3.4

func (s *SolidSurfaceEstimator) BisectInterp(p1, p2 Coord, min, max float64) float64

BisectInterp returns alpha in [min, max] to minimize the surface's distance to p1 + alpha * (p2 - p1).

It is assumed that p1 is outside the surface and p2 is inside, and that min < max.

func (*SolidSurfaceEstimator) BisectInterpRange added in v0.4.2

func (s *SolidSurfaceEstimator) BisectInterpRange(p1, p2 Coord, min,
	max float64) (float64, float64)

BisectInterpRange is like BisectInterp, but returns a range where the second value corresponds to a point inside the surface.

func (*SolidSurfaceEstimator) Normal added in v0.3.4

func (s *SolidSurfaceEstimator) Normal(c Coord) Coord

Normal computes the normal at a point on the surface. The point must be guaranteed to be on the boundary of the surface, e.g. from Bisect().

type SubtractedSolid added in v0.2.3

type SubtractedSolid struct {
	Positive Solid
	Negative Solid
}

SubtractedSolid is a Solid consisting of all the points in Positive which are not in Negative.

func (*SubtractedSolid) Contains added in v0.2.3

func (s *SubtractedSolid) Contains(c Coord) bool

func (*SubtractedSolid) Max added in v0.2.3

func (s *SubtractedSolid) Max() Coord

func (*SubtractedSolid) Min added in v0.2.3

func (s *SubtractedSolid) Min() Coord

type Transform added in v0.2.8

type Transform interface {
	// Apply applies the transformation to c.
	Apply(c Coord) Coord

	// ApplyBounds gets a new bounding rectangle that is
	// guaranteed to bound the old bounding rectangle when
	// it is transformed.
	ApplyBounds(min, max Coord) (Coord, Coord)

	// Inverse gets an inverse transformation.
	//
	// The inverse may not perfectly invert bounds
	// transformations, since some information may be lost
	// during such a transformation.
	Inverse() Transform
}

Transform is an invertible coordinate transformation.

type Translate added in v0.2.8

type Translate struct {
	Offset Coord
}

Translate is a Transform that adds an offset to coordinates.

func (*Translate) Apply added in v0.2.8

func (t *Translate) Apply(c Coord) Coord

func (*Translate) ApplyBounds added in v0.2.8

func (t *Translate) ApplyBounds(min, max Coord) (Coord, Coord)

func (*Translate) ApplyDistance added in v0.2.11

func (t *Translate) ApplyDistance(d float64) float64

func (*Translate) Inverse added in v0.2.8

func (t *Translate) Inverse() Transform

type Triangle added in v0.4.0

type Triangle struct {
	// contains filtered or unexported fields
}

A Triangle is a 2D primitive containing all of the convex combinations of three points.

func NewTriangle added in v0.4.0

func NewTriangle(p1, p2, p3 Coord) *Triangle

NewTriangle creates a Triangle from three points. The triangle's behavior will be mostly identical (up to rounding error) regardless of the order of the points.

func (*Triangle) Area added in v0.4.0

func (t *Triangle) Area() float64

Area returns the area of the triangle.

func (*Triangle) AtBarycentric added in v0.4.0

func (t *Triangle) AtBarycentric(c [3]float64) Coord

AtBarycentric computes the point at the barycentric coordinates.

func (*Triangle) Barycentric added in v0.4.0

func (t *Triangle) Barycentric(c Coord) [3]float64

Barycentric returns the barycentric coordinates of c.

The result should always sum to 1.

If the triangle is degenerate, the behavior is undefined and the results may be unstable.

func (*Triangle) BarycentricSDF added in v0.4.0

func (t *Triangle) BarycentricSDF(c Coord) ([3]float64, float64)

BarycentricSDF gets the signed distance to the triangle and the barycentric coordinates of the closest point on the border.

func (*Triangle) CircleCollision added in v0.4.0

func (t *Triangle) CircleCollision(center Coord, r float64) bool

CircleCollision checks if the surface of c collides with a solid Circle centered at c with radius r.

func (*Triangle) Contains added in v0.4.0

func (t *Triangle) Contains(c Coord) bool

Contains returns true if the triangle contains the point c. Behavior may be slightly inaccurate due to rounding errors, for example if c is on one of the edges.

func (*Triangle) Coords added in v0.4.0

func (t *Triangle) Coords() [3]Coord

Coords gets the coordinates passed to NewTriangle().

func (*Triangle) FirstRayCollision added in v0.4.0

func (t *Triangle) FirstRayCollision(r *Ray) (RayCollision, bool)

FirstRayCollision gets the first ray collision with the triangle, if one occurs.

func (*Triangle) Max added in v0.4.0

func (t *Triangle) Max() Coord

Max gets the maximum of the corners.

func (*Triangle) MetaballDistBound added in v0.4.0

func (t *Triangle) MetaballDistBound(d float64) float64

MetaballDistBound returns d always, since the metaball implemented by MetaballField() is defined in terms of standard Euclidean coordinates.

func (*Triangle) MetaballField added in v0.4.0

func (t *Triangle) MetaballField(coord Coord) float64

MetaballField returns positive values outside of the surface, and these values increase linearly with distance to the surface.

func (*Triangle) Min added in v0.4.0

func (t *Triangle) Min() Coord

Min gets the minimum of the corners.

func (*Triangle) NormalSDF added in v0.4.0

func (t *Triangle) NormalSDF(c Coord) (Coord, float64)

NormalSDF gets the signed distance to the triangle and the normal at the closest point on the border.

func (*Triangle) PointSDF added in v0.4.0

func (t *Triangle) PointSDF(c Coord) (Coord, float64)

PointSDF gets the nearest point on the border of the triangle and the corresponding SDF.

func (*Triangle) RayCollisions added in v0.4.0

func (t *Triangle) RayCollisions(r *Ray, f func(RayCollision)) int

RayCollisions calls f (if non-nil) with every ray collision.

It returns the total number of collisions.

func (*Triangle) SDF added in v0.4.0

func (t *Triangle) SDF(c Coord) float64

SDF gets the signed distance to the surface of the capsule.

type VecScale added in v0.3.5

type VecScale struct {
	Scale Coord
}

VecScale is a transform that scales an object coordinatewise.

func (*VecScale) Apply added in v0.3.5

func (v *VecScale) Apply(c Coord) Coord

func (*VecScale) ApplyBounds added in v0.3.5

func (v *VecScale) ApplyBounds(min Coord, max Coord) (Coord, Coord)

func (*VecScale) Inverse added in v0.3.5

func (v *VecScale) Inverse() Transform

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL