mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
WIP refactor of geometry for stabilisation and optimisation. Switch representation now done.
This commit is contained in:
@@ -92,7 +92,7 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
Data.load(tool.Ifc.get(), element.id())
|
||||
|
||||
|
||||
class SwitchRepresentation(bpy.types.Operator):
|
||||
class SwitchRepresentation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.switch_representation"
|
||||
bl_label = "Switch Representation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -102,77 +102,15 @@ class SwitchRepresentation(bpy.types.Operator):
|
||||
disable_opening_subtractions: bpy.props.BoolProperty()
|
||||
should_switch_all_meshes: bpy.props.BoolProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.element_obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.oprops = self.element_obj.BIMObjectProperties
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
self.context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems
|
||||
self.mesh_name = self.get_mesh_name()
|
||||
|
||||
mesh = bpy.data.meshes.get(self.mesh_name)
|
||||
if mesh:
|
||||
self.switch_mesh(mesh)
|
||||
if not mesh or self.should_reload:
|
||||
self.pull_mesh_from_ifc(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def switch_mesh(self, mesh):
|
||||
if self.should_switch_all_meshes or self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcTypeProduct"):
|
||||
self.element_obj.data.user_remap(mesh)
|
||||
else:
|
||||
self.element_obj.data = mesh
|
||||
|
||||
def get_mesh_name(self):
|
||||
representation = self.resolve_mapped_representation(self.file.by_id(self.ifc_definition_id))
|
||||
return "{}/{}".format(self.context_of_items.id(), representation.id())
|
||||
|
||||
def resolve_mapped_representation(self, representation):
|
||||
if representation.RepresentationType == "MappedRepresentation":
|
||||
return self.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
|
||||
return representation
|
||||
|
||||
def pull_mesh_from_ifc(self, context):
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
element = self.file.by_id(self.oprops.ifc_definition_id)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
|
||||
if self.context_of_items.ContextIdentifier == "Body":
|
||||
if element.is_a("IfcTypeProduct") or self.disable_opening_subtractions:
|
||||
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id))
|
||||
else:
|
||||
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.oprops.ifc_definition_id))
|
||||
else:
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id))
|
||||
|
||||
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||
ifc_importer.file = self.file
|
||||
mesh = ifc_importer.create_mesh(element, shape)
|
||||
mesh.name = self.mesh_name
|
||||
mesh.BIMMeshProperties.ifc_definition_id = self.ifc_definition_id
|
||||
self.switch_mesh(mesh)
|
||||
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
|
||||
material_creator.load_existing_materials()
|
||||
material_creator.create(element, self.element_obj, mesh)
|
||||
|
||||
if self.disable_opening_subtractions and self.context_of_items.ContextIdentifier == "Body":
|
||||
if self.oprops.ifc_definition_id not in VoidData.products:
|
||||
VoidData.load(self.file, self.oprops.ifc_definition_id)
|
||||
for opening_id in VoidData.products[self.oprops.ifc_definition_id]:
|
||||
opening = IfcStore.get_element(opening_id)
|
||||
if not opening:
|
||||
continue
|
||||
modifier = self.element_obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
|
||||
modifier.operation = "DIFFERENCE"
|
||||
modifier.object = opening
|
||||
modifier.solver = "EXACT"
|
||||
modifier.use_self = True
|
||||
else:
|
||||
for modifier in self.element_obj.modifiers:
|
||||
if modifier.type == "BOOLEAN" and "IfcOpeningElement" in modifier.name:
|
||||
self.element_obj.modifiers.remove(modifier)
|
||||
def _execute(self, context):
|
||||
core.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=bpy.data.objects.get(self.obj) if self.obj else context.active_object,
|
||||
representation=tool.Ifc.get().by_id(self.ifc_definition_id),
|
||||
should_reload=self.should_reload,
|
||||
enable_dynamic_voids=self.disable_opening_subtractions,
|
||||
is_global=self.should_switch_all_meshes,
|
||||
)
|
||||
|
||||
|
||||
class RemoveRepresentation(bpy.types.Operator, Operator):
|
||||
@@ -324,8 +262,13 @@ class UpdateParametricRepresentation(bpy.types.Operator):
|
||||
parameter = props.ifc_parameters[self.index]
|
||||
self.file.by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||
show_representation_parameters = bool(props.ifc_parameters)
|
||||
bpy.ops.bim.switch_representation(
|
||||
ifc_definition_id=props.ifc_definition_id, should_reload=True, should_switch_all_meshes=True
|
||||
core.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=tool.Ifc.get().by_id(props.ifc_definition_id),
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=False,
|
||||
is_global=True,
|
||||
)
|
||||
if show_representation_parameters:
|
||||
bpy.ops.bim.get_representation_ifc_parameters()
|
||||
|
||||
@@ -21,6 +21,8 @@ import bmesh
|
||||
import blenderbim.bim.handler
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from math import pi
|
||||
from mathutils import Vector
|
||||
@@ -57,12 +59,13 @@ def mode_callback(obj, data):
|
||||
)
|
||||
if not representation:
|
||||
continue
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=building_element_obj.name,
|
||||
should_switch_all_meshes=True,
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=building_element_obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
ifc_definition_id=representation.id(),
|
||||
disable_opening_subtractions=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
)
|
||||
IfcStore.edited_objs.add(obj)
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
@@ -22,6 +22,8 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
from . import wall, slab, profile
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.pset.data import Data as PsetData
|
||||
@@ -229,12 +231,13 @@ class DynamicallyVoidProduct(bpy.types.Operator):
|
||||
was_edit_mode = obj.mode == "EDIT"
|
||||
if was_edit_mode:
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name,
|
||||
should_switch_all_meshes=True,
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
ifc_definition_id=representation.id(),
|
||||
disable_opening_subtractions=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
)
|
||||
if was_edit_mode:
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
@@ -286,8 +289,13 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
|
||||
continue
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if representation:
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -182,8 +182,13 @@ class DumbProfileGenerator:
|
||||
profile_set_usage=profile_set_usage,
|
||||
)
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbProfile"})
|
||||
@@ -239,8 +244,13 @@ class DumbProfileRegenerator:
|
||||
return
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if representation:
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
)
|
||||
|
||||
def sync_object(self, element):
|
||||
|
||||
@@ -20,6 +20,8 @@ import bpy
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.api
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.type.data import Data
|
||||
from ifcopenshell.api.geometry.data import Data as GeometryData
|
||||
@@ -64,13 +66,23 @@ class AssignType(bpy.types.Operator):
|
||||
for representation_id in representation_ids:
|
||||
representation = GeometryData.representations[representation_id]
|
||||
if representation["ContextOfItems"]["ContextIdentifier"] == "Body":
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=related_object.name, ifc_definition_id=representation_id, should_switch_all_meshes=False
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=related_object,
|
||||
representation=tool.Ifc.get().by_id(representation_id),
|
||||
should_reload=False,
|
||||
enable_dynamic_voids=False,
|
||||
is_global=False,
|
||||
)
|
||||
has_switched = True
|
||||
if not has_switched and representation_ids:
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=related_object.name, ifc_definition_id=representation_id, should_switch_all_meshes=False
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=related_object,
|
||||
representation=tool.Ifc.get().by_id(representation_id),
|
||||
should_reload=False,
|
||||
enable_dynamic_voids=False,
|
||||
is_global=False,
|
||||
)
|
||||
|
||||
bpy.ops.bim.disable_editing_type(obj=related_object.name)
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.void.data import Data
|
||||
|
||||
@@ -120,10 +122,13 @@ class RemoveOpening(bpy.types.Operator):
|
||||
ifcopenshell.api.run("void.remove_opening", self.file, **{"opening": self.file.by_id(self.opening_id)})
|
||||
|
||||
if not is_modifier_removed:
|
||||
bpy.ops.bim.switch_representation(
|
||||
ifc_definition_id=obj.data.BIMMeshProperties.ifc_definition_id,
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id),
|
||||
should_reload=True,
|
||||
should_switch_all_meshes=True,
|
||||
enable_dynamic_voids=False,
|
||||
is_global=True,
|
||||
)
|
||||
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
@@ -66,8 +66,32 @@ def add_representation(
|
||||
ifc.run("geometry.assign_representation", product=element, representation=representation)
|
||||
|
||||
data = geometry.duplicate_object_data(obj)
|
||||
name = geometry.get_representation_name(context, representation)
|
||||
geometry.rename_object_data(data, name)
|
||||
name = geometry.get_representation_name(representation)
|
||||
geometry.rename_object(data, name)
|
||||
geometry.link(representation, data)
|
||||
|
||||
return representation
|
||||
|
||||
|
||||
def switch_representation(
|
||||
geometry,
|
||||
obj=None,
|
||||
representation=None,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
):
|
||||
representation = geometry.resolve_mapped_representation(representation)
|
||||
data = geometry.get_representation_data(representation)
|
||||
|
||||
if not data or should_reload:
|
||||
data = geometry.import_representation(obj, representation, enable_dynamic_voids=enable_dynamic_voids)
|
||||
geometry.rename_object(data, geometry.get_representation_name(representation))
|
||||
geometry.link(representation, data)
|
||||
|
||||
geometry.change_object_data(obj, data, is_global=is_global)
|
||||
|
||||
if enable_dynamic_voids and geometry.is_body_representation(representation):
|
||||
geometry.create_dynamic_voids(obj)
|
||||
else:
|
||||
geometry.clear_dynamic_voids(obj)
|
||||
|
||||
@@ -65,15 +65,22 @@ class Context:
|
||||
|
||||
@interface
|
||||
class Geometry:
|
||||
def change_object_data(cls, obj, data, is_global=False): pass
|
||||
def clear_dynamic_voids(cls, obj): pass
|
||||
def create_dynamic_voids(cls, obj): pass
|
||||
def does_object_have_mesh_with_faces(cls, obj): pass
|
||||
def duplicate_object_data(cls, obj): pass
|
||||
def get_cartesian_point_coordinate_offset(cls, obj): pass
|
||||
def get_object_data(cls, obj): pass
|
||||
def get_object_materials_without_styles(cls, obj): pass
|
||||
def get_representation_name(cls, context, representation): pass
|
||||
def get_cartesian_point_coordinate_offset(cls, obj): pass
|
||||
def get_representation_data(cls, representation): pass
|
||||
def get_representation_name(cls, representation): pass
|
||||
def get_total_representation_items(cls, obj): pass
|
||||
def import_representation(cls, obj, representation, enable_dynamic_voids=False): pass
|
||||
def is_body_representation(cls, representation): pass
|
||||
def link(cls, element, obj): pass
|
||||
def rename_object_data(cls, data, name): pass
|
||||
def rename_object(cls, obj, name): pass
|
||||
def resolve_mapped_representation(cls, representation): pass
|
||||
def should_force_faceted_brep(cls): pass
|
||||
def should_force_triangulation(cls): pass
|
||||
def should_use_presentation_style_assignment(cls): pass
|
||||
|
||||
@@ -17,13 +17,42 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import logging
|
||||
import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.import_ifc
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Geometry(blenderbim.core.tool.Geometry):
|
||||
@classmethod
|
||||
def change_object_data(cls, obj, data, is_global=False):
|
||||
if is_global:
|
||||
obj.data.user_remap(data)
|
||||
else:
|
||||
obj.data = data
|
||||
|
||||
@classmethod
|
||||
def clear_dynamic_voids(cls, obj):
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "BOOLEAN" and "IfcOpeningElement" in modifier.name:
|
||||
obj.modifiers.remove(modifier)
|
||||
|
||||
@classmethod
|
||||
def create_dynamic_voids(cls, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
for rel in element.HasOpenings:
|
||||
opening_obj = tool.Ifc.get_object(rel.RelatedOpeningElement)
|
||||
if not opening_obj:
|
||||
continue
|
||||
modifier = obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
|
||||
modifier.operation = "DIFFERENCE"
|
||||
modifier.object = opening_obj
|
||||
modifier.solver = "EXACT"
|
||||
modifier.use_self = True
|
||||
|
||||
@classmethod
|
||||
def does_object_have_mesh_with_faces(cls, obj):
|
||||
return bool(isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons))
|
||||
@@ -44,8 +73,12 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_representation_name(cls, context, representation):
|
||||
return f"{context.id()}/{representation.id()}"
|
||||
def get_representation_data(cls, representation):
|
||||
return bpy.data.meshes.get(cls.get_representation_name(representation))
|
||||
|
||||
@classmethod
|
||||
def get_representation_name(cls, representation):
|
||||
return f"{representation.ContextOfItems.id()}/{representation.id()}"
|
||||
|
||||
@classmethod
|
||||
def get_cartesian_point_coordinate_offset(cls, obj):
|
||||
@@ -63,13 +96,46 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
def get_total_representation_items(cls, obj):
|
||||
return max(1, len(obj.material_slots))
|
||||
|
||||
@classmethod
|
||||
def import_representation(cls, obj, representation, enable_dynamic_voids=False):
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
|
||||
if representation.ContextOfItems.ContextIdentifier == "Body":
|
||||
if element.is_a("IfcTypeProduct") or enable_dynamic_voids:
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
else:
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
else:
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
|
||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(ifc_import_settings)
|
||||
ifc_importer.file = tool.Ifc.get()
|
||||
mesh = ifc_importer.create_mesh(element, shape)
|
||||
ifc_importer.material_creator.load_existing_materials()
|
||||
ifc_importer.material_creator.create(element, obj, mesh)
|
||||
return mesh
|
||||
|
||||
@classmethod
|
||||
def is_body_representation(cls, representation):
|
||||
return representation.ContextOfItems.ContextIdentifier == "Body"
|
||||
|
||||
@classmethod
|
||||
def link(cls, element, obj):
|
||||
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
||||
|
||||
@classmethod
|
||||
def rename_object_data(cls, data, name):
|
||||
data.name = name
|
||||
def rename_object(cls, obj, name):
|
||||
obj.name = name
|
||||
|
||||
@classmethod
|
||||
def resolve_mapped_representation(cls, representation):
|
||||
if representation.RepresentationType == "MappedRepresentation":
|
||||
return cls.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
|
||||
return representation
|
||||
|
||||
@classmethod
|
||||
def should_force_faceted_brep(cls):
|
||||
|
||||
@@ -23,6 +23,16 @@ Scenario: Add representation
|
||||
And I press "bim.add_representation"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Switch representation
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
|
||||
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Copy representation
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
|
||||
@@ -76,8 +76,8 @@ class TestAddRepresentation:
|
||||
|
||||
# Update mesh
|
||||
geometry.duplicate_object_data("obj").should_be_called().will_return("data")
|
||||
geometry.get_representation_name("context", "representation").should_be_called().will_return("name")
|
||||
geometry.rename_object_data("data", "name").should_be_called()
|
||||
geometry.get_representation_name("representation").should_be_called().will_return("name")
|
||||
geometry.rename_object("data", "name").should_be_called()
|
||||
geometry.link("representation", "data").should_be_called()
|
||||
|
||||
assert (
|
||||
@@ -124,8 +124,8 @@ class TestAddRepresentation:
|
||||
|
||||
# Update mesh
|
||||
geometry.duplicate_object_data("obj").should_be_called().will_return("data")
|
||||
geometry.get_representation_name("context", "representation").should_be_called().will_return("name")
|
||||
geometry.rename_object_data("data", "name").should_be_called()
|
||||
geometry.get_representation_name("representation").should_be_called().will_return("name")
|
||||
geometry.rename_object("data", "name").should_be_called()
|
||||
geometry.link("representation", "data").should_be_called()
|
||||
|
||||
assert (
|
||||
@@ -176,3 +176,26 @@ class TestAddRepresentation:
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
class TestSwitchRepresentation:
|
||||
def test_run(self, ifc, geometry, style, surveyor):
|
||||
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
|
||||
geometry.get_representation_data("representation").should_be_called().will_return("data")
|
||||
geometry.import_representation(
|
||||
"obj", "representation", enable_dynamic_voids=True
|
||||
).should_be_called().will_return("new_data")
|
||||
geometry.get_representation_name("representation").should_be_called().will_return("name")
|
||||
geometry.rename_object("new_data", "name").should_be_called()
|
||||
geometry.link("representation", "new_data").should_be_called()
|
||||
geometry.change_object_data("obj", "new_data", is_global=True).should_be_called()
|
||||
geometry.is_body_representation("representation").should_be_called().will_return(True)
|
||||
geometry.create_dynamic_voids("obj").should_be_called()
|
||||
subject.switch_representation(
|
||||
geometry,
|
||||
obj="obj",
|
||||
representation="mapped_rep",
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('annotation.ifc','2021-10-19T11:50:01+11:00',(),(),'IfcOpenShell v0.7.0-6cc7d796','BlenderBIM 0.0.999999','Nobody');
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPERSON('HSeldon','Seldon','Hari',$,$,$,$,$);
|
||||
#2=IFCORGANIZATION('APTR','Aperture Science',$,$,$);
|
||||
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||
#4=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$);
|
||||
#5=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org',$);
|
||||
#6=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#4),(#5));
|
||||
#7=IFCAPPLICATION(#6,'0.0.999999','BlenderBIM Add-on','BlenderBIM');
|
||||
#8=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1634604558,#3,#7,1634604558);
|
||||
#9=IFCPROJECT('0NqkoFmWPBxvsDLeGa5rDc',#8,'My Project',$,$,$,$,(#18,#24),#13);
|
||||
#10=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#11=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#12=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#13=IFCUNITASSIGNMENT((#11,#12,#10));
|
||||
#14=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#15=IFCDIRECTION((0.,0.,1.));
|
||||
#16=IFCDIRECTION((1.,0.,0.));
|
||||
#17=IFCAXIS2PLACEMENT3D(#14,#15,#16);
|
||||
#18=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#17,$);
|
||||
#19=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#18,$,.MODEL_VIEW.,$);
|
||||
#20=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#18,$,.MODEL_VIEW.,$);
|
||||
#21=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#22=IFCDIRECTION((1.,0.,0.));
|
||||
#23=IFCAXIS2PLACEMENT2D(#21,#22);
|
||||
#24=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#23,$);
|
||||
#25=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#24,$,.PLAN_VIEW.,$);
|
||||
#26=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1634604558,#3,#7,1634604558);
|
||||
#27=IFCSITE('3kJImD6Rr4NfpxM218DNpq',#26,'My Site',$,$,#53,$,$,$,$,$,$,$,$);
|
||||
#33=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1634604558,#3,#7,1634604558);
|
||||
#34=IFCBUILDING('3afq36fJb80A_bTAB9RB0f',#33,'My Building',$,$,#60,$,$,$,$,$,$);
|
||||
#40=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1634604558,#3,#7,1634604558);
|
||||
#41=IFCBUILDINGSTOREY('2vQTqYOy13G8THztmBknz3',#40,'My Storey',$,$,#67,$,$,$,$);
|
||||
#47=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1634604558,#3,#7,1634604558);
|
||||
#48=IFCRELAGGREGATES('22R8p4a0z4jAFLUof7ha5H',#47,$,$,#9,(#27));
|
||||
#49=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#50=IFCDIRECTION((0.,0.,1.));
|
||||
#51=IFCDIRECTION((1.,0.,0.));
|
||||
#52=IFCAXIS2PLACEMENT3D(#49,#50,#51);
|
||||
#53=IFCLOCALPLACEMENT($,#52);
|
||||
#54=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1634604558,#3,#7,1634604558);
|
||||
#55=IFCRELAGGREGATES('1ty5GrCRX0vPrGpg4F_2JL',#54,$,$,#27,(#34));
|
||||
#56=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#57=IFCDIRECTION((0.,0.,1.));
|
||||
#58=IFCDIRECTION((1.,0.,0.));
|
||||
#59=IFCAXIS2PLACEMENT3D(#56,#57,#58);
|
||||
#60=IFCLOCALPLACEMENT(#53,#59);
|
||||
#61=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1634604558,#3,#7,1634604558);
|
||||
#62=IFCRELAGGREGATES('1Lbqbq3O56pg1ouvCD5WR9',#61,$,$,#34,(#41));
|
||||
#63=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#64=IFCDIRECTION((0.,0.,1.));
|
||||
#65=IFCDIRECTION((1.,0.,0.));
|
||||
#66=IFCAXIS2PLACEMENT3D(#63,#64,#65);
|
||||
#67=IFCLOCALPLACEMENT(#60,#66);
|
||||
#68=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1634604586,#3,#7,1634604586);
|
||||
#69=IFCWALL('2iJrnC5QfEH8KDFgXqLR5J',#68,'Square',$,$,#88,#81,$,.ELEMENTEDWALL.);
|
||||
#75=IFCCARTESIANPOINTLIST2D(((1.,1.),(1.,-1.),(-1.,1.),(-1.,-1.)));
|
||||
#76=IFCINDEXEDPOLYCURVE(#75,(IFCLINEINDEX((3,4))),$);
|
||||
#77=IFCINDEXEDPOLYCURVE(#75,(IFCLINEINDEX((1,3))),$);
|
||||
#78=IFCINDEXEDPOLYCURVE(#75,(IFCLINEINDEX((4,2)),IFCLINEINDEX((2,1))),$);
|
||||
#79=IFCGEOMETRICCURVESET((#76,#77,#78));
|
||||
#80=IFCSHAPEREPRESENTATION(#25,'Annotation','Annotation2D',(#79));
|
||||
#81=IFCPRODUCTDEFINITIONSHAPE($,$,(#80));
|
||||
#82=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1634604586,#3,#7,1634604586);
|
||||
#83=IFCRELCONTAINEDINSPATIALSTRUCTURE('1AG29wImP3hgw6L3xSIacB',#82,$,$,(#69),#41);
|
||||
#84=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#85=IFCDIRECTION((0.,0.,1.));
|
||||
#86=IFCDIRECTION((1.,0.,0.));
|
||||
#87=IFCAXIS2PLACEMENT3D(#84,#85,#86);
|
||||
#88=IFCLOCALPLACEMENT(#67,#87);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -33,6 +33,55 @@ class TestImplementsTool(test.bim.bootstrap.NewFile):
|
||||
assert isinstance(subject(), blenderbim.core.tool.Geometry)
|
||||
|
||||
|
||||
class TestChangeObjectData(test.bim.bootstrap.NewFile):
|
||||
def test_change_single_object_data(self):
|
||||
data1 = bpy.data.meshes.new("Mesh")
|
||||
data2 = bpy.data.meshes.new("Mesh")
|
||||
obj1 = bpy.data.objects.new("Object", data1)
|
||||
obj2 = bpy.data.objects.new("Object", data1)
|
||||
subject.change_object_data(obj1, data2, is_global=False)
|
||||
assert obj1.data == data2
|
||||
assert obj2.data == data1
|
||||
|
||||
def test_change_object_data_globally(self):
|
||||
data1 = bpy.data.meshes.new("Mesh")
|
||||
data2 = bpy.data.meshes.new("Mesh")
|
||||
obj1 = bpy.data.objects.new("Object", data1)
|
||||
obj2 = bpy.data.objects.new("Object", data1)
|
||||
subject.change_object_data(obj1, data2, is_global=True)
|
||||
assert obj1.data == data2
|
||||
assert obj2.data == data2
|
||||
|
||||
|
||||
class TestClearDynamicVoids(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
|
||||
subject.clear_dynamic_voids(obj)
|
||||
assert len(obj.modifiers) == 0
|
||||
|
||||
|
||||
class TestCreateDynamicVoids(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
opening = ifc.createIfcOpeningElement()
|
||||
ifcopenshell.api.run("void.add_opening", ifc, opening=opening, element=wall)
|
||||
wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
opening_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
tool.Ifc.link(wall, wall_obj)
|
||||
tool.Ifc.link(opening, opening_obj)
|
||||
subject.create_dynamic_voids(wall_obj)
|
||||
modifier = wall_obj.modifiers[0]
|
||||
assert modifier.type == "BOOLEAN"
|
||||
assert modifier.name == "IfcOpeningElement"
|
||||
assert modifier.operation == "DIFFERENCE"
|
||||
assert modifier.object == opening_obj
|
||||
assert modifier.solver == "EXACT"
|
||||
assert modifier.use_self is True
|
||||
|
||||
|
||||
class TestDoesObjectHaveMeshWithFaces(test.bim.bootstrap.NewFile):
|
||||
def test_empties_return_false(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
@@ -81,12 +130,23 @@ class TestGetObjectMaterialsWithoutStyles(test.bim.bootstrap.NewFile):
|
||||
assert subject.get_object_materials_without_styles(obj) == [material1, material2]
|
||||
|
||||
|
||||
class TestGetRepresentationData(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
context = ifc.createIfcGeometricRepresentationContext()
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
representation.ContextOfItems = context
|
||||
data = bpy.data.meshes.new("1/2")
|
||||
assert subject.get_representation_data(representation) == data
|
||||
|
||||
|
||||
class TestGetRepresentationName(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
context = ifc.createIfcGeometricRepresentationContext()
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
assert subject.get_representation_name(context, representation) == f"{context.id()}/{representation.id()}"
|
||||
representation.ContextOfItems = context
|
||||
assert subject.get_representation_name(representation) == f"{context.id()}/{representation.id()}"
|
||||
|
||||
|
||||
class TestGetCartesianPointCoordinateOffset(test.bim.bootstrap.NewFile):
|
||||
@@ -127,6 +187,44 @@ class TestGetTotalRepresentationItems(test.bim.bootstrap.NewFile):
|
||||
assert subject.get_total_representation_items(obj) == 2
|
||||
|
||||
|
||||
class TestImportRepresentation(test.bim.bootstrap.NewFile):
|
||||
def test_importing_a_normal_shape(self):
|
||||
ifc = ifcopenshell.open("test/files/basic.ifc")
|
||||
tool.Ifc.set(ifc)
|
||||
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
material = bpy.data.materials.new("Material")
|
||||
material.BIMMaterialProperties.ifc_style_id = 101
|
||||
element = ifc.by_type("IfcWall")[0]
|
||||
tool.Ifc.link(element, obj)
|
||||
representation = element.Representation.Representations[0]
|
||||
mesh = subject.import_representation(obj, representation, enable_dynamic_voids=False)
|
||||
assert len(mesh.polygons) == 12
|
||||
assert mesh.materials[0] == material
|
||||
|
||||
def test_importing_non_body_curves(self):
|
||||
ifc = ifcopenshell.open("test/files/annotation.ifc")
|
||||
tool.Ifc.set(ifc)
|
||||
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
element = ifc.by_type("IfcWall")[0]
|
||||
tool.Ifc.link(element, obj)
|
||||
representation = element.Representation.Representations[0]
|
||||
mesh = subject.import_representation(obj, representation, enable_dynamic_voids=False)
|
||||
assert len(mesh.polygons) == 0
|
||||
assert len(mesh.edges) == 4
|
||||
|
||||
|
||||
class TestIsBodyRepresentation(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
context = ifc.createIfcGeometricRepresentationContext()
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
representation.ContextOfItems = context
|
||||
context.ContextIdentifier = "Body"
|
||||
assert subject.is_body_representation(representation) is True
|
||||
context.ContextIdentifier = "Clearance"
|
||||
assert subject.is_body_representation(representation) is False
|
||||
|
||||
|
||||
class TestLink(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -139,10 +237,27 @@ class TestLink(test.bim.bootstrap.NewFile):
|
||||
class TestRenameObjectData(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.meshes.new("Mesh")
|
||||
subject.rename_object_data(obj, "name")
|
||||
subject.rename_object(obj, "name")
|
||||
assert obj.name == "name"
|
||||
|
||||
|
||||
class TestResolveMappedRepresentation(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
mapped_representation = ifc.createIfcShapeRepresentation()
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
mapped_representation.RepresentationType = "MappedRepresentation"
|
||||
mapped_representation.Items = [
|
||||
ifc.createIfcMappedItem(MappingSource=ifc.createIfcRepresentationMap(MappedRepresentation=representation))
|
||||
]
|
||||
assert subject.resolve_mapped_representation(mapped_representation) == representation
|
||||
|
||||
def test_return_a_representation_if_not_mapped(self):
|
||||
ifc = ifcopenshell.file()
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
assert subject.resolve_mapped_representation(representation) == representation
|
||||
|
||||
|
||||
class TestShouldForceFacetedBrep(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
|
||||
|
||||
Reference in New Issue
Block a user