mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 19:07:57 +00:00
Assigning a parametric profile type now auto switches from non parametric geometry to parametric geometry
This commit is contained in:
@@ -17,13 +17,13 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
import math
|
||||
import bmesh
|
||||
import mathutils.geometry
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
import mathutils.geometry
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.type
|
||||
@@ -100,9 +100,7 @@ def ensure_solid(usecase_path, ifc_file, settings):
|
||||
material = ifcopenshell.util.element.get_material(product)
|
||||
if material and material.is_a("IfcMaterialProfileSetUsage"):
|
||||
settings["profile_set_usage"] = material
|
||||
else:
|
||||
return
|
||||
settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage"
|
||||
settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage"
|
||||
|
||||
|
||||
class DumbProfileGenerator:
|
||||
|
||||
@@ -51,7 +51,7 @@ class AssignType(bpy.types.Operator, Operator):
|
||||
else context.selected_objects or [context.active_object]
|
||||
)
|
||||
for obj in related_objects:
|
||||
core.assign_type(tool.Ifc, tool.Geometry, tool.Type, element=tool.Ifc.get_entity(obj), type=type)
|
||||
core.assign_type(tool.Ifc, 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)
|
||||
|
||||
@@ -74,12 +74,7 @@ def add_representation(
|
||||
|
||||
|
||||
def switch_representation(
|
||||
geometry,
|
||||
obj=None,
|
||||
representation=None,
|
||||
should_reload=True,
|
||||
enable_dynamic_voids=True,
|
||||
is_global=True,
|
||||
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)
|
||||
|
||||
@@ -197,8 +197,15 @@ class Surveyor:
|
||||
class Type:
|
||||
def disable_editing(cls, obj): pass
|
||||
def get_any_representation(cls, element): pass
|
||||
def get_body_context(cls): pass
|
||||
def get_body_representation(cls, element): pass
|
||||
def get_ifc_representation_class(cls, element): pass
|
||||
def get_profile_set_usage(cls, element): pass
|
||||
def get_representation_context(cls, representation): pass
|
||||
def has_dynamic_voids(cls, obj): pass
|
||||
def has_material_usage(cls, element): pass
|
||||
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
|
||||
def run_geometry_switch_representation(cls, obj=None, representation=None, should_reload=None, enable_dynamic_voids=None, is_global=None): pass
|
||||
|
||||
|
||||
@interface
|
||||
|
||||
@@ -19,18 +19,34 @@
|
||||
import blenderbim.core.geometry
|
||||
|
||||
|
||||
def assign_type(ifc, geometry, type_tool, element=None, type=None):
|
||||
def assign_type(ifc, 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 type_tool.has_material_usage(element):
|
||||
representation = type_tool.get_body_representation(element)
|
||||
if representation:
|
||||
body_context = type_tool.get_representation_context(representation)
|
||||
ifc.run("geometry.unassign_representation", product=element, representation=representation)
|
||||
ifc.run("geometry.remove_representation", representation=representation)
|
||||
else:
|
||||
body_context = type_tool.get_body_context()
|
||||
representation = type_tool.run_geometry_add_representation(
|
||||
obj=obj,
|
||||
context=body_context,
|
||||
ifc_representation_class=type_tool.get_ifc_representation_class(element),
|
||||
profile_set_usage=type_tool.get_profile_set_usage(element),
|
||||
)
|
||||
should_reload = True
|
||||
else:
|
||||
representation = type_tool.get_body_representation(element)
|
||||
if not representation:
|
||||
representation = type_tool.get_any_representation(element)
|
||||
should_reload = False
|
||||
if representation:
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
geometry,
|
||||
type_tool.run_geometry_switch_representation(
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=False,
|
||||
should_reload=should_reload,
|
||||
enable_dynamic_voids=type_tool.has_dynamic_voids(obj),
|
||||
is_global=False,
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
|
||||
@@ -34,6 +35,10 @@ class Type(blenderbim.core.tool.Type):
|
||||
elif element.is_a("IfcTypeProduct") and element.RepresentationMaps:
|
||||
return element.RepresentationMaps[0].MappedRepresentation
|
||||
|
||||
@classmethod
|
||||
def get_body_context(cls):
|
||||
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@classmethod
|
||||
def get_body_representation(cls, element):
|
||||
if element.is_a("IfcProduct") and element.Representation and element.Representation.Representations:
|
||||
@@ -45,9 +50,64 @@ class Type(blenderbim.core.tool.Type):
|
||||
if representation_map.MappedRepresentation.ContextOfItems.ContextIdentifier == "Body":
|
||||
return representation_map.MappedRepresentation
|
||||
|
||||
@classmethod
|
||||
def get_ifc_representation_class(cls, element):
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
if material:
|
||||
if material.is_a("IfcMaterialProfileSetUsage"):
|
||||
return "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage"
|
||||
elif material.is_a("IfcMaterialLayerSetUsage"):
|
||||
return "IfcExtrudedAreaSolid/IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
||||
|
||||
@classmethod
|
||||
def get_profile_set_usage(cls, element):
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
if material:
|
||||
if material.is_a("IfcMaterialProfileSetUsage"):
|
||||
return material
|
||||
|
||||
@classmethod
|
||||
def get_representation_context(cls, representation):
|
||||
return representation.ContextOfItems
|
||||
|
||||
@classmethod
|
||||
def has_dynamic_voids(cls, obj):
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.name == "IfcOpeningElement" and modifier.type == "BOOLEAN":
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def has_material_usage(cls, element):
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
if material:
|
||||
return "Usage" in material.is_a()
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def run_geometry_add_representation(
|
||||
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||
):
|
||||
return blenderbim.core.geometry.add_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
tool.Style,
|
||||
tool.Surveyor,
|
||||
obj=obj,
|
||||
context=context,
|
||||
ifc_representation_class=ifc_representation_class,
|
||||
profile_set_usage=profile_set_usage,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def run_geometry_switch_representation(
|
||||
cls, obj=None, representation=None, should_reload=None, enable_dynamic_voids=None, is_global=None
|
||||
):
|
||||
return blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=should_reload,
|
||||
enable_dynamic_voids=enable_dynamic_voids,
|
||||
is_global=is_global,
|
||||
)
|
||||
|
||||
@@ -17,45 +17,84 @@
|
||||
# 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
|
||||
from test.core.bootstrap import ifc, type
|
||||
|
||||
|
||||
class TestAssignType:
|
||||
def test_assigning_and_switching_preferably_to_a_body_representation(self, ifc, geometry, type):
|
||||
def test_assigning_and_switching_preferably_to_a_body_representation(self, ifc, type):
|
||||
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
|
||||
type.has_material_usage("element").should_be_called().will_return(False)
|
||||
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.run_geometry_switch_representation(
|
||||
obj="obj", representation="mapped_rep", should_reload=False, enable_dynamic_voids=False, is_global=False
|
||||
).should_be_called()
|
||||
type.disable_editing("obj").should_be_called()
|
||||
subject.assign_type(ifc, type, element="element", type="type")
|
||||
|
||||
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):
|
||||
def test_assigning_and_switching_to_any_representation_as_a_fallback(self, ifc, type):
|
||||
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
|
||||
type.has_material_usage("element").should_be_called().will_return(False)
|
||||
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.run_geometry_switch_representation(
|
||||
obj="obj", representation="mapped_rep", should_reload=False, enable_dynamic_voids=False, is_global=False
|
||||
).should_be_called()
|
||||
type.disable_editing("obj").should_be_called()
|
||||
subject.assign_type(ifc, type, element="element", type="type")
|
||||
|
||||
subject.assign_type(ifc, geometry, type, element="element", type="type")
|
||||
|
||||
def test_assigning_and_not_changing_representation_if_not_available(self, ifc, type):
|
||||
def test_assigning_and_not_changing_representation_if_there_is_no_representation_to_change_to(self, ifc, type):
|
||||
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
|
||||
type.has_material_usage("element").should_be_called().will_return(False)
|
||||
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")
|
||||
subject.assign_type(ifc, type, element="element", type="type")
|
||||
|
||||
def test_updating_an_existing_body_if_there_is_a_parametric_material_usage(self, ifc, type):
|
||||
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
|
||||
|
||||
type.has_material_usage("element").should_be_called().will_return(True)
|
||||
type.get_body_representation("element").should_be_called().will_return("representation")
|
||||
type.get_representation_context("representation").should_be_called().will_return("context")
|
||||
ifc.run(
|
||||
"geometry.unassign_representation", product="element", representation="representation"
|
||||
).should_be_called()
|
||||
ifc.run("geometry.remove_representation", representation="representation").should_be_called()
|
||||
type.get_ifc_representation_class("element").should_be_called().will_return("class")
|
||||
type.get_profile_set_usage("element").should_be_called().will_return("usage")
|
||||
type.run_geometry_add_representation(
|
||||
obj="obj", context="context", ifc_representation_class="class", profile_set_usage="usage"
|
||||
).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)
|
||||
type.run_geometry_switch_representation(
|
||||
obj="obj", representation="mapped_rep", should_reload=True, enable_dynamic_voids=False, is_global=False
|
||||
).should_be_called()
|
||||
type.disable_editing("obj").should_be_called()
|
||||
subject.assign_type(ifc, type, element="element", type="type")
|
||||
|
||||
def test_creating_a_new_body_if_there_is_a_parametric_material_usage(self, ifc, type):
|
||||
ifc.run("type.assign_type", related_object="element", relating_type="type").should_be_called()
|
||||
|
||||
type.has_material_usage("element").should_be_called().will_return(True)
|
||||
type.get_body_representation("element").should_be_called().will_return(None)
|
||||
type.get_body_context().should_be_called().will_return("context")
|
||||
type.get_ifc_representation_class("element").should_be_called().will_return("class")
|
||||
type.get_profile_set_usage("element").should_be_called().will_return("usage")
|
||||
type.run_geometry_add_representation(
|
||||
obj="obj", context="context", ifc_representation_class="class", profile_set_usage="usage"
|
||||
).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)
|
||||
type.run_geometry_switch_representation(
|
||||
obj="obj", representation="mapped_rep", should_reload=True, enable_dynamic_voids=False, is_global=False
|
||||
).should_be_called()
|
||||
type.disable_editing("obj").should_be_called()
|
||||
subject.assign_type(ifc, type, element="element", type="type")
|
||||
|
||||
@@ -53,6 +53,16 @@ class TestGetAnyRepresentation(NewFile):
|
||||
assert subject.get_any_representation(element) == representation
|
||||
|
||||
|
||||
class TestGetBodyContext(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
context = ifc.createIfcGeometricRepresentationSubContext(
|
||||
ContextType="Model", ContextIdentifier="Body", TargetView="MODEL_VIEW"
|
||||
)
|
||||
tool.Ifc.set(ifc)
|
||||
assert subject.get_body_context() == context
|
||||
|
||||
|
||||
class TestGetBodyRepresentation(NewFile):
|
||||
def test_get_product_representation(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -78,6 +88,49 @@ class TestGetBodyRepresentation(NewFile):
|
||||
assert subject.get_body_representation(element) == body_rep
|
||||
|
||||
|
||||
class TestGetIfcRepresentationClass(NewFile):
|
||||
def test_detecting_profile_set_representations(self):
|
||||
ifc = ifcopenshell.file()
|
||||
element = ifc.createIfcColumn()
|
||||
ifc.createIfcRelAssociatesMaterial(
|
||||
RelatingMaterial=ifc.createIfcMaterialProfileSetUsage(), RelatedObjects=[element]
|
||||
)
|
||||
assert subject.get_ifc_representation_class(element) == "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage"
|
||||
|
||||
def test_detecting_layer_set_representations(self):
|
||||
ifc = ifcopenshell.file()
|
||||
element = ifc.createIfcColumn()
|
||||
ifc.createIfcRelAssociatesMaterial(
|
||||
RelatingMaterial=ifc.createIfcMaterialLayerSetUsage(), RelatedObjects=[element]
|
||||
)
|
||||
assert (
|
||||
subject.get_ifc_representation_class(element)
|
||||
== "IfcExtrudedAreaSolid/IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
||||
)
|
||||
|
||||
def test_returning_null_for_non_parametric_represntations(self):
|
||||
ifc = ifcopenshell.file()
|
||||
assert subject.get_ifc_representation_class(ifc.createIfcColumn()) is None
|
||||
|
||||
|
||||
class TestGetProfileSetUsage(NewFile):
|
||||
def test_getting_a_profile_set_usage(self):
|
||||
ifc = ifcopenshell.file()
|
||||
element = ifc.createIfcColumn()
|
||||
assert subject.get_profile_set_usage(element) is None
|
||||
usage = ifc.createIfcMaterialProfileSetUsage()
|
||||
ifc.createIfcRelAssociatesMaterial(RelatingMaterial=usage, RelatedObjects=[element])
|
||||
assert subject.get_profile_set_usage(element) == usage
|
||||
|
||||
|
||||
class TestGetRepresentationContext(NewFile):
|
||||
def test_getting_a_profile_set_usage(self):
|
||||
ifc = ifcopenshell.file()
|
||||
context = ifc.createIfcGeometricRepresentationSubContext()
|
||||
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context)
|
||||
assert subject.get_representation_context(representation) == context
|
||||
|
||||
|
||||
class TestHasDynamicVoids(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
@@ -87,3 +140,23 @@ class TestHasDynamicVoids(NewFile):
|
||||
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
|
||||
assert subject.has_dynamic_voids(obj) is True
|
||||
|
||||
|
||||
class TestHasMaterialUsage(NewFile):
|
||||
def test_getting_a_profile_set_usage(self):
|
||||
ifc = ifcopenshell.file()
|
||||
element = ifc.createIfcColumn()
|
||||
assert subject.has_material_usage(element) is False
|
||||
usage = ifc.createIfcMaterialProfileSetUsage()
|
||||
ifc.createIfcRelAssociatesMaterial(RelatingMaterial=usage, RelatedObjects=[element])
|
||||
assert subject.has_material_usage(element) is True
|
||||
|
||||
|
||||
class TestRunGeometryAddRepresentation(NewFile):
|
||||
def test_nothing(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestRunGeometrySwitchRepresentation(NewFile):
|
||||
def test_nothing(self):
|
||||
pass
|
||||
|
||||
@@ -22,7 +22,8 @@ class Usecase:
|
||||
ifcopenshell.util.element.remove_deep2(
|
||||
self.file,
|
||||
self.settings["representation"],
|
||||
extra_subgraph_elements=list(styled_items | presentation_layer_assignments),
|
||||
also_consider=list(styled_items | presentation_layer_assignments),
|
||||
do_not_delete=self.file.by_type("IfcGeometricRepresentationContext"),
|
||||
)
|
||||
|
||||
for element in styled_items:
|
||||
|
||||
@@ -136,17 +136,21 @@ def remove_deep(ifc_file, element):
|
||||
ifc_file.unbatch()
|
||||
|
||||
|
||||
def remove_deep2(ifc_file, element, extra_subgraph_elements=[]):
|
||||
def remove_deep2(ifc_file, element, also_consider=[], do_not_delete=[]):
|
||||
# Experimental remove deep proposal. No batch for now until this is more certain. See #1812.
|
||||
# ifc_file.batch()
|
||||
to_delete = set()
|
||||
subgraph = list(ifc_file.traverse(element, breadth_first=True))
|
||||
subgraph.extend(extra_subgraph_elements)
|
||||
subgraph.extend(also_consider)
|
||||
subgraph_set = set(subgraph)
|
||||
subelement_queue = ifc_file.traverse(element, max_levels=1)
|
||||
while subelement_queue:
|
||||
subelement = subelement_queue.pop(0)
|
||||
if subelement.id() and len(set(ifc_file.get_inverse(subelement)) - subgraph_set) == 0:
|
||||
if (
|
||||
subelement.id()
|
||||
and len(set(ifc_file.get_inverse(subelement)) - subgraph_set) == 0
|
||||
and subelement not in do_not_delete
|
||||
):
|
||||
to_delete.add(subelement)
|
||||
subelement_queue.extend(ifc_file.traverse(subelement, max_levels=1)[1:])
|
||||
for subelement in to_delete:
|
||||
|
||||
@@ -77,3 +77,9 @@ class TestRemoveRepresentation(test.bootstrap.IFC4):
|
||||
ifcopenshell.api.run("geometry.remove_representation", self.file, representation=representation)
|
||||
assert len(self.file.by_type("IfcPresentationLayerAssignment")) == 1
|
||||
assert len(self.file.by_type("IfcShapeRepresentation")) == 1
|
||||
|
||||
def test_not_purging_geometric_representation_contexts(self):
|
||||
context = self.file.createIfcGeometricRepresentationSubContext()
|
||||
representation = self.file.createIfcShapeRepresentation(ContextOfItems=context)
|
||||
ifcopenshell.api.run("geometry.remove_representation", self.file, representation=representation)
|
||||
assert len(self.file.by_type("IfcGeometricRepresentationContext")) == 1
|
||||
|
||||
Reference in New Issue
Block a user