Commit Graph

118 Commits

Author SHA1 Message Date
Hartmut 448996b546 fix: stabilize HartOMat runtime startup 2026-04-06 13:10:51 +02:00
Hartmut b795f0e6d6 refactor: rebrand project to HartOMat 2026-04-06 12:45:47 +02:00
Hartmut fa7093307a chore: snapshot before HartOMat rebrand 2026-04-06 12:41:44 +02:00
Hartmut feef2a0827 feat: AI searches product part materials — finds products WITH Durotect
Added product_material filter to find_product_renders:
- Searches cad_part_materials JSONB for materials assigned to CAD parts
- find_product_renders(transparent_only=true, product_material="Durotect")
  → finds 9 products that naturally have Durotect parts with transparent renders

Two material levels explained in system prompt rule 13:
- product_material: materials from STEP/Excel (Durotect_M, Stahl, Bronze)
- material_override: single material forced on ALL parts at render time

AI now searches product_material FIRST when user asks "with Durotect material"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:47:01 +01:00
Hartmut 9c6b210d51 fix: AI agent shows best available render when exact match not found
Rule 13: when searching with multiple criteria (transparent + Durotect),
decompose the search. Show what exists (1220 transparent renders) and
explain what's missing (no Durotect material applied). Never say
"no renders found" when transparent renders DO exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:43:01 +01:00
Hartmut 54522a63d4 feat: render metadata from render_log for past renderings
find_product_renders now reads actual render metadata from the
render_log JSONB (stored at render time) via COALESCE fallback:
- engine: render_log.engine_used > render_log.engine > output_type setting
- width/height: render_log > output_type.render_settings
- samples: render_log > output_type.render_settings
- render_type: still/turntable/cinematic from render_log.type
- has_template: whether a .blend template was used

All past renderings now have correct metadata without re-processing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:35:19 +01:00
Hartmut 86921bacbd feat: render metadata in find_product_renders — material, format, resolution
find_product_renders now returns full render job metadata:
- material_override (effective: line override > output type override)
- output_format (with render_overrides applied)
- resolution (width x height)
- engine, samples
- order_number + order_id (for linking)
- is_animation flag

New material_override filter: search renders by material name
(e.g. transparent_only=true, material_override="Durotect" finds
renders with Durotect material on transparent background)

AI can now answer: "Show me a transparent Durotect render" by filtering
both transparency AND material in one query.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:24:02 +01:00
Hartmut 59f83f10ad fix: AI agent must confirm before ANY write action including overrides
Explicitly listed all write tools that require confirmation:
create_order, dispatch_renders, set_material_override, set_render_overrides

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:14:48 +01:00
Hartmut 8897afdebb fix: AI agent must confirm before creating orders or dispatching renders
Split rules into read-only vs write actions:
- READ (search, list, show images, status): execute immediately
- WRITE (create orders, dispatch, set overrides): ALWAYS ask for
  confirmation before executing

Prevents accidental render job creation from casual questions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:11:52 +01:00
Hartmut 20bcdee2a2 feat: AI agent links to products and orders in responses
System prompt rule 12: always format product mentions as
[ProductName](/products/UUID) and orders as [OrderNumber](/orders/UUID).

ReactMarkdown in ChatPanel already renders these as clickable links
with accent color styling, so users can navigate directly from chat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:07:44 +01:00
Hartmut ef4c8eefc9 feat: AI agent knows material library — list_materials tool with alias search
Added list_materials tool to the chat agent:
- Searches SCHAEFFLER library materials by name, description, or alias
- Returns material name + schaeffler_code + aliases
- Enables: "zeig mir ein Bild mit Durotect-Material" → agent searches
  for "durotect" → finds SCHAEFFLER_020101_Durotect-Blue → uses as
  material_override

System prompt updated with rules 10-11:
- Explains alias → library material mapping
- Always use full SCHAEFFLER name for material_override

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 10:05:05 +01:00
Hartmut c159bff2df fix: clean frames directory before cinematic/turntable re-render
Old frame PNGs from previous render attempts persisted in the frames
directory, causing FFmpeg to stitch the wrong number of frames.
Now rmtree's the directory before creating it fresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 22:02:20 +01:00
Hartmut 458c6cd813 refactor: cinematic render — linear keyframes, 3 segments, 250 frames, white bg
Changes per user feedback:
- Keyframe interpolation: BEZIER → LINEAR (all fcurves set to LINEAR)
- Removed segment 4 (closeup) — now 3 segments only
- Frame count: 480 → 250 (10 seconds at 25fps)
- FPS: 24 → 25
- Easing removed — pure linear interpolation between segment params
- White background by default (World node Color = white)
- Transparent bg still available as override

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:57:42 +01:00
Hartmut 75ad397c09 fix: unbuffered stdout for live cinematic frame progress
Two fixes for frame progress not appearing in frontend:
1. Added flush=True to all print() calls in cinematic_render.py
2. Set PYTHONUNBUFFERED=1 in subprocess environment

Without these, Python buffers stdout inside Blender, so all frame
progress lines arrive in a batch after the process exits instead
of streaming line-by-line during rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:53:26 +01:00
Hartmut e0714854d2 fix: stream Blender frame output to frontend for cinematic renders
The filter only matched [cinematic_render] tag but Blender outputs its
own "Saved: frame_NNNN.png" lines per frame. Now also matches "Saved:"
lines, extracts the frame number, and formats as:
  [cinematic_render] Frame 84/480 rendered

This shows live frame progress in the LiveRenderLog on the frontend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:42:36 +01:00
Hartmut caffe7809c feat: live frame progress streaming for cinematic renders
Replaced communicate() (blocking) with selectors-based line-by-line
stdout streaming — same pattern as still render. Each frame now
streams live to the frontend:

  [cinematic_render] Frame 42/480 -- 55.3s elapsed (0.76 fps)

Pipeline: Blender stdout → log_callback → emit() → Redis →
LiveRenderLog poll (2s) → frontend display

Also added log_callback parameter to cinematic render task call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:39:41 +01:00
Hartmut e26d76154b fix: cinematic render arg order — match turntable positional layout
The service was sending (glb, frames_dir, width, height, engine, samples...)
but the script expected turntable order (glb, frames_dir, frame_count, degrees,
width, height, engine, samples...). Fixed by adding frame_count and degrees
placeholders to match the expected positional layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:32:34 +01:00
Hartmut f22b963be9 feat: cinematic highlight render — 20s procedural camera animation
New render type: 4-segment cinematic camera animation (480 frames @ 24fps)
for professional product highlight videos.

Camera sequence:
1. Establishing (5s): slow 45° orbit + push-in, 50mm lens
2. Detail sweep (5s): low-angle close arc, 85mm telephoto, shallow DOF
3. Crane up (5s): rising 30°→60°, 35mm wide reveal, pull-back
4. Hero close (5s): push-in to beauty angle, 65mm, smooth ease-out

Technical:
- cinematic_render.py: procedural camera from bounding sphere, cubic easing,
  per-frame keyframes (location, rotation, focal length, DOF)
- render_cinematic_to_file(): service function (same pattern as turntable)
- Pipeline routing: render_settings.cinematic flag → cinematic path
- Depth of field enabled (f-stop scales with product size)
- use_persistent_data for BVH caching between frames
- Same material/template/USD pipeline as turntable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:25:56 +01:00
Hartmut 0dfadbfd56 fix: make AI agent proactive — never ask for info it can look up
System prompt rewritten with explicit RULES:
- Never ask user for info you can query yourself
- "Any product" / "beliebig" → just pick one, don't ask back
- Execute immediately, no confirmation needed
- Be concise, short answers preferred

find_product_renders tool:
- No longer requires product_name or product_id
- Call with empty params → returns any recent renders
- Enables "show me any render with transparent bg"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:56:21 +01:00
Hartmut 29f7103a8b feat: chat agent can find and show existing product renders
Added find_product_renders tool to the AI agent:
- Searches completed renders by product name/ID
- Returns viewable image URLs (relative paths)
- Supports transparent_only filter
- AI formats results as Markdown images/links in chat

Frontend:
- ChatPanel ReactMarkdown renders <img> and <a> tags
- Images shown inline (max 200px height, rounded, bordered)
- Links open in new tab with accent color

System prompt updated to instruct AI to use Markdown image syntax
when showing renders: ![description](/renders/...)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:52:45 +01:00
Hartmut 531994cccd fix: OCP import for BRepGProp + route metadata task to asset_pipeline
- Fixed: from OCP.BRepGProp import BRepGProp as brepgprop (was lowercase)
- Routed reextract_rich_metadata_task to asset_pipeline queue (render-worker
  has OCC/OCP installed, worker container does not)
- Backfill verified: 45/45 products updated with volume, surface area,
  part count, complexity metrics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:50:12 +01:00
Hartmut cfccdd5397 feat: rich product metadata extraction from STEP files
Extract volume, surface area, part count, assembly hierarchy, and
complexity from STEP files via OCC B-rep analysis.

Backend:
- extract_rich_metadata() in step_processor.py: computes per-part volume
  (BRepGProp), surface area, triangle/vertex count, assembly depth,
  instance count, complexity score, largest part identification
- cad_metadata JSONB column on Product model (DB migration)
- Auto-populated during STEP processing (non-fatal, 10s timeout)
- Also stored in cad_files.mesh_attributes["rich_metadata"]
- Batch re-extract endpoint: POST /admin/settings/reextract-rich-metadata

AI Agent:
- search_products returns part_count, volume_cm3, complexity, largest_part
- query_database tool description documents cad_metadata schema

Frontend:
- ProductDetail page: CAD Metadata section with stat cards
  (parts, volume, surface area, complexity, triangles, assembly depth)
- Admin System Tools: "Re-extract Rich Metadata" button for backfill

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:49:50 +01:00
Hartmut 0ffc86589a fix: chat agent auth, auto-submit/dispatch, no confirmation prompts
- All httpx tool calls use real user_id instead of uuid(int=0) for
  service token — fixes 401 Unauthorized on dispatch/override
- create_order auto-submits and auto-dispatches in one step
- System prompt updated: execute immediately without asking for
  confirmation, respond in user's language
- Product search returns CAD dimensions (dim_x/y/z_mm)
- query_database tool description includes cad_files schema

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 15:59:53 +01:00
Hartmut f70a09886a feat: expose CAD dimensions (mm) in chat agent tools
- search_products now returns dim_x_mm, dim_y_mm, dim_z_mm from
  cad_files.mesh_attributes->'dimensions_mm'
- query_database tool description updated with cad_files schema
  including mesh_attributes->'dimensions_mm' path
- AI can now answer "what's the biggest product?" using real dimensions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 15:05:37 +01:00
Hartmut d37dd073bd feat: chat agent knows current page context (order/product)
Frontend: Layout extracts order/product UUID from URL path and passes
to ChatPanel as contextType/contextId. When on /orders/{uuid}, the
chat knows which order you're viewing.

Backend: System prompt now loads actual entity data for the context:
- Order: order_number, status, line counts (completed/processing/failed)
- Product: name, PIM-ID, category, STEP file status

The AI understands "this order", "current product" etc. Example:
"What's the status of this order?" → agent knows you mean SA-2026-00164

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:50:03 +01:00
Hartmut 48b5287baf fix: rollback DB session after failed tool execution in chat agent
When a tool like query_database fails (e.g., bad column name), the
SQLAlchemy session enters a failed transaction state. Subsequent
operations (like saving the assistant response) then also fail with
InFailedSQLTransactionError.

Fix: rollback the session in the except block of _execute_tool().
Also improved query_database tool description with correct column
names (category_key not category) to help the AI write valid SQL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:32:33 +01:00
Hartmut 9f72840722 fix: better error handling for chat AI errors
Catch all exceptions (not just ValueError) and return meaningful
error messages from OpenAI API errors instead of generic 500.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:22:54 +01:00
Hartmut 502e2d0387 fix: use max_completion_tokens instead of max_tokens for GPT-4o
Azure OpenAI GPT-4o and newer models require 'max_completion_tokens'
instead of 'max_tokens'. Fixed in all 3 services:
- chat_service.py (2 call sites)
- azure_ai.py (validation service)
- tenants/router.py (test connection endpoint)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:20:40 +01:00
Hartmut 59ce61098c feat: tenant AI chat agent with function calling
Actionable AI assistant that uses per-tenant Azure OpenAI credentials
to execute natural language commands against the render pipeline.

Backend:
- ChatMessage model + migration (session-based conversations)
- Chat service with 10 OpenAI function-calling tools:
  list_orders, search_products, create_order, dispatch_renders,
  get_order_status, set_material_override, set_render_overrides,
  get_render_stats, check_materials, query_database
- All tools tenant-scoped (queries filtered by tenant_id)
- Write operations use httpx to call backend API internally
- Chat API: POST /chat/messages, GET /chat/sessions, DELETE session
- Conversation history preserved in DB (last 50 messages per session)

Frontend:
- Slide-out ChatPanel (right side, w-96, animated)
- User/assistant message styling with avatars and timestamps
- Session management (new chat, session history, delete)
- Typing indicator while waiting for AI response
- Floating chat button in bottom-right corner
- Error state for unconfigured AI tenants

Example: "Render all Kugellager products as WebP at 1024x1024"
→ Agent calls search_products + create_order + dispatch_renders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:46:21 +01:00
Hartmut daad2c64f3 fix: revert dual queue to single GPU — light worker caused 2x regression
Root cause: render-worker and render-worker-light shared the same GPU,
causing contention. Complex TRB renders went from 17s → 36s (2x slower).

Changes:
- Thumbnails back to asset_pipeline queue (not asset_pipeline_light)
- Dispatch routing always uses asset_pipeline (no queue splitting)
- render-worker-light gated behind "multi-gpu" profile — only starts with:
  docker compose --profile multi-gpu up -d
- For single-GPU setups: all rendering is sequential on one worker

The dual queue approach is correct for multi-GPU machines where each
worker gets its own GPU. On single-GPU, serial execution is faster
than concurrent GPU contention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:33:26 +01:00
Hartmut b892f72f7e feat: per-line render overrides — override any output type setting at order time
Instead of duplicating output types for every variation (WebP vs PNG,
different resolution), keep one canonical output type and override
specific fields per order line via render_overrides JSONB.

Backend:
- render_overrides JSONB column on OrderLine (DB migration)
- Render task merges overrides with output type settings (format, width,
  height, samples, engine, denoiser, transparent_bg, cycles_device)
- POST /orders/{id}/batch-render-overrides endpoint for bulk override
- PatchLineBody accepts render_overrides for per-line patching

Frontend:
- Batch render overrides section on OrderDetail: output format dropdown
  (PNG/JPG/WebP) + resolution dropdown (512-4096)
- Clear button to remove overrides

MCP:
- create_order tool: accepts product_ids, output_type, render_overrides,
  material_override — enables "render all products as WebP" via Claude
- set_render_overrides tool: batch override on existing orders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:26:38 +01:00
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 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 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 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 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