init
This commit is contained in:
62
backend/app/services/file_parser.py
Normal file
62
backend/app/services/file_parser.py
Normal file
@@ -0,0 +1,62 @@
|
||||
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,
|
||||
)
|
||||
Reference in New Issue
Block a user