Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,129 @@
Notable changes to this project are documented in this file. Starts from
v1.2.0 — earlier releases aren't documented retroactively.

## [1.4.0] - 2026-08-09

### New

- **`Ellipsoid` shape**: a native `coal.Ellipsoid` collision primitive,
parameterised as `radii=[rx, ry, rz]` (three semi-axis lengths), following
the same pattern as `Sphere`/`Cylinder`/`Cuboid`.
- **`Shape.corners()`/`bounds()`/`extents()`** for a shape's axis-aligned
bounding box — `corners(world=False)`, `bounds(world=False)`,
`extents(world=False)`; pass `world=True` for the posed shape's envelope
in the world frame. Implemented for `Cuboid`/`Box`, `Sphere`, `Cylinder`,
`Mesh`; `Axes`/`Arrow`/`Polyline` raise `NotImplementedError` for now rather
than a silently wrong value.
- **`SceneNode.tree()`/`tree_children()`**: render a scene graph (or just
one node's own subtree) as human-readable indented text, one `repr()` per
line, with `tree()` marking the calling node's position with a trailing
`<==`. `SceneGroup` needs no special-casing — the tree walk is generic.
- **`CollisionShapeGroup`**: an ordered, list-like collection of
`CollisionShape`/`CollisionShapeGroup` instances that itself behaves like
a single collision-checkable shape — `iscollided()`/`closest_point()` work
in every combination of shape/group, including arbitrarily nested groups.
Comes with a new **`&` operator** (`a & b` is `a.iscollided(b)`) that
works uniformly across shapes and groups.
- **`Shape.opacity` property**: a convenient way to get/set just the alpha
channel of `color` without needing to know or re-specify the current
`(r, g, b)`. `set_alpha()` is deprecated in its favor.
- **`Arrow.FromTo(start, end)`**: construct an arrow spanning two 3-vectors
directly, with `length` and `pose` computed automatically instead of by
hand.
- Tutorial introduction: substantially expanded, with worked examples
covering shape creation, bounding boxes, collision checking, scene
graphs, and groups.

### Mesh

A cluster of related `Mesh` improvements landed together this release:

- **New `y_up` param**, for mesh files authored with +Y as "up" (a common
convention in general 3D/graphics tooling) rather than this ecosystem's
+Z-up convention — applies a fixed correction to the mesh's own vertex
data at load time, so it survives re-posing/animation. `scale` also now
accepts a single scalar (uniform on all 3 axes), not just a 3-element
list.
- **New `mesh` extra** (`pip install spatialgeometry[mesh]`): just
`trimesh`, split out of `collision` — unlike Coal, trimesh has no
Windows-wheel problem, so mesh-only functionality that never touches Coal
(`corners()`/`bounds()`/`extents()`, which work independent of
`collision=True/False`) is now reachable via `pip` on Windows.
`collision` still pulls in `mesh` transitively, nothing changes for
existing `collision` users.
- **Now tracks whether an explicit color was ever given**
(`use_vertex_colors` in `to_dict()`), so a renderer has a signal to
prefer a mesh file's own baked-in per-vertex/per-face colors over a flat
default grey when no color was ever actually requested.
- **`filename`/`y_up` are now read-only.** There's no use case for
repointing an existing `Mesh` at a different file or a different up-axis
convention — construct a new one instead. (Both are also only ever read
once, inside `_init_coal()`, which is cached after first use — a setter
would have silently done nothing after a shape's first
`closest_point()`/`iscollided()` call.)
- **`Mesh(filename=...)` now raises `FileNotFoundError` at construction**
if the file doesn't exist, instead of deferring the failure to the first
`closest_point()`/`iscollided()` call.
- **Fixed: `corners()`/`bounds()`/`extents()` now correctly apply the
`y_up` correction.** Previously the `+Y → +Z` correction was only applied
to the collision geometry, not the bounding box, so a `y_up=True` mesh's
bounding box didn't match its actual (corrected) orientation.

### Changed

- **`repr()` now shows `color`, and `opacity` when not fully opaque** —
previously only subclass-specific params (`radius`, `scale`, ...) and
`pose` were shown.
- **`Path` shape renamed to `Polyline`**, to avoid clashing with the
standard library's `pathlib.Path` — a source of confusing type errors in
code that imports both. `Path` remains available as a deprecated alias
(`class Path(Polyline)`), emitting a `FutureWarning` on construction.
- **`SceneNode.__init__`'s `T=` kwarg renamed to `pose=`**, now accepting
`SE3` as well as a raw `ndarray` — matching the convenience `Shape` (and
every concrete shape) already had. `SceneNode` is the base class for the
whole scene graph, so this had been implemented one level too low;
`SceneGroup` previously had no way to accept an `SE3` pose at
construction, only a raw `ndarray` via `T=`.
- **`update()` is now the public method for refreshing the scene graph's
world transforms**, replacing `_propogate_scene_tree()` (a straight
misspelling of "propagate", and not actually private in practice —
called directly by `robotics-toolbox-python` and `swift`, and
demonstrated in this project's own tutorial). `_propogate_scene_tree()`
still works, emitting a `FutureWarning`, for one release cycle.
- **Color-parsing error messages now name matplotlib explicitly** and link
to its full named-colors reference, rather than a bare "invalid color
name" with no pointer to where valid names are defined.
- Internal: `src/spatialgeometry/core` renamed to `cpp-extension`, for
naming consistency — nothing importable changed.
- Docs: substantial API reference cleanup — a new page on mesh file format
support (which formats work for display vs. collision, and why);
`autoclass_content='both'` so each class's own constructor parameters
(previously invisible on most pages, since Sphinx's default only shows
the class docstring) now actually appear; several stale/incorrect
passages fixed in the intro tutorial.

### Fixed

- **`Cylinder`'s collision geometry was half its documented length.**
`_init_coal()` passed `length / 2.0` to Coal on the mistaken assumption
its constructor took a half-length — it takes the full length, so
`Cylinder(radius=1, length=4)`'s actual collision geometry was only 2
units tall. No existing test exercised a cylinder along its own axis, only
radially, so this went undetected.
- **`scene_parent` assignments that would create a cycle are now rejected**
(`a.scene_parent = b; b.scene_parent = a`, a self-loop, or a longer
cycle). Previously nothing validated this, and the scene-graph
root-finding walk (used by `update()` every call) has no cycle detection
of its own — a cycle made it spin forever rather than raise.
- **`SceneGroup`'s list mutators (`append`/`extend`/`insert`/`remove`/
`pop`/`clear`/item assignment) didn't wire `scene_parent`** — elements
added this way never actually became children in the scene graph, only
in the list. The constructor also now accepts an initial list
(`SceneGroup([a, b])` previously raised `TypeError`).
- `repr()` could leak `numpy.float64` values (e.g. `np.float64(1.0)`
instead of `1.0`) into `color`/`opacity` — harmless functionally, ugly in
a repr.

## [1.3.0] - 2026-08-03

### New
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name = "spatialgeometry"

description = "A Shape and Geometry Description Package"

version = "1.3.0"
version = "1.4.0"

authors = [
{ name = "Jesse Haviland", email = "j.haviland@qut.edu.au" },
Expand Down
Loading