epa

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Dec 21, 2025 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package epa implements the Expanding Polytope Algorithm for computing penetration depth.

EPA is run after GJK detects a collision to determine:

  • Penetration depth (how far shapes overlap)
  • Contact normal (direction to separate shapes)
  • Contact points (where shapes touch)

The algorithm expands a polytope (starting from GJK's final simplex) toward the origin in the Minkowski difference space, finding the closest face which gives us the Minimum Translation Vector (MTV) to separate the shapes.

For detailed algorithm explanation with pseudocode and visual examples, see: ALGORITHMS.md - "EPA Algorithm" section

References:

  • Van den Bergen: "Proximity Queries and Penetration Depth Computation on 3D Game Objects" (2001)

Index

Constants

View Source
const (
	// EPAMaxIterations limits polytope expansion to prevent infinite loops.
	// Typical convergence: 5-15 iterations for simple shapes.
	// If this limit is reached, EPA returns an error.
	EPAMaxIterations = 32

	// EPAConvergenceTolerance defines when EPA has converged.
	// If the distance to a new support point improves by less than this threshold,
	// we've found the closest face to the origin.
	// Lower values = more precision but slower convergence.
	EPAConvergenceTolerance = 0.001

	// EPAMinFaceDistance is the minimum face distance before we skip it.
	// Faces very close to or behind the origin are likely degenerate.
	EPAMinFaceDistance = 0.0001

	// NormalSnapThreshold is used to clamp nearly-zero normal components to exactly zero.
	// This helps with numerical stability and axis-aligned collisions.
	NormalSnapThreshold = 1e-8

	// DegeneratePenetrationEstimate is a fallback penetration depth for degenerate cases
	// where we have insufficient simplex points to compute accurate depth.
	DegeneratePenetrationEstimate = 0.01
)

Variables

This section is empty.

Functions

func EPA

EPA computes penetration depth and contact information for overlapping convex shapes.

Algorithm overview:

  1. Start with simplex from GJK (tetrahedron containing origin)
  2. Build initial polytope faces from simplex
  3. Find face closest to origin
  4. Get support point in face normal direction
  5. If converged (new point doesn't improve distance) → done
  6. Otherwise, expand polytope by adding support point
  7. Repeat from step 3

Parameters:

  • a, b: The two colliding rigid bodies
  • simplex: Final simplex from GJK (typically 4 points forming tetrahedron)

Returns:

  • ContactConstraint: Contains contact normal, penetration depth, contact points
  • error: Non-nil if EPA failed to converge or encountered degenerate case

The contact normal points from body A toward body B (separation direction). Penetration depth is always positive (how far to move B away from A).

func GenerateManifold

func GenerateManifold(bodyA, bodyB *actor.RigidBody, normal mgl64.Vec3, depth float64) []constraint.ContactPoint

GenerateManifold is the main entry point

Types

type Edge

type Edge struct {
	A, B mgl64.Vec3
}

Edge represents an edge between two vertices. Kept for backward compatibility, but no longer used with PolytopeBuilder. PolytopeBuilder uses EdgeEntry instead with occurrence counting.

type EdgeEntry

type EdgeEntry struct {
	A, B  mgl64.Vec3 // Edge vertices (normalized: A < B)
	Count int        // Occurrence count (1 = boundary edge, 2+ = internal edge)
}

EdgeEntry represents an edge with occurrence counting for boundary detection. An edge is a boundary edge if it appears exactly once (count == 1). Edges are normalized so A < B lexicographically for consistent deduplication.

type Face

type Face struct {
	Points   [3]mgl64.Vec3 // The 3 vertices of the triangle
	Normal   mgl64.Vec3    // Outward-pointing normal
	Distance float64       // Distance from origin to the face plane
}

Face represents a triangular face of the polytope in EPA. Each face has 3 vertices, an outward-pointing normal, and distance to origin.

This struct is now used by PolytopeBuilder for zero-allocation EPA. The old pointer-based approach with facePool has been replaced.

type ManifoldBuilder

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

ManifoldBuilder contains all working buffers with fixed-size arrays to avoid allocations.

func (*ManifoldBuilder) Generate

func (b *ManifoldBuilder) Generate(bodyA, bodyB *actor.RigidBody, normal mgl64.Vec3, depth float64) []constraint.ContactPoint

Generate generates the manifold using internal buffers

func (*ManifoldBuilder) Reset

func (b *ManifoldBuilder) Reset()

Reset prepares the builder for a new use

type PolytopeBuilder

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

PolytopeBuilder manages polytope expansion with dynamic buffers and initial capacity.

func (*PolytopeBuilder) AddPointAndRebuildFaces

func (b *PolytopeBuilder) AddPointAndRebuildFaces(support mgl64.Vec3, closestIndex int) error

AddPointAndRebuildFaces expands the polytope by adding a support point. This is the main EPA expansion step that:

  1. Finds visible faces from the support point
  2. Identifies boundary edges of the visible region
  3. Removes visible faces
  4. Creates new faces connecting boundary edges to the support point

All operations use fixed buffers for zero allocations.

func (*PolytopeBuilder) BuildInitialFaces

func (b *PolytopeBuilder) BuildInitialFaces(simplex *gjk.Simplex) error

BuildInitialFaces creates the initial polytope from a GJK tetrahedron simplex. Creates 4 triangular faces from the 4 simplex points, filtering degenerate faces.

Returns error if simplex is invalid (count != 4).

func (*PolytopeBuilder) FindClosestFaceIndex

func (b *PolytopeBuilder) FindClosestFaceIndex() int

FindClosestFaceIndex returns the index of the face closest to the origin. Returns -1 if no faces exist.

func (*PolytopeBuilder) GetClosestFace

func (b *PolytopeBuilder) GetClosestFace() *Face

GetClosestFace returns a pointer to the closest face for EPA result. Returns nil if no faces exist.

func (*PolytopeBuilder) Reset

func (b *PolytopeBuilder) Reset()

Reset prepares the builder for reuse by clearing all slices. This allows the builder to be reused from the pool without reallocation.

Jump to

Keyboard shortcuts

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