mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 02:07:36 +00:00
Slabs are now first created directly in IFC instead of reverse engineering meshes
This commit is contained in:
@@ -197,6 +197,9 @@ class DumbSlabGenerator:
|
|||||||
if not sum(thicknesses):
|
if not sum(thicknesses):
|
||||||
return
|
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 = bpy.context.view_layer.active_layer_collection.collection
|
||||||
self.collection_obj = bpy.data.objects.get(self.collection.name)
|
self.collection_obj = bpy.data.objects.get(self.collection.name)
|
||||||
self.depth = sum(thicknesses) * unit_scale
|
self.depth = sum(thicknesses) * unit_scale
|
||||||
@@ -211,26 +214,12 @@ class DumbSlabGenerator:
|
|||||||
return self.create_slab(link_to_scene)
|
return self.create_slab(link_to_scene)
|
||||||
|
|
||||||
def create_slab(self, 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)
|
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
|
# Standard cases are deprecated, so let's cull them
|
||||||
ifc_class = [c for c in ifc_classes if "StandardCase" not in c][0]
|
ifc_class = [c for c in ifc_classes if "StandardCase" not in c][0]
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(name="Dumb Slab")
|
mesh = bpy.data.meshes.new("Dummy")
|
||||||
mesh.from_pydata(verts, edges, faces)
|
|
||||||
obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(self.relating_type, ifc_class), mesh)
|
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:
|
if link_to_scene:
|
||||||
obj.location = self.location
|
obj.location = self.location
|
||||||
@@ -239,26 +228,38 @@ class DumbSlabGenerator:
|
|||||||
else:
|
else:
|
||||||
obj.location[2] -= self.depth
|
obj.location[2] -= self.depth
|
||||||
self.collection.objects.link(obj)
|
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_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)
|
ifcopenshell.api.run("type.assign_type", self.file, related_object=element, relating_type=self.relating_type)
|
||||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
|
||||||
|
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")
|
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"})
|
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer3"})
|
||||||
MaterialData.load(self.file)
|
MaterialData.load(self.file)
|
||||||
try:
|
obj.select_set(True)
|
||||||
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")
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
@@ -288,6 +289,9 @@ class DumbSlabPlaner:
|
|||||||
self.change_thickness(element, thickness)
|
self.change_thickness(element, thickness)
|
||||||
|
|
||||||
def regenerate_from_type(self, usecase_path, ifc_file, settings):
|
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)
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||||
new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
|
new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
|
||||||
if not new_material or not new_material.is_a("IfcMaterialLayerSet"):
|
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")
|
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||||
extrusion = self.get_extrusion(body)
|
extrusion = self.get_extrusion(body)
|
||||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
|
||||||
position[0][3] *= self.unit_scale
|
if extrusion.Position:
|
||||||
position[1][3] *= self.unit_scale
|
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||||
position[2][3] *= self.unit_scale
|
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]
|
z_values = [v[2] for v in obj.bound_box]
|
||||||
origin = obj.matrix_world @ Vector((0, 0, max(z_values)))
|
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)
|
self.arcs[-1].append(len(self.vertices) - 1)
|
||||||
is_arc = False
|
is_arc = False
|
||||||
else:
|
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()
|
global_point = position @ Vector(self.convert_unit_to_si(local_point)).to_3d()
|
||||||
self.vertices.append(global_point)
|
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")
|
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||||
extrusion = self.get_extrusion(representation)
|
extrusion = self.get_extrusion(representation)
|
||||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
if extrusion.Position:
|
||||||
position[0][3] *= self.unit_scale
|
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||||
position[1][3] *= self.unit_scale
|
position[0][3] *= self.unit_scale
|
||||||
position[2][3] *= self.unit_scale
|
position[1][3] *= self.unit_scale
|
||||||
|
position[2][3] *= self.unit_scale
|
||||||
|
else:
|
||||||
|
position = Matrix()
|
||||||
|
|
||||||
helper = Helper(tool.Ifc.get())
|
helper = Helper(tool.Ifc.get())
|
||||||
indices = helper.auto_detect_arbitrary_profile_with_voids(obj, obj.data)
|
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):
|
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))
|
x = np.array(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0))
|
||||||
o = plc.Location.Coordinates
|
o = plc.Location.Coordinates
|
||||||
return a2p(o, z, x)
|
return a2p(o, z, x)
|
||||||
|
|||||||
Reference in New Issue
Block a user