Files
IfcOpenShell/src/ifcopenshell-python/test/test_create_shape.py
T
Thomas Krijnen 6df5632f91 black
2025-10-26 13:33:10 +01:00

221 lines
9.6 KiB
Python

import functools
import itertools
import multiprocessing
import operator
import os
import pytest
import test.bootstrap
import ifcopenshell
import ifcopenshell.api.context
import ifcopenshell.api.owner.settings
import ifcopenshell.api.project
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.shape
from ifcopenshell.util.shape_builder import ShapeBuilder
from typing import get_args
fn = os.path.join(os.path.dirname(__file__), "fixtures/ColumnPSetsOfSets.ifc")
class TestGeomSettings:
def test_settings(self):
settings = ifcopenshell.geom.settings()
assert set(get_args(ifcopenshell.geom.SETTING)) == set(
settings.setting_names()
), "Also need to update IfcPython.i, if new settings were added/removed."
assert "use-python-opencascade" in settings.setting_names()
assert settings.get(settings.USE_PYTHON_OPENCASCADE) is False
assert settings.get("use-python-opencascade") is False
assert "USE_PYTHON_OPENCASCADE = False" in repr(settings)
# Testing both new and old ways of setting geometry settings.
if ifcopenshell.geom.has_occ:
settings.set("use-python-opencascade", True)
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
assert settings.get(settings.USE_PYTHON_OPENCASCADE) is True
assert "USE_PYTHON_OPENCASCADE = True" in repr(settings)
else:
with pytest.raises(AttributeError):
settings.set("use-python-opencascade", True)
with pytest.raises(AttributeError):
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
assert "USE_PYTHON_OPENCASCADE = False" in repr(settings)
def test_serializer_settings(self):
settings = ifcopenshell.geom.serializer_settings()
assert set(get_args(ifcopenshell.geom.SERIALIZER_SETTING)) == set(
settings.setting_names()
), "Also need to update IfcPython.i, if new settings were added/removed."
# Only for settings.
assert "use-python-opencascade" not in settings.setting_names()
with pytest.raises(AttributeError):
settings.get(settings.USE_PYTHON_OPENCASCADE)
with pytest.raises(RuntimeError):
settings.get("use-python-opencascade")
with pytest.raises(RuntimeError):
settings.set("use-python-opencascade", True)
assert "USE_PYTHON_OPENCASCADE" not in repr(settings)
class TestTriangulationAttributes(test.bootstrap.IFC4):
def test_faces_representation_item_ids(self):
ifc_file = ifcopenshell.file()
ifcopenshell.api.root.create_entity(ifc_file, ifc_class="IfcProject", name="Test")
context = ifcopenshell.api.context.add_context(ifc_file, context_type="Model")
builder = ShapeBuilder(ifc_file)
extrusion = builder.extrude(builder.rectangle(), magnitude=1.0)
representation = builder.get_representation(context, extrusion)
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, representation)
faces_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(shape)
faces = ifcopenshell.util.shape.get_faces(shape)
assert set(faces_item_ids) == {extrusion.id()}
assert len(faces) == 12 # Cube has 12 tris.
assert len(faces_item_ids) == len(faces)
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(shape)
edges = ifcopenshell.util.shape.get_edges(shape)
assert set(edges_item_ids) == {extrusion.id()}
assert len(edges) == 12 # Cube has 12 edges.
assert len(edges_item_ids) == len(edges)
def test_curve_representation_item_ids(self):
ifc_file = ifcopenshell.file()
ifcopenshell.api.root.create_entity(ifc_file, ifc_class="IfcProject", name="Test")
context = ifcopenshell.api.context.add_context(ifc_file, context_type="Model")
builder = ShapeBuilder(ifc_file)
curve = builder.rectangle()
representation = builder.get_representation(context, curve)
settings = ifcopenshell.geom.settings()
settings.set("dimensionality", W.CURVES_SURFACES_AND_SOLIDS)
shape = ifcopenshell.geom.create_shape(settings, representation)
faces_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(shape)
assert len(faces_item_ids) == 0
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(shape)
edges = ifcopenshell.util.shape.get_edges(shape)
assert set(edges_item_ids) == {curve.id()}
assert len(edges) == 4
assert len(edges_item_ids) == len(edges)
def test_mixed_representation_item_ids(self):
ifc_file = ifcopenshell.file()
ifcopenshell.api.root.create_entity(ifc_file, ifc_class="IfcProject", name="Test")
context = ifcopenshell.api.context.add_context(ifc_file, context_type="Model")
builder = ShapeBuilder(ifc_file)
curve = builder.rectangle()
fill = ifc_file.create_entity("IfcAnnotationFillArea", builder.rectangle())
representation = builder.get_representation(context, (curve, fill))
settings = ifcopenshell.geom.settings()
settings.set("dimensionality", W.CURVES_SURFACES_AND_SOLIDS)
shape = ifcopenshell.geom.create_shape(settings, representation)
faces_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(shape)
faces = ifcopenshell.util.shape.get_faces(shape)
assert len(faces) == 2 # Fill area will produce a triangulated face.
assert set(faces_item_ids) == {fill.id()}
assert len(faces_item_ids) == len(faces)
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(shape)
edges = ifcopenshell.util.shape.get_edges(shape)
assert set(edges_item_ids) == {fill.id(), curve.id()}
assert len(edges) == 8 # 4 edges rectangle curve + 4 edges fill area
assert len(edges_item_ids) == len(edges)
class TestAssignObject:
def test_no_welding_on_distinct_items(self):
self.file = ifcopenshell.api.project.create_file()
ifcopenshell.api.owner.settings.get_user = lambda ifc: (ifc.by_type("IfcPersonAndOrganization") or [None])[0]
ifcopenshell.api.owner.settings.get_application = lambda ifc: (ifc.by_type("IfcApplication") or [None])[0]
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject", name="Test")
unit = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[unit])
context = ifcopenshell.api.context.add_context(self.file, context_type="Model")
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
def create_extrusion(x, y):
points = (
(x + 0.0, y + 0.0),
(x + 0.0, y + 1.0),
(x + 1.0, y + 1.0),
(x + 1.0, y + 0.0),
(x + 0.0, y + 0.0),
)
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
extrusion_direction = self.file.createIfcDirection((0.0, 0.0, 1.0))
return self.file.createIfcExtrudedAreaSolid(
self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve),
self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
),
extrusion_direction,
1.0,
)
extrusions = [create_extrusion(x, 0.0) for x in [0.0, 1.0]]
element.Representation = self.file.createIfcProductDefinitionShape(
Representations=[
self.file.createIfcShapeRepresentation(
context,
context.ContextIdentifier,
"SweptSolid",
extrusions,
)
]
)
obj = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(WELD_VERTICES=True), element)
# item_ids is a per-triangle array, so we have 12 triangles per cube
# even though not documented, the order in representation items should match
assert obj.geometry.item_ids == (extrusions[0].id(),) * 12 + (extrusions[1].id(),) * 12
# group the vertices
vs = [obj.geometry.verts[i : i + 3] for i in range(0, len(obj.geometry.verts), 3)]
# welding should not happen between distinct items so the total number of verts should be 2 times 8
assert len(vs) == 16
# even though there are only 12 unique vertices as the cubes are touching
assert len(set(vs)) == 12
def test_iterator():
# just test some permutations of invocation
settings = ifcopenshell.geom.settings()
file_or_filename = [fn, ifcopenshell.open(fn)]
with_or_without_threads = [[], [multiprocessing.cpu_count()]]
includes = [
{},
{"include": ["IfcColumn"]},
{"include": [file_or_filename[1].by_type("IfcColumn")[0]]},
]
for args in itertools.product(file_or_filename, with_or_without_threads, includes):
kwargs = functools.reduce(operator.or_, (a for a in args if isinstance(a, dict)))
pargs = []
for a in (_ for _ in args if not isinstance(_, dict)):
if isinstance(a, list):
pargs.extend(a)
else:
pargs.append(a)
iterator = ifcopenshell.geom.iterator(settings, *pargs, **kwargs)
assert iterator.initialize()
if __name__ == "__main__":
import pytest
pytest.main(["-vvsx", __file__])