63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
import logging
|
||
from dataclasses import dataclass
|
||
|
||
import trimesh
|
||
|
||
logger = logging.getLogger("app.services.file_parser")
|
||
|
||
|
||
@dataclass
|
||
class FileInfo:
|
||
volume_cm3: float
|
||
surface_area_cm2: float
|
||
bounding_box_mm: dict[str, float]
|
||
is_watertight: bool
|
||
triangle_count: int
|
||
|
||
|
||
SUPPORTED_EXTENSIONS = {".stl", ".3mf", ".obj"}
|
||
|
||
|
||
def parse_3d_file(file_path: str, file_extension: str) -> FileInfo:
|
||
"""Parse a 3D file and return geometric properties."""
|
||
ext = file_extension.lower().lstrip(".")
|
||
logger.info("Parsing 3D file: path=%s, extension=%s", file_path, ext)
|
||
|
||
logger.debug("Loading mesh with trimesh (file_type=%s)...", ext)
|
||
mesh = trimesh.load(file_path, file_type=ext)
|
||
logger.debug("Trimesh loaded object type: %s", type(mesh).__name__)
|
||
|
||
if isinstance(mesh, trimesh.Scene):
|
||
meshes = list(mesh.dump())
|
||
logger.info("File is a Scene with %d geometries, concatenating...", len(meshes))
|
||
if not meshes:
|
||
logger.error("Scene contains no geometries")
|
||
raise ValueError("Файл не содержит 3D-геометрии")
|
||
mesh = trimesh.util.concatenate(meshes)
|
||
logger.debug("Concatenated into single Trimesh")
|
||
|
||
if not isinstance(mesh, trimesh.Trimesh):
|
||
logger.error("Could not extract Trimesh object, got: %s", type(mesh).__name__)
|
||
raise ValueError("Не удалось извлечь 3D-геометрию из файла")
|
||
|
||
volume_cm3 = abs(mesh.volume) / 1000.0
|
||
surface_area_cm2 = mesh.area / 100.0
|
||
bbox = {
|
||
"x": round(float(mesh.bounding_box.extents[0]), 2),
|
||
"y": round(float(mesh.bounding_box.extents[1]), 2),
|
||
"z": round(float(mesh.bounding_box.extents[2]), 2),
|
||
}
|
||
is_watertight = bool(mesh.is_watertight)
|
||
triangle_count = len(mesh.faces)
|
||
|
||
logger.info("Parse result: volume=%.2f cm3, area=%.2f cm2, bbox=%s, watertight=%s, triangles=%d",
|
||
volume_cm3, surface_area_cm2, bbox, is_watertight, triangle_count)
|
||
|
||
return FileInfo(
|
||
volume_cm3=volume_cm3,
|
||
surface_area_cm2=surface_area_cm2,
|
||
bounding_box_mm=bbox,
|
||
is_watertight=is_watertight,
|
||
triangle_count=triangle_count,
|
||
)
|