"""Unit tests for combining STLs into one multi-object 3MF.""" import io import re import zipfile import pytest import trimesh from backend.app.services import mesh_combine, stl_thumbnail from backend.app.services.mesh_combine import ( LAYOUT_GAP_MM, MAX_COMBINE_INSTANCES, THUMBNAIL_FACE_BUDGET, THUMBNAIL_PATH, CombinePart, MeshCombineError, combine_parts_to_3mf, layout_offsets, thumbnail_faces_per_copy, ) @pytest.fixture def box_stl(tmp_path): path = tmp_path / "box.stl" path.write_bytes(trimesh.creation.box((30, 20, 10)).export(file_type="stl")) return path @pytest.fixture def cyl_stl(tmp_path): path = tmp_path / "cyl.stl" path.write_bytes(trimesh.creation.cylinder(radius=12, height=25).export(file_type="stl")) return path def _model_xml(data: bytes) -> str: with zipfile.ZipFile(io.BytesIO(data)) as zf: assert {"[Content_Types].xml", "_rels/.rels", "3D/3dmodel.model"} <= set(zf.namelist()) return zf.read("3D/3dmodel.model").decode("utf-8") def test_one_object_per_source_one_item_per_copy(box_stl, cyl_stl): data = combine_parts_to_3mf([CombinePart("box.stl", box_stl, 2), CombinePart("cyl.stl", cyl_stl, 3)]) xml = _model_xml(data) # Geometry is stored once per source; copies are build items. assert re.findall(r'