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

4.0 KiB

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:

// 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: