Files
HartOMat/review-report.md
T
Hartmut ca62319688 feat: sharp edge pipeline V02, tessellation presets, media cache-bust, GMSH plan
Sharp Edge Pipeline V02:
- export_step_to_gltf.py: replace BRep_Tool.Polygon3D_s (returns None in XCAF) with
  GCPnts_UniformAbscissa curve sampling at 0.3mm step — extracts 17,129 segment pairs
- Inject sharp_edge_pairs + sharp_threshold_deg into GLB extras (scenes[0].extras)
  via binary GLB JSON-chunk patching (no extra dependency)
- export_gltf.py: read schaeffler_sharp_edge_pairs from Blender scene custom props,
  apply via KD-tree to mark edges sharp=True + seam=True (OCC mm Z-up → Blender transform)
- tools/restore_sharp_marks.py: dual-pass (dihedral angle + OCC pairs), updated coordinate
  transform (X, -Z, Y) * 0.001

Tessellation:
- Admin UI: Draft / Standard / Fine preset buttons with active-state highlighting
- Default angular deflection: preview 0.5→0.1 rad, production 0.2→0.05 rad
- export_glb.py: read updated defaults from system_settings

Media / Cache:
- media/service.py: get_download_url appends ?v={file_size_bytes} cache-buster
- media/router.py: Cache-Control: no-cache for all download/thumbnail endpoints

Render pipeline:
- still_render.py / turntable_render.py: shared GPU activation + camera improvements
- render_order_line.py: global render position support
- render_thumbnail.py: updated defaults

Frontend:
- InlineCadViewer: file_size_bytes-aware URL update triggers re-fetch on regeneration
- ThreeDViewer: material panel, part selection, PBR mode improvements
- Admin.tsx: tessellation preset cards, GMSH setting dropdown
- MediaBrowser, ProductDetail, OrderDetail, Orders: various UI improvements
- New: MaterialPanel, GlobalRenderPositionsPanel, StepIndicator components
- New: renderPositions.ts API client

Plans / Docs:
- plan.md: GMSH Frontal-Delaunay tessellation plan (6 tasks)
- LEARNINGS.md: OCC Polygon3D_s None issue + GCPnts fix
- .gitignore: add backend/core (core dump from root process)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:40:36 +01:00

89 lines
4.0 KiB
Markdown

# Review Report: CAD Viewer Material Assignment Fix + Feature Parity
Datum: 2026-03-10
## Ergebnis: ✅ Freigabe
---
## Gefundene Probleme
### [InlineCadViewer.tsx + ThreeDViewer.tsx] Misleading comment on isolateMode reset effect
**Schwere**: Gering (Kommentar)
In both files the comment reads:
```tsx
// Reset isolateMode and hideAssigned when no part is pinned
useEffect(() => {
if (!pinnedPart) setIsolateMode('none') // ← only resets isolateMode, not hideAssigned!
}, [pinnedPart])
```
The comment says "and hideAssigned" but the effect only calls `setIsolateMode('none')`. The behavior is actually correct — `hideAssigned` should NOT be reset when unpinning (it's a persistent view toggle). Only the comment is wrong.
**Empfehlung**: Change to `// Reset isolateMode when no part is pinned`.
---
## Positiv aufgefallen
### Bug fix: MaterialPanel invisible in ThreeDViewer — root cause correctly identified
The diagnosis was precise: the outer `<div onClick={() => setPinnedPart(null)}>` was receiving the
native DOM bubble from every canvas click, calling `setPinnedPart(null)` in the same React batch as
`setPinnedPart(name)` from the THREE.js event handler — final state always `null`.
The two-part fix is clean and idiomatic:
- `onClick={(e) => e.stopPropagation()}` on the viewport div absorbs DOM clicks
- `onPointerMissed={() => setPinnedPart(null)}` on the R3F Canvas handles the "click empty space"
case via the THREE.js raycaster (fires only when no mesh is hit) — this is exactly the right
R3F API for this use case
### cadUtils.ts — normalization regex extension
`/_AF\d+(_ASM)?$/i` is minimal and correct. It handles:
- `_AF0`, `_AF1` (existing, unchanged)
- `_AF0_ASM`, `_AF1_ASM` (new — assembly-node suffix)
- Case-insensitive flag is defensive and correct
- The loop-until-stable pattern handles nested suffixes as before
- `_ASM` alone (without `_AF\d+`) is NOT stripped — correct, it's part of base names like
`GE360-HF_000_P_ASM_ASM`
### Combined visibility useEffect — correct design
Merging `hideAssigned` + `isolateMode` into a single traversal effect avoids
ordering ambiguity between two independent effects competing on the same `mesh.visible` and
`mat.opacity`. The priority order (hideAssigned first, then isolateMode) is explicit and logical.
The pinned part (`isSelected`) is always protected from hiding regardless of mode. ✓
### Effect separation is clean
- Color-apply effect: only touches `mat.color` → deps `[modelReady, partMaterials]`
- Unassigned glow effect: only touches `mat.emissive` → deps `[modelReady, showUnassigned, partMaterials]`
- Combined visibility effect: only touches `mesh.visible` / `mat.opacity` / `mat.transparent` → deps `[modelReady, pinnedPart, isolateMode, hideAssigned, partMaterials]`
No effect touches another effect's properties — no race conditions.
### GPU hint and DPR cap
`gl={{ powerPreference: 'high-performance' }}` + `dpr={[1, 1.5]}` on both Canvas elements.
`preserveDrawingBuffer: true` correctly kept only in ThreeDViewer (required for screenshot capture).
### "Hide assigned" toolbar button correctly conditional
`{assignedCount > 0 && (...)}` in InlineCadViewer and
`{modelReady && Object.keys(partMaterials).length > 0 && (...)}` in ThreeDViewer — button only
appears when there is something to hide.
### Debug log is dev-only
`if (!import.meta.env.DEV || ...)` guard ensures the console output and traversal overhead
never reach production. The output logs both matched and unmatched keys, which is exactly what's
needed to diagnose remaining name mismatches after the normalization fix.
### Feature parity achieved
ThreeDViewer and InlineCadViewer now have matching material-assignment features:
-`showUnassigned` highlight toggle with count badge
-`hideAssigned` toggle (new, both viewers)
-`isolateMode` (ghost / hide) via MaterialPanel (both viewers)
-`onPointerMissed` closes panel on empty-space click in ThreeDViewer
---
## Empfehlung
**Freigabe.** The one Gering comment issue can be fixed inline.
Review abgeschlossen. Ergebnis: ✅