from __future__ import annotations import json from pathlib import Path from typing import Iterable import numpy as np import trimesh def _sample_points(points: np.ndarray, max_points: int = 3500) -> np.ndarray: if len(points) <= max_points: return points.astype(float) idx = np.linspace(0, len(points) - 1, max_points).astype(int) return points[idx].astype(float) def load_points_from_cloud_file(path: str | Path, max_points: int = 3500) -> np.ndarray: cloud = trimesh.load(path, force="mesh") if isinstance(cloud, trimesh.points.PointCloud): points = np.asarray(cloud.vertices) elif isinstance(cloud, trimesh.Trimesh): if len(cloud.faces) > 0: count = min(max_points * 2, max(1200, len(cloud.faces) * 3)) points = cloud.sample(count) else: points = np.asarray(cloud.vertices) else: points = np.asarray(getattr(cloud, "vertices", [])) return _sample_points(np.asarray(points, dtype=float), max_points=max_points) def load_points_from_mesh_file(path: str | Path, max_points: int = 3500) -> np.ndarray: mesh = trimesh.load(path, force="mesh") if isinstance(mesh, trimesh.Scene): mesh = trimesh.util.concatenate([g for g in mesh.geometry.values() if isinstance(g, trimesh.Trimesh)]) if isinstance(mesh, trimesh.Trimesh): if len(mesh.faces) > 0: count = min(max_points * 2, max(1600, len(mesh.faces) * 2)) points = mesh.sample(count) else: points = np.asarray(mesh.vertices) else: points = np.asarray(getattr(mesh, "vertices", [])) return _sample_points(np.asarray(points, dtype=float), max_points=max_points) def empty_viewer_html(message: str = "Generate a blueprint to preview it here.") -> str: return f"""