fix: media thumbnails, product dimensions, inline 3D viewer, GLB export
Bug A: Media Library thumbnails were gray because <img src> cannot send JWT auth headers. Added useAuthBlob() hook (fetch + createObjectURL) in MediaBrowser.tsx. Also fixed publish_asset Celery task to populate product_id + cad_file_id on MediaAsset for thumbnail fallback resolution. Bug B: Product dimensions now shown in Product Details card with Ruler icon and "from CAD" label when cad_mesh_attributes.dimensions_mm exists. Bug C: Replaced 128×128 CAD thumbnail with InlineCadViewer component. Queries gltf_geometry MediaAssets, fetches GLB via auth fetch → blob URL → Three.js Canvas with OrbitControls. Falls back to thumbnail + "Load 3D Model" button. Polling when GLB generation is in progress. Bug D: trimesh was in [cad] optional extra but Dockerfile only installed [dev]. Changed to pip install -e ".[dev,cad]" — trimesh now available in backend container, GLB + Colors export works. Also added bbox extraction (STL-first numpy parsing) in render_step_thumbnail and admin "Re-extract CAD Metadata" bulk endpoint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,12 +31,50 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--output_path", required=True)
|
||||
parser.add_argument("--asset_library_blend", default=None)
|
||||
parser.add_argument("--material_map", default="{}")
|
||||
parser.add_argument("--sharp_edges_json", default="[]",
|
||||
help="JSON array of [x, y, z] midpoints (mm) to mark as sharp edges")
|
||||
return parser.parse_args(rest)
|
||||
|
||||
|
||||
def mark_sharp_edges_by_proximity(midpoints_mm: list, threshold_mm: float = 1.0) -> None:
|
||||
"""Mark Blender mesh edges as sharp based on proximity to OCC-derived midpoints.
|
||||
|
||||
midpoints_mm: list of [x, y, z] in mm (from OCC coordinate space).
|
||||
After STL import + scale-apply (mm→m), Blender vertices are in meters, so we
|
||||
convert the edge midpoint back to mm before comparing.
|
||||
threshold_mm: snap distance in mm (default 1.0 mm).
|
||||
"""
|
||||
if not midpoints_mm:
|
||||
return
|
||||
|
||||
import bpy # type: ignore[import]
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type != "MESH":
|
||||
continue
|
||||
mesh = obj.data
|
||||
mesh.use_auto_smooth = True
|
||||
mw = obj.matrix_world
|
||||
for edge in mesh.edges:
|
||||
v1 = mw @ mesh.vertices[edge.vertices[0]].co
|
||||
v2 = mw @ mesh.vertices[edge.vertices[1]].co
|
||||
# Convert Blender meters → mm for comparison
|
||||
mid_mm = [
|
||||
(v1.x + v2.x) / 2 * 1000,
|
||||
(v1.y + v2.y) / 2 * 1000,
|
||||
(v1.z + v2.z) / 2 * 1000,
|
||||
]
|
||||
for hint in midpoints_mm:
|
||||
dist_sq = sum((a - b) ** 2 for a, b in zip(mid_mm, hint))
|
||||
if dist_sq < threshold_mm ** 2:
|
||||
edge.use_edge_sharp = True
|
||||
break
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
material_map: dict = json.loads(args.material_map)
|
||||
sharp_edge_midpoints: list = json.loads(args.sharp_edges_json)
|
||||
|
||||
import bpy # type: ignore[import]
|
||||
|
||||
@@ -52,6 +90,11 @@ def main() -> None:
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.transform_apply(scale=True)
|
||||
|
||||
# Mark sharp edges for better UV seams
|
||||
if sharp_edge_midpoints:
|
||||
mark_sharp_edges_by_proximity(sharp_edge_midpoints)
|
||||
print(f"Marked sharp edges from {len(sharp_edge_midpoints)} hint points")
|
||||
|
||||
# Apply asset library materials if provided
|
||||
if args.asset_library_blend and material_map:
|
||||
import os
|
||||
|
||||
Reference in New Issue
Block a user