[FXC-5651] feat: add client-side OBB computation via DraftContext.compute_obb()#1931
Merged
benflexcompute merged 18 commits intomainfrom Mar 27, 2026
Merged
[FXC-5651] feat: add client-side OBB computation via DraftContext.compute_obb()#1931benflexcompute merged 18 commits intomainfrom
benflexcompute merged 18 commits intomainfrom
Conversation
…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>
There was a problem hiding this comment.
💡 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>
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>
flow360/component/simulation/draft_context/obb/tessellation_loader.py
Outdated
Show resolved
Hide resolved
…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>
e4d5147 to
bb501d7
Compare
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>
bb501d7 to
c4c9e83
Compare
flow360/component/simulation/draft_context/obb/tessellation_loader.py
Outdated
Show resolved
Hide resolved
…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>
flow360/component/simulation/draft_context/obb/tessellation_loader.py
Outdated
Show resolved
Hide resolved
…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>
… 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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
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>
angranl-flex
approved these changes
Mar 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
DraftContext.compute_obb()for client-side Oriented Bounding Box estimation from surface geometry via PCAList[Surface], singleSurface, andSurfaceSelectorinputsOBBResultis a frozen dataclass — all fields are properties:center,axes,extents,axis_of_rotation,radiusrotation_axis_hintis passed atcompute_obb()call time and baked into the resultTessellationFileLoaderdownloads 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 singletonunyt) using project length unit cached on Geometry duringfrom_cloud()User API
Test plan
🤖 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 optionalrotation_axis_hintand LOD override).Introduces
TessellationFileLoaderplus UVF parsing utilities, backed by a new size-based LRUCloudFileCache(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.