Spaces:
Running
Running
| from __future__ import annotations | |
| from pathlib import Path | |
| import matplotlib | |
| import numpy as np | |
| from mpl_toolkits.mplot3d.art3d import Poly3DCollection | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| def _axis_limits_from_points(points: np.ndarray) -> tuple[tuple[float, float], tuple[float, float], tuple[float, float]]: | |
| x_min, x_max = float(np.min(points[:, 0])), float(np.max(points[:, 0])) | |
| y_min, y_max = float(np.min(points[:, 1])), float(np.max(points[:, 1])) | |
| z_min, z_max = float(np.min(points[:, 2])), float(np.max(points[:, 2])) | |
| max_range = max(x_max - x_min, y_max - y_min, z_max - z_min) / 2.0 | |
| max_range = max(max_range, 1e-6) * 1.08 | |
| x_mid = (x_max + x_min) / 2.0 | |
| y_mid = (y_max + y_min) / 2.0 | |
| z_mid = (z_max + z_min) / 2.0 | |
| return ( | |
| (x_mid - max_range, x_mid + max_range), | |
| (y_mid - max_range, y_mid + max_range), | |
| (z_mid - max_range, z_mid + max_range), | |
| ) | |
| def _to_vertices(face_v_item: object) -> np.ndarray | None: | |
| verts = np.asarray(face_v_item, dtype=float) | |
| if verts.ndim == 2 and verts.shape[1] == 3 and len(verts) >= 3: | |
| return verts | |
| flat = verts.reshape(-1) | |
| if flat.size >= 9 and flat.size % 3 == 0: | |
| shaped = flat.reshape(-1, 3) | |
| if len(shaped) >= 3: | |
| return shaped | |
| return None | |
| def visualize_geometry( | |
| geometry_npz: str | Path, | |
| output_png: str | Path, | |
| *, | |
| elev: float = 45.0, | |
| azim: float = 15.0, | |
| dpi: int = 300, | |
| ) -> Path: | |
| """Render geometry polygons from PACK geometry npz (expects key: face_v).""" | |
| geometry_npz = Path(geometry_npz) | |
| output_png = Path(output_png) | |
| with np.load(geometry_npz, allow_pickle=True) as data: | |
| if "face_v" not in data: | |
| keys = ", ".join(sorted(data.files)) | |
| raise KeyError(f"Missing key 'face_v' in {geometry_npz}; keys=[{keys}]") | |
| face_v = data["face_v"] | |
| polygons: list[np.ndarray] = [] | |
| for item in face_v: | |
| verts = _to_vertices(item) | |
| if verts is not None: | |
| polygons.append(verts) | |
| if not polygons: | |
| raise ValueError(f"No valid polygons in {geometry_npz}") | |
| fig = plt.figure(figsize=(4, 4)) | |
| ax = fig.add_subplot(111, projection="3d") | |
| ax.view_init(elev=elev, azim=azim) | |
| fig.subplots_adjust(left=0, right=1, top=1, bottom=0) | |
| for verts in polygons: | |
| collection = Poly3DCollection( | |
| [verts], | |
| facecolors="#FFFFFF", | |
| edgecolors="#4D4D4D", | |
| linewidths=0.42, | |
| alpha=0.35, | |
| ) | |
| ax.add_collection3d(collection) | |
| all_points = np.vstack(polygons) | |
| x_lim, y_lim, z_lim = _axis_limits_from_points(all_points) | |
| ax.set_xlim(*x_lim) | |
| ax.set_ylim(*y_lim) | |
| ax.set_zlim(*z_lim) | |
| ax.set_box_aspect([1, 1, 1]) | |
| ax.set_axis_off() | |
| output_png.parent.mkdir(parents=True, exist_ok=True) | |
| fig.savefig(output_png, dpi=dpi, bbox_inches="tight", pad_inches=0.04, transparent=True) | |
| plt.close(fig) | |
| return output_png | |