mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 10:23:41 +00:00
bim.add_material_set to ensure adding valid material sets
This commit is contained in:
@@ -156,6 +156,7 @@ class DuplicateMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
class AddMaterialSet(bpy.types.Operator, tool.Ifc.Operator):
|
class AddMaterialSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.add_material_set"
|
bl_idname = "bim.add_material_set"
|
||||||
bl_label = "Add Material Set"
|
bl_label = "Add Material Set"
|
||||||
|
bl_description = "Create a new material set, will use a default material/profile as a placeholder"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
set_type: bpy.props.StringProperty()
|
set_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ def add_material(
|
|||||||
|
|
||||||
def add_material_set(ifc: tool.Ifc, material: tool.Material, set_type: str) -> ifcopenshell.entity_instance:
|
def add_material_set(ifc: tool.Ifc, material: tool.Material, set_type: str) -> ifcopenshell.entity_instance:
|
||||||
ifc_material = ifc.run("material.add_material_set", name="Unnamed", set_type=set_type)
|
ifc_material = ifc.run("material.add_material_set", name="Unnamed", set_type=set_type)
|
||||||
|
material.ensure_new_material_set_is_valid(ifc_material)
|
||||||
if material.is_editing_materials():
|
if material.is_editing_materials():
|
||||||
material.import_material_definitions(material.get_active_material_type())
|
material.import_material_definitions(material.get_active_material_type())
|
||||||
return ifc_material
|
return ifc_material
|
||||||
|
|||||||
@@ -510,6 +510,7 @@ class Material:
|
|||||||
def enable_editing_materials(cls): pass
|
def enable_editing_materials(cls): pass
|
||||||
def ensure_material_assigned(cls, elements, material_type, material): pass
|
def ensure_material_assigned(cls, elements, material_type, material): pass
|
||||||
def ensure_material_unassigned(cls, elements): pass
|
def ensure_material_unassigned(cls, elements): pass
|
||||||
|
def ensure_new_material_set_is_valid(cls, material): pass
|
||||||
def get_active_material_item(cls): pass
|
def get_active_material_item(cls): pass
|
||||||
def get_active_material_type(cls): pass
|
def get_active_material_type(cls): pass
|
||||||
def get_default_material(cls): pass
|
def get_default_material(cls): pass
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ import bonsai.bim.helper
|
|||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Union, Any, TYPE_CHECKING, Optional
|
from typing import Union, Any, TYPE_CHECKING, Optional, Literal
|
||||||
|
from typing_extensions import assert_never
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# Avoid circular imports.
|
# Avoid circular imports.
|
||||||
@@ -352,3 +353,38 @@ class Material(bonsai.core.tool.Material):
|
|||||||
elements.extend(tool.Model.get_occurrences_without_material_override(element))
|
elements.extend(tool.Model.get_occurrences_without_material_override(element))
|
||||||
|
|
||||||
tool.Model.apply_ifc_material_changes(elements, assigned_material=assigned_material)
|
tool.Model.apply_ifc_material_changes(elements, assigned_material=assigned_material)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ensure_new_material_set_is_valid(cls, material: ifcopenshell.entity_instance) -> None:
|
||||||
|
material_type = material.is_a()
|
||||||
|
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
default_material = cls.get_default_material()
|
||||||
|
|
||||||
|
if material_type == "IfcMaterialConstituentSet":
|
||||||
|
material_constituent = ifcopenshell.api.material.add_constituent(
|
||||||
|
ifc_file,
|
||||||
|
constituent_set=material,
|
||||||
|
material=default_material,
|
||||||
|
)
|
||||||
|
material.MaterialConstituents = [material_constituent]
|
||||||
|
elif material_type == "IfcMaterialLayerSet":
|
||||||
|
material_layer = ifcopenshell.api.material.add_layer(
|
||||||
|
ifc_file,
|
||||||
|
layer_set=material,
|
||||||
|
material=default_material,
|
||||||
|
)
|
||||||
|
material.MaterialLayers = [material_layer]
|
||||||
|
elif material_type == "IfcMaterialProfileSet":
|
||||||
|
profile = tool.Profile.get_default_profile()
|
||||||
|
material_profile = ifcopenshell.api.material.add_profile(
|
||||||
|
ifc_file,
|
||||||
|
profile_set=material,
|
||||||
|
profile=profile,
|
||||||
|
material=None,
|
||||||
|
)
|
||||||
|
material.MaterialProfiles = [material_profile]
|
||||||
|
elif material_type == "IfcMaterialList":
|
||||||
|
material.Materials = [default_material]
|
||||||
|
else:
|
||||||
|
assert False, f"Invalid material type found: {material_type}."
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api.profile
|
||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
@@ -78,6 +79,21 @@ class Profile(bonsai.core.tool.Profile):
|
|||||||
return profile
|
return profile
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_profile(cls) -> ifcopenshell.entity_instance:
|
||||||
|
"""Return first found IfcProfileDef in IFC file or create a new default profile."""
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
profile = next(iter(ifc_file.by_type("IfcProfileDef")), None)
|
||||||
|
if profile:
|
||||||
|
return profile
|
||||||
|
profile = ifcopenshell.api.profile.add_parameterized_profile(ifc_file, ifc_class="IfcRectangleProfileDef")
|
||||||
|
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||||
|
profile.ProfileType = "AREA"
|
||||||
|
profile.ProfileName = "Default Profile"
|
||||||
|
profile.XDim = 0.1 / si_conversion
|
||||||
|
profile.YDim = 0.1 / si_conversion
|
||||||
|
return profile
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_model_profiles(cls) -> list[ifcopenshell.entity_instance]:
|
def get_model_profiles(cls) -> list[ifcopenshell.entity_instance]:
|
||||||
return tool.Ifc.get().by_type("IfcProfileDef")
|
return tool.Ifc.get().by_type("IfcProfileDef")
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class TestAddMaterialSet:
|
|||||||
ifc.run("material.add_material_set", name="Unnamed", set_type="set_type").should_be_called().will_return(
|
ifc.run("material.add_material_set", name="Unnamed", set_type="set_type").should_be_called().will_return(
|
||||||
"material"
|
"material"
|
||||||
)
|
)
|
||||||
|
material.ensure_new_material_set_is_valid("material").should_be_called()
|
||||||
material.is_editing_materials().should_be_called().will_return(False)
|
material.is_editing_materials().should_be_called().will_return(False)
|
||||||
assert subject.add_material_set(ifc, material, set_type="set_type") == "material"
|
assert subject.add_material_set(ifc, material, set_type="set_type") == "material"
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ class TestAddMaterialSet:
|
|||||||
ifc.run("material.add_material_set", name="Unnamed", set_type="set_type").should_be_called().will_return(
|
ifc.run("material.add_material_set", name="Unnamed", set_type="set_type").should_be_called().will_return(
|
||||||
"material"
|
"material"
|
||||||
)
|
)
|
||||||
|
material.ensure_new_material_set_is_valid("material").should_be_called()
|
||||||
material.is_editing_materials().should_be_called().will_return(True)
|
material.is_editing_materials().should_be_called().will_return(True)
|
||||||
material.get_active_material_type().should_be_called().will_return("material_type")
|
material.get_active_material_type().should_be_called().will_return("material_type")
|
||||||
material.import_material_definitions("material_type").should_be_called()
|
material.import_material_definitions("material_type").should_be_called()
|
||||||
|
|||||||
@@ -150,3 +150,36 @@ class TestIsMaterialUsedInSets(NewFile):
|
|||||||
material_set.MaterialLayers = [material_set_item]
|
material_set.MaterialLayers = [material_set_item]
|
||||||
material_set_item.Material = material
|
material_set_item.Material = material
|
||||||
assert subject.is_material_used_in_sets(material) is True
|
assert subject.is_material_used_in_sets(material) is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnsureNewMaterialSetIsValid(NewFile):
|
||||||
|
def test_material_constituent_set(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
material_set = ifc.create_entity("IfcMaterialConstituentSet")
|
||||||
|
subject.ensure_new_material_set_is_valid(material_set)
|
||||||
|
assert len(constituents := material_set.MaterialConstituents) == 1
|
||||||
|
assert constituents[0].Material
|
||||||
|
|
||||||
|
def test_material_layer_set(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
material_set = ifc.create_entity("IfcMaterialLayerSet")
|
||||||
|
subject.ensure_new_material_set_is_valid(material_set)
|
||||||
|
assert len(layers := material_set.MaterialLayers) == 1
|
||||||
|
assert layers[0].Material
|
||||||
|
|
||||||
|
def test_material_profile_set(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
material_set = ifc.create_entity("IfcMaterialProfileSet")
|
||||||
|
subject.ensure_new_material_set_is_valid(material_set)
|
||||||
|
assert len(profiles := material_set.MaterialProfiles) == 1
|
||||||
|
assert profiles[0].Profile
|
||||||
|
|
||||||
|
def test_material_list(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
material_set = ifc.create_entity("IfcMaterialList")
|
||||||
|
subject.ensure_new_material_set_is_valid(material_set)
|
||||||
|
assert len(material_set.Materials) == 1
|
||||||
|
|||||||
Reference in New Issue
Block a user