mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +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):
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user