mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Footprints are now supported in the new profile editor
This commit is contained in:
@@ -70,9 +70,6 @@ def load_post(*args):
|
||||
"type.assign_type", "BlenderBIM.DumbWall.RegenerateFromType", wall.DumbWallPlaner().regenerate_from_type
|
||||
)
|
||||
|
||||
ifcopenshell.api.add_post_listener(
|
||||
"geometry.add_representation", "BlenderBIM.DumbSlab.GenerateFootprint", slab.generate_footprint
|
||||
)
|
||||
ifcopenshell.api.add_post_listener(
|
||||
"geometry.add_representation", "BlenderBIM.DumbSlab.CalculateQuantities", slab.calculate_quantities
|
||||
)
|
||||
|
||||
@@ -43,66 +43,6 @@ from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertForma
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
|
||||
def generate_footprint(usecase_path, ifc_file, settings):
|
||||
footprint_context = ifcopenshell.util.representation.get_context(ifc_file, "Plan", "FootPrint", "SKETCH_VIEW")
|
||||
if not footprint_context:
|
||||
return
|
||||
obj = settings["blender_object"]
|
||||
product = ifc_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.DumbLayer3":
|
||||
return
|
||||
old_footprint = ifcopenshell.util.representation.get_representation(product, "Plan", "FootPrint", "SKETCH_VIEW")
|
||||
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
||||
if old_footprint:
|
||||
blenderbim.core.geometry.remove_representation(
|
||||
tool.Ifc, tool.Geometry, obj=obj, representation=old_footprint
|
||||
)
|
||||
|
||||
helper = Helper(ifc_file)
|
||||
indices = helper.auto_detect_arbitrary_profile_with_voids_extruded_area_solid(settings["geometry"])
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(settings["geometry"])
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
profile_edges = []
|
||||
|
||||
def append_profile_edges(profile_edges, indices):
|
||||
indices.append(indices[0]) # Close the loop
|
||||
edge_vert_pairs = list(zip(indices, indices[1:]))
|
||||
for p in edge_vert_pairs:
|
||||
profile_edges.append(
|
||||
[e for e in bm.verts[p[0]].link_edges if e.other_vert(bm.verts[p[0]]).index == p[1]][0]
|
||||
)
|
||||
|
||||
append_profile_edges(profile_edges, indices["profile"])
|
||||
for inner_indices in indices["inner_curves"]:
|
||||
append_profile_edges(profile_edges, inner_indices)
|
||||
|
||||
irrelevant_edges = [e for e in bm.edges if e not in profile_edges]
|
||||
bmesh.ops.delete(bm, geom=irrelevant_edges, context="EDGES")
|
||||
mesh = bpy.data.meshes.new("Temporary Footprint")
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
|
||||
new_settings = settings.copy()
|
||||
new_settings["context"] = footprint_context
|
||||
new_settings["geometry"] = mesh
|
||||
new_footprint = ifcopenshell.api.run(
|
||||
"geometry.add_representation", ifc_file, should_run_listeners=False, **new_settings
|
||||
)
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation",
|
||||
ifc_file,
|
||||
should_run_listeners=False,
|
||||
**{"product": product, "representation": new_footprint}
|
||||
)
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
|
||||
def calculate_quantities(usecase_path, ifc_file, settings):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
obj = settings["blender_object"]
|
||||
@@ -246,6 +186,7 @@ class DumbSlabGenerator:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation
|
||||
)
|
||||
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
@@ -256,6 +197,22 @@ class DumbSlabGenerator:
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
if self.footprint_context:
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
if extrusion.SweptArea.is_a("IfcArbitraryClosedProfileDef"):
|
||||
curves = [extrusion.SweptArea.OuterCurve]
|
||||
if extrusion.SweptArea.is_a("IfcArbitraryProfileDefWithVoids"):
|
||||
curves.extend(extrusion.SweptArea.InnerCurves)
|
||||
representation = ifcopenshell.api.run(
|
||||
"geometry.add_footprint_representation",
|
||||
tool.Ifc.get(),
|
||||
context=self.footprint_context,
|
||||
curves=curves,
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -332,7 +289,7 @@ class DumbSlabPlaner:
|
||||
obj.location[2] -= delta_thickness
|
||||
|
||||
|
||||
class EnableEditingSketchExtrusionProfile(bpy.types.Operator):
|
||||
class EnableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.xxx"
|
||||
bl_label = "Enable Editing Sketch Extrusion Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -341,12 +298,12 @@ class EnableEditingSketchExtrusionProfile(bpy.types.Operator):
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
extrusion = self.get_extrusion(body)
|
||||
extrusion = tool.Model.get_extrusion(body)
|
||||
profile = extrusion.SweptArea
|
||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||
|
||||
@@ -382,28 +339,18 @@ class EnableEditingSketchExtrusionProfile(bpy.types.Operator):
|
||||
bpy.ops.view3d.slvs_set_active_sketch(index=sketch.slvs_index)
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_extrusion(self, representation):
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
return item
|
||||
elif item.is_a("IfcBooleanClippingResult"):
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
class EditSketchExtrusionProfile(bpy.types.Operator):
|
||||
class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.xxe"
|
||||
bl_label = "Edit Extrusion Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
extrusion = self.get_extrusion(representation)
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||
position_i = position.inverted()
|
||||
|
||||
@@ -450,18 +397,8 @@ class EditSketchExtrusionProfile(bpy.types.Operator):
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_extrusion(self, representation):
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
return item
|
||||
elif item.is_a("IfcBooleanClippingResult"):
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
class DisableEditingExtrusionProfile(bpy.types.Operator):
|
||||
class DisableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_extrusion_profile"
|
||||
bl_label = "Disable Editing Extrusion Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -470,7 +407,7 @@ class DisableEditingExtrusionProfile(bpy.types.Operator):
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
def _execute(self, context):
|
||||
DecorationsHandler.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
@@ -490,7 +427,7 @@ class DisableEditingExtrusionProfile(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingExtrusionProfile(bpy.types.Operator):
|
||||
class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_extrusion_profile"
|
||||
bl_label = "Enable Editing Extrusion Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -499,13 +436,13 @@ class EnableEditingExtrusionProfile(bpy.types.Operator):
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
def _execute(self, context):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
extrusion = self.get_extrusion(body)
|
||||
extrusion = tool.Model.get_extrusion(body)
|
||||
|
||||
if extrusion.Position:
|
||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||
@@ -549,16 +486,6 @@ class EnableEditingExtrusionProfile(bpy.types.Operator):
|
||||
bpy.ops.wm.tool_set_by_id(name="bim.cad_tool")
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_extrusion(self, representation):
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
return item
|
||||
elif item.is_a("IfcBooleanClippingResult"):
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
|
||||
def process_curve(self, obj, position, curve):
|
||||
offset = len(self.vertices)
|
||||
|
||||
@@ -619,12 +546,12 @@ class EnableEditingExtrusionProfile(bpy.types.Operator):
|
||||
return value * self.unit_scale
|
||||
|
||||
|
||||
class EditExtrusionProfile(bpy.types.Operator):
|
||||
class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_extrusion_profile"
|
||||
bl_label = "Edit Extrusion Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
def _execute(self, context):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
DecorationsHandler.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
@@ -633,7 +560,7 @@ class EditExtrusionProfile(bpy.types.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
extrusion = self.get_extrusion(representation)
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
if extrusion.Position:
|
||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||
position[0][3] *= self.unit_scale
|
||||
@@ -686,6 +613,26 @@ class EditExtrusionProfile(bpy.types.Operator):
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
footprint_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "FootPrint", "SKETCH_VIEW")
|
||||
if footprint_context:
|
||||
curves = [profile.OuterCurve]
|
||||
if profile.is_a("IfcArbitraryProfileDefWithVoids"):
|
||||
curves.extend(profile.InnerCurves)
|
||||
new_footprint = ifcopenshell.api.run(
|
||||
"geometry.add_footprint_representation", tool.Ifc.get(), context=footprint_context, curves=curves
|
||||
)
|
||||
old_footprint = ifcopenshell.util.representation.get_representation(element, "Plan", "FootPrint", "SKETCH_VIEW")
|
||||
if old_footprint:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_footprint):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_footprint, new_footprint)
|
||||
blenderbim.core.geometry.remove_representation(
|
||||
tool.Ifc, tool.Geometry, obj=obj, representation=old_footprint
|
||||
)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_footprint
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
def create_points(self, position, indices):
|
||||
@@ -722,16 +669,6 @@ class EditExtrusionProfile(bpy.types.Operator):
|
||||
segments.append(tool.Ifc.get().createIfcArcIndex([i + 1 for i in segment]))
|
||||
return tool.Ifc.get().createIfcIndexedPolyCurve(self.points, segments, False)
|
||||
|
||||
def get_extrusion(self, representation):
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
return item
|
||||
elif item.is_a("IfcBooleanClippingResult"):
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
|
||||
def convert_si_to_unit(self, value):
|
||||
if isinstance(value, (tuple, list)):
|
||||
return [v / self.unit_scale for v in value]
|
||||
|
||||
@@ -35,3 +35,14 @@ class Model(blenderbim.core.tool.Model):
|
||||
return eval(props.occurrence_name_function) or "Instance"
|
||||
except:
|
||||
return "Instance"
|
||||
|
||||
@classmethod
|
||||
def get_extrusion(cls, representation):
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
return item
|
||||
elif item.is_a("IfcBooleanClippingResult"):
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# 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
|
||||
"curves": [], # A list of IFC curves to include in the curve set
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.createIfcShapeRepresentation(
|
||||
self.settings["context"],
|
||||
self.settings["context"].ContextIdentifier,
|
||||
"GeometricCurveSet",
|
||||
[self.file.createIfcGeometricCurveSet(self.settings["curves"])],
|
||||
)
|
||||
Reference in New Issue
Block a user