Skip to content

fix: make Mesh.filename/y_up read-only, fix corners() ignoring y_up - #33

Merged
petercorke merged 4 commits into
jhavl:mainfrom
petercorke:fix/mesh-readonly-yup-corners
Aug 9, 2026
Merged

fix: make Mesh.filename/y_up read-only, fix corners() ignoring y_up#33
petercorke merged 4 commits into
jhavl:mainfrom
petercorke:fix/mesh-readonly-yup-corners

Conversation

@petercorke

Copy link
Copy Markdown
Collaborator

Summary

  • Mesh.filename/Mesh.y_up had public setters, but both are only ever read once, inside _init_coal() -- which is cached (self._cinit) and never re-runs after a shape's first closest_point()/iscollided() call. Mutating either after that point silently left the collision geometry stale, no error. Made both read-only, set only via the constructor -- matches the existing pattern for collision on this same class. No real use case for repointing an existing Mesh at a different file; construct a new one instead.
  • While tracing that, found a real, independent bug: _local_corners() (backing corners()/bounds()/extents()) does its own separate trimesh.load() and never applied the _Y_UP_TO_Z_UP correction _init_coal() applies -- so a y_up=True mesh's bounding box was silently computed in the wrong (uncorrected) frame, inconsistent with what's actually rendered/collision-checked. Fixed by re-deriving min/max from the corrected vertices.
  • Added cast(trimesh.Trimesh, ...) at both trimesh.load(..., force="mesh") call sites -- the return type is statically the looser Geometry, but force="mesh" guarantees a Trimesh at runtime.

Test plan

  • pytest tests/ -- 161 passed (159 existing + 2 new: read-only enforcement on both properties, and a y_up-vs-extents() regression test: a 1x2x3 box becomes 1x3x2 under the correction).

🤖 Generated with Claude Code

Both properties are only ever read once, inside _init_coal(), which is
cached via self._cinit and never re-runs after a shape's first
closest_point()/iscollided() call -- so their setters were a footgun:
mutate either after that point and the collision geometry silently
keeps using the old value. Made both read-only (set only via the
constructor), matching the existing pattern for `collision` on this
same class. There's no use case for repointing an existing Mesh at a
different file -- construct a new one instead.

Also fixes a real, independent bug this surfaced: _local_corners() (used
by corners()/bounds()/extents()) did its own separate trimesh.load() and
never applied the _Y_UP_TO_Z_UP correction _init_coal() applies, so a
y_up=True mesh's bounding box was silently computed in the wrong
(uncorrected) frame -- inconsistent with what's actually rendered/
collision-checked. Fixed by re-deriving min/max from the corrected
vertices, matching _init_coal()'s own approach.

Also adds `cast(trimesh.Trimesh, ...)` in both places that call
trimesh.load(..., force="mesh") -- the return type is statically a
looser Geometry, but force="mesh" guarantees a Trimesh at runtime.

Two new tests: read-only enforcement on both properties, and a
y_up-vs-extents() regression test (1x2x3 box -> 1x3x2 under the
correction).
…-corners

# Conflicts:
#	src/spatialgeometry/geom/CollisionShape.py
Merge fallout, not a real regression: jhavl#35 (merged after this branch
was written) added a FileNotFoundError check at Mesh construction, so
this test's literal "test.stl" placeholder -- fine when the test was
written, since nothing checked it existed -- now fails construction
before ever reaching the read-only assertions the test is actually
about. Use the real placeholder file (self.mesh_path, from jhavl#35's own
setUpClass) instead, matching every other Mesh-constructing test in
this file.
@petercorke
petercorke merged commit 4e00c28 into jhavl:main Aug 9, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant