Skip to content

[FXC-5651] feat: add client-side OBB computation via DraftContext.compute_obb()#1931

Merged
benflexcompute merged 18 commits intomainfrom
fxc-5651-obb-client-side
Mar 27, 2026
Merged

[FXC-5651] feat: add client-side OBB computation via DraftContext.compute_obb()#1931
benflexcompute merged 18 commits intomainfrom
fxc-5651-obb-client-side

Conversation

@benflexcompute
Copy link
Copy Markdown
Collaborator

@benflexcompute benflexcompute commented Mar 26, 2026

Summary

  • Add DraftContext.compute_obb() for client-side Oriented Bounding Box estimation from surface geometry via PCA
  • Supports List[Surface], single Surface, and SurfaceSelector inputs
  • OBBResult is a frozen dataclass — all fields are properties: center, axes, extents, axis_of_rotation, radius
  • rotation_axis_hint is passed at compute_obb() call time and baked into the result
  • TessellationFileLoader downloads UVF manifest + bin files with two-layer caching (L1 in-memory, L2 disk)
  • CloudFileCache: general-purpose size-based LRU disk cache at ~/.flow360/cache/ with module-level singleton
  • Output is dimensioned (unyt) using project length unit cached on Geometry during from_cloud()
  • Non-Surface selectors rejected upfront with clear error message

User API

with fl.create_draft(new_run_from=geometry) as draft:
    obb = draft.compute_obb(draft.surfaces["wheel*"], rotation_axis_hint=[0, 0, 1])
    print(obb.center)            # [x, y, z] m
    print(obb.radius)            # float m
    print(obb.axis_of_rotation)  # [x, y, z]
    print(obb.axes)              # [[ax0], [ax1], [ax2]]
    print(obb.extents)           # [e0, e1, e2] m

Test plan

  • Unit tests: CloudFileCache (get/put, LRU eviction, self-eviction protection, overwrite accounting, oversized skip, graceful degradation)
  • Unit tests: UVF parser (indexed/unindexed geometry, LOD, error paths)
  • Unit tests: OBB compute (cube, cylinder, rotation_axis_hint selection, all-properties API)
  • E2E tests: full pipeline through DraftContext with mocked downloads
  • E2E tests: SurfaceSelector, List[Surface], single Surface input routes
  • E2E tests: non-Surface selector rejection
  • E2E tests: unyt dimensioned output with length_unit
  • Manual E2E: verified against POC reference results on car model (4 wheels, exact match)
  • CI passes

🤖 Generated with Claude Code


Note

Medium Risk
Adds new geometry-processing logic (UVF parsing + PCA) and a persistent on-disk cache for cloud downloads, which may impact correctness and disk usage if edge cases or eviction behavior are off.

Overview
Adds client-side DraftContext.compute_obb() to compute an oriented bounding box from selected surface entities by downloading UVF tessellation data, extracting vertices, and running a PCA-based OBB algorithm (with optional rotation_axis_hint and LOD override).

Introduces TessellationFileLoader plus UVF parsing utilities, backed by a new size-based LRU CloudFileCache (shared singleton) to persist downloaded manifest/bin files across sessions and avoid partial-resource eviction.

Wires geometry-root drafts to include the tessellation loader and cached project length unit (from Geometry.from_cloud) so OBB outputs can be returned with units; adds comprehensive unit and E2E tests plus fixture tessellation data.

Written by Cursor Bugbot for commit e32bf53. This will update automatically on new commits. Configure here.

…pute_obb()

Implements oriented bounding box (OBB) estimation on the Python client,
enabling users to compute center, rotation axis, and radius for selected
surfaces without a backend API endpoint. Key components:

- OBB PCA computation (obb/compute.py) with OBBResult dataclass
- UVF tessellation parser (obb/uvf_parser.py) for manifest + bin extraction
- TessellationFileLoader with two-layer caching (L1 memory + L2 disk via CloudFileCache)
- CloudFileCache: general-purpose size-based LRU disk cache (flow360/cloud/file_cache.py)
- DraftContext.compute_obb() supporting List[Surface], single Surface, and SurfaceSelector inputs
- Dimensioned output (unyt) using project length unit cached on Geometry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71fe6383a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…ll OBBResult fields properties

rotation_axis_hint is now passed at compute_obb() call time and baked into the
result. OBBResult.radius and OBBResult.axis_of_rotation are now frozen dataclass
fields instead of methods, giving a consistent property-only access pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
benflexcompute and others added 3 commits March 26, 2026 20:20
A single file larger than _max_size_bytes would evict everything then
still be written, violating the cache cap. Now put() returns early
without writing when incoming_bytes > _max_size_bytes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Passing EdgeSelector/BodyGroupSelector would expand to non-surface entities
and fail with a low-level KeyError during tessellation lookup. Now validates
target_class == "Surface" immediately with a clear Flow360ValueError.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
benflexcompute and others added 4 commits March 26, 2026 21:06
…on in CloudFileCache

Two edge cases fixed:
1. Eviction now protects the resource directory currently being populated,
   preventing a second put (e.g., bin file) from evicting the first (manifest).
2. Overwriting an existing file accounts for net size delta instead of gross
   incoming size, avoiding unnecessary eviction of other resources.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ache

1. _ensure_face_index_built now only indexes Face entries (not solids/groups)
   and raises ValueError on duplicate face IDs across geometries instead of
   silently overwriting.

2. CloudFileCache uses a module-level singleton (get_shared_cloud_file_cache)
   to avoid redundant directory scans across multiple create_draft calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When both perpendicular extents are zero (degenerate geometry), 0/0
produced NaN which silently fell through to default index 0. Now treats
equal extents (including both-zero) as perfect circularity (ratio = 1.0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@benflexcompute benflexcompute force-pushed the fxc-5651-obb-client-side branch from e4d5147 to bb501d7 Compare March 27, 2026 01:49
SurfaceSelector expansion can return MirroredSurface entities which lack
private_attribute_sub_components and have no tessellation data. Now filters
to Surface instances only before collecting face IDs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@benflexcompute benflexcompute force-pushed the fxc-5651-obb-client-side branch from bb501d7 to c4c9e83 Compare March 27, 2026 01:50
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
benflexcompute and others added 2 commits March 27, 2026 10:00
…Wrapper

Both preview_selector() and compute_obb() defined identical inline
dataclasses to wrap selectors for expand_entity_list_selectors. Replaced
with a single module-level _SelectorWrapper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chmod(0o000) doesn't restrict writes on Windows. Mock Path.mkdir to raise
OSError instead — works cross-platform.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n lookup

Bare KeyError on missing face ID gave no context. Now reports the face ID
and lists available geometry IDs to help diagnose mismatches between
entity info and manifest content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
np.concatenate on empty list crashes with unhelpful ValueError. Now
raises a descriptive error when all requested faces have empty buffer
locations (degenerate geometry).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
benflexcompute and others added 2 commits March 27, 2026 10:57
… type validation

1. _collect_resource_dirs now excludes .last_access sentinel from size
   calculation, matching put()'s net_incoming accounting.

2. compute_obb() rejects non-Surface EntityRegistryView upfront (e.g.
   draft.edges) with a clear error instead of falling through to
   "No face IDs collected".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, cleanup getattr

1. compute_obb() else branch now raises Flow360ValueError for unexpected
   input types instead of silently treating them as a list.
2. CloudFileCache._file_path validates resolved path stays under cache_root,
   preventing path traversal via malicious file_path keys.
3. Replaced getattr fallback with direct _project_length_unit access since
   Geometry.__init__ always initializes it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Use getattr with fallback for .name in the warning message so objects
without a name attribute don't cause AttributeError during filtering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@benflexcompute benflexcompute merged commit 746fa41 into main Mar 27, 2026
20 checks passed
@benflexcompute benflexcompute deleted the fxc-5651-obb-client-side branch March 27, 2026 15:48
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.

2 participants