When assigning types, your preference of dynamic voids are now preserved.

This commit is contained in:
Dion Moult
2021-10-19 15:43:14 +11:00
parent c1b53859ee
commit 3daf4203d1
16 changed files with 345 additions and 53 deletions
@@ -23,6 +23,7 @@ import ifcopenshell.api
import ifcopenshell.util.element
import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.core.type
import blenderbim.core.geometry
from . import wall, slab, profile
from blenderbim.bim.ifc import IfcStore
@@ -119,7 +120,13 @@ class AddTypeInstance(bpy.types.Operator):
collection.objects.link(obj)
collection_obj = bpy.data.objects.get(collection.name)
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
bpy.ops.bim.assign_type(relating_type=int(tprops.relating_type), related_object=obj.name)
blenderbim.core.type.assign_type(
tool.Ifc,
tool.Geometry,
tool.Type,
element=tool.Ifc.get_entity(obj),
type=tool.Ifc.get().by_id(int(tprops.relating_type)),
)
if building_obj:
if instance_class in ["IfcWindow", "IfcDoor"]:
@@ -26,6 +26,7 @@ import ifcopenshell.util.element
import mathutils.geometry
import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.type
import blenderbim.core.geometry
from blenderbim.bim.ifc import IfcStore
from math import pi, degrees, inf
@@ -169,7 +170,9 @@ class DumbProfileGenerator:
obj.rotation_euler[2] = math.pi / 2
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name)
blenderbim.core.type.assign_type(
tool.Ifc, tool.Geometry, tool.Type, element=tool.Ifc.get_entity(obj), type=self.relating_type
)
profile_set_usage = ifcopenshell.util.element.get_material(element)
blenderbim.core.geometry.add_representation(
tool.Ifc,
@@ -25,6 +25,8 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element
import mathutils.geometry
import blenderbim.bim.handler
import blenderbim.core.type
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from math import pi, degrees
from mathutils import Vector, Matrix
@@ -305,7 +307,9 @@ class DumbSlabGenerator:
ifc_class=ifc_class,
ifc_representation_class="IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids",
)
bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name)
blenderbim.core.type.assign_type(
tool.Ifc, tool.Geometry, tool.Type, element=tool.Ifc.get_entity(obj), type=self.relating_type
)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
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.DumbLayer3"})
@@ -26,6 +26,8 @@ import ifcopenshell.util.element
import ifcopenshell.util.representation
import mathutils.geometry
import blenderbim.bim.handler
import blenderbim.core.type
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data as PsetData
from ifcopenshell.api.material.data import Data as MaterialData
@@ -792,7 +794,9 @@ class DumbWallGenerator:
ifc_representation_class="IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef",
)
bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name)
blenderbim.core.type.assign_type(
tool.Ifc, tool.Geometry, tool.Type, element=tool.Ifc.get_entity(obj), type=self.relating_type
)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
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.DumbLayer2"})
@@ -22,10 +22,11 @@ import ifcopenshell.api
import ifcopenshell.util.schema
import ifcopenshell.util.element
import blenderbim.bim.handler
import blenderbim.core.spatial
import blenderbim.core.style
import blenderbim.core.geometry
import blenderbim.core.material
import blenderbim.core.spatial
import blenderbim.core.style
import blenderbim.core.type
import blenderbim.tool as tool
from ifcopenshell.api.void.data import Data as VoidData
from blenderbim.bim.ifc import IfcStore
@@ -360,7 +361,7 @@ class CopyClass(bpy.types.Operator):
IfcStore.link_element(result, obj)
relating_type = ifcopenshell.util.element.get_type(result)
if relating_type and relating_type.RepresentationMaps:
bpy.ops.bim.assign_type(relating_type=relating_type.id(), related_object=obj.name)
blenderbim.core.type.assign_type(tool.Ifc, tool.Geometry, tool.Type, element=result, type=relating_type)
else:
blenderbim.core.geometry.add_representation(
tool.Ifc,
@@ -22,72 +22,41 @@ import ifcopenshell.util.type
import ifcopenshell.api
import blenderbim.tool as tool
import blenderbim.core.geometry
import blenderbim.core.type as core
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.type.data import Data
from ifcopenshell.api.geometry.data import Data as GeometryData
from ifcopenshell.api.material.data import Data as MaterialData
class AssignType(bpy.types.Operator):
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class AssignType(bpy.types.Operator, Operator):
bl_idname = "bim.assign_type"
bl_label = "Assign Type"
bl_options = {"REGISTER", "UNDO"}
relating_type: bpy.props.IntProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
relating_type = self.relating_type or int(context.active_object.BIMTypeProperties.relating_type)
type = tool.Ifc.get().by_id(self.relating_type or int(context.active_object.BIMTypeProperties.relating_type))
related_objects = (
[bpy.data.objects.get(self.related_object)]
if self.related_object
else context.selected_objects or [context.active_object]
)
for related_object in related_objects:
oprops = related_object.BIMObjectProperties
ifcopenshell.api.run(
"type.assign_type",
self.file,
**{
"related_object": self.file.by_id(oprops.ifc_definition_id),
"relating_type": self.file.by_id(relating_type),
},
)
for obj in related_objects:
core.assign_type(tool.Ifc, tool.Geometry, tool.Type, element=tool.Ifc.get_entity(obj), type=type)
oprops = obj.BIMObjectProperties
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
GeometryData.load(IfcStore.get_file(), oprops.ifc_definition_id)
MaterialData.load(IfcStore.get_file(), oprops.ifc_definition_id)
representation_ids = GeometryData.products[oprops.ifc_definition_id]
if not representation_ids:
pass # TODO: clear geometry? Make void? Make none type?
has_switched = False
for representation_id in representation_ids:
representation = GeometryData.representations[representation_id]
if representation["ContextOfItems"]["ContextIdentifier"] == "Body":
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:
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)
MaterialData.load(self.file)
return {"FINISHED"}
MaterialData.load(IfcStore.get_file())
class UnassignType(bpy.types.Operator):
+9
View File
@@ -90,6 +90,7 @@ class Geometry:
class Ifc:
def get(cls): pass
def get_entity(cls, obj): pass
def get_object(cls, obj): pass
def link(cls, element, obj): pass
def run(cls, command, **kwargs): pass
def unlink(cls, element=None, obj=None): pass
@@ -169,6 +170,14 @@ class Surveyor:
def get_absolute_matrix(cls, obj): pass
@interface
class Type:
def disable_editing(cls, obj): pass
def get_any_representation(cls, element): pass
def get_body_representation(cls, element): pass
def has_dynamic_voids(cls, obj): pass
@interface
class Unit:
def clear_active_unit(cls): pass
+37
View File
@@ -0,0 +1,37 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blenderbim.core.geometry
def assign_type(ifc, geometry, type_tool, element=None, type=None):
ifc.run("type.assign_type", related_object=element, relating_type=type)
representation = type_tool.get_body_representation(element)
if not representation:
representation = type_tool.get_any_representation(element)
obj = ifc.get_object(element)
if representation:
blenderbim.core.geometry.switch_representation(
geometry,
obj=obj,
representation=representation,
should_reload=False,
enable_dynamic_voids=type_tool.has_dynamic_voids(obj),
is_global=False,
)
type_tool.disable_editing(obj)
@@ -28,4 +28,5 @@ from blenderbim.tool.misc import Misc
from blenderbim.tool.owner import Owner
from blenderbim.tool.style import Style
from blenderbim.tool.surveyor import Surveyor
from blenderbim.tool.type import Type
from blenderbim.tool.unit import Unit
+53
View File
@@ -0,0 +1,53 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
import blenderbim.bim.helper
class Type(blenderbim.core.tool.Type):
@classmethod
def disable_editing(cls, obj):
obj.BIMTypeProperties.is_editing_type = False
@classmethod
def get_any_representation(cls, element):
if element.is_a("IfcProduct") and element.Representation and element.Representation.Representations:
return element.Representation.Representations[0]
elif element.is_a("IfcTypeProduct") and element.RepresentationMaps:
return element.RepresentationMaps[0].MappedRepresentation
@classmethod
def get_body_representation(cls, element):
if element.is_a("IfcProduct") and element.Representation and element.Representation.Representations:
for representation in element.Representation.Representations:
if representation.ContextOfItems.ContextIdentifier == "Body":
return representation
elif element.is_a("IfcTypeProduct") and element.RepresentationMaps:
for representation_map in element.RepresentationMaps:
if representation_map.MappedRepresentation.ContextOfItems.ContextIdentifier == "Body":
return representation_map.MappedRepresentation
@classmethod
def has_dynamic_voids(cls, obj):
for modifier in obj.modifiers:
if modifier.name == "IfcOpeningElement" and modifier.type == "BOOLEAN":
return True
return False
+1
View File
@@ -11,5 +11,6 @@ markers =
root
spatial
style
type
unit
void
@@ -0,0 +1,17 @@
@type
Feature: Type
Scenario: Assign type
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"
And I add an empty
And the object "Empty" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
When the variable "type" is "{ifc}.by_type('IfcWallType')[0].id()"
And I press "bim.assign_type(relating_type={type}, related_object="IfcWall/Cube")"
Then nothing happens
+7
View File
@@ -77,6 +77,13 @@ def owner():
prophet.verify()
@pytest.fixture
def type():
prophet = Prophecy(blenderbim.core.tool.Type)
yield prophet
prophet.verify()
@pytest.fixture
def voider():
prophet = Prophecy(blenderbim.core.tool.Voider)
+30 -1
View File
@@ -179,7 +179,7 @@ class TestAddRepresentation:
class TestSwitchRepresentation:
def test_run(self, ifc, geometry, style, surveyor):
def test_switching_to_a_freshly_reloaded_representation(self, geometry):
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(
@@ -199,3 +199,32 @@ class TestSwitchRepresentation:
enable_dynamic_voids=True,
is_global=True,
)
def test_switching_to_an_existing_representation(self, geometry):
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "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=False,
enable_dynamic_voids=True,
is_global=True,
)
def test_switching_to_non_dynamic_baked_voids(self, geometry):
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "data", is_global=False).should_be_called()
geometry.clear_dynamic_voids("obj").should_be_called()
subject.switch_representation(
geometry,
obj="obj",
representation="mapped_rep",
should_reload=False,
enable_dynamic_voids=False,
is_global=False,
)
+61
View File
@@ -0,0 +1,61 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blenderbim.core.type as subject
from test.core.bootstrap import ifc, geometry, type
class TestAssignType:
def test_assigning_and_switching_preferably_to_a_body_representation(self, ifc, geometry, type):
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
type.get_body_representation("element").should_be_called().will_return("mapped_rep")
ifc.get_object("element").should_be_called().will_return("obj")
type.has_dynamic_voids("obj").should_be_called().will_return(False)
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "data", is_global=False).should_be_called()
geometry.clear_dynamic_voids("obj").should_be_called()
type.disable_editing("obj").should_be_called()
subject.assign_type(ifc, geometry, type, element="element", type="type")
def test_assigning_and_switching_to_any_representation_as_a_fallback(self, ifc, geometry, type):
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
type.get_body_representation("element").should_be_called().will_return(None)
type.get_any_representation("element").should_be_called().will_return("mapped_rep")
ifc.get_object("element").should_be_called().will_return("obj")
type.has_dynamic_voids("obj").should_be_called().will_return(False)
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "data", is_global=False).should_be_called()
geometry.clear_dynamic_voids("obj").should_be_called()
type.disable_editing("obj").should_be_called()
subject.assign_type(ifc, geometry, type, element="element", type="type")
def test_assigning_and_not_changing_representation_if_not_available(self, ifc, type):
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
type.get_body_representation("element").should_be_called().will_return(None)
type.get_any_representation("element").should_be_called().will_return(None)
ifc.get_object("element").should_be_called().will_return("obj")
type.disable_editing("obj").should_be_called()
subject.assign_type(ifc, geometry, type, element="element", type="type")
+89
View File
@@ -0,0 +1,89 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.type import Type as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Type)
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMTypeProperties.is_editing_type = True
subject.disable_editing(obj)
assert obj.BIMTypeProperties.is_editing_type is False
class TestGetAnyRepresentation(NewFile):
def test_get_product_representation(self):
ifc = ifcopenshell.file()
representation = ifc.createIfcShapeRepresentation()
element = ifc.createIfcWall(Representation=ifc.createIfcProductRepresentation(Representations=[representation]))
assert subject.get_any_representation(element) == representation
def test_get_type_product_representation(self):
ifc = ifcopenshell.file()
representation = ifc.createIfcShapeRepresentation()
element = ifc.createIfcWallType(
RepresentationMaps=[ifc.createIfcRepresentationMap(MappedRepresentation=representation)]
)
assert subject.get_any_representation(element) == representation
class TestGetBodyRepresentation(NewFile):
def test_get_product_representation(self):
ifc = ifcopenshell.file()
rep = ifc.createIfcShapeRepresentation(ContextOfItems=ifc.createIfcGeometricRepresentationSubContext())
body_rep = ifc.createIfcShapeRepresentation(
ContextOfItems=ifc.createIfcGeometricRepresentationSubContext(ContextIdentifier="Body")
)
element = ifc.createIfcWall(Representation=ifc.createIfcProductRepresentation(Representations=[rep, body_rep]))
assert subject.get_body_representation(element) == body_rep
def test_get_type_product_representation(self):
ifc = ifcopenshell.file()
rep = ifc.createIfcShapeRepresentation(ContextOfItems=ifc.createIfcGeometricRepresentationSubContext())
body_rep = ifc.createIfcShapeRepresentation(
ContextOfItems=ifc.createIfcGeometricRepresentationSubContext(ContextIdentifier="Body")
)
element = ifc.createIfcWallType(
RepresentationMaps=[
ifc.createIfcRepresentationMap(MappedRepresentation=rep),
ifc.createIfcRepresentationMap(MappedRepresentation=body_rep),
]
)
assert subject.get_body_representation(element) == body_rep
class TestHasDynamicVoids(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
assert subject.has_dynamic_voids(obj) is False
def test_checking_modifiers(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
assert subject.has_dynamic_voids(obj) is True