model3d

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: 19

Documentation

Overview

Package model3d provides a set of APIs for creating, manipulating, and storing 3D models.

model3d includes a few sub-packages:

  • render3d - ray tracing, materials, etc.
  • model2d - 2D graphics, smoothing, bitmaps.
  • toolbox3d - modular 3D-printable components to use in larger 3D models.

In addition, model3d comes with a large collection of examples for both modeling and rendering.

Representations

Models can be represented in three different ways, and model3d can convert between them seamlessly.

In particular, models can be:

  • *Mesh - a triangle mesh, good for exporting to most 3D CAD tools, renderers, etc.
  • Solid - a 3D boolean function defining which points are contained in the model. Ideal for composition, hand-coding, etc.
  • Collider - a surface that reports collisions with rays and other geometric shapes. Ideal for ray tracing, rendering, and physics.

Generally, it is easiest to create new models by implementing the Solid interface, or by using existing solids like *Sphere or *Cylinder and combining them with JoinedSolid or SubtractedSolid.

To convert a Solid to a *Mesh, use MarchingCubes() or MarchingCubesSearch() for more precision. To convert a Solid to a Collider, use SolidCollider or simply create a Mesh and convert that to a Collider.

To convert a *Mesh to a Collider, use MeshToCollider(). To convert a *Mesh to a Solid, first convert it to a Collider and then convert that to a Solid.

To convert a Collider to a Solid, use NewColliderSolid() or NewColliderSolidHollow(). To convert a Collider to a *Mesh, the simplest approach is to convert it to a Solid and then to convert the Solid to a *Mesh.

Creating models

The easiest way to create new models is by defining an object that implements the Solid interface. Once defined, a Solid can be converted to a Mesh and exported to a file (e.g. an STL file for 3D printing).

For example, here's how to implement a sphere as a Solid, taken from the actual model3d.Sphere type:

type Sphere struct {
    Center Coord3D
    Radius float64
}

func (s *Sphere) Min() Coord3D {
    return Coord3D{
        X: s.Center.X - s.Radius,
        Y: s.Center.Y - s.Radius,
        Z: s.Center.Z - s.Radius,
    }
}

func (s *Sphere) Max() Coord3D {
    return Coord3D{
        X: s.Center.X + s.Radius,
        Y: s.Center.Y + s.Radius,
        Z: s.Center.Z + s.Radius,
    }
}

func (s *Sphere) Contains(c Coord3D) bool {
    return c.Dist(s.Center) <= s.Radius
}

Once you have implemented a Solid, you can create a mesh and export it to a file like so:

solid := &Sphere{...}
mesh := MarchingCubesSearch(solid, 0.1, 8)
mesh.SaveGroupedSTL("output.stl")

In the above example, the mesh is created with an epsilon of 0.01 and 8 search steps. These parameters control the mesh resolution. See MarchingCubesSearch() for more details.

Mesh manipulation

The Mesh type provides various methods to check for singularities, fix small holes, eliminate redundant triangles, etc. There are also APIs that operate on a Mesh in more complex ways, making it easier to generate meshes programmatically:

  • Decimator - polygon reduction.
  • MeshSmoother - smoothing for reducing sharp edges or corners.
  • Subdivider - edge-based sub-division to add resolution where it is needed.

Exporting models

Software for 3D printing, rendering, and modeling typically expects to import 3D models as triangle meshes. Thus, model3d provides a number of ways to import and export triangle meshes. The simplest method is Mesh.SaveGroupedSTL(), which exports and STL file to a path. For colored models, Mesh.SaveMaterialOBJ() is the method to use.

Index

Constants

View Source
const (
	DefaultDualContouringBufferSize           = 1000000
	DefaultDualContouringRepairEpsilon        = 0.01
	DefaultDualContouringCubeMargin           = 0.001
	DefaultDualContouringSingularValueEpsilon = 0.1
)
View Source
const (
	DefaultDecimatorMinAspectRatio = 0.1
	DefaultDecimatorFeatureAngle   = 0.5
)
View Source
const (
	// ARAPDefaultTolerance is the default convergence
	// tolerance for ARAP.
	// It allows for early convergence stopping.
	ARAPDefaultTolerance = 1e-3

	// ARAPMaxIterations is the default maximum number
	// of iterations for ARAP.
	ARAPMaxIterations = 5000

	// ARAPMinIterations is the default minimum number
	// of iterations before early stopping is allowed.
	ARAPMinIterations = 2
)
View Source
const (
	Floater97DefaultMSETol   = 1e-16
	Floater97DefaultMaxIters = 5000
)
View Source
const (
	DefaultSurfaceEstimatorBisectCount         = 32
	DefaultSurfaceEstimatorNormalSamples       = 40
	DefaultSurfaceEstimatorNormalBisectEpsilon = 1e-4
	DefaultSurfaceEstimatorNormalNoiseEpsilon  = 1e-4
)

Variables

View Source
var Origin = Coord3D{}

Functions

func BoundsUnion added in v0.4.2

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

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 BuildMaterialOBJ added in v0.2.15

func BuildMaterialOBJ(t []*Triangle, c func(t *Triangle) [3]float64) (o *fileformats.OBJFile,
	m *fileformats.MTLFile)

BuildMaterialOBJ constructs obj and mtl files from a triangle mesh where each triangle's color is determined by a function c.

Since the obj file must reference the mtl file, it does so by the name "material.mtl". Change o.MaterialFiles if this is not desired.

func BuildQuantizedMaterialOBJ added in v0.3.3

func BuildQuantizedMaterialOBJ(t []*Triangle, textureSize int,
	c func(t *Triangle) [3]float64) (*fileformats.OBJFile, *fileformats.MTLFile, *image.RGBA)

BuildQuantizedMaterialOBJ is like BuildMaterialOBJ, but quantizes the triangle colors to fit in a single texture image, where each pixel is a different color.

Returns the texture image as well as the obj file. The texture should be called "texture.png", and the material "material.mtl".

func BuildUVMapMaterialOBJ added in v0.4.0

func BuildUVMapMaterialOBJ(t []*Triangle, uvMap MeshUVMap) (*fileformats.OBJFile, *fileformats.MTLFile)

BuildUVMapMaterialOBJ is like BuildMaterialOBJ, but writes texture coordinates based on a UV map.

The generated material file should be saved to the name "material.mtl", and references a texture "texture.png" that should be the target of the UV map.

func BuildVertexColorOBJ added in v0.4.3

func BuildVertexColorOBJ(t []*Triangle, c func(Coord3D) [3]float64) *fileformats.OBJFile

BuildVertexColorOBJ constructs an obj file with vertex colors.

func ColliderContains added in v0.2.11

func ColliderContains(c Collider, coord Coord3D, 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 CrossSectionSolid added in v0.2.13

func CrossSectionSolid(solid Solid, axis int, axisValue float64) model2d.Solid

CrossSectionSolid creates a 2D cross-section of a 3D solid as a 2D solid.

The axis is 0, 1, or 2 for X, Y, or Z respectively. The axisValue is the value for the axis at which a plane is constructed.

func DualContourInterior added in v0.4.2

func DualContourInterior(s Solid, delta float64, repair, clip bool) (*Mesh, []Coord3D)

DualContourInterior is like DualContour, but also returns a list of points which are known to be contained within the solid.

func EncodeMaterialOBJ

func EncodeMaterialOBJ(triangles []*Triangle, colorFunc func(t *Triangle) [3]float64) []byte

EncodeMaterialOBJ encodes a 3D model as a zip file containing both an OBJ and an MTL file.

The colorFunc maps faces to real-valued RGB colors.

The encoding creates a different material for every color, so the resulting file will be much smaller if a few identical colors are reused for many triangles.

func EncodePLY

func EncodePLY(triangles []*Triangle, colorFunc func(Coord3D) [3]uint8) []byte

EncodePLY encodes a 3D model as a PLY file, including colors for every vertex.

The colorFunc maps coordinates to 24-bit RGB colors.

func EncodeSTL

func EncodeSTL(triangles []*Triangle) []byte

EncodeSTL encodes a list of triangles in the binary STL format for use in 3D printing.

func ExtendBoundaryUVs added in v0.4.0

func ExtendBoundaryUVs(m *Mesh, param *CoordMap[model2d.Coord], maxDist float64)

ExtendBoundaryUVs rescales vertices of triangles on the boundary of a plane graph triangulation to ensure that these triangles are not highly stretched or even fully degenerate.

The maxDist argument specifies the maximum distance to extend the point.

It is assumed that the boundary parameterization is centered around the origin, as done by CircleBoundary() and similar helpers.

func Floater97DefaultSolver added in v0.4.0

func Floater97DefaultSolver() *numerical.BiCGSTABSolver

Floater97DefaultSolver creates a reasonable numerical solver for most small-to-medium parameterization systems.

Floater97DefaultMaxIters and Floater97DefaultMAETol are used as stopping criteria.

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 GroupTriangles

func GroupTriangles(faces []*Triangle)

GroupTriangles is like GroupBounders, but for triangles 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 GroupedTrianglesToCollider().

func InBounds

func InBounds(b Bounder, c Coord3D) bool

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

func MarchingCubesInterior added in v0.3.4

func MarchingCubesInterior(s Solid, delta float64, iters int) (*Mesh, *CoordMap[Coord3D])

MarchingCubesInterior is like MarchingCubesSearch, but in addition to a mesh, it returns a mapping from each vertex to a nearby point which is known to be contained within the solid.

func QuarticMetaballFalloffFunc added in v0.3.5

func QuarticMetaballFalloffFunc(r float64) float64

QuarticMetaballFalloffFunc implements 1/r^4 falloff.

func ReadColorPLY added in v0.4.2

func ReadColorPLY(r io.Reader) ([]*Triangle, *CoordMap[[3]uint8], error)

ReadColorPLY decodes a PLY file with vertex colors.

func Triangulate

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

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.

func VertexColorsToTriangle

func VertexColorsToTriangle(f func(c Coord3D) [3]float64) func(t *Triangle) [3]float64

VertexColorsToTriangle creates a per-triangle color function that averages the colors at each of the vertices.

func Write3MF added in v0.4.4

func Write3MF(w io.Writer, unit fileformats.ThreeMFUnit, tris []*Triangle) error

Write3MF encodes the mesh as a 3MF file.

func WriteMaterialOBJ added in v0.1.1

func WriteMaterialOBJ(w io.Writer, ts []*Triangle, colorFunc func(t *Triangle) [3]float64) error

WriteMaterialOBJ encodes a 3D model as a zip file containing both an OBJ and an MTL file.

The colorFunc maps faces to real-valued RGB colors.

The encoding creates a different material for every color, so the resulting file will be much smaller if a few identical colors are reused for many triangles.

func WritePLY added in v0.1.1

func WritePLY(w io.Writer, triangles []*Triangle, colorFunc func(Coord3D) [3]uint8) error

WritePLY writes the 3D model as a PLY file, including colors for every vertex.

The colorFunc maps coordinates to 24-bit RGB colors.

func WriteQuantizedMaterialOBJ added in v0.3.3

func WriteQuantizedMaterialOBJ(w io.Writer, ts []*Triangle, textureSize int,
	colorFunc func(t *Triangle) [3]float64) error

WriteQuantizedMaterialOBJ is like WriteMaterialOBJ, but uses a fixed-size texture image to store face colors.

func WriteSTL

func WriteSTL(w io.Writer, triangles []*Triangle) error

WriteSTL writes a list of triangles in the binary STL format to w.

func WriteTexturedMaterialOBJ added in v0.4.0

func WriteTexturedMaterialOBJ(w io.Writer, obj *fileformats.OBJFile, mtl *fileformats.MTLFile,
	texture image.Image) error

func WriteVertexColorOBJ added in v0.4.3

func WriteVertexColorOBJ(w io.Writer, ts []*Triangle, colorFunc func(Coord3D) [3]float64) error

WriteVertexColorOBJ encodes a 3D model as an OBJ file with vertex colors.

The colorFunc maps vertices to real-valued RGB colors.

Types

type ARAP added in v0.2.2

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

ARAP implements as-rigid-as-possible deformations for a pre-determined mesh.

func NewARAP added in v0.2.2

func NewARAP(m *Mesh) *ARAP

NewARAP creates an ARAP instance for the given mesh topology.

The ARAP instance will not hold a reference to m or its triangles. Rather, it copies the data as needed.

The instance uses cotangent weights, which are only guaranteed to work on meshes with smaller-than-right angles. For other weighting options, see NewARAPWeighted().

func NewARAPWeighted added in v0.2.2

func NewARAPWeighted(m *Mesh, linear, rotation ARAPWeightingScheme) *ARAP

NewARAPWeighted creates an ARAP with a specified weighting scheme.

The linear weighting scheme is used for linear solves, whereas the rotation weighting scheme is used for finding rigid transformations.

The ARAP instance will not hold a reference to m or its triangles. Rather, it copies the data as needed.

func (*ARAP) Deform added in v0.2.2

func (a *ARAP) Deform(constraints ARAPConstraints) *Mesh

Deform creates a new mesh by enforcing constraints on some points of the mesh.

func (*ARAP) DeformMap added in v0.2.2

func (a *ARAP) DeformMap(constraints ARAPConstraints,
	initialGuess map[Coord3D]Coord3D) map[Coord3D]Coord3D

DeformMap performs constrained mesh deformation.

The constraints argument maps coordinates from the original mesh to their new, fixed locations.

If the initialGuess is specified, it is used for the first iteration of the algorithm as a starting point for the deformation.

The result maps all old coordinates to new coordinates.

func (*ARAP) Laplace added in v0.2.2

func (a *ARAP) Laplace(constraints ARAPConstraints) map[Coord3D]Coord3D

Laplace deforms the mesh using a simple Laplacian heuristic.

This can be used to generate an initial guess for the more general Deform() method.

The result maps all old coordinates to new coordinates.

func (*ARAP) MaxIterations added in v0.2.2

func (a *ARAP) MaxIterations() int

MaxIterations gets the maximum allowed number of steps before optimization terminates.

func (*ARAP) MinIterations added in v0.2.2

func (a *ARAP) MinIterations() int

MinIterations gets the minimum allowed number of steps before optimization terminates.

func (*ARAP) SeqDeformer added in v0.2.2

func (a *ARAP) SeqDeformer(coldStart bool) func(ARAPConstraints) *Mesh

SeqDeformer creates a function that deforms the mesh, potentially caching computations across calls.

If coldStart is true, then the previous deformed mesh is used as an initial guess for the next deformation. This can reduce computation cost during animations.

The returned function is not safe to call from multiple Goroutines concurrently.

func (*ARAP) SetMaxIterations added in v0.2.2

func (a *ARAP) SetMaxIterations(m int)

SetMaxIterations sets the maximum allowed number of steps before optimization terminates.

func (*ARAP) SetMinIterations added in v0.2.2

func (a *ARAP) SetMinIterations(m int)

SetMinIterations sets the minimum allowed number of steps before optimization terminates.

func (*ARAP) SetTolerance added in v0.2.2

func (a *ARAP) SetTolerance(t float64)

SetTolerance changes the convergence tolerance. Lower values make the algorithm run longer but arrive at more accurate values.

See ARAPDefaultTolerance.

func (*ARAP) Tolerance added in v0.2.2

func (a *ARAP) Tolerance() float64

Tolerance gets the current convergence tolerance. Will be ARAPDefaultTolerance by default.

type ARAPConstraints added in v0.2.2

type ARAPConstraints map[Coord3D]Coord3D

ARAPConstraints maps coordinates from an original mesh to destination coordinates on a deformed mesh.

func (ARAPConstraints) AddAround added in v0.2.2

func (a ARAPConstraints) AddAround(arap *ARAP, c Coord3D, r float64, target Coord3D)

AddAround adds all of the points within r distance of c to the constraints, moving them such that c would move to target.

type ARAPWeightingScheme added in v0.2.2

type ARAPWeightingScheme int
const (
	// ARAPWeightingCotangent is the default weighting scheme
	// for ARAP from the paper. Unfortunately, it creates a
	// loss function that can potentially become negative.
	ARAPWeightingCotangent ARAPWeightingScheme = iota

	ARAPWeightingAbsCotangent
	ARAPWeightingUniform
)

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

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

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

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

This is good for efficient ray collision detection.

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() Coord3D
	Max() Coord3D
}

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

type Capsule added in v0.3.0

type Capsule struct {
	P1     Coord3D
	P2     Coord3D
	Radius float64
}

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

func (*Capsule) Contains added in v0.3.0

func (c *Capsule) Contains(coord Coord3D) 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() Coord3D

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

Min gets the minimum point of the bounding box.

func (*Capsule) NormalSDF added in v0.3.0

func (c *Capsule) NormalSDF(coord Coord3D) (Coord3D, 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 Coord3D) (Coord3D, 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 Coord3D) float64

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

func (*Capsule) SphereCollision added in v0.3.0

func (c *Capsule) SphereCollision(center Coord3D, r float64) bool

SphereCollision checks if the surface of c collides with a solid sphere centered at c with radius r.

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)

	// SphereCollision checks if the collider touches a
	// sphere with origin c and radius r.
	SphereCollision(c Coord3D, r float64) bool
}

A Collider is a surface which can detect intersections with linear rays and spheres.

All methods of a Collider are safe for concurrency.

func GroupedCollidersToCollider added in v0.3.4

func GroupedCollidersToCollider(colliders []Collider) Collider

GroupedCollidersToCollider creates a joined collider similarly to GroupedTrianglesToCollider, but operates on a sequence of grouped colliders instead of triangles.

To group the colliders, see GroupBounders(). Alternatively, if the colliders are wrappers around triangles, such as *InterpTriangle, then you may simply use GroupTriangles() before wrapping the triangles.

func ProfileCollider added in v0.2.5

func ProfileCollider(coll2d model2d.Collider, minZ, maxZ float64) Collider

ProfileCollider turns a 2D Collider into a 3D Collider by elongating the 2D Collider along the Z axis.

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 added in v0.2.11

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 Coord3D) bool

Contains checks if coord is in the solid.

func (*ColliderSolid) Max

func (c *ColliderSolid) Max() Coord3D

Max gets the maximum of the bounding box.

func (*ColliderSolid) Min

func (c *ColliderSolid) Min() Coord3D

Min gets the minimum of the bounding box.

type Cone added in v0.2.14

type Cone struct {
	Tip    Coord3D
	Base   Coord3D
	Radius float64
}

A Cone is a 3D cone, eminating from a point towards the center of a base, where the base has a given radius.

func (*Cone) Contains added in v0.2.14

func (c *Cone) Contains(p Coord3D) bool

Contains checks if p is inside the cone.

func (*Cone) FirstRayCollision added in v0.3.0

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

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

func (*Cone) Max added in v0.2.14

func (c *Cone) Max() Coord3D

Max gets the maximum point of the bounding box.

func (*Cone) MetaballDistBound added in v0.3.5

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

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

func (*Cone) MetaballField added in v0.3.5

func (c *Cone) MetaballField(coord Coord3D) float64

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

func (*Cone) Min added in v0.2.14

func (c *Cone) Min() Coord3D

Min gets the minimum point of the bounding box.

func (*Cone) NormalSDF added in v0.3.0

func (c *Cone) NormalSDF(coord Coord3D) (Coord3D, float64)

NormalSDF is like SDF, but also returns the normal on the surface of the cone at the closest point to coord.

func (*Cone) PointSDF added in v0.3.0

func (c *Cone) PointSDF(coord Coord3D) (Coord3D, float64)

PointSDF is like SDF, but also returns the closest point on the surface of the cone.

func (*Cone) RayCollisions added in v0.3.0

func (c *Cone) 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 (*Cone) SDF added in v0.2.14

func (c *Cone) SDF(coord Coord3D) float64

SDF determines the minimum distance from a point to the surface of the cone.

func (*Cone) SphereCollision added in v0.3.0

func (c *Cone) SphereCollision(center Coord3D, r float64) bool

SphereCollision checks if the surface of c collides with a solid sphere centered at c with radius r.

type ConvexPolytope

type ConvexPolytope []*LinearConstraint

A ConvexPolytope is the intersection of some linear constraints.

func NewConvexPolytopeRect added in v0.2.9

func NewConvexPolytopeRect(min, max Coord3D) ConvexPolytope

NewConvexPolytopeRect creates a rectangular convex polytope.

func (ConvexPolytope) Contains

func (c ConvexPolytope) Contains(coord Coord3D) bool

Contains checks that c satisfies the constraints.

func (ConvexPolytope) Mesh

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^3) in the constraints.

func (ConvexPolytope) Solid

func (c ConvexPolytope) Solid() Solid

Solid creates a solid out of the polytope.

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

type Coord2D

type Coord2D = model2d.Coord

func NewCoord2DPolar added in v0.2.9

func NewCoord2DPolar(theta, radius float64) Coord2D

NewCoord2DPolar converts polar coordinates to a Coord2D.

func NewCoord2DRandBounds added in v0.3.0

func NewCoord2DRandBounds(min, max Coord2D) Coord2D

NewCoord2DRandBounds creates a random Coord2D uniformly inside the given rectangular boundary.

func NewCoord2DRandNorm

func NewCoord2DRandNorm() Coord2D

NewCoord2DRandNorm creates a random Coord2D with normally distributed components.

func NewCoord2DRandUniform added in v0.2.4

func NewCoord2DRandUniform() Coord2D

NewCoord2DRandUniform creates a random Coord2D with uniformly random coordinates in [0, 1).

func NewCoord2DRandUnit

func NewCoord2DRandUnit() Coord2D

NewCoord2DRandUnit creates a random Coord2D with magnitude 1.

type Coord3D

type Coord3D struct {
	X float64
	Y float64
	Z float64
}

A Coord3D is a coordinate in 3-D Euclidean space.

func NewCoord3DArray

func NewCoord3DArray(a [3]float64) Coord3D

NewCoord3DArray creates a Coord3D from an array of x, y, and z.

func NewCoord3DRandBounds added in v0.2.8

func NewCoord3DRandBounds(min, max Coord3D) Coord3D

NewCoord3DRandBounds creates a random Coord3D uniformly inside the given rectangular boundary.

func NewCoord3DRandNorm

func NewCoord3DRandNorm() Coord3D

NewCoord3DRandNorm creates a random Coord3D with normally distributed components.

func NewCoord3DRandUniform added in v0.2.4

func NewCoord3DRandUniform() Coord3D

NewCoord3DRandUniform creates a random Coord3D with uniformly random coordinates in [0, 1).

func NewCoord3DRandUnit

func NewCoord3DRandUnit() Coord3D

NewCoord3DRandUnit creates a random Coord3D with magnitude 1.

func Ones added in v0.2.6

func Ones(a float64) Coord3D

Ones creates the unit vector scaled by a constant.

func X added in v0.2.4

func X(x float64) Coord3D

X gets a coordinate in the X direction.

func XY added in v0.2.4

func XY(x, y float64) Coord3D

XY gets a coordinate in the X and Y direction.

func XYZ added in v0.2.5

func XYZ(x, y, z float64) Coord3D

XYZ creates a 3D coordinate.

func XZ added in v0.2.4

func XZ(x, z float64) Coord3D

XZ gets a coordinate in the X and Z direction.

func Y added in v0.2.4

func Y(y float64) Coord3D

Y gets a coordinate in the Y direction.

func YZ added in v0.2.4

func YZ(y, z float64) Coord3D

YZ gets a coordinate in the Y and Z direction.

func Z added in v0.2.4

func Z(z float64) Coord3D

Z gets a coordinate in the Z direction.

func (Coord3D) Abs added in v0.3.4

func (c Coord3D) Abs() Coord3D

Abs computes the per-coordinate absolute value of c.

func (Coord3D) Add

func (c Coord3D) Add(c1 Coord3D) Coord3D

Add computes the sum of c and c1.

func (Coord3D) AddScalar added in v0.2.6

func (c Coord3D) AddScalar(s float64) Coord3D

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

func (Coord3D) Array

func (c Coord3D) Array() [3]float64

Array creates an array with the x, y, and z. This can be useful for some vectorized code.

func (Coord3D) Coord2D

func (c Coord3D) Coord2D() Coord2D

Coord2D projects c onto the x,y plane and drops the Z value. It is equivalent to c.XY().

func (Coord3D) Cross

func (c Coord3D) Cross(c1 Coord3D) Coord3D

Cross computes the cross product of c and c1.

func (Coord3D) Dist

func (c Coord3D) Dist(c1 Coord3D) float64

Dist computes the Euclidean distance to c1.

func (Coord3D) Div added in v0.2.2

func (c Coord3D) Div(c1 Coord3D) Coord3D

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

func (Coord3D) Dot

func (c Coord3D) Dot(c1 Coord3D) float64

Dot computes the dot product of c and c1.

func (Coord3D) Geo

func (c Coord3D) Geo() GeoCoord

Geo computes a normalized geo coordinate.

func (Coord3D) L1Dist added in v0.2.13

func (c Coord3D) L1Dist(c1 Coord3D) float64

L1Dist computes the L1 distance to c1.

func (Coord3D) Max

func (c Coord3D) Max(c1 Coord3D) Coord3D

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

func (Coord3D) MaxCoord added in v0.3.4

func (c Coord3D) MaxCoord() float64

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

func (Coord3D) Mid

func (c Coord3D) Mid(c1 Coord3D) Coord3D

Mid computes the midpoint between c and c1.

func (Coord3D) Min

func (c Coord3D) Min(c1 Coord3D) Coord3D

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

func (Coord3D) Mul

func (c Coord3D) Mul(c1 Coord3D) Coord3D

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

func (Coord3D) Norm

func (c Coord3D) Norm() float64

Norm computes the vector L2 norm.

func (Coord3D) NormSquared added in v0.3.0

func (c Coord3D) NormSquared() float64

NormSquared computes the squared vector L2 norm.

func (Coord3D) Normalize

func (c Coord3D) Normalize() Coord3D

Normalize gets a unit vector from c.

func (Coord3D) OrthoBasis

func (c Coord3D) OrthoBasis() (Coord3D, Coord3D)

OrthoBasis creates two unit vectors which are orthogonal to c and to each other.

If c is axis-aligned, the other vectors will be as well.

func (Coord3D) ProjectOut

func (c Coord3D) ProjectOut(c1 Coord3D) Coord3D

ProjectOut projects the c1 direction out of c.

func (Coord3D) Recip added in v0.2.9

func (c Coord3D) Recip() Coord3D

Recip gets a coordinate as 1 / c.

func (Coord3D) Reflect

func (c Coord3D) Reflect(c1 Coord3D) Coord3D

Reflect reflects c1 around c on the plane spanned by both vectors.

func (Coord3D) Scale

func (c Coord3D) Scale(s float64) Coord3D

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

func (Coord3D) SquaredDist added in v0.2.13

func (c Coord3D) SquaredDist(c1 Coord3D) float64

SquaredDist gets the squared Euclidean distance to c1.

func (Coord3D) Sub

func (c Coord3D) Sub(c1 Coord3D) Coord3D

Sub computes c - c1.

func (Coord3D) Sum

func (c Coord3D) Sum() float64

Sum sums the elements of c.

func (Coord3D) XY added in v0.2.4

func (c Coord3D) XY() Coord2D

XY gets (x, y) as a 2D coordinate.

func (Coord3D) XZ added in v0.2.4

func (c Coord3D) XZ() Coord2D

XZ gets (x, z) as a 2D coordinate.

func (Coord3D) YX added in v0.2.4

func (c Coord3D) YX() Coord2D

YX gets (y, x) as a 2D coordinate.

func (Coord3D) YZ added in v0.2.4

func (c Coord3D) YZ() Coord2D

YZ gets (y, z) as a 2D coordinate.

func (Coord3D) ZX added in v0.2.4

func (c Coord3D) ZX() Coord2D

ZX gets (z, x) as a 2D coordinate.

func (Coord3D) ZY added in v0.2.4

func (c Coord3D) ZY() Coord2D

ZY gets (z, y) as a 2D coordinate.

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 Coord3D 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 CircleBoundary added in v0.4.0

func CircleBoundary(m *Mesh) *CoordMap[model2d.Coord]

CircleBoundary computes a mapping of the boundary of a mesh m to the unit circle based on segment length.

The mesh must be properly oriented, and be manifold except along the boundary. The mesh's boundary must contain at least three segments which are all connected in a cycle. This means that the mesh must be mappable to a disc.

func Floater97 added in v0.4.0

func Floater97(m *Mesh, boundary *CoordMap[model2d.Coord],
	edgeWeights *EdgeMap[float64], solver numerical.LargeLinearSolver) *CoordMap[model2d.Coord]

Floater97 computes the 2D parameterization of a mesh.

The mesh m must be a simple-connected triangulated plane graph; in other words, it must be mappable to a disc. The boundary of this mesh must contain at least three points, as is the case for a single triangle.

The boundary argument maps each boundary vertex in m to a coordinate on the 2D plane. The boundary must be a convex polygon for the resulting parameterization to be valid.

The edgeWeights argument maps ordered pairs of connected vertices to a non-negative weight, where the first vertex in each pair is the "center" and the second vertex is a neighbor of that center. Boundary vertices are never used as centers, so these vertices never need to be the first vertex in an edge. For every center vertex, the weights of all of its connected edges must sum to 1 (sum_j of w[i][j] = 1).

The solver argument should be able to solve the sparse linear system produced by the algorithm efficiently. If nil is provided, Floater97DefaultSolver() is used.

The returned mapping assigns a 2D coordinate to every vertex in the original mesh, including the fixed boundary vertices.

This is based on the paper: "Parametrization and smooth approximation of surface triangulations" (Floater, 1996). https://www.cs.jhu.edu/~misha/Fall09/Floater97.pdf

func NewCoordMap added in v0.2.14

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

NewCoordMap creates an empty map.

func PNormBoundary added in v0.4.0

func PNormBoundary(m *Mesh, p float64) *CoordMap[model2d.Coord]

PNormBoundary is similar to CircleBoundary, except that the circle is defined under any p-norm, not just p=2.

func SquareBoundary added in v0.4.0

func SquareBoundary(m *Mesh) *CoordMap[model2d.Coord]

SquareBoundary computes a mapping of the boundary of a mesh m to the unit square. This may result in some triangles being mapped to three colinear points if the boundary contains two consecutive segments from one triangle.

See CircleBoundary for restrictions on the mesh m.

func StretchMinimizingParameterization added in v0.4.0

func StretchMinimizingParameterization(m *Mesh, boundary *CoordMap[model2d.Coord],
	edgeWeights *EdgeMap[float64], solver numerical.LargeLinearSolver, nIters int,
	eta float64, verbose bool) *CoordMap[model2d.Coord]

StretchMinimizingParameterization implements the stretch-minimizing mesh parameterization technique from "A fast and simple stretch-minimizing mesh parameterization" (Yoshizawa et al., 2004).

The usage is similar to Floater97, except that the edgeWeights mapping will be modified with the final weights used to solve for the final parameterization.

The nIters parameter determines the number of optimization steps to perform. If it is -1, then the the method terminates when the objective function increases.

The eta parameter determines the step size. If it is 1, then the standard solver is used; values between 0 and 1 slow convergence.

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

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

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

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

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 Coord3D) 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 Coord3D) ([]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 Coord3D, 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 Coord3D, 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 Coord3D) []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 Coord3D

	// 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 Coord3Ds.

A nil *CoordTree represents an empty tree.

func NewCoordTree added in v0.2.13

func NewCoordTree(points []Coord3D) *CoordTree

func (*CoordTree) Contains added in v0.2.13

func (c *CoordTree) Contains(p Coord3D) 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 Coord3D) 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 Coord3D) []Coord3D

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

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() []Coord3D

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 Coord3D, r float64) bool

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

type Cylinder

type Cylinder struct {
	P1     Coord3D
	P2     Coord3D
	Radius float64
}

A Cylinder is a cylindrical 3D primitive.

The cylinder is defined as all the positions within a radius distance from the line segment between P1 and P2.

func (*Cylinder) Contains

func (c *Cylinder) Contains(p Coord3D) bool

Contains checks if a point p is within the cylinder.

func (*Cylinder) FirstRayCollision

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

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

func (*Cylinder) Max

func (c *Cylinder) Max() Coord3D

Max gets the maximum point of the bounding box.

func (*Cylinder) MetaballDistBound added in v0.3.5

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

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

func (*Cylinder) MetaballField added in v0.3.5

func (c *Cylinder) MetaballField(coord Coord3D) float64

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

func (*Cylinder) Min

func (c *Cylinder) Min() Coord3D

Min gets the minimum point of the bounding box.

func (*Cylinder) NormalSDF added in v0.3.0

func (c *Cylinder) NormalSDF(coord Coord3D) (Coord3D, float64)

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

func (*Cylinder) PointSDF added in v0.3.0

func (c *Cylinder) PointSDF(coord Coord3D) (Coord3D, float64)

PointSDF gets the signed distance to the cylinder and the closest point on the surface.

func (*Cylinder) RayCollisions

func (c *Cylinder) 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 (*Cylinder) SDF

func (c *Cylinder) SDF(coord Coord3D) float64

SDF gets the signed distance to the cylinder.

func (*Cylinder) SphereCollision

func (c *Cylinder) SphereCollision(center Coord3D, r float64) bool

SphereCollision detects if a sphere collides with the cylinder.

type CylinderSolid

type CylinderSolid = Cylinder

type Decimator

type Decimator struct {
	// The minimum dihedral angle between two triangles
	// to consider an edge a "feature edge".
	//
	// If 0, DefaultDecimatorFeatureAngle is used.
	//
	// This is measured in radians.
	FeatureAngle float64

	// The maximum distance for a vertex to be from its
	// average plane for it to be deleted.
	PlaneDistance float64

	// The maximum distance for a vertex to be from the
	// line defining a feature edge.
	BoundaryDistance float64

	// If true, use PlaneDistance to evaluate all vertices
	// rather than consulting BoundaryDistance.
	NoEdgePreservation bool

	// If true, eliminate corner vertices.
	EliminateCorners bool

	// MinimumAspectRatio is the minimum aspect ratio for
	// triangulation splits.
	//
	// If 0, a default of DefaultDecimatorMinAspectRatio
	// is used.
	MinimumAspectRatio float64

	// SplitAttempts specifies the number of different
	// separator lines the triangulation routine may try
	// to achieve a minimum aspect ratio.
	//
	// By default, one attempt is made.
	SplitAttempts int

	// FilterFunc, if specified, can be used to prevent
	// certain vertices from being removed.
	// If FilterFunc returns false for a coordinate, it
	// may not be removed; otherwise it may be removed.
	FilterFunc func(c Coord3D) bool
}

Decimator implements a decimation algorithm to simplify triangle meshes.

This may only be applied to closed, manifold meshes. Thus, all edges are touching exactly two triangles, and there are no singularities or holes.

The algorithm is described in: "Decimation of Triangle Meshes" - William J. Schroeder, Jonathan A. Zarge and William E. Lorensen. https://webdocs.cs.ualberta.ca/~lin/ABProject/papers/4.pdf.

func (*Decimator) Decimate

func (d *Decimator) Decimate(m *Mesh) *Mesh

Decimate applies the decimation algorithm to m, producing a new mesh.

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(axis Coord3D, theta float64) DistTransform

Rotation creates a rotation transformation using an angle in radians around a unit vector direction.

type DualContouring added in v0.3.4

type DualContouring struct {
	// S specifies the Solid and is used to compute hermite
	// data on line segments.
	S SolidSurfaceEstimator

	// Delta specifies the grid size of the algorithm.
	Delta float64

	// NoJitter, if true, disables a small jitter applied to
	// coordinates. This jitter is enabled by default to
	// avoid common error cases when attempting to estimate
	// normals exactly on the edges of boxy objects.
	NoJitter bool

	// MaxGos, if specified, limits the number of Goroutines
	// for parallel processing. If 0, GOMAXPROCS is used.
	MaxGos int

	// BufferSize is a soft-limit on the number of cached
	// vertices that are stored in memory at once.
	// Defaults to DefaultDualContouringBufferSize.
	BufferSize int

	// Repair, if true, attempts to make non-manifold
	// meshes manifold. It is not guaranteed to work, even
	// if Clip is true.
	Repair bool

	// Clip, if true, clips vertices to their cubes.
	// The CubeMargin is used to add a small buffer to the
	// edge of the cubes.
	// When paired with Repair, this can helps meshes be
	// manifold, although it still will not guarantee
	// orientability.
	Clip bool

	// CubeMargin is space around the edges of each cube
	// that a vertex is not allowed to fall into. This can
	// prevent various types of non-manifold geometry.
	//
	// This size is relative to Delta.
	//
	// Defaults to DefaultDualContouringCubeMargin.
	// Only is used if CLip is true.
	CubeMargin float64

	// RepairEpsilon is a small value indicating the amount
	// to move vertices when fixing singularities.
	// It will be scaled relative to Delta to prevent large
	// changes relative to the grid size.
	//
	// Defaults to DefaultDualContouringRepairEpsilon.
	// Only is used if Repair is true.
	RepairEpsilon float64

	// SingularValueEpsilon is the smallest singular value
	// to allow for pseudoinverse calculations.
	//
	// Defaults to DefaultDualContouringSingularValueEpsilon.
	SingularValueEpsilon float64

	// L2Penalty is the regularization coefficient imposed
	// on solutions to the QEF.
	L2Penalty float64

	// TriangleMode controls how quads are triangulated.
	TriangleMode DualContouringTriangleMode
}

DualContouring is a configurable but simplified version of Dual Contouring, a technique for turning a field into a mesh.

By default, DualContouring does not attempt to produce manifold meshes, since doing so can result in ugly edge artifacts, reducing the primary benefit of DC. To attempt manifold meshes, set Clip and Repair to true.

func (*DualContouring) Mesh added in v0.3.4

func (d *DualContouring) Mesh() *Mesh

Mesh computes a mesh for the surface.

func (*DualContouring) MeshInterior added in v0.4.2

func (d *DualContouring) MeshInterior() (*Mesh, []Coord3D)

MeshInterior is like Mesh(), but also returns a slice of coordinates which are known to be inside the solid.

The interior points are found along edge-surface intersections during the dual contouring algorithm, and therefore correspond to faces of the mesh rather than vertices.

type DualContouringTriangleMode added in v0.3.4

type DualContouringTriangleMode int
const (
	DualContouringTriangleModeMaxMinArea DualContouringTriangleMode = iota
	DualContouringTriangleModeSharpest
	DualContouringTriangleModeFlattest
)

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]Coord3D 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 Floater97InvChordLengthWeights added in v0.4.0

func Floater97InvChordLengthWeights(m *Mesh, r float64) *EdgeMap[float64]

Floater97InvChordLengthWeights computes the weighting scheme based on 1/(distance^r), where r is some exponent applied to the distance between points along each edge.

func Floater97ShapePreservingWeights added in v0.4.0

func Floater97ShapePreservingWeights(m *Mesh) *EdgeMap[float64]

Floater97ShapePreservingWeights computes the shape-preserving weights described in Floater (1997).

The mesh must be properly connected, consistently oriented, and have exactly one boundary loop. Otherwise, this may panic() or return invalid results.

func Floater97UniformWeights added in v0.4.0

func Floater97UniformWeights(m *Mesh) *EdgeMap[float64]

Floater97UniformWeights computes the uniform weighting scheme for the edgeWeights argument of Floater97(). This is the simplest possible weighting scheme, but may result in distortion.

As proved in Floater (1997), this weighting attempts to minimize the sum of squares of edge lengths in the resulting parameterization.

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

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]Coord3D) 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]Coord3D) (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]Coord3D, 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]Coord3D, 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]Coord3D) 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]Coord3D 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]Coord3D, 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]Coord3D)

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]Coord3D) 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]Coord3D) (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]Coord3D, 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]Coord3D, 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]Coord3D) 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]Coord3D 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]Coord3D, 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]Coord3D)

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]Coord3D) 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]Coord3D) ([]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]Coord3D, 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]Coord3D, 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]Coord3D) []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 Coord3D) (*Triangle, Coord3D, float64)
}

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

func DualContourSDF added in v0.3.4

func DualContourSDF(s Solid, delta float64) FaceSDF

DualContourSDF constructs an SDF of a Solid using dual contouring. This approach accounts for the fact that dual contouring produces self-intersecting meshes and will therefore not obey the even-odd rule.

func GroupedTrianglesToSDF

func GroupedTrianglesToSDF(faces []*Triangle) FaceSDF

GroupedTrianglesToSDF creates a FaceSDF from a slice of triangles. If the triangles are not grouped by GroupTriangles(), the resulting PointSDF is inefficient.

func MeshToSDF

func MeshToSDF(m *Mesh) FaceSDF

MeshToSDF turns a mesh into a FaceSDF.

type GeoCoord

type GeoCoord struct {
	Lat float64
	Lon float64
}

A GeoCoord specifies a location on a sphere with a unit radius.

The latitude is an angle from -math.Pi/2 to math.pi/2 representing the North-South direction. The longitude is an angle from -math.Pi to math.Pi representing the West-East direction.

func (GeoCoord) Coord3D

func (g GeoCoord) Coord3D() Coord3D

Coord3D converts g to Euclidean coordinates on a unit sphere centered at the origin.

func (GeoCoord) Distance

func (g GeoCoord) Distance(g1 GeoCoord) float64

Distance gets the Euclidean distance between g and g1 when traveling on the surface of the sphere.

func (GeoCoord) Normalize

func (g GeoCoord) Normalize() GeoCoord

Normalize brings the latitude and longitude into the standard range while (approximately) preserving the absolute position.

type InterpNormalTriangle added in v0.3.4

type InterpNormalTriangle struct {
	Triangle

	VertexNormals [3]Coord3D
}

A InterpNormalTriangle is a Triangle which returns smoothed normals for ray collisions.

func (*InterpNormalTriangle) FirstRayCollision added in v0.3.4

func (i *InterpNormalTriangle) FirstRayCollision(r *Ray) (RayCollision, bool)

func (*InterpNormalTriangle) InterpNormal added in v0.3.4

func (i *InterpNormalTriangle) InterpNormal(barycentric [3]float64) Coord3D

InterpNormal returns the interpolated normal for the barycentric coordinates within the triangle.

func (*InterpNormalTriangle) RayCollisions added in v0.3.4

func (i *InterpNormalTriangle) RayCollisions(r *Ray, f func(RayCollision)) int

type IntersectedSolid

type IntersectedSolid []Solid

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

func (IntersectedSolid) Contains

func (i IntersectedSolid) Contains(c Coord3D) bool

func (IntersectedSolid) Max

func (i IntersectedSolid) Max() Coord3D

func (IntersectedSolid) Min

func (i IntersectedSolid) Min() Coord3D

type JoinedCollider

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

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

func NewJoinedCollider

func NewJoinedCollider(other []Collider) *JoinedCollider

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

func (*JoinedCollider) FirstRayCollision

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

func (*JoinedCollider) Max

func (j *JoinedCollider) Max() Coord3D

func (*JoinedCollider) Min

func (j *JoinedCollider) Min() Coord3D

func (*JoinedCollider) RayCollisions

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

func (*JoinedCollider) SphereCollision

func (j *JoinedCollider) SphereCollision(center Coord3D, r float64) bool

type JoinedSolid

type JoinedSolid []Solid

A JoinedSolid is a Solid composed of other solids.

func (JoinedSolid) Contains

func (j JoinedSolid) Contains(c Coord3D) bool

func (JoinedSolid) Max

func (j JoinedSolid) Max() Coord3D

func (JoinedSolid) Min

func (j JoinedSolid) Min() Coord3D

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

type JoinedTransform []Transform

A JoinedTransform composes transformations from left to right.

func (JoinedTransform) Apply

func (j JoinedTransform) Apply(c Coord3D) Coord3D

func (JoinedTransform) ApplyBounds

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

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

func (j JoinedTransform) Inverse() Transform

type LinearConstraint

type LinearConstraint struct {
	Normal Coord3D
	Max    float64
}

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

func (*LinearConstraint) Contains

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

Contains checks if the half-space contains c.

type Matrix2

type Matrix2 = model2d.Matrix2

type Matrix3

type Matrix3 [9]float64

Matrix3 is a 3x3 matrix, stored in row-major order.

func NewMatrix3Columns

func NewMatrix3Columns(c1, c2, c3 Coord3D) *Matrix3

NewMatrix3Columns creates a Matrix3 with the given coordinates as column entries.

func NewMatrix3Rotation

func NewMatrix3Rotation(axis Coord3D, angle float64) *Matrix3

NewMatrix3Rotation creates a 3D rotation matrix. Points are rotated around the given vector in a right-handed direction.

The axis is assumed to be normalized. The angle is measured in radians.

func (*Matrix3) Add added in v0.2.2

func (m *Matrix3) Add(m1 *Matrix3) *Matrix3

Add computes m+m1 and returns the sum.

func (*Matrix3) Det

func (m *Matrix3) Det() float64

Det computes the determinant of the matrix.

func (*Matrix3) Eigenvalues added in v0.2.2

func (m *Matrix3) Eigenvalues() [3]complex128

Eigenvalues computes the eigenvalues of the matrix.

There may be repeated eigenvalues, but for numerical reasons three are always returned.

func (*Matrix3) Inverse

func (m *Matrix3) Inverse() *Matrix3

Inverse computes the inverse matrix.

func (*Matrix3) InvertInPlace

func (m *Matrix3) InvertInPlace()

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

func (*Matrix3) InvertInPlaceDet added in v0.2.14

func (m *Matrix3) InvertInPlaceDet(det float64)

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

func (*Matrix3) Mul

func (m *Matrix3) Mul(m1 *Matrix3) *Matrix3

Mul computes m*m1 and returns the product.

func (*Matrix3) MulColumn

func (m *Matrix3) MulColumn(c Coord3D) Coord3D

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

func (*Matrix3) MulColumnInv added in v0.2.14

func (m *Matrix3) MulColumnInv(c Coord3D, det float64) Coord3D

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

func (*Matrix3) SVD added in v0.2.2

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

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 (*Matrix3) Scale

func (m *Matrix3) Scale(s float64)

Scale scales m by a factor s.

func (*Matrix3) Transpose

func (m *Matrix3) Transpose() *Matrix3

Transpose computes the matrix transpose.

type Matrix3Transform

type Matrix3Transform struct {
	Matrix *Matrix3
}

Matrix3Transform is a Transform that applies a matrix to coordinates.

func (*Matrix3Transform) Apply

func (m *Matrix3Transform) Apply(c Coord3D) Coord3D

func (*Matrix3Transform) ApplyBounds

func (m *Matrix3Transform) ApplyBounds(min, max Coord3D) (Coord3D, Coord3D)

func (*Matrix3Transform) Inverse

func (m *Matrix3Transform) Inverse() Transform

type Mesh

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

A Mesh is a collection of triangles.

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

Triangles in a mesh are "connected" when they contain exactly identical points. Thus, small rounding errors can cause triangles 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 DecimateSimple

func DecimateSimple(m *Mesh, epsilon float64) *Mesh

DecimateSimple decimates a mesh using a specified distance epsilon combined with default parameters.

For more fine-grained control, use Decimator.

func DualContour added in v0.3.4

func DualContour(s Solid, delta float64, repair, clip bool) *Mesh

DualContour is a shortcut for creating a DualContouring instance and calling Mesh() on it.

func LoopSubdivision

func LoopSubdivision(m *Mesh, iters int) *Mesh

LoopSubdivision subdivides the mesh using the Loop subdivision rule, creating a smoother surface with more triangles.

The mesh is subdivided iters times.

The mesh must not have singular edges.

func MarchingCubes

func MarchingCubes(s Solid, delta float64) *Mesh

MarchingCubes turns a Solid into a surface mesh using a corrected marching cubes algorithm.

func MarchingCubesC2F added in v0.3.0

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

MarchingCubesC2F 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 MarchingCubesConj added in v0.2.9

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

MarchingCubesConj is like MarchingCubesSearch, 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 MarchingCubesFilter added in v0.3.0

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

MarchingCubesFilter is like MarchingCubes, but does not scan rectangular volumes for which f returns false. This can be much more efficient than MarchingCubes 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 triangles to be missed.

func MarchingCubesSearch

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

MarchingCubesSearch is like MarchingCubes, but applies an additional search step to move the vertices along the edges of each cube.

The tightness of the triangulation will double for every iteration.

func MarchingCubesSearchFilter added in v0.3.0

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

MarchingCubesSearchFilter combines MarchingCubesSearch with MarchingCubesFilter.

func MeshToPlaneGraphs added in v0.4.0

func MeshToPlaneGraphs(m *Mesh) []*Mesh

MeshToPlaneGraphs splits a mesh m into one or more sub-meshes which are simply-connected triangulated plane graphs. These sub-meshes are suitable for Floater97().

The mesh m must either be manifold, or be a subset of a manifold mesh. For example, calling MeshToPlaneGraphs() on a result of MeshToPlaneGraphs() should be an identity operation.

func MeshToPlaneGraphsLimited added in v0.4.0

func MeshToPlaneGraphsLimited(m *Mesh, maxSize int, maxArea float64) []*Mesh

MeshToPlaneGraphsLimited is like MeshToPlaneGraphs, but limits the number of triangles per sub-mesh or the area of the triangles, or both.

If maxSize > 0, it is the maximum triangle count. If maxArea > 0, it is a soft limit on the maximum area. It may be exceeded if the first triangle has greater than the maximum area.

func NewMesh

func NewMesh() *Mesh

NewMesh creates an empty mesh.

func NewMeshCone added in v0.2.14

func NewMeshCone(tip, base Coord3D, radius float64, numStops int) *Mesh

NewMeshCone creates a 3D mesh representing a cone.

This is done by discretizing the base circle into numStops segments and attaching triangles to the tip.

func NewMeshCylinder added in v0.2.14

func NewMeshCylinder(p1, p2 Coord3D, radius float64, numStops int) *Mesh

NewMeshCylinder creates a 3D mesh representing a cylinder.

This is done by discretizing the side circle into numStops segments and attaching triangles between them.

func NewMeshIcosahedron added in v0.2.14

func NewMeshIcosahedron() *Mesh

NewMeshIcosahedron creates a regular icosahedron.

The resulting icosahedron is oriented such that two points will be created at (0, -1, 0) and (0, 1, 0), and exactly two other points will have an x value of zero.

func NewMeshIcosphere added in v0.2.14

func NewMeshIcosphere(center Coord3D, radius float64, n int) *Mesh

NewMeshIcosphere approximates a sphere using a geodesic polyhedron, which is more symmetrical than the spheres created with NewMeshPolar().

The resulting model has 20*n^2 triangles.

func NewMeshPolar

func NewMeshPolar(radius func(g GeoCoord) float64, stops int) *Mesh

NewMeshPolar creates a mesh with a 3D polar function.

If radius is nil, a radius of 1 is used.

func NewMeshRect

func NewMeshRect(min, max Coord3D) *Mesh

NewMeshRect creates a new mesh around the rectangular bounds.

func NewMeshTorus added in v0.2.14

func NewMeshTorus(center, axis Coord3D, innerRadius, outerRadius float64, innerStops,
	outerStops int) *Mesh

NewMeshTorus creates a 3D mesh representing a torus.

This is done by discretizing the local ring into innerStops segments, and the larger ring into outerStops segments.

The innerRadius must be smaller than the outerRadius, since doing otherwise would cause self-intersections.

func NewMeshTriangles

func NewMeshTriangles(faces []*Triangle) *Mesh

NewMeshTriangles creates a mesh with the given collection of triangles.

func ProfileMesh added in v0.2.11

func ProfileMesh(m2d *model2d.Mesh, minZ, maxZ float64) *Mesh

ProfileMesh creates a 3D mesh from a 2D mesh by using the 2D mesh as a face surface and extending it along the Z axis.

The 2D mesh must be manifold, closed, and oriented.

func SplitPlaneGraph added in v0.4.0

func SplitPlaneGraph(m *Mesh, decision func(t *Triangle) float64) []*Mesh

SplitPlaneGraph splits a plane graph into at least two sub-graphs such that one subgraph mostly has higher decision values than the other subgraph(s). It attempts to split off roughly half of the total area in the first subgraph.

If the decision function is nil, a distance-based heuristic is used to search triangles in order of distance from the initial triangle.

If the mesh is minimal and cannot be split, one item is returned instead of two or more.

func SubdivideEdges added in v0.2.14

func SubdivideEdges(m *Mesh, n int) *Mesh

SubdivideEdges sub-divides each edge into n sub-edges and fills each original triangle with n^2 coplanar triangles.

func (*Mesh) Add

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

Add adds the triangle f to the mesh.

func (*Mesh) AddMesh

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

AddMesh adds all the triangles from m1 to m.

func (*Mesh) AddQuad added in v0.2.9

func (m *Mesh) AddQuad(p1, p2, p3, p4 Coord3D) [2]*Triangle

AddQuad adds a quadrilateral to the mesh.

For correct normals, the vertices should be in counter- clockwise order as seen from the outside of the mesh.

func (*Mesh) AllVertexNeighbors added in v0.4.0

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

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

func (*Mesh) Area added in v0.2.8

func (m *Mesh) Area() float64

Area computes the total surface area of the mesh.

func (*Mesh) Blur

func (m *Mesh) Blur(rates ...float64) *Mesh

Blur creates a new mesh by moving every vertex closer to its connected vertices.

The rate argument specifies how much the vertex should be moved, 0 being no movement and 1 being the most. If multiple rates are passed, then multiple iterations of the algorithm are performed in succession. If a rate of -1 is passed, then all of neighbors are averaged together with each point, and the resulting average is used.

func (*Mesh) BlurFiltered

func (m *Mesh) BlurFiltered(f func(c1, c2 Coord3D) bool, rates ...float64) *Mesh

BlurFiltered is like Blur, but vertices are only considered neighbors if f returns true for their initial coordinates.

Once vertices are considered neighbors, they will be treated as such for every blur iteration, even if the coordinates change in such a way that f would no longer consider them neighbors.

If f is nil, then this is equivalent to Blur().

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 *Triangle) 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 triangles are the same exact pointers.

func (*Mesh) DeepCopy added in v0.2.23

func (m *Mesh) DeepCopy() *Mesh

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

func (*Mesh) EliminateCoplanar

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

EliminateCoplanar eliminates vertices whose neigboring triangles are all co-planar.

The epsilon argument controls how close two normals must be for the triangles to be considered coplanar. A good value for very precise results is 1e-8.

func (*Mesh) EliminateCoplanarFiltered added in v0.3.3

func (m *Mesh) EliminateCoplanarFiltered(epsilon float64, f func(Coord3D) bool) *Mesh

EliminateCoplanarFiltered is like EliminateCoplanar, but vertices are only removed if f(vertex) is true.

func (*Mesh) EliminateEdges

func (m *Mesh) EliminateEdges(f func(tmp *Mesh, segment Segment) bool) *Mesh

EliminateEdges creates a new mesh by iteratively removing edges according to the function f.

The f function takes the current new mesh and a line segment, and returns true if the segment should be removed.

func (*Mesh) EncodeMaterialOBJ

func (m *Mesh) EncodeMaterialOBJ(colorFunc func(t *Triangle) [3]float64) []byte

EncodeMaterialOBJ encodes the mesh as a zip file with per-triangle material.

func (*Mesh) EncodePLY

func (m *Mesh) EncodePLY(colorFunc func(c Coord3D) [3]uint8) []byte

EncodePLY encodes the mesh as a PLY file with color.

func (*Mesh) EncodeSTL

func (m *Mesh) EncodeSTL() []byte

EncodeSTL encodes the mesh as STL data.

func (*Mesh) FaceOrientations added in v0.3.4

func (m *Mesh) FaceOrientations() []map[*Triangle]bool

FaceOrientations returns, for each group of connected faces, the relative orientation of every face. For meshes with correct normals, all faces of each group should have the same orientation. The values can be thought of as flags indicating whether or not each triangle should be flipped.

This method should only be called on orientable, manifold meshes. If not, it may panic(). One intended use case is to re-orient faces to repair surfaces with incorrect normals.

func (*Mesh) Find

func (m *Mesh) Find(ps ...Coord3D) []*Triangle

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

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

func (*Mesh) FlattenBase

func (m *Mesh) FlattenBase(maxAngle float64) *Mesh

FlattenBase flattens out the bases of objects for printing on an FDM 3D printer. It is intended to be used for meshes based on flat-based solids, where the base's edges got rounded by smoothing.

The maxAngle argument specifies the maximum angle (in radians) to flatten. If left at zero, 45 degrees is used since this is the recommended angle on many FDM 3D printers.

In some meshes, this may cause triangles to overlap on the base of the mesh. Thus, it is only intended to be used when the base is clearly defined, and all of the triangles touching it are not above any other triangles (along the Z-axis).

func (*Mesh) FlipDelaunay added in v0.2.2

func (m *Mesh) FlipDelaunay() *Mesh

FlipDelaunay "flips" edges in triangle pairs until the mesh is Delaunay.

This can be used to prepare a mesh for cotangent weights, either for smoothing, deformation, or some other mesh operation.

See: https://arxiv.org/abs/math/0503219.

func (*Mesh) InconsistentEdges added in v0.3.4

func (m *Mesh) InconsistentEdges() [][2]Coord3D

InconsistentEdges finds edges which are ordered the same way for at least two triangles, indicating an inconsistent mesh orientation.

For properly connected (i.e. manifold) meshes, this can determine if the surface normals are consistent.

func (*Mesh) InvertNormals added in v0.3.4

func (m *Mesh) InvertNormals() *Mesh

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

func (*Mesh) Iterate

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

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

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

func (*Mesh) IterateSorted

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

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

func (*Mesh) IterateVertices added in v0.1.1

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

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

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

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

func (*Mesh) Max

func (m *Mesh) Max() Coord3D

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

func (*Mesh) Min

func (m *Mesh) Min() Coord3D

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

func (*Mesh) NeedsRepair

func (m *Mesh) NeedsRepair() bool

NeedsRepair checks if every edge touches exactly two triangles. If not, NeedsRepair returns true.

func (*Mesh) Neighbors

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

Neighbors gets all the triangles with a side touching a given triangle f.

The triangle f itself is not included in the results.

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

func (*Mesh) NumTriangles added in v0.4.0

func (m *Mesh) NumTriangles() int

NumTriangles returns the number of triangles in m.

func (*Mesh) Orientable added in v0.3.4

func (m *Mesh) Orientable() bool

Orientable checks if the mesh is orientable. This will return true for closed, manifold meshes, and false for shapes like Mobius strips.

func (*Mesh) Remove

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

Remove removes the triangle 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

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

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

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

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

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

func (*Mesh) RepairNormalsMajority added in v0.3.4

func (m *Mesh) RepairNormalsMajority() (*Mesh, int)

RepairNormalsMajority leverages FaceOrientations to flip the fewest faces so that every connected group of faces is oriented consistently.

Like FaceOrientations(), this should only be called for orientable, manifold meshes.

Note that this makes no guarantees about correctness, and will give exactly opposite results when more than half of all faces are flipped incorrectly. For global correctness, see RepairNormals().

func (*Mesh) Rotate added in v0.2.15

func (m *Mesh) Rotate(axis Coord3D, angle float64) *Mesh

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

func (*Mesh) Save3MF added in v0.4.4

func (m *Mesh) Save3MF(path string, units fileformats.ThreeMFUnit) error

Save3MF writes the mesh to a 3MF file.

func (*Mesh) SaveGroupedSTL

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

SaveGroupedSTL writes the mesh to an STL file with the triangles grouped in such a way that the file can be compressed efficiently.

func (*Mesh) SaveMaterialOBJ added in v0.2.13

func (m *Mesh) SaveMaterialOBJ(path string, colorFunc func(t *Triangle) [3]float64) error

SaveMaterialOBJ saves the mesh to a zip file with a per-triangle material.

func (*Mesh) SaveQuantizedMaterialOBJ added in v0.3.3

func (m *Mesh) SaveQuantizedMaterialOBJ(path string, textureSize int,
	colorFunc func(t *Triangle) [3]float64) error

SaveQuantizedMaterialOBJ is like SaveMaterialOBJ, but a square texture is used to store face colors.

func (*Mesh) SaveVertexColorOBJ added in v0.4.3

func (m *Mesh) SaveVertexColorOBJ(path string, colorFunc func(Coord3D) [3]float64) error

SaveVertexColorOBJ saves the mesh to an OBJ file with a color per vertex.

func (*Mesh) Scale

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

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

func (*Mesh) SelfIntersections

func (m *Mesh) SelfIntersections() int

SelfIntersections counts the number of times the mesh intersects itself. In an ideal mesh, this would be 0.

func (*Mesh) SingularVertices

func (m *Mesh) SingularVertices() []Coord3D

SingularVertices gets the points at which the mesh is squeezed to zero volume. In other words, it gets the points where two pieces of volume are barely touching by a single point.

func (*Mesh) SmoothAreas

func (m *Mesh) SmoothAreas(stepSize float64, iters int) *Mesh

SmoothAreas uses gradient descent to iteratively smooth out the surface by moving every vertex in the direction that minimizes the area of its adjacent triangles.

The stepSize argument specifies how much the vertices are moved at each iteration. Good values depend on the mesh, but a good start is on the order of 0.1.

The iters argument specifies how many gradient steps are taken.

This algorithm can produce very smooth objects, but it is much less efficient than Blur(). Consider Blur() when perfect smoothness is not required.

This method is a simpler version of the MeshSmoother object, which provides a more controlled smoothing API.

func (*Mesh) Transform

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 Coord3D) *Mesh

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

func (*Mesh) TriangleSlice

func (m *Mesh) TriangleSlice() []*Triangle

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

func (*Mesh) VertexNormals added in v0.3.4

func (m *Mesh) VertexNormals() *CoordMap[Coord3D]

VertexNormals approximates normals for every vertex on the mesh. The normals are returned in a mapping from vertex coordinates to normals (always of unit length).

If the mesh has self-intersections, degenerate geometry, or is non-manifold, the resulting normals may include inf or NaN values due to divisions by zero.

This employs the "Mean Weighted by Angle" approach as described in "A Comparison of Algorithms for Vertex Normal Computations" http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.99.2846&rep=rep1&type=pdf.

func (*Mesh) VertexSlice added in v0.1.1

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

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.

func (*Mesh) Volume added in v0.2.8

func (m *Mesh) Volume() float64

Volume measures the volume of the mesh.

This assumes that the mesh is manifold and the normals are consistent.

type MeshHierarchy added in v0.2.13

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 surface, 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.13

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.13

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

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

func (*MeshHierarchy) FullMesh added in v0.2.13

func (m *MeshHierarchy) FullMesh() *Mesh

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

func (*MeshHierarchy) MapCoords added in v0.2.13

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

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

func (*MeshHierarchy) Max added in v0.2.13

func (m *MeshHierarchy) Max() Coord3D

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

func (*MeshHierarchy) Min added in v0.2.13

func (m *MeshHierarchy) Min() Coord3D

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

type MeshSmoother

type MeshSmoother struct {
	// StepSize controls how fast the mesh is updated.
	// A good value will depend on the mesh, but a good
	// default is 0.1.
	StepSize float64

	// Iterations controls the number of gradient steps
	// to take in order to smooth a mesh.
	// More values result in smoother meshes but take
	// more time.
	Iterations int

	// ConstraintDistance is the minimum distance after
	// which the origin constraint will take effect.
	// This allows points to move freely a little bit
	// without being constrained at all, which is good
	// for sharp meshes created from voxel grids.
	//
	// If this is 0, then points will always be pulled
	// towards their origin by a factor of
	// ConstraintWeight.
	ConstraintDistance float64

	// ConstraintWeight is the weight of the distance
	// constraint term, ||x-x0||^2.
	// If this is 0, no constraint is applied.
	ConstraintWeight float64

	// ConstraintFunc, if specified, is a totally custom
	// gradient term that constrains points to their
	// original positions.
	//
	// The return value of the function is added to the
	// gradient at every step.
	//
	// This is independent of ConstraintDistance and
	// ConstraintWeight, which can be used simultaneously.
	ConstraintFunc func(origin, newCoord Coord3D) Coord3D

	// HardConstraintFunc, if non-nil, is a function that
	// returns true for all of the initial points that
	// should not be modified at all.
	HardConstraintFunc func(origin Coord3D) bool
}

A MeshSmoother uses gradient descent to smooth out the surface of a mesh by minimizing surface area.

The smoother can be constrained to discourage vertices from moving far from their origins, making the surface locally smooth without greatly modifying the volume.

func (*MeshSmoother) Smooth

func (m *MeshSmoother) Smooth(mesh *Mesh) *Mesh

Smooth applies gradient descent to smooth the mesh.

func (*MeshSmoother) SmoothMapping added in v0.4.2

func (m *MeshSmoother) SmoothMapping(mesh *Mesh) *CoordMap[Coord3D]

SmoothMapping returns a mapping from old vertices to smoothed ones.

type MeshUVMap added in v0.4.0

type MeshUVMap map[*Triangle][3]model2d.Coord

A MeshUVMap is a mapping between triangles in a 3D mesh and triangles on a 2D surface.

The order of 3D triangles corresponds to the order of 2D triangles (e.g. tri3d[i] maps to tri2d[i], 0 <= i < 3).

func BuildAutomaticUVMap added in v0.4.0

func BuildAutomaticUVMap(m *Mesh, resolution int, verbose bool) MeshUVMap

BuildAutomaticUVMap creates a MeshUVMap for an entire mesh which fits in the unit square (0, 0) to (1, 1) and should work best at the given resolution.

The resolution specifies the side-length of the targeted texture image. It must be a power of two. This is used to determine spacing in the final layout.

The mesh itself should be manifold, but needn't have any special kind of topology.

This is meant for quick applications that don't need a lot of control over the resulting parameterization. The underlying algorithm and exact results are subject to change.

func JoinMeshUVMaps added in v0.4.0

func JoinMeshUVMaps(ms ...MeshUVMap) MeshUVMap

JoinMeshUVMaps adds all keys and values from all UV maps to a resulting mapping.

This will not modify the coordinates in the mappings.

func NewMeshUVMapForCoords added in v0.4.0

func NewMeshUVMapForCoords(mesh *Mesh, mapping *CoordMap[model2d.Coord]) MeshUVMap

NewMeshUVMapForCoords maps triangles in the mesh to 2D triangles using direct per-point lookups.

The mapping must have an entry for every vertex in the mesh.

func PackMeshUVMaps added in v0.4.0

func PackMeshUVMaps(min, max model2d.Coord, border float64,
	params []MeshUVMap) MeshUVMap

PackMeshUVMaps rescales and combines all of the provided UV maps into a single rectangle given by the bounds min and max.

The border argument is an amount of space to put around the edges of each separate UV map in the texture to avoid interpolation from mixing them.

func (MeshUVMap) Area3D added in v0.4.0

func (m MeshUVMap) Area3D() float64

Area3D gets the total area of all the 3D triangles.

func (MeshUVMap) Bounds2D added in v0.4.0

func (m MeshUVMap) Bounds2D() (min, max model2d.Coord)

Bounds2D gets the bounding box of the 2D triangles.

func (MeshUVMap) MapFn added in v0.4.0

func (m MeshUVMap) MapFn() func(c model2d.Coord) (Coord3D, *Triangle)

MapFn creates a function that maps 2D coordinates to 3D using the UV map.

The resulting function also returns the 3D triangle corresponding to the mapped point.

Resulting 3D points will always be produced, even if the 2D point lands outside the 2D triangulation. In this case, the nearest 2D point on the triangulation is used.

func (MeshUVMap) ToBounds added in v0.4.0

func (m MeshUVMap) ToBounds(min, max model2d.Coord) MeshUVMap

ToBounds creates a new UV map where the 2D bounding box is rescaled and translated to a new min and max.

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 Coord3D) 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, axis Coord3D, angle float64) Metaball

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

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 Coord3D) 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 Coord3D) 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 {
	TriangleCollider
	SegmentCollider
	RectCollider
}

func BVHToCollider

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

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

func GroupedTrianglesToCollider

func GroupedTrianglesToCollider(tris []*Triangle) MultiCollider

GroupedTrianglesToCollider converts a mesh of triangles into a MultiCollider.

The triangles should be sorted by GroupTriangles. Otherwise, the resulting Collider may not be efficient.

func MeshToCollider

func MeshToCollider(m *Mesh) MultiCollider

MeshToCollider creates an efficient MultiCollider out of a mesh.

func MeshToInterpNormalCollider added in v0.3.4

func MeshToInterpNormalCollider(m *Mesh) MultiCollider

MeshToInterpNormalCollider creates an efficient MultiCollider for the mesh that computes collision normals using Phong shading rather than flat shading.

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 Coord3D) (Coord3D, 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 Coord3D) (Coord3D, 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 Coord3D, f func(Coord3D) (Coord3D, 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.

func ProfilePointSDF added in v0.2.5

func ProfilePointSDF(sdf2d model2d.PointSDF, minZ, maxZ float64) PointSDF

ProfilePointSDF turns a 2D PointSDF into a 3D PointSDF by elongating the 2D SDF along the Z axis.

type Ray

type Ray struct {
	Origin    Coord3D
	Direction Coord3D
}

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 surface at the
	// point of collision.
	Normal Coord3D

	// Extra contains additional, implementation-specific
	// information about the collision.
	//
	// For an example, see TriangleCollision.
	Extra any
}

RayCollision is a point where a ray intersects a surface.

type Rect

type Rect struct {
	MinVal Coord3D
	MaxVal Coord3D
}

A Rect is a 3D 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 Coord3D) *Rect

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

func (*Rect) Contains

func (r *Rect) Contains(c Coord3D) 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

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

FirstRayCollision gets the first ray collision with the rectangular surface.

func (*Rect) Max

func (r *Rect) Max() Coord3D

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 Coord3D) float64

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

func (*Rect) Min

func (r *Rect) Min() Coord3D

Min yields r.MinVal.

func (*Rect) NormalSDF added in v0.3.0

func (r *Rect) NormalSDF(c Coord3D) (Coord3D, 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 Coord3D) (Coord3D, float64)

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

func (*Rect) RayCollisions

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

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

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

func (*Rect) SphereCollision

func (r *Rect) SphereCollision(c Coord3D, radius float64) bool

SphereCollision checks if a solid sphere touches any part of the rectangular surface.

type RectCollider added in v0.2.6

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

A RectCollider is a surface which can detect if a 3D axis-aligned rectangular volume collides with the surface.

type RectSolid

type RectSolid = Rect

Backwards compatibility type aliases.

type SDF

type SDF interface {
	Bounder

	SDF(c Coord3D) 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

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 Coord3D, f func(Coord3D) 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 ProfileSDF added in v0.2.5

func ProfileSDF(sdf2d model2d.SDF, minZ, maxZ float64) SDF

ProfileSDF turns a 2D SDF into a 3D SDF by elongating the 2D SDF along the Z axis.

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

func (*Scale) ApplyBounds added in v0.2.11

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

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]Coord3D

A Segment is a line segment in a canonical ordering, such that segments can be compared via the == operator even if they were created with their points in the opposite order.

func NewSegment

func NewSegment(p1, p2 Coord3D) Segment

NewSegment creates a segment with the canonical ordering.

func (Segment) Closest added in v0.2.3

func (s Segment) Closest(c Coord3D) Coord3D

Closest gets the point on the segment closest to c.

func (*Segment) ClosestL1 added in v0.2.13

func (s *Segment) ClosestL1(c Coord3D) Coord3D

ClosestL1 gets the point on the segment closest to c as measured in L1 distance.

func (Segment) Dist

func (s Segment) Dist(c Coord3D) float64

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

func (*Segment) L1Dist added in v0.2.13

func (s *Segment) L1Dist(c Coord3D) float64

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

func (Segment) Length added in v0.3.4

func (s Segment) Length() float64

Length gets the length of the segment.

func (Segment) Mid

func (s Segment) Mid() Coord3D

Mid gets the midpoint of the segment.

func (Segment) Other added in v0.2.11

func (s Segment) Other(t *Triangle) Coord3D

Other gets a point in t that is not in s, provided that t is not degenerate.

func (Segment) RectCollision added in v0.2.6

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

RectCollision checks if the segment intersects a Rect.

type SegmentCollider added in v0.2.6

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

A SegmentCollider is a surface which can detect if a line segment collides with the surface.

type Solid

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

	Contains(p Coord3D) 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 CacheSolidBounds

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 Coord3D, f func(Coord3D) 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 Coord3D) 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 Coord3D, f func(Coord3D) 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 ProfileSolid added in v0.2.5

func ProfileSolid(solid2d model2d.Solid, minZ, maxZ float64) Solid

ProfileSolid turns a 2D solid into a 3D solid by elongating the 2D solid along the Z axis.

func RevolveSolid added in v0.2.13

func RevolveSolid(solid model2d.Solid, axis Coord3D) Solid

RevolveSolid rotates a 2D solid around an axis to create a 3D solid. The y-axis of the 2D solid is extended along the axis of revolution, while the x-axis is used as a radius.

The 2D solid should either be symmetrical around the axis, or be empty on one side of the axis. Either way, the union of both reflections of the 2D solid is used.

func RotateSolid added in v0.2.15

func RotateSolid(solid Solid, axis Coord3D, angle float64) Solid

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

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 added in v0.2.11

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.2

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 StackSolids added in v0.1.1

func StackSolids(s ...Solid) Solid

StackSolids joins solids together and moves each solid after the first so that the lowest Z value of its bounding box collides with the highest Z value of the previous solid's bounding box. In other words, the solids are stacked on top of each other along the Z axis.

func TransformSolid

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 Coord3D) 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 Coord3D) 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 SolidCollider

type SolidCollider struct {
	Solid Solid

	// Epsilon is a distance considered "small" in the
	// context of the solid.
	// It is used to walk along rays to find
	// intersections.
	Epsilon float64

	// BisectCount, if non-zero, specifies the number of
	// bisections to use to narrow down collisions.
	// If it is zero, a reasonable default is used.
	BisectCount int

	// NormalSamples, if non-zero, specifies how many
	// samples to use to approximate normals.
	// If not specified, a default is used.
	NormalSamples int

	// NormalBisectEpsilon, if non-zero, specifies a small
	// distance to use in a bisection-based method to
	// compute approximate normals.
	//
	// If set, this should typically be smaller than
	// Epsilon, since smaller values don't affect runtime
	// but do improve accuracy (up to a point).
	//
	// If this is 0, bisection is not used to approximate
	// normals, but rather a more noisy but less brittle
	// algorithm.
	NormalBisectEpsilon float64
}

A SolidCollider approximates the behavior of a Collider based on nothing but a Solid.

func (*SolidCollider) FirstRayCollision

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

FirstRayCollision approximately finds the first time the ray collides with the solid.

The result may be inaccurate for parts of the solid smaller than epsilon.

func (*SolidCollider) Max

func (s *SolidCollider) Max() Coord3D

Max gets the maximum boundary of the Solid.

func (*SolidCollider) Min

func (s *SolidCollider) Min() Coord3D

Min gets the minimum boundary of the Solid.

func (*SolidCollider) RayCollisions

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

RayCollisions counts the approximate number of times the ray collides with the solid's border.

The result may be inaccurate for parts of the solid smaller than epsilon.

func (*SolidCollider) SphereCollision

func (s *SolidCollider) SphereCollision(c Coord3D, r float64) bool

SphereCollision checks if the solid touches a sphere with origin c and radius r.

The result may be inaccurate for parts of the solid smaller than epsilon.

This grows slower with r as O(r^3).

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 Coord3D) []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 Coord3D) bool

func (*SolidMux) IterContains added in v0.4.0

func (s *SolidMux) IterContains(c Coord3D, 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() Coord3D

func (*SolidMux) Min added in v0.3.6

func (s *SolidMux) Min() Coord3D

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

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

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 Coord3D, 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 Coord3D, 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 Coord3D) Coord3D

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 Sphere

type Sphere struct {
	Center Coord3D
	Radius float64
}

A Sphere is a spherical 3D primitive.

func (*Sphere) Contains

func (s *Sphere) Contains(coord Coord3D) bool

Contains checks if a point c is inside the sphere.

func (*Sphere) FirstRayCollision

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

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

func (*Sphere) Max

func (s *Sphere) Max() Coord3D

Max gets the maximum point of the bounding box.

func (*Sphere) MetaballDistBound added in v0.3.5

func (s *Sphere) MetaballDistBound(d float64) float64

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

func (*Sphere) MetaballField added in v0.3.5

func (s *Sphere) MetaballField(coord Coord3D) float64

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

func (*Sphere) Min

func (s *Sphere) Min() Coord3D

Min gets the minimum point of the bounding box.

func (*Sphere) NormalSDF added in v0.3.0

func (s *Sphere) NormalSDF(coord Coord3D) (Coord3D, float64)

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

func (*Sphere) PointSDF added in v0.2.3

func (s *Sphere) PointSDF(coord Coord3D) (Coord3D, float64)

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

func (*Sphere) RayCollisions

func (s *Sphere) 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 (*Sphere) SDF

func (s *Sphere) SDF(coord Coord3D) float64

SDF gets the signed distance relative to the sphere.

func (*Sphere) SphereCollision

func (s *Sphere) SphereCollision(center Coord3D, r float64) bool

SphereCollision checks if the surface of s collides with a solid sphere centered at c with radius r.

type SphereSolid

type SphereSolid = Sphere

type StackedSolid

type StackedSolid []Solid

A StackedSolid is like a JoinedSolid, but the solids after the first are moved so that the lowest Z value of their bounding box collides with the highest Z value of the previous solid. In other words, the solids are stacked on top of each other along the Z axis.

This API is deprecated in favor of the StackSolids() function.

func (StackedSolid) Contains

func (s StackedSolid) Contains(c Coord3D) bool

func (StackedSolid) Max

func (s StackedSolid) Max() Coord3D

func (StackedSolid) Min

func (s StackedSolid) Min() Coord3D

type Subdivider

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

A Subdivider is used for sub-dividing triangles in a mesh to add levels of detail where it is needed in a model.

To achieve this, it tracks line segments that are to be split, and then replaces triangles in the mesh such that the given lines are split and the midpoint is replaced with a more accurate value.

func NewSubdivider

func NewSubdivider() *Subdivider

NewSubdivider creates an empty Subdivider.

func (*Subdivider) Add

func (s *Subdivider) Add(p1, p2 Coord3D)

Add adds a line segment that needs to be split.

func (*Subdivider) AddFiltered

func (s *Subdivider) AddFiltered(m *Mesh, f func(p1, p2 Coord3D) bool)

AddFiltered adds line segments for which f returns true.

func (*Subdivider) NumSegments added in v0.3.4

func (s *Subdivider) NumSegments() int

NumSegments returns the current number of segments added to s.

func (*Subdivider) Subdivide

func (s *Subdivider) Subdivide(mesh *Mesh, midpointFunc func(p1, p2 Coord3D) Coord3D)

Subdivide modifies the mesh by replacing triangles whose sides are affected by subdivision.

The midpointFunc computes a 3D coordinate that should replace the midpoint of a given line segment from the original mesh.

type SubtractedSolid

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

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

func (*SubtractedSolid) Max

func (s *SubtractedSolid) Max() Coord3D

func (*SubtractedSolid) Min

func (s *SubtractedSolid) Min() Coord3D

type Torus

type Torus struct {
	Center      Coord3D
	Axis        Coord3D
	OuterRadius float64
	InnerRadius float64
}

A Torus is a 3D primitive that represents a torus.

The torus is defined by revolving a sphere of radius InnerRadius around the point Center and around the axis Axis, at a distance of OuterRadius from Center.

The Torus is only valid if the inner radius is lower than the outer radius. Otherwise, invalid ray collisions and SDF values may be reported.

func (*Torus) Contains

func (t *Torus) Contains(c Coord3D) bool

Contains determines if c is within the torus.

func (*Torus) FirstRayCollision added in v0.3.0

func (t *Torus) FirstRayCollision(ray *Ray) (RayCollision, bool)

FirstRayCollision gets the first ray collision with the surface of the torus.

func (*Torus) Max

func (t *Torus) Max() Coord3D

Max gets the maximum point of the bounding box.

func (*Torus) MetaballDistBound added in v0.3.5

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

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

func (*Torus) MetaballField added in v0.3.5

func (t *Torus) MetaballField(coord Coord3D) float64

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

func (*Torus) Min

func (t *Torus) Min() Coord3D

Min gets the minimum point of the bounding box.

func (*Torus) NormalSDF added in v0.3.0

func (t *Torus) NormalSDF(c Coord3D) (Coord3D, float64)

NormalSDF is like SDF, but also returns the normal on the surface of the torus at the closest point to c.

func (*Torus) PointSDF added in v0.3.0

func (t *Torus) PointSDF(c Coord3D) (Coord3D, float64)

PointSDF is like SDF, but also returns the closest point on the surface of the torus.

func (*Torus) RayCollisions added in v0.3.0

func (t *Torus) RayCollisions(ray *Ray, f func(RayCollision)) int

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

func (*Torus) SDF

func (t *Torus) SDF(c Coord3D) float64

SDF determines the minimum distance from a point to the surface of the torus.

func (*Torus) SphereCollision added in v0.3.0

func (t *Torus) SphereCollision(c Coord3D, r float64) bool

SphereCollision checks if any part of the surface of the torus is contained in a sphere.

type TorusSolid

type TorusSolid = Torus

type Transform

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

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

	// 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

type Translate struct {
	Offset Coord3D
}

Translate is a Transform that adds an offset to coordinates.

func (*Translate) Apply

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

func (*Translate) ApplyBounds

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

func (*Translate) ApplyDistance added in v0.2.11

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

func (*Translate) Inverse

func (t *Translate) Inverse() Transform

type Triangle

type Triangle [3]Coord3D

A Triangle is a triangle in 3D Euclidean space.

func ReadOFF

func ReadOFF(r io.Reader) ([]*Triangle, error)

ReadOFF decodes a file in the object file format. See http://segeval.cs.princeton.edu/public/off_format.html.

func ReadSTL

func ReadSTL(r io.Reader) ([]*Triangle, error)

ReadSTL decodes a file in the STL file format.

func TriangulateFace

func TriangulateFace(polygon []Coord3D) []*Triangle

TriangulateFace turns any simple polygon face into a set of triangles.

If the points are not coplanar, the result is approximated.

func (*Triangle) Area

func (t *Triangle) Area() float64

Area computes the area of the triangle.

func (*Triangle) AreaGradient

func (t *Triangle) AreaGradient() *Triangle

AreaGradient computes the gradient of the triangle's area with respect to every coordinate.

func (*Triangle) AtBarycentric added in v0.4.0

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

AtBarycentric computes the point at the barycentric coordinates.

func (*Triangle) Closest added in v0.2.3

func (t *Triangle) Closest(c Coord3D) Coord3D

Closest gets the point on the triangle closest to c.

func (*Triangle) Dist

func (t *Triangle) Dist(c Coord3D) float64

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

func (*Triangle) FirstRayCollision

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

FirstRayCollision gets the ray collision if there is one.

The Extra field is a *TriangleCollision.

func (*Triangle) Max

func (t *Triangle) Max() Coord3D

Max gets the element-wise maximum of all the points.

func (*Triangle) Min

func (t *Triangle) Min() Coord3D

Min gets the element-wise minimum of all the points.

func (*Triangle) Normal

func (t *Triangle) Normal() Coord3D

Normal computes a normal vector for the triangle using the right-hand rule.

func (*Triangle) RayCollisions

func (t *Triangle) 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).

The Extra field is a *TriangleCollision.

func (*Triangle) RectCollision added in v0.2.6

func (t *Triangle) RectCollision(rect *Rect) bool

RectCollision checks if any part of the triangle is inside the rect.

func (*Triangle) SegmentCollision added in v0.2.6

func (t *Triangle) SegmentCollision(s Segment) bool

SegmentCollision checks if the segment collides with the triangle.

func (*Triangle) Segments

func (t *Triangle) Segments() [3]Segment

Segments gets all three line segments in the triangle.

func (*Triangle) SharesEdge

func (t *Triangle) SharesEdge(t1 *Triangle) bool

SharesEdge checks if p shares exactly one edge with p1.

func (*Triangle) SphereCollision

func (t *Triangle) SphereCollision(c Coord3D, r float64) bool

SphereCollision checks if any part of the triangle is within the sphere.

func (*Triangle) TriangleCollisions

func (t *Triangle) TriangleCollisions(t1 *Triangle) []Segment

TriangleCollisions finds the segment where t intersects t1. If no segment exists, an empty slice is returned.

If t and t1 are (nearly) co-planar, no collisions are reported, since small numerical differences can have a major impact.

type TriangleCollider

type TriangleCollider interface {
	Collider

	// TriangleCollisions gets all of the segments on the
	// surface which intersect the triangle t.
	TriangleCollisions(t *Triangle) []Segment
}

A TriangleCollider is like a Collider, but it can also check if and where a triangle intersects the surface.

This is intended to be used for piecewise-linear surfaces where triangle-surface intersections can be parameterized as line segments.

All methods of a TriangleCollider are safe for concurrency.

type TriangleCollision

type TriangleCollision struct {
	// The triangle that reported the collision.
	Triangle *Triangle

	// Barycentric coordinates in the triangle,
	// corresponding to the corners.
	Barycentric [3]float64
}

TriangleCollision is triangle-specific collision information.

type VecScale added in v0.3.5

type VecScale struct {
	Scale Coord3D
}

VecScale is a transform that scales an object coordinatewise.

func (*VecScale) Apply added in v0.3.5

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

func (*VecScale) ApplyBounds added in v0.3.5

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

func (*VecScale) Inverse added in v0.3.5

func (v *VecScale) Inverse() Transform

type VoxelSmoother

type VoxelSmoother struct {
	StepSize   float64
	Iterations int

	// MaxDistance is the maximum L_infinity distance a
	// vertex must move.
	MaxDistance float64
}

VoxelSmoother uses hard-constraints on top of gradient descent to minimize the surface area of a mesh while keeping vertices within a square region of space.

This is based on the surface nets algorithm.

Also see MeshSmoother, which is similar but more general-purpose.

func (*VoxelSmoother) Smooth

func (v *VoxelSmoother) Smooth(mesh *Mesh) *Mesh

Smooth applies gradient descent to smooth the mesh.

func (*VoxelSmoother) SmoothMapping added in v0.4.2

func (v *VoxelSmoother) SmoothMapping(mesh *Mesh) *CoordMap[Coord3D]

SmoothMapping returns a mapping from original vertices to smoothed vertices.

Jump to

Keyboard shortcuts

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