jel

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 5 Imported by: 0

README

jel

2D soft-body physics library written in Go.

Overview

jel simulates deformable bodies made of connected point masses. Physics behavior comes from attachable Components (an extensible interface), bodies connect via Joints, and collision response varies by material group.

Components

Components are interfaces you attach to bodies to add physics behavior. The library includes:

  • SpringComponent - Edge springs connecting adjacent point masses; supports custom extra springs
  • ShapeMatchComponent - Constrains body to original shape, creating elasticity and rigidity
  • GravityComponent - Applies directional force (gravity, wind, etc.)
  • PressureComponent - Internal pressure pushes outward on body edges
  • StickyRayComponent - Casts rays outward and sticks point masses to contacted surfaces via spring forces

Implement the Component interface to write your own.

Joints rigidly or elastically connect bodies. They work with JointLinks - abstractions that represent connection points:

  • BodyJointLink - Connects to the entire body's center (DerivedPos)
  • PointJointLink - Connects to a specific point mass within a body
  • ShapeJointLink - Connects to a group of point masses (weighted average position)

Joint types:

  • SpringJoint - Maintains rest distance with spring forces; supports plasticity
  • PinJoint - Rigidly welds two points together like a weld

Example - connect tire to car body:

tireLink := jel.NewBodyJointLink(tire)
carLink := jel.NewShapeJointLink(carBody, []int{1, 3, 4, 5})
joint := jel.NewPinJoint(carLink, tireLink, 0)
world.AddJoint(joint)

Raycast & Collision

Raycast - Cast rays through the world to test line-of-sight or detect surfaces:

func (b *Body) Raycast(start, end Vec2) (closestHit Vec2, ok bool)

Collision - Automatic collision detection between bodies with configurable:

  • Material pairs - Set friction, elasticity, and enable/disable collisions per material combination
  • Collision observers - Listen to collision events and react with custom logic (sounds, damage, etc.)
  • Bitmask filtering - Fine-grained collision groups using bitmask operations

Example

world := jel.NewWorld()

// Create a soft body
shape := jel.RegularPolygon(radius, 12)
body := jel.NewBody(shape, position, angle, mass, world)

// Add physics components
body.AddComponent(jel.NewSpringComponent(stiffness, damping))
body.AddComponent(jel.NewShapeMatchComponent(stiffness, damping, nil))
body.AddComponent(jel.NewGravityComponent(0, 9.8, true))

// Define materials
mat1 := world.AddMaterial()
mat2 := world.AddMaterial()
world.SetMaterialPairData(mat1, mat2, friction, elasticity)
body.Material = mat1

// Listen to collisions
world.CollisionObserver = myObserver

// Update world
world.Update(deltaTime)

Documentation

Overview

Jel is a 2D soft-body physics library for games.

Index

Constants

View Source
const (

	// Pi is a convenience re-export of math.Pi.
	Pi float64 = math.Pi

	// Tau (τ) is two times pi , representing a full circle in radians. https://oeis.org/A019692
	Tau = 6.2831853071795864769252867665590057683943387987502
)

Variables

View Source
var Infinity = math.Inf(1)

Infinity represents positive infinity, commonly used to mark a PointMass as having infinite mass (i.e. immovable / static).

View Source
var Vec2One = Vec2{X: 1, Y: 1}

Vec2One is a Vec2 with both components set to 1. Do not modify.

Functions

func CalcSpringTension

func CalcSpringTension(a, b Vec2, rd RestDistance) float64

CalcSpringTension computes the normalized tension (-1..1) of a spring based on the current distance between its two endpoints (a, b) relative to its rest distance range.

If dist falls within RestDistance.MinDist, RestDistance.MaxDist the spring is considered relaxed and tension is 0. Otherwise, the ratio is computed against the nearest bound (RestDistance.MaxDist when stretched, RestDistance.MinDist when compressed), clamped so that a ratio of ±30% or more maps to ±1. A positive value indicates stretching, a negative value indicates compression.

func DefaultCollisionFilterFunc

func DefaultCollisionFilterFunc(info CollisionInfo, normalVelocity float64) bool

DefaultCollisionFilterFunc is the default collision filter. It always returns true, so all collisions passed through it are approved.

func StiffnessDampingFrom

func StiffnessDampingFrom(mass, softness float64) (stiffness, damping float64)

StiffnessDampingFrom returns (stiffness, damping) for a spring, given only a mass and a softness value in [0, 1].

- softness = 0 -> rigid (stiff, snaps back fast, no wobble)

- softness = 1 -> ideal loose/jelly (soft, wobbles, settles slowly)

Same softness value gives the same "feel" regardless of mass.

Types

type AABB

type AABB struct {
	Valid bool
	Min   Vec2
	Max   Vec2
}

AABB is an axis-aligned bounding box. Valid is false for an empty/unset box.

func NewAABB

func NewAABB(min, max Vec2) AABB

NewAABB creates a valid AABB with the given min and max corners.

func NewAABBFromPoints

func NewAABBFromPoints(points []Vec2) (aabb AABB)

NewAABBFromPoints creates an AABB enclosing all the given points.

func NewAABBOf

func NewAABBOf(points ...Vec2) AABB

NewAABBOf creates an AABB enclosing the given points.

func (*AABB) Clear

func (a *AABB) Clear()

Clear marks the box as invalid/empty.

func (AABB) Contains

func (a AABB) Contains(point Vec2) bool

Contains reports whether point lies inside the box (inclusive).

func (*AABB) ExpandToInclude

func (a *AABB) ExpandToInclude(point Vec2)

ExpandToInclude grows the box, if needed, so it contains point.

func (*AABB) ExpandToIncludePoints

func (a *AABB) ExpandToIncludePoints(points []Vec2)

ExpandToIncludePoints grows the box, if needed, so it contains all points.

func (AABB) Expanded

func (a AABB) Expanded(margin float64) AABB

Expanded returns a copy of the AABB grown outward by margin on every side.

func (AABB) Intersects

func (a AABB) Intersects(box AABB) bool

Intersects reports whether the two boxes overlap.

type Bitmask

type Bitmask uint64

Bitmask is a 64-bit flag set, typically used for collision layers/masks.

func (*Bitmask) SetOn

func (b *Bitmask) SetOn(index int)

SetOn sets the bit corresponding to the given 1-based index. Indices less than 1 are clamped to bit 0.

type Body

type Body struct {
	// The base shape for the body with local vertices
	BaseShape Shape
	// The global shape for the body - rotated and translated around the world
	GlobalShape Shape
	// Point masses for the body.
	PointMasses []*PointMass
	// Edges on the body.
	Edges []*BodyEdge
	// Body joints this body participates in
	Joints []Joint
	// Body components for this body object
	Components []Component
	// The scale for this body's shape
	Scale Vec2
	// The velocity damping to apply to the body. Values closer to 0
	// decelerate faster, values closer to 1 decelerate slower.
	//
	// 1 never decelerates. Values outside the range [0, 1] inclusive may
	// introduce instability. Default is 0.999
	VelDamping float64
	// The axis-aligned bounding box for this body's point masses.
	//
	// Will be slightly expanded to include the velocity of the points, in case
	// this body is kinematic, so it won't always match exactly the position of
	// the point masses.
	AABB AABB
	// The index of the material in the world material slice to use for this
	// body.
	Material int
	// Whether this body is static.
	IsStatic bool
	// Whether this body is kinematic. When true, [Body.DerivePositionAndAngle]
	// skips recomputing [Body.DerivedPos], [Body.DerivedVel], [Body.DerivedAngle],
	// and [Body.DerivedOmega] from the point masses each frame. Instead, the
	// body's transform is driven externally via [Body.SetScaleAnglePosition],
	// which sets [Body.DerivedPos]/[Body.DerivedAngle] directly and pushes
	// that transform onto the point masses (overwriting their positions to
	// match the new base-shape placement). In other words, the usual
	// point-masses -> derived-transform flow is reversed: for kinematic
	// bodies, the derived transform drives the point masses instead of being
	// computed from them.
	//
	// [Body.SetScaleAnglePosition] does not update [Body.DerivedVel],
	// [Body.DerivedOmega], or the point masses' [PointMass.Velocity] — it
	// only overwrites [PointMass.Position]. So a kinematic body's
	// velocity/angular-velocity fields go stale (or stay zero) unless the
	// caller updates them separately. Consumers should not assume these
	// fields reflect the body's actual motion when IsKinematic is true (e.g.
	// [ShapeMatchComponent.Damping] term ignores them in this case).
	IsKinematic bool
	// Whether this body is pinned - pinned bodies rotate around their axis,
	// but try to remain in place, like a kinematic body.
	IsPinned bool
	// Whether the body is able to rotate while moving. Default is true
	FreeRotate bool
	// The collision bitmask for this body.
	Bitmask Bitmask
	// The X-axis bitmask for the body - used for collision filtering.
	BitmaskX Bitmask
	// The Y-axis bitmask for the body - used for collision filtering.
	BitmaskY Bitmask
	// The derived center position of this body - in world coordinates
	DerivedPos Vec2
	// The derived velocity of this body - in world coordinates. The derivation
	DerivedVel Vec2
	// The derived rotation of the body, in radians
	DerivedAngle float64
	// Omega (ω) is the relative angular speed of the body, in radians/s
	DerivedOmega float64
	// Custom user data attached to the body
	UserData any
	// contains filtered or unexported fields
}

Represents a soft body on the World

func NewBody

func NewBody(shape Shape, pos Vec2, angle, mass float64, world ...*World) *Body

NewBody returns a new Body from shape.

To ensure a stable physics simulation, the shape is automatically re-centered and inverted if it is not CCW.

 - pos: initial position in world coordinates.
 - angle:  refers to the initial rotation angle from the center in radians (clockwise).
 - mass is the individual mass of each point within [Body.PointMasses].
   See [Body.SetMassesFromSlice],  [Body.SetMassAll], [Body.SetMassByIndex]
 - world: The world this body will be added to (optional). See [World.AddBody] and [World.AddBodies].

func NewStaticBody

func NewStaticBody(shape Shape, pos Vec2, angle float64, world ...*World) *Body

NewStaticBody returns a new static Body.

func (*Body) AccumulateExternalForces

func (b *Body) AccumulateExternalForces(world *World)

This function should add all external forces to the Force member variable of each PointMass in the body.

These are external forces acting on the PointMasses, such as gravity, etc.

func (*Body) AccumulateInternalForces

func (b *Body) AccumulateInternalForces(relaxing bool)

This function should add all internal forces to the Force member variable of each PointMass in the body.

These should be forces that try to maintain the shape of the body.

func (*Body) AddAngularVelocity

func (b *Body) AddAngularVelocity(vel float64)

Accumulates the angular velocity for this body

func (*Body) AddComponent

func (b *Body) AddComponent(comp Component)

Adds a body component to this body.

func (*Body) AddVelocity

func (b *Body) AddVelocity(velocity Vec2)

Adds a velocity vector to all the point masses in this body. Does nothing, if body is static.

func (*Body) AddVelocityToPointAt

func (b *Body) AddVelocityToPointAt(velocity Vec2, pointMassIndex int)

Adds velocity to the current velocity of a single point mass.

func (*Body) ApplyForceAtGlobalPoint

func (b *Body) ApplyForceAtGlobalPoint(force Vec2, pt Vec2)

ApplyForce applies a force to the body at `pt` in world coordinates. If `pt` is not at the center (`derivedPos`), torque is applied causing spin. Ignored if the body is static.

Parameters:

  • force: The force vector to apply
  • pt: The world position where the force is applied. Use `derivedPos` for center.

func (*Body) ApplyForceToPointAt

func (b *Body) ApplyForceToPointAt(force Vec2, pointMassIndex int)

Applies a relative velocity change to a single point mass at the given index..

func (*Body) ApplyGlobalForce

func (b *Body) ApplyGlobalForce(force Vec2)

ApplyGlobalForce applies the same force to every point mass directly. No torque is calculated, so the body translates without rotating. Ignored if the body is static.

func (*Body) ApplyTorque

func (b *Body) ApplyTorque(force float64)

Applies a rotational clockwise torque of a given force on this body. Ignored, if body is static.

func (*Body) ClosestEdge

func (b *Body) ClosestEdge(point Vec2, tolerance float64) (edgePosition Vec2, edgeRatio float64, edgePoint1, edgePoint2 int, ok bool)

ClosestEdge finds the point on any of the body's edges that is closest to point. point must be given in world coordinates.

tolerance limits how far an edge may be from point to be considered: any edge whose closest point is farther than tolerance is ignored. Pass math.Inf(1) to consider every edge regardless of distance (this mirrors the original default behavior). Passing 0 means no edge will ever be accepted, since a distance can never be strictly less than 0 — this is rarely what you want, so double-check the value before hardcoding 0.

Returns:

  • edgePosition: the closest point on the edge to point
  • edgeRatio: where along the edge that point falls, in [0, 1], with 0 at edgePoint1 and 1 at edgePoint2
  • edgePoint1: index of the first point mass forming the edge
  • edgePoint2: index of the second point mass forming the edge
  • ok: false if the body has no edges or point masses, or if no edge was found within tolerance — in that case the other return values are zero values and should not be used

func (*Body) ClosestPoint

func (b *Body) ClosestPoint(pt Vec2) (hitPoint, normal Vec2, pointA, pointB int, edgeD, distance float64)

ClosestPoint finds the closest point on any edge of the body to the given global point. It returns detailed information about the edge, including its endpoints, normal, and the ratio along the edge.

Precondition: len(Body.PointMasses) > 0.

Parameters:

  • pt: The point in world coordinates to find the closest point to

Returns:

  • hitPoint: The closest point on the body's surface
  • normal: The unit normal vector of the edge at the closest point
  • pointA: The index of the first endpoint of the edge
  • pointB: The index of the second endpoint of the edge
  • edgeD: The ratio along the edge [0,1] where the closest point lies
  • distance: The Euclidean distance from pt to the closest point

func (*Body) ClosestPointMass

func (b *Body) ClosestPointMass(pos Vec2) (point int, distance float64)

Find the closest PointMass index in this body, given a global point

func (*Body) ClosestPointOnEdge

func (b *Body) ClosestPointOnEdge(pt Vec2, edgeNum int) (hitPoint, normal Vec2, edgeD, distance float64)

Given a global point, finds the closest point on an edge of a specified index, returning the distance to the edge found.

Precondition: len(Body.PointMasses) > 0.

Parameters:

  • pt: The point to get the closest edge of, in world coordinates
  • edgeNum: The index of the edge to search

Returns:

  • hitPoint: The closest point in the edge to the global point provided
  • normal: A unit vector containing information about the normal of the edge found
  • edgeD: The ratio of the edge where the point was grabbed, [0-1] inclusive
  • distance: The distance to the closest edge found

func (*Body) ClosestPointOnEdgeSq

func (b *Body) ClosestPointOnEdgeSq(pt Vec2, edgeNum int) (hitPoint, normal Vec2, edgeD, distance float64)

ClosestPointOnEdgeSq finds the closest point on a specific edge of the body to the given global point.

Precondition: len(Body.PointMasses) > 0.

Parameters:

  • pt: The point in world coordinates to find the closest edge point to
  • edgeNum: The index of the edge to search

Returns:

  • hitPoint: The closest point on the edge to the given point
  • normal: A unit vector representing the normal of the edge
  • edgeD: The ratio along the edge where the point was found, in range [0, 1]
  • distance: The squared distance to the closest edge point

func (*Body) Contains

func (b *Body) Contains(pt Vec2) bool

Returns whether a global point is inside this body.

func (*Body) DampenVelocity

func (b *Body) DampenVelocity(elapsed float64)

Applies the velocity damping to the point masses. Ignored, if body is static.

func (*Body) DerivePositionAndAngle

func (b *Body) DerivePositionAndAngle(elapsed float64)

DerivePositionAndAngle derives the global position and angle of this body, based on the average of all the points.

This updates the [Body.derivedPos], [Body.derivedAngle], and [Body.derivedVel] fields.

This is called by World.Update, so usually a user does not need to call this. Instead you can just use the Body.DerivedPos, Body.DerivedAngle, Body.DerivedVel, and Body.DerivedOmega getter methods.

func (*Body) GetComponent

func (b *Body) GetComponent[T Component]() (component T)

GetComponent returns the first Component attached to this Body matching type T, or nil if not found.

Example:

if springComp := body.GetComponent[*jel.SpringComponent](); springComp != nil {
	fmt.Println(springComp.EdgeSpringsCount)
}

func (*Body) Integrate

func (b *Body) Integrate(elapsed float64)

Integrates the point masses for this Body. Ignored, if body is static.

func (*Body) IntersectsLine

func (b *Body) IntersectsLine(start, end Vec2) bool

Returns whether the given line consisting of two points intersects this body.

func (*Body) PointMass

func (b *Body) PointMass(pointMassIndex int) *PointMass

PointMass returns the point at the given index.

func (*Body) Raycast

func (b *Body) Raycast(start, end Vec2) (closestHit Vec2, ok bool)

Tests a ray starting and ending at a given interval, returning the point at which the ray intersects this body the closest to `start`.

If the ray does not crosses this body, `nil` is returned, instead.

func (*Body) RemoveComponent

func (b *Body) RemoveComponent(comp Component)

Removes a component from this body.

func (*Body) Reset

func (b *Body) Reset()

func (*Body) SetAngularVelocity

func (b *Body) SetAngularVelocity(vel float64)

Sets the angular velocity for this body. Ignored, if body is static.

The method keeps the average velocity of the point masses the same during the procedure.

func (*Body) SetAverageVelocity

func (b *Body) SetAverageVelocity(velocity Vec2)

SetAverageVelocity modifies the average velocity of all point masses to a certain value.

The method keeps the individual difference of velocity between the point masses and the average body velocity while making the operation.

Does nothing if the body is static.

Parameters:

  • velocity: The velocity to set. Set to Zero to reset average velocity of the body to 0.

func (*Body) SetMassAll

func (b *Body) SetMassAll(mass float64)

SetMassAll sets the mass for all Body.PointMasses elements in the body.

func (*Body) SetMassByIndex

func (b *Body) SetMassByIndex(pointMassIndex int, mass float64)

SetMassByIndex sets the mass of the point mass at the given index.

If any point mass has an infinite mass, the body is marked as static.

func (*Body) SetMassesFromSlice

func (b *Body) SetMassesFromSlice(masses []float64)

SetMassesFromSlice sets the masses of the point masses from the given slice, up to the smaller of the two lengths.

If any point mass has an infinite mass, the body is marked as static.

func (*Body) SetPointPositionAt

func (b *Body) SetPointPositionAt(position Vec2, pointMassIndex int)

Sets the absolute position of a single point mass.

func (*Body) SetPointVelocityAt

func (b *Body) SetPointVelocityAt(velocity Vec2, pointMassIndex int)

Sets the absolute velocity of a single point mass.

func (*Body) SetScaleAnglePosition

func (b *Body) SetScaleAnglePosition(scale Vec2, angle float64, pos Vec2)

Sets the scale, angle and position of the body manually.

Setting the position and angle resets the current shape to the original base shape of the object.

func (*Body) SetShape

func (b *Body) SetShape(shape Shape)

SetShape sets the body's shape to a new Shape. If the vertex count differs from the current shape, existing Body.PointMasses are replaced with new ones (mass set to zero). Otherwise, the shape is updated without affecting existing Body.PointMasses.

To ensure a stable physics simulation, the shape is automatically re-centered and inverted if it is not CCW.

func (*Body) TranslatePointAt

func (b *Body) TranslatePointAt(offset Vec2, i int)

Translates PointMass.Position at index i

func (*Body) UpdateAABB

func (b *Body) UpdateAABB(elapsed float64, forceUpdate bool)

UpdateAABB updates the body's AABB with velocity padding for the given timestep. Called automatically by World.Update. Use forceUpdate to force update even for static bodies.

type BodyEdge

type BodyEdge struct {
	// EdgeIndex is the index of the edge on the body.
	EdgeIndex int
	// StartPointIndex is the index of the start point mass of this edge on the
	// [Body]'s [Body.PointMasses] slice.
	StartPointIndex int
	// EndPointIndex is the index of the end point mass of this edge on the
	// body's [Body.PointMasses] slice.
	EndPointIndex int
	// Start is the start position of the edge.
	Start Vec2
	// End is the end position of the edge.
	End Vec2
	// Normal is the normal for the edge.
	Normal Vec2
	// Difference is the difference between the start and end points, normalized.
	Difference Vec2
	// Length is the edge's length.
	Length float64
	// LengthSquared is the edge's length, squared.
	LengthSquared float64
}

BodyEdge contains information about the edge of a body.

func NewBodyEdge

func NewBodyEdge(edgeIndex, startPointIndex, endPointIndex int, start, end Vec2) *BodyEdge

NewBodyEdge creates and initializes a new BodyEdge with the given index, start point index, end point index, and start/end vectors.

The BodyEdge.Difference, BodyEdge.Normal, BodyEdge.Length and BodyEdge.LengthSquared fields are automatically initialized from these values.

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

BodyJointLink represents a joint link that links to a while body

func NewBodyJointLink(body *Body) *BodyJointLink

NewBodyJointLink inits a new body joint link with the specified parameter

func (*BodyJointLink) AddVelocity

func (b *BodyJointLink) AddVelocity(velocity Vec2)

AddVelocity adds a velocity delta to every point mass of the body.

func (*BodyJointLink) ApplyForce

func (b *BodyJointLink) ApplyForce(force Vec2)

ApplyForce applies a given force to the subject of this joint link

func (*BodyJointLink) Body

func (b *BodyJointLink) Body() *Body

func (*BodyJointLink) IsStatic

func (b *BodyJointLink) IsStatic() bool

IsStatic returns a value specifying whether the object referenced by this JointLinkType is static

func (*BodyJointLink) Mass

func (b *BodyJointLink) Mass() (totalMass float64)

Mass calculates and returns the total mass of the subject of this joint link

func (*BodyJointLink) Position

func (b *BodyJointLink) Position() Vec2

Position returns the position, in world coordinates, at which this joint links with the underlying Body.

func (*BodyJointLink) Translate

func (b *BodyJointLink) Translate(offset Vec2)

Translate applies a direct positional translation of this joint link

func (*BodyJointLink) Velocity

func (b *BodyJointLink) Velocity() Vec2

Velocity returns the velocity of the object this joint links to

type CollisionInfo

type CollisionInfo struct {
	// BodyA is the body owning the colliding point mass (index BodyApm).
	BodyA *Body
	// BodyApm is the index of the colliding point mass on BodyA.
	BodyApm int
	// BodyB is the other body involved in the collision.
	BodyB *Body
	// BodyBpmA is the index of the first point mass of the edge on BodyB
	// involved in the collision, or -1 if the collision is against a single
	// point mass rather than an edge.
	BodyBpmA int
	// BodyBpmB is the index of the second point mass of the edge on BodyB
	// involved in the collision, or -1 if not applicable.
	BodyBpmB int
	// HitPt is the world-space point at which the collision occurred.
	HitPt Vec2
	// EdgeD is the interpolation factor (0-1) along the BodyB edge
	// (BodyBpmA -> BodyBpmB) at which the hit point lies.
	EdgeD float64
	// Normal is the collision normal, typically pointing from BodyB toward BodyA.
	Normal Vec2
	// Penetration is the overlap depth between the colliding shapes.
	Penetration float64
}

CollisionInfo describes a single detected collision between a point mass on BodyA and either a point mass or an edge on BodyB.

func NewCollisionInfo

func NewCollisionInfo(bodyA *Body, bodyApm int, bodyB *Body) CollisionInfo

NewCollisionInfo creates a CollisionInfo representing a collision between a point mass on bodyA and bodyB as a whole (not a specific edge). BodyBpmA and BodyBpmB are set to -1 to indicate no edge is involved.

func NewCollisionInfoWithEdge

func NewCollisionInfoWithEdge(bodyA *Body, bodyApm int, bodyB *Body, bodyBpmA, bodyBpmB int) CollisionInfo

NewCollisionInfoWithEdge creates a CollisionInfo representing a collision between a point mass on bodyA and a specific edge on bodyB, defined by the point mass indices bodyBpmA and bodyBpmB.

type CollisionObserver

type CollisionObserver interface {
	// BodiesDidCollide is called once per step with all collisions detected
	// during that step.
	BodiesDidCollide(infos []CollisionInfo)
	// BodyCollision is called for an individual collision, allowing the
	// observer to react only when the penetration exceeds penetrationThreshold.
	BodyCollision(info CollisionInfo, penetrationThreshold float64)
}

CollisionObserver receives notifications about collisions detected during a simulation step, allowing external code to react to them (e.g. play a sound, apply damage, trigger gameplay logic).

type Component

type Component interface {
	// Prepare is called once after the component is added to a body.
	Prepare(body *Body)
	// AccumulateInternalForces adds shape-preserving internal forces to
	// each PointMass.Force in the body. worldRelaxing indicates whether the
	// world is currently in its relaxation phase.
	AccumulateInternalForces(body *Body, worldRelaxing bool)
	// AccumulateExternalForces adds external forces (e.g. gravity) to
	// each PointMass.Force in the body.
	AccumulateExternalForces(body *Body, world *World)

	Disabled() bool
}

Component represents something that can be attached to a Body to affect its physical behavior.

type EdgeJointLink struct {

	// A [0-1] ratio defining the point along the edge; 0.5 is the middle, while 0 or 1 acts like a [PointJointLink].
	EdgeRatio float64
	// contains filtered or unexported fields
}

Represents a joint link that links to an edge of a body

func NewEdgeJointLink(body *Body, edgeIndex int, edgeRatio ...float64) *EdgeJointLink

NewEdgeJointLink returns new edge joint link with the specified parameters.

  • edgeIndex: the index of the first point mass on the edge, the second is the next one with wrap around
  • edgeRatio: A [0, 1] ratio defining the point along the edge; 0.5 is the middle, while 0 or 1 acts like a PointJointLink. See EdgeJointLink.EdgeRatio

func (*EdgeJointLink) AddVelocity

func (e *EdgeJointLink) AddVelocity(velocity Vec2)

AddVelocity adds a velocity delta to both endpoints of the edge, weighted by EdgeRatio — mirrors ApplyForce's distribution.

func (*EdgeJointLink) ApplyForce

func (e *EdgeJointLink) ApplyForce(force Vec2)

Applies a given force to the subject of this joint link

func (*EdgeJointLink) Body

func (b *EdgeJointLink) Body() *Body

func (*EdgeJointLink) IsStatic

func (e *EdgeJointLink) IsStatic() bool

IsStatic returns a value specifying whether the object referenced by this JointLinkType is static

func (*EdgeJointLink) Mass

func (e *EdgeJointLink) Mass() (totalMass float64)

Gets the total mass of the subject of this joint link

func (*EdgeJointLink) Position

func (e *EdgeJointLink) Position() Vec2

Gets the position, in world coordinates, at which this joint links with the underlying body

func (*EdgeJointLink) Translate

func (e *EdgeJointLink) Translate(offset Vec2)

Applies a direct positional translation of this joint link by a given offset

func (*EdgeJointLink) Velocity

func (e *EdgeJointLink) Velocity() Vec2

Velocity returns the velocity of the object this joint links to

type GravityComponent

type GravityComponent struct {
	Gravity   Vec2 // The gravity vector to apply to the body
	Relaxable bool
	// contains filtered or unexported fields
}

Represents a Gravity component that can be added to a body to make it constantly affected by gravity

func NewGravityComponent

func NewGravityComponent(gravityX, gravityY float64, relaxable bool) *GravityComponent

func (*GravityComponent) AccumulateExternalForces

func (g *GravityComponent) AccumulateExternalForces(body *Body, world *World)

func (*GravityComponent) AccumulateInternalForces

func (b *GravityComponent) AccumulateInternalForces(body *Body, worldRelaxing bool)

func (*GravityComponent) Disabled

func (b *GravityComponent) Disabled() bool

func (*GravityComponent) Prepare

func (b *GravityComponent) Prepare(body *Body)

type InternalSpring

type InternalSpring struct {
	Spring
	PointMassA int
	PointMassB int
}

func NewInternalSpring

func NewInternalSpring(pmA int, pmB int, distance RestDistance, stiffness float64, damping float64, plasticity *SpringPlasticity) *InternalSpring

NewSpring creates a new spring.

type Joint

type Joint interface {
	Resolve(dt float64)
	LinkA() JointLink
	LinkB() JointLink
	CollisionsAllowed() bool
}

Joint is implemented by every joint type (BodyJoint, SpringBodyJoint, ...). This is what allows the World to store and resolve different joint kinds polymorphically.

type JointLink interface {
	// Gets the body that this joint link is linked to.
	Body() *Body
	// Gets the position, in world coordinates, at which this joint links with
	// the underlying body
	Position() Vec2
	// Gets the velocity of the object this joint links to
	Velocity() Vec2
	// Gets the total mass of the subject of this joint link
	Mass() float64
	// Gets a value specifying whether the object referenced by this
	// JointLinkType is static
	IsStatic() bool
	// Applies a given force to the subject of this joint link.
	ApplyForce(force Vec2)
	// Applies a direct positional translation of this joint link by a given offset.
	Translate(offset Vec2)
	// AddVelocity adds a velocity delta to the subject(s) of this joint
	// link, distributing it the same way ApplyForce does (e.g. across
	// multiple point masses for Edge/Shape links).
	AddVelocity(velocity Vec2)
}

Interface to be implemented by objects that specify the way a joint links with a body

type LineIntersectResult

type LineIntersectResult struct {
	// HitPt is the world-space point where the two line segments intersect.
	HitPt Vec2
	// Ua is the interpolation factor (0-1) along lineA at which the
	// intersection occurs.
	Ua float64
	// Ub is the interpolation factor (0-1) along lineB at which the
	// intersection occurs.
	Ub float64
}

LineIntersectResult holds the outcome of a successful LineIntersect call.

func LineIntersect

func LineIntersect(aStart, aEnd, bStart, bEnd Vec2) (LineIntersectResult, bool)

LineIntersect computes the intersection point of two finite line segments, lineA and lineB. It returns the intersection details and true if the segments intersect within their bounds; otherwise it returns a zero value and false (including the degenerate case where the segments are parallel or collinear).

type MaterialPair

type MaterialPair struct {
	//  Whether the collision between the two bodies should happen
	Collide bool
	// The elasticity of the point mass when bouncing off the bodies
	Elasticity float64
	// The relative friction between the two bodies
	Friction float64
	// A function to call and utilize as a collision filter when figuring out
	// whether the two bodies should collide
	CollisionFilterFunc func(info CollisionInfo, normalVelocity float64) bool
}

MaterialPair represents information about the collision response behavior between two bodies

func DefaultMaterialPair

func DefaultMaterialPair() MaterialPair

DefaultMaterialPair returns default MaterialPair

Friction:        0.3
Elasticity:      0.2

type Matrix3x3

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

Matrix3x3 represents a 2D affine transformation matrix.

The underlying structure is optimized so that its zero-value inherently represents the identity matrix. To achieve this, the main diagonal components

func NewMatrix3x3

func NewMatrix3x3(scale Vec2, angle float64, pos Vec2) Matrix3x3

NewMatrix3x3 returns a matrix that combines scale, rotation, and translation. Transformation order: scale -> rotate -> translate.

func (Matrix3x3) Apply

func (g Matrix3x3) Apply(v Vec2) Vec2

Apply transforms the given 2D vector 'v' by multiplying it with the matrix.

func (*Matrix3x3) Clear

func (g *Matrix3x3) Clear()

Clear resets the matrix to the identity transformation (zero-value).

func (*Matrix3x3) Rotate

func (g *Matrix3x3) Rotate(theta float64)

Rotate applies a counter-clockwise rotation (in radians) to the matrix. This effectively pre-multiplies the current matrix by a rotation matrix.

func (*Matrix3x3) Scale

func (g *Matrix3x3) Scale(x, y float64)

Scale applies a scaling transformation to the matrix. This effectively pre-multiplies the current matrix by a scaling matrix.

func (*Matrix3x3) Translate

func (g *Matrix3x3) Translate(tx, ty float64)

Translate applies a translation offset to the matrix.

type PinJoint

type PinJoint struct {

	// MaxCorrection clamps the magnitude of the per-step position
	// correction (and matching velocity impulse) applied to close the
	// separation, to avoid explosive corrections after a large
	// disturbance (e.g. body teleported, high dt spike). Zero means
	// unclamped.
	MaxCorrection float64
	// contains filtered or unexported fields
}

PinJoint rigidly binds two JointLinks to occupy the same world-space point, using velocity impulses (not forces or direct position snapping) to resolve separation. Both bodies remain free to rotate/deform around the shared point — only the point itself is prevented from separating.

This is a fully rigid weld: there is no softness factor and no rest-distance range. Separation is closed completely every Resolve call, both in position and in relative velocity along the separation axis, so no residual gap is left to accumulate step to step.

func NewPinJoint

func NewPinJoint(a, b JointLink, maxCorrection float64) *PinJoint

NewPinJoint creates a joint that rigidly locks link a and link b to the same world point. There is no distance parameter (unlike NewSpringJoint) and no softness parameter (unlike a Baumgarte-style pin) — the joint always fully closes separation to zero every step, behaving like a weld.

func (*PinJoint) CollisionsAllowed

func (j *PinJoint) CollisionsAllowed() bool

func (*PinJoint) LinkA

func (j *PinJoint) LinkA() JointLink

func (*PinJoint) LinkB

func (j *PinJoint) LinkB() JointLink

func (*PinJoint) Resolve

func (j *PinJoint) Resolve(dt float64)

Resolve resolves this joint by rigidly closing the separation between the two links: position is corrected fully (not fractionally) and the full relative velocity component along the separation axis is cancelled, not just the diverging part. Called once per physics step, same as SpringJoint.Resolve.

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

Represents a joint link that links directly to a point mass of a body

func NewPointJointLink(body *Body, pointMassIndex int) *PointJointLink

NewPointJointLink returns new NewPointJointLink

func (*PointJointLink) AddVelocity

func (p *PointJointLink) AddVelocity(velocity Vec2)

AddVelocity adds a velocity delta to the linked point mass.

func (*PointJointLink) ApplyForce

func (p *PointJointLink) ApplyForce(force Vec2)

Applies a given force to the subject of this joint link

func (*PointJointLink) Body

func (b *PointJointLink) Body() *Body

func (*PointJointLink) IsStatic

func (p *PointJointLink) IsStatic() bool

IsStatic returns a value specifying whether the object referenced by this JointLinkType is static

func (*PointJointLink) Mass

func (p *PointJointLink) Mass() (totalMass float64)

Gets the total mass of the subject of this joint link

func (*PointJointLink) Position

func (p *PointJointLink) Position() Vec2

Gets the position, in world coordinates, at which this joint links with the underlying body

func (*PointJointLink) Translate

func (p *PointJointLink) Translate(offset Vec2)

Applies a direct positional translation of this joint link by a given offset

func (*PointJointLink) Velocity

func (p *PointJointLink) Velocity() Vec2

Velocity returns the velocity of the object this joint links to

type PointMass

type PointMass struct {
	// Mass is the mass of the point. Use Infinity (or math.Inf(1)) to mark
	// the point as immovable/static; Integrate becomes a no-op in that case.
	Mass float64
	// Position is the current world-space position of the point.
	Position Vec2
	// Velocity is the current velocity of the point.
	Velocity Vec2
	// Force is the force currently accumulated on the point, to be applied
	// during the next call to Integrate. It is reset to zero after each
	// integration step.
	Force Vec2
	// Normal is an optional surface normal associated with the point,
	// typically used for collision response.
	Normal Vec2
}

PointMass represents a single simulated particle with mass, position, velocity, and any accumulated force to apply on the next integration step. Point masses are the fundamental building blocks that soft/rigid Bodies.

func NewPointMass

func NewPointMass(mass float64, position Vec2) *PointMass

NewPointMass creates a new PointMass with the given mass and initial position. Velocity, Force, and Normal are left at their zero values.

func (*PointMass) ApplyForce

func (p *PointMass) ApplyForce(force Vec2)

ApplyForce accumulates force into the point mass's current Force, to be applied on the next call to Integrate.

func (*PointMass) Integrate

func (p *PointMass) Integrate(elapsed float64)

Integrate advances the point mass's velocity and position by elapsed seconds using simple semi-implicit (symplectic) Euler integration: the accumulated Force is converted to acceleration (Force / Mass), applied to Velocity, and Velocity is then applied to Position. The accumulated Force is cleared afterward.

If Mass is infinite or NaN (i.e. the point is static/immovable), this is a no-op and the point's position and velocity are left unchanged.

type PressureComponent

type PressureComponent struct {
	// The current volume (area) enclosed by the body's shape
	Volume float64
	// The gas pressure constant used to push outward on the body's edges
	GasPressure float64
	// contains filtered or unexported fields
}

PressureComponent simulates internal gas pressure that pushes a body's edges outward, keeping soft-body shapes inflated (like a balloon).

func NewPressureComponent

func NewPressureComponent(gasPressure float64) *PressureComponent

NewPressureComponent returns a new PressureComponent that can be added to a body to simulate outward gas pressure against its edges.

func (*PressureComponent) AccumulateExternalForces

func (b *PressureComponent) AccumulateExternalForces(body *Body, world *World)

func (*PressureComponent) AccumulateInternalForces

func (p *PressureComponent) AccumulateInternalForces(body *Body, relaxing bool)

AccumulateInternalForces recomputes the body's enclosed volume (area) and applies an outward force along each point's normal proportional to the gas pressure and inversely proportional to the current volume, so the body resists being squeezed.

func (*PressureComponent) Disabled

func (b *PressureComponent) Disabled() bool

func (*PressureComponent) Prepare

func (b *PressureComponent) Prepare(body *Body)

type Resolver

type Resolver interface {
	// Resolve advances the implementation's state by dt seconds.
	Resolve(dt float64)
}

Resolver is implemented by anything that can advance its own state by a fixed time step, such as a physics world or constraint solver.

type RestDistance

type RestDistance struct {
	// Distance is ranged between a minimum and maximum value
	IsRanged bool
	// Fixed distance (used when IsRanged is false)
	Fixed float64
	// contains filtered or unexported fields
}

Specifies a rest distance for a body joint or spring. Distances can either be fixed by a distance, or ranged so forces only apply when distance is outside a tolerance range.

func CalcPlasticity

func CalcPlasticity(dist float64, rd RestDistance, sp *SpringPlasticity) RestDistance

CalcPlasticity calculates a new resting distance based on provided plasticity parameters. The resulting resting distance is returned by the function.

- Parameters:

  • dist: The current distance of the spring
  • rd: The resting distance for the spring

- Returns: The new rest distance to the spring, after plasticity is applied.

func NewFixedRestDistance

func NewFixedRestDistance(value float64) RestDistance

func NewRangedRestDistance

func NewRangedRestDistance(min, max float64) RestDistance

func (RestDistance) Clamp

func (r RestDistance) Clamp(value float64) float64

Clamp clamps a given value to be within the range of this rest distance. If RestDistance.IsRanged is false (fixed mode), the value parameter is ignored and RestDistance.Fixed is returned. If RestDistance.IsRanged is true (ranged mode), the value is clamped between min and max.

func (RestDistance) InRange

func (r RestDistance) InRange(value float64) bool

InRange returns whether a given value is within the range of this rest distance. If RestDistance.IsRanged is false (fixed mode), checks for exact equality. If RestDistance.IsRanged is true (ranged mode), performs value >= min && value <= max.

func (*RestDistance) MaxDist

func (r *RestDistance) MaxDist() float64

MaxDist returns the maximum distance for this rest distance. If RestDistance.IsRanged is false (fixed mode), returns the fixed distance. If RestDistance.IsRanged is true (ranged mode), returns the maximum distance.

func (*RestDistance) MinDist

func (r *RestDistance) MinDist() float64

MinDist returns the minimum distance for this rest distance. If RestDistance.IsRanged is false (fixed mode), returns the fixed distance. If RestDistance.IsRanged is true (ranged mode), returns the minimum distance.

func (*RestDistance) SetMaxDist

func (r *RestDistance) SetMaxDist(value float64)

SetMaximumDistance sets the maximum distance for this rest distance. If RestDistance.IsRanged is false (fixed mode), updates the RestDistance.Fixed value. If RestDistance.IsRanged is true (ranged mode), updates the max value.

func (*RestDistance) SetMinDist

func (r *RestDistance) SetMinDist(value float64)

SetMinDist sets the minimum distance for this rest distance. If RestDistance.IsRanged is false (fixed mode), updates the RestDistance.Fixed value. If RestDistance.IsRanged is true (ranged mode), updates the min value.

func (RestDistance) Squared

func (r RestDistance) Squared() RestDistance

Squared returns a new rest distance structure which represents the square of this rest distance's parameters. If RestDistance.IsRanged is false (fixed mode), returns a new fixed rest distance with squared RestDistance.Fixed value. If RestDistance.IsRanged is true (ranged mode), returns a new ranged rest distance with squared min and max.

type Shape

type Shape []Vec2

Shape contains a set of points that is equivalent as the local shape of a Body.

Points must be added in a counter-clockwise (CCW) fashion to align with screen space coordinates where the y-axis grows downwards, ensuring outward-facing edge normals.

func Rectangle

func Rectangle(w, h float64, cornerRatio ...float64) (s Shape)

Rectangle creates a rectangle shape with optional corner chamfering. Parameters:

  • w, h: width and height of the rectangle.
  • cornerRatio: optional, ratio of corner cut from 0.0 to 0.5. 0.5 means the cut reaches exactly the center of the shortest edge.

func RegularPolygon

func RegularPolygon(radius float64, n int) (s Shape)

RegularPolygon returns a new regular polygon shape with radius and n number of vertices. It can also be used to create circles. The polygon is rotated so that an edge is always flat at the bottom. Vertices are in CCW (counter-clockwise) order.

func ShapeFromCoords

func ShapeFromCoords(coords ...float64) (s Shape)

ShapeFromCoords creates a shape from a flat list of x, y, x, y, ... coordinates. Points must be given in counter-clockwise (CCW) order.

If the attribute contains an odd number of coordinates, the last one will be ignored.

	Example:
 ShapeFromCoords(0.29, 0.22, -0.09, -0.37, -0.3, 0.24)
 ShapeFromCoords([]float64{1, -1, 2, 4, 3, 1})

func ShapeFromSVGPath

func ShapeFromSVGPath(path string) Shape

ShapeFromSVGPath makes Shape from an SVG path data string ('d' attribute) Note: Only 'M' (MoveTo) and 'L' (LineTo) commands are supported.

https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d

If the attribute contains an odd number of coordinates, the last one will be ignored. Points must be given in counter-clockwise (CCW) order.

	Example:
 ShapeFromSVGPath("M 0.29 0.22 L -0.09 -0.37 L -0.3 0.24")

func ShapeFromSVGPolygonPoints

func ShapeFromSVGPolygonPoints(points string) (s Shape)

ShapeFromSVGPolygonPoints makes shape from SVG polygon points string.

https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/points

If the attribute contains an odd number of coordinates, the last one will be ignored. Points must be given in counter-clockwise (CCW) order.

	Example:
 ShapeFromSVGPolygonPoints("0.29 0.22 -0.09 -0.37 -0.3 0.24")

func Square

func Square(length float64) Shape

Square returns a shape that represents a square, with side of the specified length

func (*Shape) AddVertex

func (c *Shape) AddVertex(pos Vec2)

Adds a vertex to this shape

func (*Shape) AddVertexXY

func (c *Shape) AddVertexXY(x, y float64)

Adds a vertex to this shape

func (*Shape) Clone

func (c *Shape) Clone() Shape

func (*Shape) FitHeight

func (c *Shape) FitHeight(h float64)

FitHeight uniformly scales the shape so its height becomes exactly h.

func (*Shape) FitWidth

func (c *Shape) FitWidth(w float64)

FitWidth uniformly scales the shape so its width becomes exactly w.

func (Shape) IsCCW

func (c Shape) IsCCW() bool

IsCCW reports whether the shape's vertices are wound counter-clockwise (CCW) in screen space, where the Y axis grows downward. Returns false for shapes with fewer than 3 vertices, since winding is undefined.

func (Shape) PolygonPointsString

func (s Shape) PolygonPointsString(precision int) string

PolygonPointsString returns the vertices in the format used by the SVG <polygon> element's points, which is also the format accepted by ShapeFromSVGPolygonPoints. Precision specifies the number of digits after the decimal point.

func (*Shape) Recenter

func (c *Shape) Recenter()

Recenter re-centers the points of this shape in-place so its centroid lies at (0, 0).

func (*Shape) Reverse

func (c *Shape) Reverse()

Reverse reverses the vertices, so they rotate clockwise if they were counterclockwise, and counterclockwise if they were clockwise.

func (*Shape) Scale

func (c *Shape) Scale(x, y float64)

Scale scales vertices on this this shape inplace using a given x, y.

func (*Shape) TransformByMatrix

func (c *Shape) TransformByMatrix(m Matrix3x3)

TransformByMatrix transforms vertices on this this shape inplace using a given Matrix3x3.

func (*Shape) TransformByMatrixToTarget

func (c *Shape) TransformByMatrixToTarget(target []Vec2, matrix Matrix3x3)

TransformByMatrixToTarget transforms the points on this this shape using a given transformation matrix, applying the result into a given target slice of points.

  • note: The target slice of points must have the **same** count of vertices as this shape.

func (*Shape) TranslateVerticesToTarget

func (c *Shape) TranslateVerticesToTarget(target Shape, pos Vec2)
type ShapeJointLink struct {
	// The indices of this shape joint link
	Indexes []int
	/// The Offset to apply to the position of this shape joint, in body
	/// coordinates
	Offset Vec2
	// contains filtered or unexported fields
}

Represents a joint link that links to multiple point masses of a body

func NewShapeJointLink(body *Body, pointMassIndexes []int) *ShapeJointLink

ShapeJointLink returns new ShapeJointLink. See ShapeJointLink

func (*ShapeJointLink) AddVelocity

func (s *ShapeJointLink) AddVelocity(velocity Vec2)

AddVelocity adds a velocity delta at the offset point, distributing it across the indexed point masses the same way ApplyForce distributes a force — including the rotational component so a velocity change at an offset point produces angular motion around the shape's centroid, not just a uniform translation.

func (*ShapeJointLink) ApplyForce

func (s *ShapeJointLink) ApplyForce(force Vec2)

Applies a given force to the subject of this joint link

func (*ShapeJointLink) Body

func (b *ShapeJointLink) Body() *Body

func (*ShapeJointLink) IsStatic

func (s *ShapeJointLink) IsStatic() bool

IsStatic returns a value specifying whether the object referenced by this JointLinkType is static

func (*ShapeJointLink) Mass

func (s *ShapeJointLink) Mass() (mass float64)

Gets the total mass of the subject of this joint link

func (*ShapeJointLink) Position

func (s *ShapeJointLink) Position() Vec2

Position returns the position, in world coordinates, at which this joint links with the underlying body — the centroid of the indexed point masses, plus Offset rotated into world space by the shape's current deformation angle (so the offset point follows the body's rotation, not just its centroid).

func (*ShapeJointLink) SubDerivedAngle

func (s *ShapeJointLink) SubDerivedAngle() float64

SubDerivedAngle returns the average angle of the vertices of this ShapeJointLink, based on the body's original shape's vertices.

This represents the local deformation angle of this specific point mass subset, which may differ from the body's overall derivedAngle in a soft body where different regions can rotate independently.

func (*ShapeJointLink) Translate

func (s *ShapeJointLink) Translate(offset Vec2)

Applies a direct positional translation of this joint link by a given offset

func (*ShapeJointLink) Velocity

func (s *ShapeJointLink) Velocity() (vel Vec2)

Velocity returns the velocity of the object this joint links to — the average velocity of the indexed point masses (centroid velocity), plus the rotational contribution from Offset, computed as ω × r using the body's current angular velocity. This keeps Velocity() consistent with the offset point returned by Position(): both describe the same physical point on the rotating body, not just the centroid.

type ShapeMatchComponent

type ShapeMatchComponent struct {
	// The spring stiffness for all points used to pull points back toward the base shape
	Stiffness float64
	// The spring damping for all points used to pull points back toward the base shape
	Damping float64
	// The point mass indices to match against; empty means match the full shape
	TargetIndices []int
	// contains filtered or unexported fields
}

ShapeMatchComponent pulls a body's point masses back toward its original base shape using spring forces, so the body tends to retain its shape after deformation. It can target the full shape or a subset of points.

func DefaultShapeMatchComponent

func DefaultShapeMatchComponent() *ShapeMatchComponent

DefaultShapeMatchComponent returns a full-shape ShapeMatchComponent with default spring constants.

func NewShapeMatchComponent

func NewShapeMatchComponent(stiffness, damping float64, indices []int) *ShapeMatchComponent

NewShapeMatchComponent returns a new ShapeMatchComponent that pulls a body (or a subset of its points) back toward its original base shape using a spring force. Pass nil (or an empty slice) for indices to match the full shape, or a list of point mass indices to match only that subset.

func (*ShapeMatchComponent) AccumulateExternalForces

func (b *ShapeMatchComponent) AccumulateExternalForces(body *Body, world *World)

func (*ShapeMatchComponent) AccumulateInternalForces

func (s *ShapeMatchComponent) AccumulateInternalForces(body *Body, relaxing bool)

AccumulateInternalForces applies shape-matching spring forces if shape matching is enabled and the spring constant is positive.

func (*ShapeMatchComponent) Disabled

func (b *ShapeMatchComponent) Disabled() bool

func (*ShapeMatchComponent) Prepare

func (b *ShapeMatchComponent) Prepare(body *Body)

func (*ShapeMatchComponent) SetTarget

func (s *ShapeMatchComponent) SetTarget(indices []int)

SetTarget changes what the component matches against: pass nil (or an empty slice) to match the full shape, or a list of point mass indices to match only that subset.

type Spring

type Spring struct {
	// Rest distance of the spring, or the distance the spring tries to maintain.
	RestDistance RestDistance
	// Stiffness is the spring stiffness.
	Stiffness float64
	// Damping is the spring damping.
	Damping float64
	// Plasticity specifies the plasticity properties of this spring.
	// If nil, plasticity is disabled and spring never deforms permanently.
	Plasticity *SpringPlasticity
	// Spring type
	Type SpringType
}

Spring represents an spring and keeps points close together.

func (*Spring) UpdatePlasticity

func (s *Spring) UpdatePlasticity(distance float64)

UpdatePlasticity updates the plasticity settings of this spring. Does nothing, if plasticity is not configured.

type SpringComponent

type SpringComponent struct {
	// The number of default edge springs built between consecutive point masses
	EdgeSpringsCount int
	// All springs belonging to this component (edge springs first, then extra springs) See [SpringComponent.AddExtraSpring]
	Springs []*InternalSpring
	// The stiffness constant
	DefaultStiffness float64
	// The damping constant
	DefaultDamping float64
	// contains filtered or unexported fields
}

SpringComponent adds spring-based physics to a body's point masses.

By default it places a spring between every pair of consecutive point masses along the body's outline (i.e. one spring per edge, wrapping around to close the shape), plus any extra springs added manually via SpringComponent.AddExtraSpring. Each spring pulls its two endpoints back toward a rest distance whenever they drift apart or are pushed together, which is what keeps neighboring points from separating or overlapping.

func DefaultSpringComponent

func DefaultSpringComponent() *SpringComponent

Returns default SpringComponent

func NewSpringComponent

func NewSpringComponent(stiffness, damping float64) *SpringComponent

NewSpringComponent returns new SpringComponent.

func (*SpringComponent) AccumulateExternalForces

func (b *SpringComponent) AccumulateExternalForces(body *Body, world *World)

func (*SpringComponent) AccumulateInternalForces

func (s *SpringComponent) AccumulateInternalForces(body *Body, relaxing bool)

AccumulateInternalForces applies each spring's force between its two point masses, clamping the rest distance to the spring's actual current distance when applicable, and updates spring plasticity (permanent deformation) when not relaxing. AccumulateInternalForces applies each spring's force between its two point masses, clamping the rest distance to the spring's actual current distance when applicable, and updates spring plasticity (permanent deformation) when not relaxing.

func (*SpringComponent) AddExtraSpring

func (s *SpringComponent) AddExtraSpring(body *Body, pointA, pointB int, stiffness, damping float64, plasticity *SpringPlasticity) *InternalSpring

AddExtraSpring adds an internal spring to this body. plasticity is an optional argument and may be nil.

func (*SpringComponent) ApplyDefaults

func (s *SpringComponent) ApplyDefaults()

ApplyDefaults sets the SpringComponent.DefaultStiffness and SpringComponent.DefaultDamping values ​​for all springs.

func (*SpringComponent) ClearAllSprings

func (s *SpringComponent) ClearAllSprings(body *Body)

ClearAllSprings removes all springs (including any added via SpringComponent.AddExtraSpring

func (*SpringComponent) Disabled

func (b *SpringComponent) Disabled() bool

func (*SpringComponent) Prepare

func (s *SpringComponent) Prepare(body *Body)

Prepare rebuilds all internal springs for the body, discarding any previously added springs and re-creating the default edge springs.

func (*SpringComponent) SetAll

func (s *SpringComponent) SetAll(stiffness, damping float64)

SetAll sets the stiffness and damping of all springs (edge + extra)

func (*SpringComponent) SetAllEdges

func (s *SpringComponent) SetAllEdges(stiffness, damping float64)

SetAllEdges sets the stiffness and damping of every edge springs

func (*SpringComponent) SetExtraAt

func (s *SpringComponent) SetExtraAt(relativeIndex int, stiffness, damping float64)

SetExtraAt sets the stiffness and damping of an extra (non-edge) spring. relativeIndex is the spring's position among the extra springs only (0 = first extra spring added after the edge springs), not its index in the full Springs slice. See SpringComponent.AddExtraSpring

func (*SpringComponent) SetSpringPlasticity

func (s *SpringComponent) SetSpringPlasticity(relativeIndex int, plasticity *SpringPlasticity)

type SpringJoint

type SpringJoint struct {
	Spring
	// contains filtered or unexported fields
}

SpringJoint represents a joint that links two JointLink's with spring forces

func NewSpringJoint

func NewSpringJoint(a, b JointLink, coefficient, damping float64, distance ...RestDistance) *SpringJoint

func (*SpringJoint) CollisionsAllowed

func (j *SpringJoint) CollisionsAllowed() bool

func (*SpringJoint) LinkA

func (j *SpringJoint) LinkA() JointLink

func (*SpringJoint) LinkB

func (j *SpringJoint) LinkB() JointLink

func (*SpringJoint) Resolve

func (j *SpringJoint) Resolve(dt float64)

Resolve resolves this joint

dt is the delta time to update the resolve on

func (*SpringJoint) SetPlasticity

func (j *SpringJoint) SetPlasticity(p *SpringPlasticity)

SetPlasticity enables plasticity for this joint, using the joint's current rest distance as the reference point for the plasticity limit. Pass nil to disable plasticity.

type SpringPlasticity

type SpringPlasticity struct {
	// YieldRatio is the ratio (of resting distance vs actual length) before plasticity starts
	// to change the resting length of the spring, deforming it permanently.
	YieldRatio float64

	// Rate is the plasticity rate for the spring.
	// When the rest distance of a spring goes past its yield limit, the
	// resting distance of the spring is stretched so it deforms 'plastically'
	// by adapting the resting length to be the resulting factor between the
	// rest length and the actual length, times this rate.
	Rate float64

	// Limit is a factor limit at which the plasticity stops affecting the rest length
	// of the spring beyond its initial rest length.
	Limit float64
	// contains filtered or unexported fields
}

SpringPlasticity specifies plasticity properties of a spring.

Plasticity permanently affects a spring's rest length by modifying it when its length is stretched beyond a certain limit.

func DefaultSpringPlasticity

func DefaultSpringPlasticity() *SpringPlasticity

DefaultSpringPlasticity returns a SpringPlasticity with default values.

func NewSpringPlasticity

func NewSpringPlasticity(yieldRatio float64, rate float64, limit float64) *SpringPlasticity

NewSpringPlasticity creates a new SpringPlasticity with the given values.

type SpringType

type SpringType uint8

SpringType identifies what a spring connection represents within the simulation, distinguishing default perimeter springs, extra internal springs, and springs used by joints between separate bodies.

const (
	// EdgeSpring is a default spring between two consecutive point masses
	// along a body's outline (perimeter), automatically created to keep
	// the shape's outline together.
	EdgeSpring SpringType = iota
	// ExtraSpring is an extra spring added between two point masses
	// that are not necessarily adjacent on the outline, used to connect
	// edges to each other and help the body resist internal deformation
	// (e.g. shape-holding cross braces). Added via [SpringComponent.AddExtraSpring].
	ExtraSpring
	// JointSpring is the spring used by a [SpringBodyJoint], connecting two
	// joint links — typically belonging to two separate bodies — rather
	// than two point masses within the same body.
	JointSpring
)

type StickyRayComponent

type StickyRayComponent struct {

	// IgnoreJoinedBodies skips raycasting against bodies connected to this
	// one via a Joint, avoiding redundant sticky forces on an existing joint.
	IgnoreJoinedBodies bool
	// RayLength is the length of the ray cast out from each point mass
	RayLength float64
	// Stiffness is the stiffness of the spring pulling toward the hit edge
	Stiffness float64
	// Damping is the damping applied to the spring force
	Damping float64
	// contains filtered or unexported fields
}

StickyRayComponent sticks a body to whatever other body its point masses' rays hit. For each point mass, a short ray is cast outward along that point's normal; if it hits another body, the closest point on that body's nearest edge is found, and the originating point mass is pulled toward that point with a spring force (like tape/velcro between two soft bodies). The target is not the middle of the edge — it's whichever point along the edge sits closest to where the ray actually hit.

func DefaultStickyRayComponent

func DefaultStickyRayComponent() *StickyRayComponent

DefaultStickyRayComponent returns a StickyRayComponent with default settings

func NewStickyRayComponent

func NewStickyRayComponent(rayLength, stiffness, damping float64, ignoreJoinedBodies bool) *StickyRayComponent

NewStickyRayComponent creates a new StickyRayComponent See StickyRayComponent for info

func (*StickyRayComponent) AccumulateExternalForces

func (s *StickyRayComponent) AccumulateExternalForces(body *Body, world *World)

AccumulateExternalForces applies sticky rays to other bodies

func (*StickyRayComponent) AccumulateInternalForces

func (b *StickyRayComponent) AccumulateInternalForces(body *Body, worldRelaxing bool)

func (*StickyRayComponent) Disabled

func (b *StickyRayComponent) Disabled() bool

func (*StickyRayComponent) Prepare

func (b *StickyRayComponent) Prepare(body *Body)

type UnimplementedCollisionObserver

type UnimplementedCollisionObserver struct{}

UnimplementedCollisionObserver is a no-op CollisionObserver that can be embedded to satisfy the CollisionObserver interface without implementing every method.

func (*UnimplementedCollisionObserver) BodiesDidCollide

func (u *UnimplementedCollisionObserver) BodiesDidCollide(infos []CollisionInfo)

BodiesDidCollide is a no-op implementation of CollisionObserver.

func (*UnimplementedCollisionObserver) BodyCollision

func (u *UnimplementedCollisionObserver) BodyCollision(info CollisionInfo, penetrationThreshold float64)

BodyCollision is a no-op implementation of CollisionObserver.

type Vec2

type Vec2 struct {
	X, Y float64
}

Vec2 represents a 2D vector with X and Y components.

func AveragePointMassPosition

func AveragePointMassPosition(pointMasses []*PointMass) (centroid Vec2)

AveragePointMassPosition returns the centroid (mean position) of pointMasses. Returns the zero Vec2 if pointMasses is empty.

func AveragePointMassVelocity

func AveragePointMassVelocity(pointMasses []*PointMass) (average Vec2)

AveragePointMassVelocity returns the mean velocity across pointMasses. Returns the zero Vec2 if pointMasses is empty.

func AverageVec2

func AverageVec2(vectors []Vec2) (average Vec2)

AverageVec2 returns the mean of the given vectors. Returns the zero Vec2 if vectors is empty.

func CalcSpringForce

func CalcSpringForce(
	posA, velA, posB, velB Vec2,
	distance, stiffness, damping float64,
) Vec2

CalcSpringForce computes the force exerted by a damped spring connecting two points (posA, velA) and (posB, velB), given the spring's rest length (distance), stiffness, and damping factor. The returned force is directed along the axis between the two points and should be applied to posA (and its negation to posB). Returns the zero Vec2 if the two points are closer than epsilonSpring, to avoid dividing by a near-zero distance.

func FromAngle

func FromAngle(angle float64) Vec2

FromAngle makes a new 2D unit vector from an angle

func (Vec2) Abs

func (v Vec2) Abs() Vec2

Abs returns the absolute value of vector.

func (Vec2) AbsX

func (v Vec2) AbsX() float64

AbsX returns the absolute X value of vector.

func (Vec2) AbsY

func (v Vec2) AbsY() float64

AbsY returns the absolute Y value of vector.

func (Vec2) Add

func (v Vec2) Add(a Vec2) Vec2

Add returns this + a

func (Vec2) AddX

func (v Vec2) AddX(n float64) Vec2

Add adds n to v.X

func (Vec2) AddY

func (v Vec2) AddY(n float64) Vec2

Add adds n to v.Y

func (Vec2) Angle

func (v Vec2) Angle() float64

Angle returns the angular direction v is pointing in (in radians).

func (Vec2) AngleTo

func (v Vec2) AngleTo(other Vec2) float64

AngleTo returns the angle to the given vector, in radians.

func (Vec2) Ceil

func (v Vec2) Ceil() Vec2

Ceil returns vector with all components rounded up (towards positive infinity).

func (Vec2) Cross

func (v Vec2) Cross(other Vec2) float64

Cross calculates the 2D vector cross product analog. The cross product of 2D vectors results in a 3D vector with only a z component. This function returns the magnitude of the z value.

func (Vec2) Dist

func (v Vec2) Dist(other Vec2) float64

Dist returns distance between v and other.

func (Vec2) DistSq

func (v Vec2) DistSq(other Vec2) float64

DistSq returns the squared distance between this and other.

Faster than v.Dist() when you only need to compare distances.

func (Vec2) Div

func (v Vec2) Div(a Vec2) Vec2

Div divides this vector by a.

func (Vec2) DivS

func (v Vec2) DivS(s float64) Vec2

DivS divides this vector by scalar value s.

func (Vec2) Dot

func (v Vec2) Dot(other Vec2) float64

Dot returns dot product

func (Vec2) Equals

func (v Vec2) Equals(other Vec2) bool

Equals checks if two vectors are equal. (Be careful when comparing floating point numbers!)

func (Vec2) EqualsPr

func (v Vec2) EqualsPr(other Vec2, allowedDelta float64) bool

EqualsP returns they are practically equal with each other within a delta tolerance.

func (Vec2) Floor

func (v Vec2) Floor() Vec2

Floor returns vector with all components rounded down (towards negative infinity).

func (Vec2) IsZero

func (v Vec2) IsZero() bool

IsZero returns true if vector is zero vector

func (Vec2) Lerp

func (v Vec2) Lerp(other Vec2, t float64) Vec2

Lerp linearly interpolates between this and other vector.

func (Vec2) Limit

func (v Vec2) Limit(max float64) Vec2

Limits a vector's magnitude to a maximum value.

func (Vec2) Mag

func (v Vec2) Mag() float64

Mag returns the magnitude (length) of the vector.

func (Vec2) MagSq

func (v Vec2) MagSq() float64

MagSq returns the magnitude (length) of the vector, squared.

This method is often used to improve performance since, unlike Mag(), it does not require a Sqrt() operation.

func (Vec2) Max

func (v Vec2) Max(other Vec2) Vec2

Max returns the component-wise maximum of two vectors.

func (Vec2) Min

func (v Vec2) Min(other Vec2) Vec2

Min returns the component-wise minimum of two vectors.

func (Vec2) Mul

func (v Vec2) Mul(a Vec2) Vec2

Mul returns this * a

func (Vec2) Neg

func (v Vec2) Neg() Vec2

Neg negates a vector.

func (Vec2) NegX

func (v Vec2) NegX() Vec2

NegY negates X.

func (Vec2) NegY

func (v Vec2) NegY() Vec2

NegY negates Y.

func (Vec2) Perp

func (v Vec2) Perp() Vec2

Perp returns the perpendicular vector rotated 90 degrees counter-clockwise.

func (Vec2) Project

func (v Vec2) Project(other Vec2) Vec2

Returns the vector projection onto other.

func (Vec2) Reflect

func (v Vec2) Reflect(normal Vec2) Vec2

Reflect returns the reflection of the vector v over the given normal. normal should be a normalized (unit) vector.

func (Vec2) Rotate

func (v Vec2) Rotate(angle float64) Vec2

Rotate a vector by an angle in radians

func (Vec2) Round

func (v Vec2) Round() Vec2

Round returns the nearest integer Vector, rounding half away from zero.

func (Vec2) Scale

func (v Vec2) Scale(s float64) Vec2

Scale scales vector

func (Vec2) SetMag

func (v Vec2) SetMag(m float64) Vec2

SetMag sets the magnitude (length) of the vector.

func (Vec2) Slerp

func (v Vec2) Slerp(to Vec2, weight float64) Vec2

Slerp performs spherical linear interpolation between two vectors with given weight value in [0,1] range, returning interpolated vector

func (Vec2) String

func (v Vec2) String() string

String returns string representation of this vector.

func (Vec2) Sub

func (v Vec2) Sub(a Vec2) Vec2

Sub returns this - a

func (Vec2) Unit

func (v Vec2) Unit() Vec2

Unit returns a normalized copy of this vector (unit vector).

type World

type World struct {
	// The bodies contained within this world
	Bodies []*Body
	// The joints contained within this world
	Joints []Joint
	// MaterialPairs is a 2D lookup table used to resolve collisions between different materials.
	// It is accessed using material IDs (e.g., MaterialPairs[idA][idB]) to determine
	// shared physical properties like friction, bounciness, and custom collision rules.
	MaterialPairs [][]MaterialPair
	// The default material pair for newly created materials
	DefaultMatPair MaterialPair
	// The object to report collisions to
	CollisionObserver CollisionObserver
	// The threshold at which penetrations are ignored, since they are far too
	// deep to be resolved without applying unreasonable forces that will
	// destabilize the simulation. Default is 0.3
	PenetrationThreshold float64
	// contains filtered or unexported fields
}

func NewWorld

func NewWorld() *World

NewWorld inits an returns empty world

func (*World) AddBodies

func (w *World) AddBodies(bodies ...*Body)

AddBodies adds bodies to the world.

func (*World) AddBody

func (w *World) AddBody(body *Body)

AddBody adds a Body to the World.

func (*World) AddJoint

func (w *World) AddJoint(joint Joint)

Adds a joint to the world. Joints call this automatically during their initialization

func (*World) AddMaterial

func (w *World) AddMaterial() int

Adds a new material to the world. All previous material data is kept intact.

func (*World) AreBodiesJoined

func (w *World) AreBodiesJoined(body1, body2 *Body) bool

Returns `true` if the two given bodies are joined to one another. SONRA

func (*World) BodiesIntersectingLine

func (w *World) BodiesIntersectingLine(start, end Vec2, bitmask Bitmask) []*Body

Returns a vector of bodies intersecting with the given line.

func (*World) BodiesIntersectingShape

func (w *World) BodiesIntersectingShape(
	shape Shape,
	worldPos Vec2,
	ignoreTest func(*Body) bool,
	outResults []*Body,
	tempBuffer Shape,
) []*Body

BodiesIntersectingShape returns all bodies that overlap a given shape at a specified point in world coordinates.

This method is optimized for zero-allocation performance. It utilizes pre-allocated slices for both the result set and the vertex transformations to prevent heap allocations and reduce Garbage Collector overhead during the game loop.

Parameters:

  • Shape: A shape that represents the segments to query. Must contain at least 2 vertices.
  • worldPos: The location in world coordinates to apply to the shape when performing the query.
  • ignoreTest: An optional function applied to every body intersecting the shape to filter out results. If the function returns true, the body is ignored. Defaults to nil.
  • outResults: A pre-allocated slice where the intersecting bodies will be appended. To reuse this slice across frames, it should be cleared beforehand (e.g., results = results[:0]).
  • tempBuffer: A pre-allocated slice used internally to store the transformed vertices of the shape. Its capacity must be greater than or equal to the number of vertices in the shape.

Returns:

  • A slice containing all bodies that intersect with the shape. This is the populated 'outResults' slice. If the shape contains less than 2 points, the unmodified 'outResults' slice is returned.

func (*World) BodiesUnder

func (w *World) BodiesUnder(pt Vec2, bitmask Bitmask) []*Body

Given a global point, returns all bodies that contain this point. Useful for picking objects with a cursor, etc.

func (*World) BodyUnder

func (w *World) BodyUnder(pt Vec2, bitmask Bitmask) *Body

Given a global point, returns a body (if any) that contains this point. Useful for picking objects with a cursor, etc.

func (*World) ClosestPoint

func (w *World) ClosestPoint(pt Vec2, ignoreFunction func(*Body) bool) (closestBody *Body, closestHitPoint Vec2, found bool)

ClosestPoint returns the closest body and the nearest point on its surface to the given position. The hit point always lies on the body's boundary. Bodies can be excluded using ignoreFunction.

func (*World) ClosestPointMass

func (w *World) ClosestPointMass(pt Vec2, ignoreFunction func(*Body, int) bool) (*Body, int, bool)

Finds the closest PointMass in the world to a given point

func (*World) IsRelaxing

func (w *World) IsRelaxing() bool

Is world in relaxing phase?

func (*World) RayCast

func (w *World) RayCast(start, end Vec2, bitmask Bitmask, ignoreTest func(*Body) bool) (retPt Vec2, body *Body)

Casts a ray between the given points and returns the first body it comes in contact with.

Parameters:

  • start: The start point to cast the ray from, in world coordinates
  • end: The end point to end the ray cast at, in world coordinates
  • bitmask: An optional collision bitmask that filters the bodies to collide using a bitwise AND (&) operation. If the value specified is 0, collision filtering is ignored and all bodies are considered for collision.
  • ignoreTest: Optional function that will be called for each body along the way (not guaranteed to execute in order of farthest to closest body) that tests whether the body should be ignored during ray casting.

Returns:

  • An optional tuple containing the farthest point reached by the ray, and a Body value specifying the body that was closest to the ray, if it hit any body, or nil if it hit nothing.

func (*World) RelaxBodies

func (w *World) RelaxBodies(bodies []*Body, timestep float64, iterations int)

Relaxes a list of bodies in this simulation so they match a more approximate rest shape once simulation starts.

This will move or change the position of each body after iterations are done. It performs collisions and joint resolving of only the bodies or joints that are related to the bodies array, and resets the velocities to 0 before finishing.

Only body joints that involve bodies contained within the passed body list are executed. Joints that involve a body within this list and another body that is not in the list are not resolved during relaxation.

All body joints, velocities, and components are executed, except those with relaxable == false.

Parameters:

  • bodies: The list of bodies to relax.
  • iterations: The number of iterations of relaxation to apply.
  • timestep: The timestep (in seconds) of each iteration.

Precondition: iterations > 0.

func (*World) RelaxWorld

func (w *World) RelaxWorld(timestep float64, iterations int)

Relaxes all bodies in this simulation so they match a more approximate rest shape once simulation starts.

This will move or change the position of each body after iterations are done. It performs collisions and joint resolving, and resets the velocities to 0 before finishing.

All body joints, velocities, and components are executed, except those with relaxable == false.

Parameters:

  • iterations: The number of iterations of relaxation to apply.
  • timestep: The timestep (in seconds) of each iteration.

Precondition: iterations > 0.

func (*World) RemoveBody

func (w *World) RemoveBody(body *Body)

Removes a body from the world. Call this outside of an update to remove the body.

func (*World) RemoveJoint

func (w *World) RemoveJoint(joint Joint)

Removes a joint from the world

func (*World) Reset

func (w *World) Reset()

Reset resets the world's contents to their initial state and readies it to be loaded again.

func (*World) SetMaterialPairCollide

func (w *World) SetMaterialPairCollide(a, b int, collide bool)

Enables or disables collision between 2 materials.

func (*World) SetMaterialPairData

func (w *World) SetMaterialPairData(a, b int, friction, elasticity float64)

Sets the collision response variables for a pair of materials.

func (*World) SetMaterialPairFilterCallback

func (w *World) SetMaterialPairFilterCallback(a, b int, filter func(CollisionInfo, float64) bool)

Sets a user function to call when 2 bodies of the given materials collide.

func (*World) SetWorldLimits

func (w *World) SetWorldLimits(min, max Vec2)

SetWorldLimits sets the boundaries of the simulation world. The world is divided into a grid for broad-phase collision detection, with the grid step size calculated based on the world size and subdivision count. The world is divided into worldGridSubdivision x worldGridSubdivision cells (default is 64x64 = 4096 cells).

Parameters:

  • min: The minimum corner of the world bounds.
  • max: The maximum corner of the world bounds.

func (*World) Update

func (w *World) Update(elapsed float64)

Updates the world by a specific timestep. This method performs body point mass force/velocity/position simulation, and collision detection & resolving.

Parameters:

  • elapsed: The elapsed time to update by, usually in 1/60ths of a second.

func (*World) WorldLimits

func (w *World) WorldLimits() AABB

Returns world limits

Directories

Path Synopsis
examples module

Jump to

Keyboard shortcuts

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