Commit Graph

146 Commits

Author SHA1 Message Date
Hartmut 5a148554c0 perf: dual queue, GLB caching, WebP output, persistent BVH
Task 4: Dual render queue
- render-worker: heavy (asset_pipeline, concurrency=1) — HQ 2048x2048, animations
- render-worker-light: light (asset_pipeline_light, concurrency=2) — thumbnails, <=1024
- Thumbnails routed to light queue automatically
- Order line renders routed by resolution at dispatch time

Task 5: GLB caching (skip re-tessellation)
- Before tessellating, check if gltf_geometry MediaAsset exists for the cad_file_id
- If found, copy to expected path — render_blender.py finds it and skips tessellation
- Saves 7-11s per re-render of the same product

Task 6: WebP output format
- New 'webp' option in output_format (OutputType admin)
- Blender renders PNG intermediate, Pillow converts to WebP (quality=90, method=4)
- 50-70% smaller files with no visible quality loss
- Correct MIME type (image/webp) in MediaAsset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:07:12 +01:00
Hartmut ffe3eebfca perf: render pipeline optimizations — sample scaling, USD logging, persistent BVH
Task 1: Resolution-aware sample count
- Auto-scale samples for resolutions <= 1024: max(32, samples * max_dim / 2048)
- 512x512 thumbnails: 256 → 64 samples (75% GPU savings)
- Thumbnail tasks capped at 64 samples via context manager
- 2048x2048 HQ renders unchanged

Task 2: USD path preference audit + logging
- Verified USD master path is correctly preferred over GLB tessellation
- Added clear emit() messages: "Using USD master" vs "No USD master — GLB path"
- Dynamic render log label: "USD → Blender" vs "STEP → GLB → Blender"

Task 3: Persistent BVH for turntable animations
- Added scene.render.use_persistent_data = True before frame loop
- BVH acceleration structure cached between frames (not rebuilt per frame)
- Applies to both camera orbit and object rotation modes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:03:31 +01:00
Hartmut ce15526a15 docs: update review report for UI/UX cleanup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:00:05 +01:00
Hartmut 8c65c52271 fix: persist OptiX kernel cache — 9x faster first render after restart
The OptiX cache was mounted at /root/.nv but NVIDIA writes to
/var/tmp/OptixCache_root/optix7cache.db (28MB). Fixed volume mount.

Before: first render after container restart = 181s (OptiX recompilation)
After: first render after container restart = 20s (cached kernels)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 09:57:11 +01:00
Hartmut db9f6f45ed fix: auto-poll order detail while renders are active (3s interval)
Added refetchInterval to the order query that polls every 3 seconds
while render_progress has pending or processing lines. Stops polling
automatically when all renders reach a terminal state (completed/failed/cancelled).

Fixes: render log and backend status not appearing until manual page reload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 09:40:49 +01:00
Hartmut 2c7eb81aab refactor: clean up Render Settings — remove 11 unused settings, fix Blender status
Removed from UI (saved to DB but never read by any service):
- Max Concurrent Renders, Stall Timeout, Thumbnail Format, Product Thumbnail Priority
- Render Linear/Angular Deflection (only Scene deflections are used)
- GLB Scale Factor, Smooth Normals, GLB Material Mode, PBR Roughness, PBR Metallic

Fixed Blender status check:
- Old: called is_blender_available() in backend container (Blender not installed there)
- New: dispatches Celery task on asset_pipeline queue → runs in render-worker container
- Returns: available=true, version="Blender 5.0.1", binary path
- Status card moved to System Tools tab with refresh button

Kept active: engine, device, samples, smooth angle, tessellation, scene deflections,
3D viewer zoom limits

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 09:37:54 +01:00
Hartmut 9a794ff2da refactor: full UI/UX cleanup — expandable edit rows, better controls, cleaner UX
Admin tables (same pattern as OutputTypeTable):
- RenderTemplateTable: 11 cramped columns → expandable form row with grouped fields,
  boolean flags consolidated into compact badges, .blend upload in proper section
- PricingTierTable: inline cell editing → expandable form with labeled fields,
  shared renderEditFormGrid() for add/edit modes
- GlobalRenderPositionsPanel: tiny rotation inputs → expandable form with w-24 inputs,
  proper labels, sensor_width_mm added to edit form

Page polish:
- WorkerManagement: larger scale controls (p-2 rounded-lg), wider number displays (w-12),
  proper labels, more prominent Save button
- Billing: status select gets visible dropdown indicator (ChevronDown icon),
  hover border to signal interactivity, larger action buttons with borders
- OrderDetail: batch override in proper card with title/description,
  per-line override shows compact "+ override" link (expands on click),
  active overrides show as amber badge with X to clear

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 09:20:45 +01:00
Hartmut 5b92375d86 refactor: OutputType edit — expandable form row instead of inline cell editing
Replaced the 18-column inline cell editing with an expandable detail row:

- Display row always visible (no more disappearing content on edit)
- Edit form opens BELOW in a full-width colSpan row with accent border
- Fields organized in a 4-column grid with proper labels:
  Row 1: Name, Renderer, Format, Animation
  Row 2: Turntable, Background, Device, Engine
  Row 3: Samples, Resolution, Pricing, Workflow
  Row 4: Categories, Material Override, Sort Order, Active
  Row 5: Denoising settings (6 cols, Blender only)
- Shared renderEditFormGrid() helper for both edit and add modes
- Save/Cancel buttons at bottom of form
- React.Fragment wrappers for dual-row rendering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 08:30:48 +01:00
Hartmut 966876dced refactor: reorganize Admin page — 8 focused tabs, grouped system tools
Restructured the overloaded 6-tab Admin page into 8 focused tabs:

1. Overview — pricing summary (unchanged)
2. Users — user CRUD (unchanged)
3. Render Settings — engine, samples, tessellation, viewer config only
4. Output Types — OutputTypeTable (extracted from old Pricing tab)
5. Templates & Positions — render templates + camera positions + material lib
6. Pricing — pricing tiers only (cleaned up)
7. Libraries — asset libraries + template editor (unchanged)
8. System Tools — ALL maintenance buttons organized into cards:
   - Reprocessing (stuck recovery, thumbnails, metadata, workflows)
   - USD/Canonical Scenes (missing masters, regenerate all)
   - Cleanup (orphaned media/STEP, purge renders)
   - GPU Status, SMTP, Dashboard Config

Section header pattern (icon + title + description) for each section.
Card pattern for grouped maintenance actions.
Scrollable tab bar for smaller screens.

No sub-components changed — only Admin.tsx reorganization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 01:58:45 +01:00
Hartmut ed7efd923b fix: add custom accent color picker to Preferences page
The Preferences page had its own inline accent color section that wasn't
updated with the custom picker. Now shows:
- 5 preset swatches + separator + custom swatch with pipette icon
- Color picker + hex input appear when custom is selected
- Larger swatches (w-8 h-8) matching the page's spacious layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 01:45:27 +01:00
Hartmut 2eab705a8a feat: custom accent color picker in theme preferences
- Added 'custom' accent option with color picker + hex input
- Pipette icon on the custom swatch (switches to solid when active)
- Color picker appears inline when custom is selected
- Generates hover/light variants automatically from hex (darken/lighten)
- Dark mode accent-light uses rgba for translucency
- Persisted in localStorage, applied before React hydration (no flash)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 01:42:38 +01:00
Hartmut 79651bc41d feat: dashboard widget animations — staggered entrance, hover glow, progress bars
- Widgets scale in with staggered delays (60ms per widget, up to 12)
- Widget hover: lift 3px + accent border glow (dark mode: accent shadow)
- Inner numbers animate up with count-up effect
- Progress bars grow from left with spring curve (800ms delay for content-first feel)
- All wrapped in prefers-reduced-motion guard

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 01:22:35 +01:00
Hartmut bd1c5eec20 feat: micro-interactions and animations — cards, grids, tables, modals, nav
CSS-only animation layer (zero component changes):

- Staggered grid item entrance (fadeInUp with 30ms delays per item)
- Staggered table row entrance (fadeIn with 25ms delays)
- Card hover lift in grids (translateY -2px + elevated shadow)
- Sidebar nav: active link accent bar, hover slide-right
- Badge hover scale, status badge pop-in
- Modal backdrop fade + panel pop-in (fadeInScale)
- Floating action bar slide-up entrance
- Image/thumbnail zoom on group hover (scale 1.03)
- Number/stat counter entrance animation
- Checkbox pop-in on check
- Link animated underline (background-size transition)
- Select hover border highlight
- Loading skeleton shimmer gradient
- Smooth scroll behavior
- prefers-reduced-motion: respects user preference

Keyframes: fadeIn, fadeInUp, fadeInScale, slideInRight, slideInLeft,
slideUp, popIn, breathe, countUp, shimmer, wiggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 01:18:00 +01:00
Hartmut 98a6dbee87 refactor: visual refresh — Inter font, neutral dark theme, modern component styles
CSS-layer-only changes for zero-regression visual modernization:

- Typography: Inter font with OpenType features (cv02-cv04, cv11), tighter letter-spacing
- Dark theme: neutral zinc tones (#09090b/#18181b) replacing blue-slate (#0f172a/#1e293b)
- Borders: rgba-based for subtlety instead of solid hex colors
- Cards: rounded-xl, refined shadows with dark mode variant
- Buttons: rounded-lg, subtle lift on hover, smoother transitions (150ms ease-out)
- Badges: rounded-md (softer than full-round), tracking-wide
- Inputs: rounded-lg, ring-offset-0 for tighter focus rings
- Scrollbars: thin 6px custom scrollbars matching theme
- Selection: accent-colored text selection
- Table headers: uppercase tracking-wider for modern data table look
- Utility: fadeIn animation, subtle-pulse for loading states

No component files changed — all styling propagates via CSS variables and Tailwind config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 00:29:14 +01:00
Hartmut 9b54d66322 feat: extend MCP server with render/media metadata tools
Added 5 new tools:
- get_product_detail: full product with parts, materials, render history, media assets
- get_render_detail: complete render metadata for an order line (timing, engine, log)
- get_completed_renders: filterable list of completed renders with timing/paths
- get_failed_renders: recent failures with error messages
- get_media_assets: browse media assets by product/type

Total: 17 tools + 2 resources

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 00:16:48 +01:00
Hartmut 2a9337b8a3 feat: MCP server for Claude Code integration
Exposes the render pipeline, product library, material system, and database
as MCP tools. 12 tools + 2 resources:

Tools: query_database, list_orders, get_order_detail, search_products,
check_materials, list_materials, dispatch_renders, set_material_override,
list_output_types, get_worker_activity, get_render_stats, get_queue_status

Resources: schaeffler://schema, schaeffler://output-types

- Uses FastMCP (Python SDK) with stdio transport
- .mcp.json for automatic team-wide registration
- uv-managed dependencies (no global install needed)
- Documentation in docs/mcp-server.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:46:52 +01:00
Hartmut dbadfdf489 feat: per-line material override in product order wizard Step 3
- Added "Mat Override" column to the review table
- Each line has its own dropdown (per-line takes priority over global)
- Default shows global override if set, otherwise "No override"
- "Clear" option to explicitly remove override on a line when global is set
- Amber background when override is active

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:52:21 +01:00
Hartmut 24833ce52e fix: pass material_override through when creating order lines
create_order and add_order_line endpoints were not passing
material_override from the request body to the OrderLine constructor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:48:30 +01:00
Hartmut f7aeeec5d8 feat: material override in new product order wizard (Step 3)
- Dropdown in Step 3 review to set a single material override for all lines
- Override is passed to each OrderLine.material_override at creation time
- Amber background when override is active
- Library materials loaded on Step 3 entry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:41:42 +01:00
Hartmut d84ce8252e feat: batch material override — apply to all lines in an order at once
- POST /orders/{id}/batch-material-override endpoint
- Dropdown above the lines table: "Apply to all lines…"
- Options: clear all overrides, or select a library material
- Updates all order lines in one request

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:36:18 +01:00
Hartmut 9d6def84c1 feat: per-order-line material override — override materials for individual renders
- Add `material_override` nullable column on OrderLine (DB migration)
- Line override takes priority over OutputType override
- PATCH /orders/{id}/lines/{id} endpoint to update material_override
- Inline dropdown on each order line in the OrderDetail page
- Amber background when override is active
- Same output type, different material per line — no need to create a new output type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:33:00 +01:00
Hartmut 7e57dba085 fix: add --material-override support to turntable_render.py
Both camera-orbit and object-rotation modes now parse and apply
the --material-override CLI arg, overriding USD primvar lookups
and material_map before assignment — same pattern as still renders.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:21:53 +01:00
Hartmut c054236d22 fix: material override pipeline — pass --material-override CLI arg to Blender scripts
The initial implementation only overrode the material_map dict in the task,
but the Blender USD primvar path bypassed it. Now:
- Added --material-override named CLI arg parsed in _blender_args.py
- Both Mode A (factory) and Mode B (template) in _blender_scene_setup.py
  override usd_material_lookup and material_map when set
- Passed through full chain: task → step_processor → render_blender → CLI → Blender
- Tested: 175-part bearing rendered with single Steel-Bare material (1/1 materials)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:19:21 +01:00
Hartmut 7c606953ec feat: global material override on OutputType for x-ray/clay render modes
- Add `material_override` nullable column on OutputType (DB migration)
- When set, ALL product parts get rendered with this single material
- Override applies after alias resolution in render_order_line task
- Admin UI: dropdown in OutputType table to select a library material
- Display: amber badge showing active override material name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:16:00 +01:00
Hartmut b6bac080bb feat: duplicate product detection — STEP conflict warnings on Excel import and CAD upload
- Excel preview detects when a product already has a different STEP file linked
- Excel preview detects intra-Excel conflicts (same product, different CAD model names)
- Product STEP upload warns when replacing an existing file and shows render count
- All warnings are non-blocking (amber badges, toast warnings)
- LEARNINGS.md: all open items resolved

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:05:40 +01:00
Hartmut f0dd952f63 feat: material alias seeds expansion, bulk product delete, dashboard stats widgets
- Material alias seeds: 95 → 855 aliases covering German variants, DIN standards,
  Werkstoffnummern, industry terms, English equivalents, polymer abbreviations
- Batch product delete/deactivate endpoint (POST /products/batch-delete)
- Multi-select UI on Products page with floating action bar
- Dashboard: RenderThroughput + MaterialCoverage widgets
- Dashboard stats endpoint (GET /admin/dashboard-stats)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:45:41 +01:00
Hartmut 4f4a128e08 fix: add missing func import in product delete endpoint
NameError on `func.count()` when checking orphaned CadFile references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:21:53 +01:00
Hartmut b583b0d7a2 feat: per-position camera settings, material alias dialog, product delete, media browser links
- Per-render-position focal_length_mm/sensor_width_mm (DB → pipeline → Blender)
- FOV-based camera distance with min clamp fix for wide-angle lenses
- Unmapped materials blocking dialog on "Dispatch Renders" with batch alias creation
- Material check endpoint (GET /orders/{id}/check-materials)
- Batch alias endpoint (POST /materials/batch-aliases)
- Quick-map "No alias" badges on Materials page
- Full product hard-delete with storage cleanup (MinIO + disk files + orphaned CadFile)
- Delete button on ProductDetail page with confirmation
- Clickable product names in Media Browser (links to product page)
- Single-line render dispatch/retry (POST /orders/{id}/lines/{id}/dispatch-render)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:16:37 +01:00
Hartmut 0020376702 fix: GLB tessellation destroyed by BRepBuilderAPI_Transform + MergeFaces
Root cause 1: BRepBuilderAPI_Transform(shape, trsf, copy=True) destroys
all Poly_Triangulation data. The mm→m scaling was applied before export,
wiping the tessellation from BRepMesh_IncrementalMesh.

Fix: Remove BRepBuilderAPI_Transform entirely — RWGltf_CafWriter already
handles mm→m conversion and Z-up→Y-up rotation internally.

Root cause 2: RWGltf_CafWriter with MergeFaces=False (the default) fails
to find per-face tessellation from the XCAF component hierarchy, producing
degenerate meshes (~2 vertices per face instead of thousands).

Fix: SetMergeFaces(True) to compose face triangulations into proper
per-shape mesh buffers. Vertex count goes from 1,212 to 46,573.

Also bumps cache key version to v2 to invalidate broken cached GLBs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:23:41 +01:00
Hartmut 7054fa4b40 fix: skip render for cancelled order lines and rejected orders
Adds early-exit checks in dispatch_order_line_render and
render_order_line_task to prevent rendering when order lines are
cancelled or the parent order is rejected/completed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:28:39 +01:00
Hartmut 5afe502bc0 feat(admin): add "Regenerate All GLB + USD" button
New endpoint POST /admin/settings/regenerate-all-canonical-scenes
queues GLB + USD master export for ALL completed CAD files, replacing
existing assets. Used after pipeline changes that affect tessellation
or normals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:21:04 +01:00
Hartmut 253f11a945 feat: surface-evaluated normals, GMSH tessellation, draw call batching
USD exporter:
- Compute normals from B-Rep surface via BRepLProp_SLProps at each vertex
  UV parameter — eliminates faceting on curved surfaces (same as Stepper)
- Add GMSH Frontal-Delaunay tessellation engine (opt-in via --tessellation_engine gmsh)
  with per-solid strategy matching export_step_to_gltf.py
- Use vertex normal interpolation instead of faceVarying (6x smaller normals)
- Default engine remains OCC (GMSH has coordinate-space bug with instanced parts)

Frontend:
- Fix faceted shading in InlineCadViewer: only call computeVertexNormals()
  when geometry lacks normals, preserving smooth GLB normals from pipeline
- Add useGeometryMerge hook for draw call batching (merge by material)
- Fix unused import in cadUtils, optional props in ThreeDViewer

Backend:
- Move dataclass import to top of step_processor.py (PEP 8)
- Unified single-read STEP metadata extraction with fallback

Render worker:
- Fix USD import seam/sharp restoration: read primvars via pxr directly
  (Blender's USD importer doesn't expose custom Int2Array primvars)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:14:23 +01:00
Hartmut 6c5873d51f feat: performance optimizations + part-materials validation
- @timed_step decorator with wall-clock + RSS tracking (pipeline_logger)
- Blender timing laps for sharp edges and material assignment
- MeshRegistry pattern: eliminate 13 scene.traverse() calls across viewers
- Lazy material cloning (clone-on-first-write in both viewers)
- _pipeline_session context manager: 7 create_engine() → 2 in render_thumbnail
- KD-tree spatial pre-filter for sharp edge marking (bbox-based pruning)
- Batch material library append: N bpy.ops.wm.append → single bpy.data.libraries.load
- GMSH single-session batching: compound all solids into one tessellation call
- Validate part-materials save endpoints against parsed_objects (prevents bogus keys)
- ROADMAP updated with completion status

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:53:14 +01:00
Hartmut ec667dd56a refactor: remove dead export_gltf.py, cleanup rendering tasks, improve tessellation UI
- Remove export_gltf.py (Blender-based GLB export replaced by OCC direct)
- Remove unused export_gltf_for_order_line_task
- Add Ultra tessellation preset to Admin settings
- Improve tessellation preset descriptions and styling
- Minor cleanup across media, rendering, and workflow modules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:37:35 +01:00
Hartmut d843162e5f feat(PBR): extract Blender PBR properties and apply in 3D viewer
Extract Base Color, Metallic, Roughness, Transmission, IOR from Blender
asset library materials via catalog_assets.py. Store in catalog JSON and
serve via /api/asset-libraries/pbr-map endpoint. Frontend viewers apply
PBR properties to Three.js MeshStandardMaterial using hex color strings
(avoiding Three.js ColorManagement sRGB/linear issues).

Key fixes:
- RLS bypass for material alias lookup in pbr-map endpoint
- pbrMap empty guard prevents premature grey fallback in viewers
- Cache-Control: no-cache on pbr-map requests to avoid stale data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:37:23 +01:00
Hartmut 577dd1ca7e refactor(P11+P12): codebase hygiene — CLAUDE.md rewrite, type safety, dead code removal
- Rewrite CLAUDE.md to match current 8-service architecture (was 11, 5 deleted)
- Remove all as-any casts in OrderDetail.tsx (9 casts → 0)
- Add cad_parsed_objects/cad_part_materials to OrderItem interface
- Rename require_admin → require_global_admin across 6 router files (22 calls)
- Remove EXPORT_GLB_PRODUCTION enum + generate_gltf_production_task (dead code)
- Remove worker-thumbnail from ALLOWED_SERVICES, replace Flamenco link
- Delete obsolete PLAN.md (1455 lines) and PLAN_REFACTOR.md (1174 lines)
- Fix digit-only USD prim names with p_ prefix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:22:04 +01:00
Hartmut 3dcfa7c0bd fix(USD): preserve full XCAF hierarchy with local transforms
Rewrite _traverse_xcaf → _author_xcaf_to_usd that recursively authors
USD prims mirroring the XCAF assembly tree:
- Assembly nodes become UsdGeom.Xform prims with local transforms from
  each component label's Location (not composed with parents)
- Leaf shapes get definition-space vertices (face_loc only, no instance
  placement) — the USD scene graph composes transforms hierarchically
- Coordinate swap (X,-Z,Y) now authored once as a root Xform on
  /Root/Assembly instead of per-vertex transformation
- Sharp/seam edges extracted per-part from definition shape (not global)

This fixes misplaced geometry for sub-assembly parts (e.g. KOMP-EIN
roller cages with -45° Z rotation) that were previously lost by the
flat traversal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:45:02 +01:00
Hartmut 078420c5f1 fix(USD): use canonical SCHAEFFLER material names for USD material bindings
Material prims now use resolved canonical names (e.g.
SCHAEFFLER_010101_Steel_Bare) instead of source object names. When
importing USD in Blender, materials show the correct SCHAEFFLER names
directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:19:58 +01:00
Hartmut b72d8498b9 fix(USD): correct coordinate transform (Z-up stage) and store canonical_material in DB
- export_step_to_usd.py: change stage from Y-up to Z-up, keep (X,-Z,Y)
  transform — matches GLB orientation exactly (verified: bounding box match)
- export_glb.py: include canonical_material in resolved_material_assignments
  DB field (was being dropped during manifest parsing)
- import_usd.py: use pxr customData read (not string primvars — Blender
  ignores those, confirmed by testing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:17:45 +01:00
Hartmut cc3071297b feat(M5-M7): embed canonical material names in USD via customData + pxr direct read
- export_step_to_usd.py: accept --material_map CLI arg, write
  schaeffler:canonicalMaterialName as customData on each Mesh prim,
  fix geometry transform (strip shape Location before face exploration,
  apply both face_loc and shape_loc sequentially)
- import_usd.py: after Blender USD import, use pxr to read customData
  directly from the USD file — builds {part_key: material_name} lookup
  (Blender ignores STRING primvars and customData, but pxr reads both)
- _blender_materials.py: add apply_material_library_direct() for exact
  dict-based material assignment without name-matching heuristics
- _blender_scene_setup.py: prefer direct USD lookup, fall back to
  name-matching for legacy USD files without material metadata
- export_glb.py (generate_usd_master_task): resolve material_map via
  material_service.resolve_material_map() and pass to subprocess;
  include material hash in cache key for invalidation
- ROADMAP.md: update P5 status, add M5-M7 milestones

Tested: 3/3 parts matched (ans_lfs120), 172/175 parts matched
(F-802007.TR4-D1-H122AG). Previous: 0/25 matched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:04:26 +01:00
Hartmut 1321ef2bd4 refactor: rename thumbnail_rendering queue to asset_pipeline
The queue handles far more than thumbnails: OCC tessellation, USD master
generation, GLB production, order line renders, and workflow renders.
asset_pipeline better reflects its role as the render-worker's primary queue.

Updated all references in: task decorators, celery_app.py, beat_tasks.py,
docker-compose.yml worker command, worker.py MONITORED_QUEUES, admin.py,
CLAUDE.md, LEARNINGS.md, Dockerfile, helpTexts.ts, test files,
and all .claude/commands/*.md skill files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:28:38 +01:00
Hartmut e7b70a35ea fix(admin): limit generate-missing-usd-masters to product-linked CadFiles
Previously the endpoint queued USD generation for ALL 295 completed CadFiles,
including 250 orphan CadFiles not linked to any product. Now filters to only
CadFiles referenced by at least one Product.cad_file_id, reducing the backfill
from ~285 to ~41 tasks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:23:32 +01:00
Hartmut 8e1cd41868 fix(critical): SQLAlchemy mapper crash + material matching for USD renders + kanban drag-to-reject
- beat_tasks.py: import app.models at module level so SQLAlchemy can
  resolve relationship("Template") and relationship("User") when domain
  models are imported in isolation inside task functions. Fixes all
  beat tasks (batch_render_notifications, recover_stuck_cad_files) that
  crashed every 60s with mapper initialization error.

- _blender_materials.py: build_mat_map_lower() now adds a slug-normalized
  key variant (re.sub([^a-z0-9]+, _, kl)) for each mat_map entry. OCC
  part names like 'F-802007_TR4-D1-H122AG' → slug 'f_802007_tr4_d1_h122ag'
  now matches USD-imported Blender objects. Existing prefix fallback
  (key.startswith(part_key)) catches AF-suffix variants.

- Orders.tsx: kanban drag-to-reject implemented. submitted/processing
  cards are draggable (cursor-grab). Rejected column highlights with
  red ring on drag-over. Drop opens reject reason modal via createPortal.
  Confirm calls rejectOrder() mutation + invalidates orders cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:21:46 +01:00
Hartmut 71584edce6 docs: learning erfasst - OCC face_loc behavior und GLB-Cache-Invalidierung nach Tessellierungs-Fixes
Dokumentiert drei neue Learnings aus der GE360-HF Wälzkörper-Positions-Untersuchung:
1. BRepMesh auf Compound: Triangulation in Definition-Space, Face-loc = Instance-Placement
2. IsSame() vs IsPartner() für Assembly-Instanz-Deduplizierung
3. Stale GLB-Cache maskiert Code-Fixes — nach Tessellierungs-Änderungen Cache invalidieren

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 18:08:48 +01:00
Hartmut de7f97be87 fix(render): fix double-transform bug in USD mesh extraction causing wrong part positions
In _extract_mesh(), BRep_Tool.Triangulation_s(face, face_loc) returns a face_loc
that already encodes the instance's full placement transform when a compound shape
is tessellated with BRepMesh_IncrementalMesh. Applying shape_trsf on top doubled
every rotation/translation, causing multiple roller elements to collapse to the same
wrong world position (e.g. Z(-75°)×2 ≡ Z(+105°)×2 mod 360° → identical positions).

Fix: use elif so shape_loc is only applied as a fallback when face_loc is identity.
Adds seam edge extraction (UV seam primvar) and improves _traverse_xcaf doc.

docs: learning erfasst - OCC face_loc double-transform in compound tessellation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 16:43:33 +01:00
Hartmut 71e099305c fix: deduplicate GLB/USD generation with Redis locks + review fixes
- Add per-file Redis SET NX EX 1800 locks to generate_gltf_geometry_task
  and generate_usd_master_task — concurrent duplicates (e.g. double-click
  of bulk action buttons) now log a warning and return immediately instead
  of running two expensive OCC tessellation subprocesses on the same file
- Fix eng.dispose() called inside with Session() block in cache-hit path
  of both tasks — moved to after the with block exits (Tasks 3+4 from plan)
- Add cad.updated_at = datetime.utcnow() in save_manual_material_overrides
  (was missing vs parallel save_part_materials endpoint)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:50:05 +01:00
Hartmut 409fb92899 feat(P2): USD Foundation — canonical part identity + material overrides
M1 — USD exporter:
- render-worker/scripts/export_step_to_usd.py (631 lines)
  Full XCAF traversal, one UsdGeom.Mesh per leaf part,
  schaeffler:partKey on every prim, index-space sharpEdgeVertexPairs
- render-worker/Dockerfile: usd-core>=24.11 installed (USD 0.26.3)

M2 — usd_master MediaAsset + pipeline auto-chain:
- migrations 060 (usd_master enum), 061 (3 JSONB columns),
  062 (rename tessellation settings keys)
- generate_usd_master_task: runs export_step_to_usd.py, upserts
  usd_master MediaAsset, writes resolved_material_assignments to CadFile
- Auto-chained from generate_gltf_geometry_task after every GLB export
- step_tasks.py shim re-exports generate_usd_master_task

M3 — scene-manifest API:
- part_key_service.py: build_scene_manifest(), generate_part_key(),
  four-layer material priority resolution with provenance
- SceneManifest / PartEntry Pydantic models in products/schemas.py
- GET /api/cad/{id}/scene-manifest endpoint (graceful fallback to
  parsed_objects when USD not yet generated)
- POST /api/cad/{id}/generate-usd-master endpoint
- frontend/src/api/sceneManifest.ts: fetchSceneManifest(),
  triggerUsdMasterGeneration()

M4 — manual-material-overrides API:
- GET/PUT /api/cad/{id}/manual-material-overrides endpoints
- CadFile.manual_material_overrides JSONB column (migration 061)
- getManualOverrides() / saveManualOverrides() in cad.ts

M5 — ThreeDViewer partKey integration:
- export_step_to_gltf.py injects partKeyMap into GLB extras
- ThreeDViewer: partKeyMap extraction, resolvePartKey(), effectiveMaterials
  merges legacy partMaterials + new manualOverrides (server-side persistence)
- MaterialPanel: dual-path save (partKey vs legacy), provenance badge,
  reconciliation panel for unmatched/unassigned parts

Also:
- Admin.tsx: generate-missing-usd-masters + canonical scenes bulk actions
- ProductDetail.tsx: usd_master row in asset table
- vite-env.d.ts: fix ImportMeta.env TypeScript error
- GPUProbeResult: add timestamp/devices/render_time_s fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:11:09 +01:00
Hartmut 47b5d42bb5 refactor(P1): M1 dead code removal + M3 blender_render.py split
M1 — dead code removed:
- Delete blender-renderer/ and threejs-renderer/ source files
- Remove PIL/Pillow fallback block from step_processor.py
  (_generate_thumbnail_placeholder, _finalise_image JPG path)
- Remove stl_quality param from render_blender.py, render_still_task,
  render_turntable_task (was always "low"; hardcode deflection values)
- render_turntable_task now reads scene_linear/angular_deflection from
  system_settings (consistent with export_glb.py pipeline)

M3 — blender_render.py split from 263 → 68 lines:
- _blender_args.py: parse_args() — all 25 positional + named args
- _blender_scene_setup.py: setup_scene() — MODE A/B including USD import
- _blender_render_config.py: configure_and_render() — engine + output

Post-review fixes:
- _db_engine.dispose() after settings read in render_turntable_task
- _finalise_image() fmt param removed (always PNG; PIL never installed)
- _blender_import.py committed together with new submodules to satisfy
  import_usd_file dependency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 12:54:40 +01:00
Hartmut 393e4b92a7 refactor(P1): complete pipeline cleanup — M1 dead code + M3 blender split
M1 dead code removal:
- admin.py: remove VALID_STL_QUALITIES + stl_quality (7 locations)
- frontend: remove stl_quality from 6 files (api/orders.ts, api/worker.ts,
  WorkerActivity.tsx, RenderInfoModal.tsx, helpTexts.ts, mocks/handlers.ts)
- blender_render.py: delete _mark_sharp_and_seams() — dead, never called (62 lines)
- step_processor.py: delete _render_via_service() + 2 elif renderer=="threejs" branches
- renderproblems_tmp/: remove 3 orphaned debug images

M3 blender_render.py decomposition (858 → 248 lines):
- _blender_gpu.py: activate_gpu(), configure_engine()
- _blender_import.py: import_glb(), apply_rotation()
- _blender_materials.py: FAILED_MATERIAL_NAME, assign_failed_material(),
  build_mat_map_lower(), apply_material_library()
- _blender_camera.py: setup_auto_camera(), setup_auto_lights()
- _blender_scene.py: ensure_collection(), apply_smooth_batch(),
  apply_sharp_edges_from_occ(), setup_shadow_catcher()
- Entry-point: sys.path.insert for submodule discovery; arg-parse + Mode A/B orchestration only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 22:19:59 +01:00
Hartmut 4f0fe2c8c7 docs: update ROADMAP.md + USD plan after Phase B completion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 21:51:38 +01:00