Skip to main content

Shape

C++: phynexis::utils::shape::Shape Python: phynexis.utils.Shape (alias), phynexis.utils.shape.Shape Header: src/utils/shape/shape.hpp

Base class for all geometric shapes in phynexis. Provides common interface for volume, inertia, bounding box, signed distance, surface sampling, and serialization. Subclasses implement specific geometries (sphere, cuboid, ellipsoid, etc.).

Constructor

Shape()

Creates a default shape (sphere-like defaults: size=1.0, volume=0.5236).

Properties

PropertyTypeAccessDescription
tagintread-onlyGeometry family tag (see Tag enum)
shape_typeintread-onlyAlias for tag
featuresintread-onlyCapability/feature flags
namestrread-onlyRegistered shape name
render_meshSTLModelread-onlySurface mesh for rendering

Methods

size()

Return shape size (diameter of equal-volume sphere).

Returns: float

volume()

Return the volume of the shape.

Returns: float

inertia()

Return the inertia tensor.

Returns: Mat3d

inertia_principal()

Return principal moments of inertia as a Vec3d.

Returns: Vec3d

bound_sphere_radius()

Return bounding sphere radius for broad-phase contact detection.

Returns: float

bound_aabb() / bound_aabb(pos, quat)

Return axis-aligned bounding box as (min, max) tuple.

Parameters (overloaded):

ParameterTypeDescription
posVec3dPosition offset
quatVec4dQuaternion orientation

Returns: tuple[Vec3d, Vec3d]

is_convex()

Check if the shape is convex.

Returns: bool

signature()

Return a stable integer signature for solver lookup and caching.

Returns: int

signed_distance(pos)

Compute signed distance from a point to the shape surface.

Parameters:

ParameterTypeDescription
posVec3dQuery point

Returns: float

enclose(pos)

Check if a point is inside the shape.

Parameters:

ParameterTypeDescription
posVec3dQuery point

Returns: bool

support_point(dir)

Get the support point in a given direction.

Parameters:

ParameterTypeDescription
dirVec3dDirection vector

Returns: Vec3d

surface_projection(pos)

Find the closest surface point to an intruding node.

Parameters:

ParameterTypeDescription
posVec3dQuery point

Returns: Vec3d

get_stl_model(num_nodes=200)

Generate an STL model representation of the shape.

Parameters:

ParameterTypeDefaultDescription
num_nodesint200Number of nodes for mesh generation

Returns: STLModel

update_surface_nodes() / update_render_mesh(num_nodes=200) / update_shape_properties()

Refresh internal data (surface nodes, render mesh, cached properties).

enable_surface_nodes() / disable_surface_nodes() / surface_nodes_enabled()

Toggle and query surface node usage for contact solver.

surface_node_num() / surface_node_spacing()

Return surface node count and spacing.

Returns: int / float

surface_nodes() / surface_node_areas()

Access surface node positions and per-node areas.

Returns: list[Vec3d] / list[float]

sample_surface_points(n)

Sample random points on the shape surface.

Parameters:

ParameterTypeDescription
nintNumber of points to sample

Returns: list[Vec3d]

support_points(dir)

Get support points (all contact-relevant points in a direction).

Parameters:

ParameterTypeDescription
dirVec3dDirection vector

Returns: list[Vec3d]

set_size(d) / set_surface_node_num(num)

Set shape size or surface node count.

set_surface_nodes(nodes)

Set surface node positions directly.

Parameters:

ParameterTypeDescription
nodeslist[Vec3d]Surface node positions

transpose(pos)

Translate the shape to a new position.

Parameters:

ParameterTypeDescription
posVec3dNew position

clone()

Create a copy of the shape.

Returns: Shape

check_principal()

Check if the shape is in principal orientation.

Returns: bool

Print shape properties to console.

save_to(path, file, opt=SaveOptions()) / load_from(path, file, opt=LoadOptions())

Save/load shape to/from file.

to_json() / from_json(js)

Serialize/deserialize shape to/from JSON.

Example

import phynexis

# Create a sphere of size 2.0 (radius 1.0)
s = phynexis.utils.shape.Sphere(2.0)
print("tag:", s.tag, "name:", s.name)
print("size:", s.size(), "volume:", s.volume())
print("inertia:", s.inertia())
print("bound_aabb:", s.bound_aabb())
print("is_convex:", s.is_convex())

# Signed distance and enclosure
print("sd at origin:", s.signed_distance(phynexis.utils.Vec3d(0, 0, 0)))
print("enclose(0,0,0):", s.enclose(phynexis.utils.Vec3d(0, 0, 0)))
print("enclose(2,0,0):", s.enclose(phynexis.utils.Vec3d(2, 0, 0)))

# Support point
print("support:", s.support_point(phynexis.utils.Vec3d(1, 0, 0)))

# Generate STL model
stl = s.get_stl_model(50)
print("stl size:", stl.size())

# Transform
s.transpose(phynexis.utils.Vec3d(1.0, 0.0, 0.0))
print("aabb after translate:", s.bound_aabb())

Output:

tag: 2 name: sphere
size: 2.0 volume: 4.1887902047863905
inertia: Mat3d([1.67552, 0, 0], [0, 1.67552, 0], [0, 0, 1.67552])
bound_aabb: (Vec3d(-1, -1, -1), Vec3d(1, 1, 1))
is_convex: True
sd at origin: 1.0
enclose(0,0,0): True
enclose(2,0,0): True
support: Vec3d(1, 0, 0)
stl size: 0.18819206523895264
aabb after translate: (Vec3d(0, -1, -1), Vec3d(2, 1, 1))

Shape Subclasses

All subclasses inherit from Shape and are available under phynexis.utils.shape.

Sphere

s = phynexis.utils.shape.Sphere() # default size=1.0
s = phynexis.utils.shape.Sphere(2.0) # size=2.0

Cuboid

c = phynexis.utils.shape.Cuboid() # default center=(0,0,0), length=(1,1,1)
PropertyTypeDescription
centerVec3dCenter position (read/write)
lengthVec3dSide lengths in x, y, z (read/write)

Ellipsoid

e = phynexis.utils.shape.Ellipsoid() # default axes (1,1,1)
e = phynexis.utils.shape.Ellipsoid(2.0, 3.0, 4.0) # semi-axes a, b, c
PropertyTypeDescription
axis_afloatSemi-axis a (read/write)
axis_bfloatSemi-axis b (read/write)
axis_cfloatSemi-axis c (read/write)

Cylinder

cy = phynexis.utils.shape.Cylinder() # default r=0.5, h=1.0
cy = phynexis.utils.shape.Cylinder(1.0, 2.0) # radius=1.0, height=2.0
PropertyTypeDescription
radiusfloatCylinder radius (read/write)
heightfloatCylinder height (read/write)

Plane

p = phynexis.utils.shape.Plane()
p.set_extent(10.0)
p.set_center(0, 0, 0)
p.set_normal(0, 1, 0)
PropertyTypeDescription
centerVec3dPlane center (read/write)
dir_nVec3dNormal direction (read/write)
extentfloatPlane extent (read/write)

Triangle

t = phynexis.utils.shape.Triangle()
t = phynexis.utils.shape.Triangle(v0, v1, v2) # three Vec3d vertices
t.set_vertices(v0, v1, v2)
PropertyTypeDescription
verticesMat3d3x3 matrix of vertex positions (read/write)
dir_nVec3dNormal direction (read/write)

PointSphere

Degenerate sphere representing a point mass (size=1.0, volume=0.5236).

ps = phynexis.utils.shape.PointSphere()

Other Shapes

The following shapes are also exposed but have more specialized APIs:

  • PolySuperEllipsoid — Super-ellipsoids with polynomial corrections
  • PolySuperQuadrics — Super-quadric shapes
  • SphericalHarmonics — Shapes represented by spherical harmonic coefficients
  • TriMesh — Triangle mesh shape wrapper
  • LevelSet — Implicit surface shape (see LevelSetField for the underlying grid representation)
  • Polybezier — Bezier curve-based shape
  • BoundingBox — Axis-aligned bounding box shape

ShapeFactory

Static factory for creating shapes by name.

MethodDescription
create(name, js)Create a shape by registered name with JSON parameters

Note: ShapeFactory has no public constructor; use ShapeFactory.create() directly.

ShapeRegistry

Singleton registry of available shape types.

MethodDescription
instance()Get the singleton registry instance
is_registered(name)Check if a shape type is registered
print_info()Print registered shape types

Note: get_registered_types() returns VecX<String> (unregistered type); use print_info() as a workaround.

Example: Surface nodes and sampling

import phynexis

s = phynexis.utils.shape.Sphere(2.0)
s.enable_surface_nodes()
s.update_surface_nodes()

print("surface_node_num:", s.surface_node_num())
print("surface_nodes_enabled:", s.surface_nodes_enabled())
print("surface_node_spacing:", s.surface_node_spacing())

# Access surface nodes and areas
nodes = s.surface_nodes()
areas = s.surface_node_areas()
print("num nodes:", len(nodes), "num areas:", len(areas))
print("first node:", nodes[0])

# Sample surface points
pts = s.sample_surface_points(5)
print("sampled points:", len(pts))
for p in pts:
print(" ", p)

# Support points in a direction
supp = s.support_points(phynexis.utils.Vec3d(1.0, 0.0, 0.0))
print("support points:", supp)

# Render mesh
s.update_render_mesh(50)
rm = s.render_mesh
print("render_mesh size:", rm.size())

Output:

surface_node_num: 1000
surface_nodes_enabled: True
surface_node_spacing: 0.12629780026048754
num nodes: 1000 num areas: 1000
first node: Vec3d(0.0191123, 0.0177418, 0.999641)
sampled points: 5
Vec3d(0.510644, -0.334648, 0.790675)
Vec3d(-0.547768, -0.458434, 0.699488)
Vec3d(0.170926, 0.70943, 0.683829)
Vec3d(-0.252001, -0.842175, 0.475395)
Vec3d(-0.93542, -0.101945, 0.338865)
support points: [Vec3d(1, 0, 0)]
render_mesh size: 0.18819206523895264

Unexposed C++ API

  • Feature enum and FeatureMask bitflags
  • pack_common / pack_specific / unpack_common / unpack_specific (internal serialization hooks)
  • vertices_of_aabb static method