Files
IfcOpenShell/src/ifcopenshell-python/test/test_create_shape.py
T

244 lines
10 KiB
Python

import functools
import itertools
import multiprocessing
import operator
import os
from typing import get_args
import pytest
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
import test.bootstrap
from ifcopenshell.util.shape_builder import ShapeBuilder
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()
def test_logging():
assert ifcopenshell.logger
logger = ifcopenshell.logger()
logger.OutputFormat(logger.FMT_INMEMORY)
settings = ifcopenshell.geom.settings()
f = ifcopenshell.open(fn)
col = f.by_type("IfcColumn")[0]
_ = ifcopenshell.geom.create_shape(settings, col, logger=logger)
num_log_items = len(list(logger))
col.Representation.Representations[0].Items[0].MappingSource.MappedRepresentation.Items[0].Depth *= -1.0
with pytest.raises(RuntimeError):
_ = ifcopenshell.geom.create_shape(settings, col, logger=logger)
new_items = list(logger)[num_log_items:]
assert ("GEO089", "Non-positive extrusion height encountered for:") in [
(msg.code, msg.message) for msg in new_items
]
if __name__ == "__main__":
import pytest
pytest.main(["-vvsx", __file__])