simvx.core.physics.shapes

Role

A Shape is a plain collision-geometry resource (a value), not a scene node. It is held by a :class:~simvx.core.physics.nodes.CollisionShape3D node (via a Property) the way the old bodies held a radius/extents field. A body never branches on shape kind: it calls shape.build(world) and each subclass dispatches to the seam’s matching world.create_* factory. This keeps body nodes open/closed over new shape kinds.

Stage 3a status

Additive and non-breaking. The concrete classes are dimension-suffixed SphereShape3D / BoxShape3D (the abstract Shape base stays un-suffixed, being dimensionless), distinct from the GJK CollisionShape subclasses in collision.py. They live ONLY here. They are NOT exported via physics/__init__.py or core/__init__.py: tests import them via this module path. The facade flip + removal of the old shapes is Stage 4.

Shape resources for the new physics seam (Stage 3a).

Module Contents

Classes

Shape

Abstract collision-geometry resource.

SphereShape3D

A sphere collision shape of a given radius.

BoxShape3D

An axis-aligned box collision shape centred at the origin.

CapsuleShape3D

A Y-axis capsule collision shape (a segment swept by a sphere).

CylinderShape3D

A Y-axis cylinder collision shape.

ConvexHullShape3D

A convex-hull collision shape defined by a point cloud (Tier-1).

ConcaveMeshShape3D

A static triangle-mesh collision shape (level geometry, Tier-1 static-only).

Data

API

simvx.core.physics.shapes.__all__

[‘Shape’, ‘SphereShape3D’, ‘BoxShape3D’, ‘CapsuleShape3D’, ‘CylinderShape3D’, ‘ConvexHullShape3D’, ‘…

class simvx.core.physics.shapes.Shape[source]

Bases: abc.ABC

Abstract collision-geometry resource.

A Shape knows how to turn itself into an opaque seam shape handle via

Meth:

build. Bodies call shape.build(world) and never inspect the concrete kind, so adding a new shape requires no body changes.

abstractmethod build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]

Create the backend shape for this resource and return its handle.

Each subclass dispatches to the matching world.create_* factory.

Args: world: The :class:~simvx.core.physics.world.PhysicsWorld whose shape factory builds the opaque handle.

Returns: An opaque ShapeHandle for use with world.create_body.

abstract property bounding_radius: float[source]

Radius of the shape’s origin-centred bounding sphere, in local units.

A conservative, rotation-invariant size used for cheap broad-phase / CPU picking (a sphere test against the shape’s extent). Each subclass returns the radius of the smallest sphere centred on the shape’s local origin that contains it.

__slots__

()

class simvx.core.physics.shapes.SphereShape3D(radius: float = 0.5)[source]

Bases: simvx.core.physics.shapes.Shape

A sphere collision shape of a given radius.

Args: radius: Sphere radius in world units (must be > 0).

Initialization

build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]
property bounding_radius: float[source]
__slots__

()

class simvx.core.physics.shapes.BoxShape3D(half_extents: simvx.core.math.Vec3 | tuple[float, float, float] = (0.5, 0.5, 0.5))[source]

Bases: simvx.core.physics.shapes.Shape

An axis-aligned box collision shape centred at the origin.

Args: half_extents: Half-sizes along x/y/z. Coerced to Vec3 (float32); every component must be > 0.

Initialization

build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]
property bounding_radius: float[source]
__slots__

()

class simvx.core.physics.shapes.CapsuleShape3D(radius: float = 0.5, height: float = 2.0)[source]

Bases: simvx.core.physics.shapes.Shape

A Y-axis capsule collision shape (a segment swept by a sphere).

height is the TOTAL extent along Y, including the two hemispherical caps, so the central segment half-length is max(0.0, height / 2 - radius) and the segment endpoints are centre +- [0, half_len, 0]. When height <= 2 * radius the segment collapses to a point and the capsule degenerates to a sphere of the given radius: that is a valid, documented case (matching Godot’s capsule and the segment-reduction maths in the builtin backend), NOT an error. Accordingly height >= 2 * radius is NOT validated.

Args: radius: Capsule radius in world units (must be > 0). height: Total extent along Y including both caps (must be > 0).

Initialization

build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]
property bounding_radius: float[source]
__slots__

()

class simvx.core.physics.shapes.CylinderShape3D(radius: float = 0.5, height: float = 2.0)[source]

Bases: simvx.core.physics.shapes.Shape

A Y-axis cylinder collision shape.

height is the TOTAL extent along Y, with flat circular top/bottom caps at +-height / 2.

Args: radius: Cylinder radius in world units (must be > 0). height: Total extent along Y (must be > 0).

Initialization

build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]
property bounding_radius: float[source]
__slots__

()

class simvx.core.physics.shapes.ConvexHullShape3D(points: collections.abc.Sequence[simvx.core.math.Vec3 | tuple[float, float, float]])[source]

Bases: simvx.core.physics.shapes.Shape

A convex-hull collision shape defined by a point cloud (Tier-1).

The hull is the convex hull of points; the resource stores the raw cloud and the backend computes its own representation. At least 4 points are required (a 3D hull needs a tetrahedron). A coplanar / collinear cloud is accepted (it is finite and has >= 4 points) but yields a degenerate hull whose penetration depth is approximate: a full coplanarity test is deliberately NOT done at the resource layer (the backend tolerates degenerate clouds).

Basic-tier honesty (see builtin/world.py): the BuiltinPhysics backend does GJK for overlap (exact) and EPA-lite for penetration depth/normal (bounded iteration, documented approximation); hull rotation is ignored. The Jolt backend does a proper hull.

Args: points: Iterable of >= 4 points (Vec3 or (x, y, z) tuples). Coerced to a single (N, 3) float32 array. Must be finite.

Initialization

classmethod from_mesh(vertices: collections.abc.Sequence[simvx.core.math.Vec3 | tuple[float, float, float]], indices: collections.abc.Sequence[int] | None = None) simvx.core.physics.shapes.ConvexHullShape3D[source]

Build a hull from a mesh’s VERTEX CLOUD (opt-in, per design S3).

The convex hull of a mesh equals the convex hull of its vertex cloud, so indices are ignored: this is a cheap, honest hull-from-mesh with no convex decomposition. Pass the mesh vertices and the topology is irrelevant to the result.

Args: vertices: The mesh vertex cloud ((N, 3) after coercion). indices: Ignored (the hull is independent of triangle topology).

Returns: A ConvexHullShape3D over the vertex cloud.

build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]
property bounding_radius: float[source]
__slots__

()

class simvx.core.physics.shapes.ConcaveMeshShape3D(vertices: collections.abc.Sequence[simvx.core.math.Vec3 | tuple[float, float, float]], indices: collections.abc.Sequence[int])[source]

Bases: simvx.core.physics.shapes.Shape

A static triangle-mesh collision shape (level geometry, Tier-1 static-only).

Holds a triangle-list mesh as a vertex array plus a flat index array (three indices per triangle). This collider is STATIC-ONLY: placing it on a non-STATIC body is an error raised at body creation (every serious engine enforces this, including Jolt). It has no inertia / mass.

The Shape resource itself is a pure value with no static-mode awareness; the static-only contract is enforced at the world seam (create_body / set_body_mode), not here.

Args: vertices: Vertex positions, coerced to a (N, 3) float32 array; finite. indices: Flat triangle-list indices, coerced to a (3 * T,) int64 array. len(indices) must be a non-zero multiple of 3 and every index in [0, N).

Initialization

build(world: simvx.core.physics.world.PhysicsWorld) simvx.core.physics.world.ShapeHandle[source]
property bounding_radius: float[source]
__slots__

()