New dumb column tool. Creates a parametric solid column from profile sets.

This commit is contained in:
Dion Moult
2021-06-07 19:43:02 +10:00
parent ca0dac1353
commit 7d841c3f27
8 changed files with 188 additions and 7 deletions
@@ -58,6 +58,7 @@ class AddRepresentation(bpy.types.Operator):
obj: bpy.props.StringProperty()
context_id: bpy.props.IntProperty()
ifc_representation_class: bpy.props.StringProperty()
profile_set: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
@@ -93,6 +94,7 @@ class AddRepresentation(bpy.types.Operator):
"should_force_faceted_brep": context.scene.BIMGeometryProperties.should_force_faceted_brep,
"should_force_triangulation": context.scene.BIMGeometryProperties.should_force_triangulation,
"ifc_representation_class": self.ifc_representation_class,
"profile_set": self.file.by_id(self.profile_set) if self.profile_set else None
}
result = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
@@ -0,0 +1,129 @@
import bpy
import bmesh
import math
import ifcopenshell
import ifcopenshell.util.type
import ifcopenshell.util.unit
import ifcopenshell.util.element
import mathutils.geometry
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore
from math import pi, degrees
from mathutils import Vector, Matrix
from ifcopenshell.api.pset.data import Data as PsetData
from ifcopenshell.api.material.data import Data as MaterialData
from blenderbim.bim.module.geometry.helper import Helper
def element_listener(element, obj):
blenderbim.bim.handler.subscribe_to(obj, "mode", mode_callback)
def mode_callback(obj, data):
for obj in bpy.context.selected_objects + [bpy.context.active_object]:
if (
obj.mode != "EDIT"
or not obj.data
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
or not obj.BIMObjectProperties.ifc_definition_id
or not bpy.context.scene.BIMProjectProperties.is_authoring
):
return
product = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
if not parametric or parametric["Engine"] != "BlenderBIM.DumbColumn":
return
IfcStore.edited_objs.add(obj)
bm = bmesh.from_edit_mesh(obj.data)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
bmesh.update_edit_mesh(obj.data)
bm.free()
def ensure_solid(usecase_path, ifc_file, settings):
product = ifc_file.by_id(settings["blender_object"].BIMObjectProperties.ifc_definition_id)
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
if not parametric or parametric["Engine"] != "BlenderBIM.DumbColumn":
return
material = ifcopenshell.util.element.get_material(product)
if material and material.is_a("IfcMaterialProfileSet"):
settings["profile_set"] = material
else:
return
settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSet"
class DumbColumnGenerator:
def __init__(self, relating_type):
self.relating_type = relating_type
def generate(self):
self.file = IfcStore.get_file()
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
material = ifcopenshell.util.element.get_material(self.relating_type)
if material and material.is_a("IfcMaterialProfileSet"):
self.profile_set = material
else:
return
self.collection = bpy.context.view_layer.active_layer_collection.collection
self.collection_obj = bpy.data.objects.get(self.collection.name)
self.length = 3
self.rotation = 0
self.location = Vector((0, 0, 0))
return self.derive_from_cursor()
def derive_from_cursor(self):
self.location = bpy.context.scene.cursor.location
return self.create_column()
def create_column(self):
# A cube
verts = [
Vector((-1, -1, -1)),
Vector((-1, -1, 1)),
Vector((-1, 1, -1)),
Vector((-1, 1, 1)),
Vector((1, -1, -1)),
Vector((1, -1, 1)),
Vector((1, 1, -1)),
Vector((1, 1, 1)),
]
edges = []
faces = [
[0, 2, 3, 1],
[2, 3, 7, 6],
[4, 5, 7, 6],
[0, 1, 5, 4],
[1, 3, 7, 5],
[0, 2, 6, 4],
]
mesh = bpy.data.meshes.new(name="Dumb Column")
mesh.from_pydata(verts, edges, faces)
obj = bpy.data.objects.new("Column", mesh)
obj.name = "Column"
obj.location = self.location
if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id:
obj.location[2] = self.collection_obj.location[2]
self.collection.objects.link(obj)
bpy.ops.bim.assign_class(
obj=obj.name, ifc_class="IfcColumn", predefined_type="COLUMN", should_add_representation=False
)
bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name)
bpy.ops.bim.add_representation(
obj=obj.name,
context_id=ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW").id(),
ifc_representation_class="IfcExtrudedAreaSolid/IfcMaterialProfileSet",
profile_set=self.profile_set.id(),
)
representation = ifcopenshell.util.representation.get_representation(
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), "Model", "Body", "MODEL_VIEW"
)
bpy.ops.bim.switch_representation(obj=obj.name, ifc_definition_id=representation.id(), should_reload=True)
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.DumbColumn"})
MaterialData.load(self.file)
obj.select_set(True)
return obj
@@ -1,7 +1,7 @@
import bpy
import ifcopenshell
import ifcopenshell.api
from blenderbim.bim.module.model import product, wall, slab
from blenderbim.bim.module.model import product, wall, slab, column
from blenderbim.bim.ifc import IfcStore
from bpy.app.handlers import persistent
@@ -45,3 +45,8 @@ def load_post(*args):
ifcopenshell.api.add_pre_listener(
"type.assign_type", "BlenderBIM.DumbSlab.RegenerateFromType", slab.DumbSlabPlaner().regenerate_from_type
)
IfcStore.add_element_listener(column.element_listener)
ifcopenshell.api.add_pre_listener(
"geometry.add_representation", "BlenderBIM.DumbColumn.EnsureSolid", column.ensure_solid
)
@@ -3,7 +3,7 @@ import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
import ifcopenshell.util.representation
from . import wall, slab
from . import wall, slab, column
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data as PsetData
from mathutils import Vector
@@ -32,6 +32,10 @@ class AddTypeInstance(bpy.types.Operator):
obj = slab.DumbSlabGenerator(self.file.by_id(int(relating_type))).generate()
if obj:
return {"FINISHED"}
elif ifc_class == "IfcColumnType":
obj = column.DumbColumnGenerator(self.file.by_id(int(relating_type))).generate()
if obj:
return {"FINISHED"}
# A cube
verts = [
Vector((-1, -1, -1)),
@@ -83,6 +83,7 @@ class AssignClass(bpy.types.Operator):
predefined_type: bpy.props.StringProperty()
userdefined_type: bpy.props.StringProperty()
context_id: bpy.props.IntProperty()
should_add_representation: bpy.props.BoolProperty(default=True)
ifc_representation_class: bpy.props.StringProperty()
def execute(self, context):
@@ -112,9 +113,10 @@ class AssignClass(bpy.types.Operator):
obj.name = "{}/{}".format(product.is_a(), obj.name)
IfcStore.link_element(product, obj)
bpy.ops.bim.add_representation(
obj=obj.name, context_id=self.context_id, ifc_representation_class=self.ifc_representation_class
)
if self.should_add_representation:
bpy.ops.bim.add_representation(
obj=obj.name, context_id=self.context_id, ifc_representation_class=self.ifc_representation_class
)
if product.is_a("IfcElementType"):
self.place_in_types_collection(obj)
@@ -30,7 +30,9 @@ class Usecase:
# IfcExtrudedAreaSolid/IfcCircleProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids
# IfcExtrudedAreaSolid/IfcMaterialProfileSet
"ifc_representation_class": None, # Whether to cast a mesh into a particular class
"profile_set": None, # The material profile set if the extrusion requires it
}
self.ifc_vertices = []
for key, value in settings.items():
@@ -217,6 +219,8 @@ class Usecase:
return self.create_arbitrary_extrusion_representation()
elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids":
return self.create_arbitrary_void_extrusion_representation()
elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcMaterialProfileSet":
return self.create_material_profile_set_extrusion_representation()
return self.create_mesh_representation()
def create_camera_block_representation(self):
@@ -404,6 +408,31 @@ class Usecase:
[item],
)
def create_material_profile_set_extrusion_representation(self):
profile_def = (
self.settings["profile_set"].CompositeProfile
or self.settings["profile_set"].MaterialProfiles[0].Profile
)
position = None
if self.file.schema == "IFC2X3":
position = self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.file.createIfcDirection((1.0, 0.0, 0.0)),
)
item = self.file.createIfcExtrudedAreaSolid(
profile_def,
position,
self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.convert_si_to_unit(self.settings["blender_object"].dimensions[2]),
)
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
"SweptSolid",
[item],
)
def create_mesh_representation(self):
if self.file.schema == "IFC2X3" or self.settings["should_force_faceted_brep"]:
return self.create_faceted_brep()
@@ -38,7 +38,15 @@ class Usecase:
material_set = self.file.create_entity(self.settings["type"])
self.create_material_association(material_set)
elif self.settings["type"] == "IfcMaterialProfileSetUsage":
material_set = self.file.create_entity("IfcMaterialProfileSet")
element_type = ifcopenshell.util.element.get_type(self.settings["product"])
if element_type:
element_type_material = ifcopenshell.util.element.get_material(element_type)
if element_type_material and element_type_material.is_a("IfcMaterialProfileSet"):
material_set = element_type_material
else:
material_set = self.file.create_entity("IfcMaterialProfileSet")
else:
material_set = self.file.create_entity("IfcMaterialProfileSet")
material_set_usage = self.create_profile_set_usage(material_set)
self.create_material_association(material_set_usage)
elif self.settings["type"] == "IfcMaterialList":
@@ -52,7 +52,9 @@ def get_properties(properties):
def get_type(element):
if hasattr(element, "IsTypedBy") and element.IsTypedBy:
if element.is_a("IfcTypeObject"):
return element
elif hasattr(element, "IsTypedBy") and element.IsTypedBy:
return element.IsTypedBy[0].RelatingType
elif hasattr(element, "IsDefinedBy") and element.IsDefinedBy: # IFC2X3
for relationship in element.IsDefinedBy: