Slabs are now first created directly in IFC instead of reverse engineering meshes

This commit is contained in:
Dion Moult
2022-09-30 12:08:57 +10:00
parent 9fc5f72831
commit a08a78b3c9
3 changed files with 140 additions and 40 deletions
@@ -197,6 +197,9 @@ class DumbSlabGenerator:
if not sum(thicknesses):
return
self.body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
self.footprint_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "FootPrint", "SKETCH_VIEW")
self.collection = bpy.context.view_layer.active_layer_collection.collection
self.collection_obj = bpy.data.objects.get(self.collection.name)
self.depth = sum(thicknesses) * unit_scale
@@ -211,26 +214,12 @@ class DumbSlabGenerator:
return self.create_slab(link_to_scene)
def create_slab(self, link_to_scene):
verts = [
Vector((0, 0, 0)),
Vector((0, self.width, 0)),
Vector((self.length, self.width, 0)),
Vector((self.length, 0, 0)),
]
edges = []
faces = [[0, 3, 2, 1]]
ifc_classes = ifcopenshell.util.type.get_applicable_entities(self.relating_type.is_a(), self.file.schema)
# Standard cases are deprecated, so let's cull them
ifc_class = [c for c in ifc_classes if "StandardCase" not in c][0]
mesh = bpy.data.meshes.new(name="Dumb Slab")
mesh.from_pydata(verts, edges, faces)
mesh = bpy.data.meshes.new("Dummy")
obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(self.relating_type, ifc_class), mesh)
modifier = obj.modifiers.new("Slab Depth", "SOLIDIFY")
modifier.use_even_offset = True
modifier.offset = 1
modifier.thickness = self.depth
if link_to_scene:
obj.location = self.location
@@ -239,26 +228,38 @@ class DumbSlabGenerator:
else:
obj.location[2] -= self.depth
self.collection.objects.link(obj)
bpy.ops.bim.assign_class(
obj=obj.name,
element = blenderbim.core.root.assign_class(
tool.Ifc,
tool.Collector,
tool.Root,
obj=obj,
ifc_class=ifc_class,
ifc_representation_class="IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids",
should_add_representation=False,
context=self.body_context,
)
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=tool.Ifc.get_entity(obj), type=self.relating_type)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
ifcopenshell.api.run("type.assign_type", self.file, related_object=element, relating_type=self.relating_type)
representation = ifcopenshell.api.run(
"geometry.add_slab_representation", tool.Ifc.get(), context=self.body_context, depth=self.depth
)
ifcopenshell.api.run(
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation
)
blenderbim.core.geometry.switch_representation(
tool.Geometry,
obj=obj,
representation=representation,
should_reload=True,
enable_dynamic_voids=False,
is_global=True,
should_sync_changes_first=False,
)
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"})
MaterialData.load(self.file)
try:
obj.select_set(True)
except RuntimeError:
def msg(self, context):
txt = "The created object could not be assigned to a collection. "
txt += "Has any IfcSpatialElement been deleted?"
self.layout.label(text=txt)
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
obj.select_set(True)
return obj
@@ -288,6 +289,9 @@ class DumbSlabPlaner:
self.change_thickness(element, thickness)
def regenerate_from_type(self, usecase_path, ifc_file, settings):
obj = tool.Ifc.get_object(settings["related_object"])
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
return
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
if not new_material or not new_material.is_a("IfcMaterialLayerSet"):
@@ -502,10 +506,14 @@ class EnableEditingExtrusionProfile(bpy.types.Operator):
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
extrusion = self.get_extrusion(body)
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
position[0][3] *= self.unit_scale
position[1][3] *= self.unit_scale
position[2][3] *= self.unit_scale
if extrusion.Position:
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
position[0][3] *= self.unit_scale
position[1][3] *= self.unit_scale
position[2][3] *= self.unit_scale
else:
position = Matrix()
z_values = [v[2] for v in obj.bound_box]
origin = obj.matrix_world @ Vector((0, 0, max(z_values)))
@@ -585,7 +593,7 @@ class EnableEditingExtrusionProfile(bpy.types.Operator):
self.arcs[-1].append(len(self.vertices) - 1)
is_arc = False
else:
for local_point in curve.PointsCoordList:
for local_point in curve.Points.CoordList:
global_point = position @ Vector(self.convert_unit_to_si(local_point)).to_3d()
self.vertices.append(global_point)
@@ -626,10 +634,13 @@ class EditExtrusionProfile(bpy.types.Operator):
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
extrusion = self.get_extrusion(representation)
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
position[0][3] *= self.unit_scale
position[1][3] *= self.unit_scale
position[2][3] *= self.unit_scale
if extrusion.Position:
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
position[0][3] *= self.unit_scale
position[1][3] *= self.unit_scale
position[2][3] *= self.unit_scale
else:
position = Matrix()
helper = Helper(tool.Ifc.get())
indices = helper.auto_detect_arbitrary_profile_with_voids(obj, obj.data)
@@ -0,0 +1,89 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.unit
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
"depth": 0.2,
# Planes are defined as a matrix. The XY plane is the clipping boundary and +Z is removed.
"clippings": [], # A list of planes that define clipping half space solids
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
"Clipping" if self.settings["clippings"] else "SweptSolid",
[self.create_item()],
)
def create_item(self):
size = self.convert_si_to_unit(1)
points = (
(0.0, 0.0, 0.0),
(size, 0.0, 0.0),
(size, size, 0.0),
(0.0, size, 0.0),
(0.0, 0.0, 0.0),
)
if self.file.schema == "IFC2X3":
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
else:
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(points))
extrusion = self.file.createIfcExtrudedAreaSolid(
self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve),
None,
self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.convert_si_to_unit(self.settings["depth"]),
)
if self.settings["clippings"]:
return self.apply_clippings(extrusion)
return extrusion
def apply_clippings(self, first_operand):
while self.settings["clippings"]:
clipping = self.settings["clippings"].pop()
second_operand = self.file.createIfcHalfSpaceSolid(
self.file.createIfcPlane(
self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint(
(
self.convert_si_to_unit(clipping[0][3]),
self.convert_si_to_unit(clipping[1][3]),
self.convert_si_to_unit(clipping[2][3]),
)
),
self.file.createIfcDirection((clipping[0][2], clipping[1][2], clipping[2][2])),
self.file.createIfcDirection((clipping[0][0], clipping[1][0], clipping[2][0])),
)
),
False,
)
first_operand = self.file.createIfcBooleanClippingResult("DIFFERENCE", first_operand, second_operand)
return first_operand
def convert_si_to_unit(self, co):
return co / self.settings["unit_scale"]
@@ -28,7 +28,7 @@ def a2p(o, z, x):
def get_axis2placement(plc):
z = np.array(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1))
z = np.array(plc.Axis.DirectionRatios if getattr(plc, "Axis", None) else (0, 0, 1))
x = np.array(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0))
o = plc.Location.Coordinates
return a2p(o, z, x)