feat: extract workflow bbox services phase 3
This commit is contained in:
@@ -87,6 +87,18 @@ class AutoPopulateMaterialsResult:
|
||||
cad_parts: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class BBoxResolutionResult:
|
||||
bbox_data: dict[str, dict[str, float]] | None
|
||||
source_kind: Literal["glb", "step", "none"]
|
||||
step_path: str
|
||||
glb_path: str | None = None
|
||||
|
||||
@property
|
||||
def has_bbox(self) -> bool:
|
||||
return self.bbox_data is not None
|
||||
|
||||
|
||||
def _emit(emit: EmitFn, order_line_id: str, message: str, level: str | None = None) -> None:
|
||||
if emit is None:
|
||||
return
|
||||
@@ -105,6 +117,86 @@ def _resolve_asset_path(storage_key: str | None) -> Path | None:
|
||||
return None
|
||||
|
||||
|
||||
def extract_bbox_from_glb(glb_path: str) -> dict[str, dict[str, float]] | None:
|
||||
"""Extract a bounding box from a GLB file in meters and convert to mm."""
|
||||
try:
|
||||
import trimesh
|
||||
|
||||
path = Path(glb_path)
|
||||
if not path.exists():
|
||||
return None
|
||||
scene = trimesh.load(str(path), force="scene")
|
||||
bounds = getattr(scene, "bounds", None)
|
||||
if bounds is None:
|
||||
return None
|
||||
mins, maxs = bounds
|
||||
dims = maxs - mins
|
||||
return {
|
||||
"dimensions_mm": {
|
||||
"x": round(float(dims[0]) * 1000, 2),
|
||||
"y": round(float(dims[1]) * 1000, 2),
|
||||
"z": round(float(dims[2]) * 1000, 2),
|
||||
},
|
||||
"bbox_center_mm": {
|
||||
"x": round(float((mins[0] + maxs[0]) / 2) * 1000, 2),
|
||||
"y": round(float((mins[1] + maxs[1]) / 2) * 1000, 2),
|
||||
"z": round(float((mins[2] + maxs[2]) / 2) * 1000, 2),
|
||||
},
|
||||
}
|
||||
except Exception as exc:
|
||||
logger.debug("extract_bbox_from_glb failed for %s: %s", glb_path, exc)
|
||||
return None
|
||||
|
||||
|
||||
def extract_bbox_from_step_cadquery(step_path: str) -> dict[str, dict[str, float]] | None:
|
||||
"""Fallback: extract a bounding box by re-parsing the STEP file via cadquery."""
|
||||
try:
|
||||
import cadquery as cq
|
||||
|
||||
bb = cq.importers.importStep(step_path).val().BoundingBox()
|
||||
return {
|
||||
"dimensions_mm": {
|
||||
"x": round(bb.xlen, 2),
|
||||
"y": round(bb.ylen, 2),
|
||||
"z": round(bb.zlen, 2),
|
||||
},
|
||||
"bbox_center_mm": {
|
||||
"x": round((bb.xmin + bb.xmax) / 2, 2),
|
||||
"y": round((bb.ymin + bb.ymax) / 2, 2),
|
||||
"z": round((bb.zmin + bb.zmax) / 2, 2),
|
||||
},
|
||||
}
|
||||
except Exception as exc:
|
||||
logger.debug("extract_bbox_from_step_cadquery failed for %s: %s", step_path, exc)
|
||||
return None
|
||||
|
||||
|
||||
def resolve_cad_bbox(
|
||||
step_path: str,
|
||||
*,
|
||||
glb_path: str | None = None,
|
||||
) -> BBoxResolutionResult:
|
||||
"""Resolve CAD bounding-box data with the legacy GLB-first fallback order."""
|
||||
bbox_data = None
|
||||
source_kind: Literal["glb", "step", "none"] = "none"
|
||||
if glb_path:
|
||||
bbox_data = extract_bbox_from_glb(glb_path)
|
||||
if bbox_data:
|
||||
source_kind = "glb"
|
||||
|
||||
if bbox_data is None:
|
||||
bbox_data = extract_bbox_from_step_cadquery(step_path)
|
||||
if bbox_data:
|
||||
source_kind = "step"
|
||||
|
||||
return BBoxResolutionResult(
|
||||
bbox_data=bbox_data,
|
||||
source_kind=source_kind,
|
||||
step_path=step_path,
|
||||
glb_path=glb_path,
|
||||
)
|
||||
|
||||
|
||||
def prepare_order_line_render_context(
|
||||
session: Session,
|
||||
order_line_id: str,
|
||||
|
||||
Reference in New Issue
Block a user