WIP refactor of geometry for stabilisation and optimisation. Switch representation now done.

This commit is contained in:
Dion Moult
2021-10-19 12:57:21 +11:00
parent 63f85beb7e
commit c1b53859ee
13 changed files with 415 additions and 112 deletions
@@ -92,7 +92,7 @@ class AddRepresentation(bpy.types.Operator, Operator):
Data.load(tool.Ifc.get(), element.id()) Data.load(tool.Ifc.get(), element.id())
class SwitchRepresentation(bpy.types.Operator): class SwitchRepresentation(bpy.types.Operator, Operator):
bl_idname = "bim.switch_representation" bl_idname = "bim.switch_representation"
bl_label = "Switch Representation" bl_label = "Switch Representation"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -102,77 +102,15 @@ class SwitchRepresentation(bpy.types.Operator):
disable_opening_subtractions: bpy.props.BoolProperty() disable_opening_subtractions: bpy.props.BoolProperty()
should_switch_all_meshes: bpy.props.BoolProperty() should_switch_all_meshes: bpy.props.BoolProperty()
def execute(self, context): def _execute(self, context):
self.element_obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object core.switch_representation(
self.oprops = self.element_obj.BIMObjectProperties tool.Geometry,
obj=bpy.data.objects.get(self.obj) if self.obj else context.active_object,
self.file = IfcStore.get_file() representation=tool.Ifc.get().by_id(self.ifc_definition_id),
self.context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems should_reload=self.should_reload,
self.mesh_name = self.get_mesh_name() enable_dynamic_voids=self.disable_opening_subtractions,
is_global=self.should_switch_all_meshes,
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)
class RemoveRepresentation(bpy.types.Operator, Operator): class RemoveRepresentation(bpy.types.Operator, Operator):
@@ -324,8 +262,13 @@ class UpdateParametricRepresentation(bpy.types.Operator):
parameter = props.ifc_parameters[self.index] parameter = props.ifc_parameters[self.index]
self.file.by_id(parameter.step_id)[parameter.index] = parameter.value self.file.by_id(parameter.step_id)[parameter.index] = parameter.value
show_representation_parameters = bool(props.ifc_parameters) show_representation_parameters = bool(props.ifc_parameters)
bpy.ops.bim.switch_representation( core.switch_representation(
ifc_definition_id=props.ifc_definition_id, should_reload=True, should_switch_all_meshes=True 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: if show_representation_parameters:
bpy.ops.bim.get_representation_ifc_parameters() bpy.ops.bim.get_representation_ifc_parameters()
@@ -21,6 +21,8 @@ import bmesh
import blenderbim.bim.handler import blenderbim.bim.handler
import ifcopenshell import ifcopenshell
import ifcopenshell.util.representation import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.core.geometry
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from math import pi from math import pi
from mathutils import Vector from mathutils import Vector
@@ -57,12 +59,13 @@ def mode_callback(obj, data):
) )
if not representation: if not representation:
continue continue
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=building_element_obj.name, tool.Geometry,
should_switch_all_meshes=True, obj=building_element_obj,
representation=representation,
should_reload=True, should_reload=True,
ifc_definition_id=representation.id(), enable_dynamic_voids=True,
disable_opening_subtractions=True, is_global=True,
) )
IfcStore.edited_objs.add(obj) IfcStore.edited_objs.add(obj)
bm = bmesh.from_edit_mesh(obj.data) bm = bmesh.from_edit_mesh(obj.data)
@@ -22,6 +22,8 @@ import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.representation import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.core.geometry
from . import wall, slab, profile from . import wall, slab, profile
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data as PsetData from ifcopenshell.api.pset.data import Data as PsetData
@@ -229,12 +231,13 @@ class DynamicallyVoidProduct(bpy.types.Operator):
was_edit_mode = obj.mode == "EDIT" was_edit_mode = obj.mode == "EDIT"
if was_edit_mode: if was_edit_mode:
bpy.ops.object.mode_set(mode="OBJECT") bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=obj.name, tool.Geometry,
should_switch_all_meshes=True, obj=obj,
representation=representation,
should_reload=True, should_reload=True,
ifc_definition_id=representation.id(), enable_dynamic_voids=True,
disable_opening_subtractions=True, is_global=True,
) )
if was_edit_mode: if was_edit_mode:
bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="EDIT")
@@ -286,8 +289,13 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
continue continue
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if representation: if representation:
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True 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, profile_set_usage=profile_set_usage,
) )
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True 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") 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"}) ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbProfile"})
@@ -239,8 +244,13 @@ class DumbProfileRegenerator:
return return
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if representation: if representation:
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True tool.Geometry,
obj=obj,
representation=representation,
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
) )
def sync_object(self, element): def sync_object(self, element):
@@ -20,6 +20,8 @@ import bpy
import ifcopenshell.util.schema import ifcopenshell.util.schema
import ifcopenshell.util.type import ifcopenshell.util.type
import ifcopenshell.api import ifcopenshell.api
import blenderbim.tool as tool
import blenderbim.core.geometry
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.type.data import Data from ifcopenshell.api.type.data import Data
from ifcopenshell.api.geometry.data import Data as GeometryData from ifcopenshell.api.geometry.data import Data as GeometryData
@@ -64,13 +66,23 @@ class AssignType(bpy.types.Operator):
for representation_id in representation_ids: for representation_id in representation_ids:
representation = GeometryData.representations[representation_id] representation = GeometryData.representations[representation_id]
if representation["ContextOfItems"]["ContextIdentifier"] == "Body": if representation["ContextOfItems"]["ContextIdentifier"] == "Body":
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=related_object.name, ifc_definition_id=representation_id, should_switch_all_meshes=False 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 has_switched = True
if not has_switched and representation_ids: if not has_switched and representation_ids:
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
obj=related_object.name, ifc_definition_id=representation_id, should_switch_all_meshes=False 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) bpy.ops.bim.disable_editing_type(obj=related_object.name)
@@ -19,6 +19,8 @@
import bpy import bpy
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.representation import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.core.geometry
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.void.data import Data 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)}) ifcopenshell.api.run("void.remove_opening", self.file, **{"opening": self.file.by_id(self.opening_id)})
if not is_modifier_removed: if not is_modifier_removed:
bpy.ops.bim.switch_representation( blenderbim.core.geometry.switch_representation(
ifc_definition_id=obj.data.BIMMeshProperties.ifc_definition_id, tool.Geometry,
obj=obj,
representation=tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id),
should_reload=True, 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) Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
+26 -2
View File
@@ -66,8 +66,32 @@ def add_representation(
ifc.run("geometry.assign_representation", product=element, representation=representation) ifc.run("geometry.assign_representation", product=element, representation=representation)
data = geometry.duplicate_object_data(obj) data = geometry.duplicate_object_data(obj)
name = geometry.get_representation_name(context, representation) name = geometry.get_representation_name(representation)
geometry.rename_object_data(data, name) geometry.rename_object(data, name)
geometry.link(representation, data) geometry.link(representation, data)
return representation 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)
+10 -3
View File
@@ -65,15 +65,22 @@ class Context:
@interface @interface
class Geometry: 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 does_object_have_mesh_with_faces(cls, obj): pass
def duplicate_object_data(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_data(cls, obj): pass
def get_object_materials_without_styles(cls, obj): pass def get_object_materials_without_styles(cls, obj): pass
def get_representation_name(cls, context, representation): pass def get_representation_data(cls, representation): pass
def get_cartesian_point_coordinate_offset(cls, obj): pass def get_representation_name(cls, representation): pass
def get_total_representation_items(cls, obj): 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 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_faceted_brep(cls): pass
def should_force_triangulation(cls): pass def should_force_triangulation(cls): pass
def should_use_presentation_style_assignment(cls): pass def should_use_presentation_style_assignment(cls): pass
+70 -4
View File
@@ -17,13 +17,42 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import logging
import ifcopenshell
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.import_ifc
from mathutils import Vector from mathutils import Vector
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
class Geometry(blenderbim.core.tool.Geometry): 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 @classmethod
def does_object_have_mesh_with_faces(cls, obj): def does_object_have_mesh_with_faces(cls, obj):
return bool(isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons)) return bool(isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons))
@@ -44,8 +73,12 @@ class Geometry(blenderbim.core.tool.Geometry):
] ]
@classmethod @classmethod
def get_representation_name(cls, context, representation): def get_representation_data(cls, representation):
return f"{context.id()}/{representation.id()}" 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 @classmethod
def get_cartesian_point_coordinate_offset(cls, obj): 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): def get_total_representation_items(cls, obj):
return max(1, len(obj.material_slots)) 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 @classmethod
def link(cls, element, obj): def link(cls, element, obj):
obj.BIMMeshProperties.ifc_definition_id = element.id() obj.BIMMeshProperties.ifc_definition_id = element.id()
@classmethod @classmethod
def rename_object_data(cls, data, name): def rename_object(cls, obj, name):
data.name = 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 @classmethod
def should_force_faceted_brep(cls): def should_force_faceted_brep(cls):
@@ -23,6 +23,16 @@ Scenario: Add representation
And I press "bim.add_representation" And I press "bim.add_representation"
Then nothing happens 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 Scenario: Copy representation
Given an empty IFC project Given an empty IFC project
And I add a cube And I add a cube
+27 -4
View File
@@ -76,8 +76,8 @@ class TestAddRepresentation:
# Update mesh # Update mesh
geometry.duplicate_object_data("obj").should_be_called().will_return("data") geometry.duplicate_object_data("obj").should_be_called().will_return("data")
geometry.get_representation_name("context", "representation").should_be_called().will_return("name") geometry.get_representation_name("representation").should_be_called().will_return("name")
geometry.rename_object_data("data", "name").should_be_called() geometry.rename_object("data", "name").should_be_called()
geometry.link("representation", "data").should_be_called() geometry.link("representation", "data").should_be_called()
assert ( assert (
@@ -124,8 +124,8 @@ class TestAddRepresentation:
# Update mesh # Update mesh
geometry.duplicate_object_data("obj").should_be_called().will_return("data") geometry.duplicate_object_data("obj").should_be_called().will_return("data")
geometry.get_representation_name("context", "representation").should_be_called().will_return("name") geometry.get_representation_name("representation").should_be_called().will_return("name")
geometry.rename_object_data("data", "name").should_be_called() geometry.rename_object("data", "name").should_be_called()
geometry.link("representation", "data").should_be_called() geometry.link("representation", "data").should_be_called()
assert ( assert (
@@ -176,3 +176,26 @@ class TestAddRepresentation:
) )
is None 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,
)
+77
View File
@@ -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;
+117 -2
View File
@@ -33,6 +33,55 @@ class TestImplementsTool(test.bim.bootstrap.NewFile):
assert isinstance(subject(), blenderbim.core.tool.Geometry) 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): class TestDoesObjectHaveMeshWithFaces(test.bim.bootstrap.NewFile):
def test_empties_return_false(self): def test_empties_return_false(self):
obj = bpy.data.objects.new("Object", None) 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] 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): class TestGetRepresentationName(test.bim.bootstrap.NewFile):
def test_run(self): def test_run(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext() context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation() 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): class TestGetCartesianPointCoordinateOffset(test.bim.bootstrap.NewFile):
@@ -127,6 +187,44 @@ class TestGetTotalRepresentationItems(test.bim.bootstrap.NewFile):
assert subject.get_total_representation_items(obj) == 2 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): class TestLink(test.bim.bootstrap.NewFile):
def test_run(self): def test_run(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
@@ -139,10 +237,27 @@ class TestLink(test.bim.bootstrap.NewFile):
class TestRenameObjectData(test.bim.bootstrap.NewFile): class TestRenameObjectData(test.bim.bootstrap.NewFile):
def test_run(self): def test_run(self):
obj = bpy.data.meshes.new("Mesh") obj = bpy.data.meshes.new("Mesh")
subject.rename_object_data(obj, "name") subject.rename_object(obj, "name")
assert obj.name == "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): class TestShouldForceFacetedBrep(test.bim.bootstrap.NewFile):
def test_run(self): def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep