(body: TransformRequest)
| 202 | |
| 203 | @router.post("/transform") |
| 204 | def transform_mesh(body: TransformRequest): |
| 205 | # Bake an interactive-gizmo transform into the GLB at scene level so it |
| 206 | # persists to export. Pure trimesh — no pymeshlab needed. |
| 207 | input_path = _resolve_input_path(body.path) |
| 208 | |
| 209 | matrix = np.asarray(body.matrix, dtype=float) |
| 210 | if matrix.shape != (4, 4): |
| 211 | raise HTTPException(400, "matrix must be a 4x4 array") |
| 212 | if not np.all(np.isfinite(matrix)): |
| 213 | raise HTTPException(400, "matrix contains non-finite values") |
| 214 | |
| 215 | # Keep the loaded result as-is (Scene when textured/multi-geometry) so |
| 216 | # apply_transform preserves materials and UVs. |
| 217 | loaded = trimesh.load(str(input_path)) |
| 218 | loaded.apply_transform(matrix) |
| 219 | |
| 220 | stem = input_path.stem |
| 221 | output_name = f"{stem}_xf_{uuid.uuid4().hex[:8]}.glb" |
| 222 | output_dir = input_path.parent if str(input_path).startswith(str(WORKSPACE_DIR.resolve())) else WORKSPACE_DIR / "Workflows" |
| 223 | output_dir.mkdir(parents=True, exist_ok=True) |
| 224 | output_path = output_dir / output_name |
| 225 | loaded.export(str(output_path)) |
| 226 | |
| 227 | rel = output_path.relative_to(WORKSPACE_DIR).as_posix() |
| 228 | return {"url": f"/workspace/{rel}"} |
| 229 | |
| 230 | |
| 231 | def _smooth(input_path: str, iterations: int, tmp_dir: str) -> trimesh.Trimesh: |
nothing calls this directly
no test coverage detected