From 68f0147392a56b0972cf82a0304e92305ef11edf Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Tue, 18 Aug 2026 09:02:20 +1000 Subject: [PATCH] chore: bump version to 1.4.0, add CHANGELOG.md Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21330e0..aae3a5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 42bdfba..695ac63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" },