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())
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)
+26 -2
View File
@@ -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)
+10 -3
View File
@@ -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
+70 -4
View File
@@ -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
+27 -4
View File
@@ -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,
)
+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)
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