CAD sketcher constraints can now be saved and loaded from IFC

This commit is contained in:
Dion Moult
2022-09-30 16:04:16 +10:00
parent 8c7b2ab2a1
commit 22e69a606d
4 changed files with 160 additions and 29 deletions
@@ -49,3 +49,8 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
row.operator("bim.draw_system_arrows") row.operator("bim.draw_system_arrows")
row = layout.row() row = layout.row()
row.operator("bim.clean_wireframes") row.operator("bim.clean_wireframes")
row = layout.row(align=True)
row.operator("bim.enable_editing_sketch_extrusion_profile", text="Start Sketching")
row.operator("bim.edit_sketch_extrusion_profile", text="", icon="FILE_REFRESH")
row.operator("bim.disable_editing_sketch_extrusion_profile", text="", icon="CANCEL")
@@ -41,6 +41,7 @@ classes = (
profile.RecalculateProfile, profile.RecalculateProfile,
profile.Rotate90, profile.Rotate90,
slab.DisableEditingExtrusionProfile, slab.DisableEditingExtrusionProfile,
slab.DisableEditingSketchExtrusionProfile,
slab.EditExtrusionProfile, slab.EditExtrusionProfile,
slab.EditSketchExtrusionProfile, slab.EditSketchExtrusionProfile,
slab.EnableEditingExtrusionProfile, slab.EnableEditingExtrusionProfile,
@@ -21,8 +21,9 @@ import gpu
import blf import blf
import bgl import bgl
import bpy import bpy
import bmesh
import math import math
import json
import bmesh
import ifcopenshell import ifcopenshell
import ifcopenshell.util.type import ifcopenshell.util.type
import ifcopenshell.util.unit import ifcopenshell.util.unit
@@ -138,7 +139,9 @@ class DumbSlabGenerator:
return return
self.body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") 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.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)
@@ -306,7 +309,7 @@ class DumbSlabPlaner:
class EnableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator): class EnableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.xxx" bl_idname = "bim.enable_editing_sketch_extrusion_profile"
bl_label = "Enable Editing Sketch Extrusion Profile" bl_label = "Enable Editing Sketch Extrusion Profile"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -315,13 +318,28 @@ class EnableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator)
return context.selected_objects 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 obj = context.active_object
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric", None)
if pset and pset["Engine"] == "CADSketcher" and pset["Entities"]:
context.scene["sketcher"].update(json.loads(pset["Entities"]))
bpy.context.view_layer.update()
bpy.ops.wm.tool_set_by_id(name="sketcher.slvs_select")
bpy.ops.view3d.slvs_set_all_constraints_visibility(visibility='SHOW')
return {"FINISHED"}
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
extrusion = tool.Model.get_extrusion(body) extrusion = tool.Model.get_extrusion(body)
profile = extrusion.SweptArea profile = extrusion.SweptArea
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist()) 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] z_values = [v[2] for v in obj.bound_box]
@@ -339,26 +357,93 @@ class EnableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator)
wp = entities.add_workplane(origin, normal) wp = entities.add_workplane(origin, normal)
sketch = entities.add_sketch(wp) sketch = entities.add_sketch(wp)
self.vertices = []
self.edges = []
self.arcs = []
self.circles = []
profile = extrusion.SweptArea
if profile.is_a("IfcArbitraryClosedProfileDef"):
self.process_curve(obj, position, profile.OuterCurve)
if profile.is_a("IfcArbitraryProfileDefWithVoids"):
for inner_curve in profile.InnerCurves:
self.process_curve(obj, position, inner_curve)
points = [] points = []
total_points = len(profile.OuterCurve.Points)
last_index = len(profile.OuterCurve.Points) - 1 for vertex in self.vertices:
for i, point in enumerate(profile.OuterCurve.Points): points.append(context.scene.sketcher.entities.add_point_2d(vertex.to_2d(), sketch))
if i == last_index: for edge in self.edges:
continue context.scene.sketcher.entities.add_line_2d(points[edge[0]], points[edge[1]], sketch)
global_point = (position @ Vector(point.Coordinates).to_3d()).to_2d()
p = context.scene.sketcher.entities.add_point_2d(global_point, sketch)
if i != 0:
context.scene.sketcher.entities.add_line_2d(points[-1], p, sketch)
points.append(p)
context.scene.sketcher.entities.add_line_2d(points[-1], points[0], sketch)
bpy.ops.view3d.slvs_set_active_sketch(index=sketch.slvs_index) bpy.ops.view3d.slvs_set_active_sketch(index=sketch.slvs_index)
return {"FINISHED"} return {"FINISHED"}
def process_curve(self, obj, position, curve):
offset = len(self.vertices)
if curve.is_a("IfcPolyline"):
total_points = len(curve.Points)
last_index = len(curve.Points) - 1
for i, point in enumerate(curve.Points):
if i == last_index:
continue
global_point = position @ Vector(self.convert_unit_to_si(point.Coordinates)).to_3d()
self.vertices.append(global_point)
self.edges.extend([(i, i + 1) for i in range(offset, len(self.vertices))])
self.edges[-1] = (len(self.vertices) - 1, offset) # Close the loop
elif curve.is_a("IfcIndexedPolyCurve"):
is_arc = False
if curve.Segments:
for segment in curve.Segments:
if len(segment[0]) == 3: # IfcArcIndex
is_arc = True
local_point = self.convert_unit_to_si(curve.Points.CoordList[segment[0][0] - 1])
global_point = position @ Vector(local_point).to_3d()
self.vertices.append(global_point)
local_point = self.convert_unit_to_si(curve.Points.CoordList[segment[0][1] - 1])
global_point = position @ Vector(local_point).to_3d()
self.vertices.append(global_point)
self.arcs.append([len(self.vertices) - 2, len(self.vertices) - 1])
else:
local_point = self.convert_unit_to_si(curve.Points.CoordList[segment[0][0] - 1])
global_point = position @ Vector(local_point).to_3d()
self.vertices.append(global_point)
if is_arc:
self.arcs[-1].append(len(self.vertices) - 1)
is_arc = False
else:
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)
# Curves without segments are self closing
del self.vertices[-1]
self.edges.extend([(i, i + 1) for i in range(offset, len(self.vertices))])
self.edges[-1] = (len(self.vertices) - 1, offset) # Close the loop
elif curve.is_a("IfcCircle"):
center = self.convert_unit_to_si(
Matrix(ifcopenshell.util.placement.get_axis2placement(curve.Position).tolist()).col[3].to_3d()
)
radius = self.convert_unit_to_si(curve.Radius)
self.vertices.extend(
[
position @ Vector((center[0], center[1] - curve.Radius, 0.0)),
position @ Vector((center[0], center[1] + curve.Radius, 0.0)),
]
)
self.circles.append([offset, offset + 1])
self.edges.append((offset, offset + 1))
def convert_unit_to_si(self, value):
if isinstance(value, (tuple, list)):
return [v * self.unit_scale for v in value]
return value * self.unit_scale
class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator): class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.xxe" bl_idname = "bim.edit_sketch_extrusion_profile"
bl_label = "Edit Extrusion Profile" bl_label = "Edit Sketch Extrusion Profile"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
@@ -367,7 +452,10 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
extrusion = tool.Model.get_extrusion(representation) extrusion = tool.Model.get_extrusion(representation)
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist()) if extrusion.Position:
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
else:
position = Matrix()
position_i = position.inverted() position_i = position.inverted()
cad_sketcher = __import__("CAD_Sketcher-main") cad_sketcher = __import__("CAD_Sketcher-main")
@@ -391,6 +479,18 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bpy.foo = converter.paths bpy.foo = converter.paths
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric", None)
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
else:
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Parametric")
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=pset,
properties={"Engine": "CADSketcher", "Entities": json.dumps(context.scene["sketcher"].to_dict())},
)
bpy.ops.view3d.slvs_set_active_sketch(index=-1) bpy.ops.view3d.slvs_set_active_sketch(index=-1)
workplane = sketch.wp workplane = sketch.wp
p1 = workplane.p1 p1 = workplane.p1
@@ -414,6 +514,30 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"} return {"FINISHED"}
class DisableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_sketch_extrusion_profile"
bl_label = "Disable Editing Sketch Extrusion Profile"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.selected_objects
def _execute(self, context):
sketch = context.scene.sketcher.active_sketch
bpy.ops.view3d.slvs_set_active_sketch(index=-1)
workplane = sketch.wp
p1 = workplane.p1
p1.origin = False
nm = workplane.nm
nm.origin = False
bpy.ops.view3d.slvs_delete_entity(index=sketch["slvs_index"])
bpy.ops.view3d.slvs_delete_entity(index=workplane["slvs_index"])
bpy.ops.view3d.slvs_delete_entity(index=p1["slvs_index"])
bpy.ops.view3d.slvs_delete_entity(index=nm["slvs_index"])
return {"FINISHED"}
class DisableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator): class DisableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_extrusion_profile" bl_idname = "bim.disable_editing_extrusion_profile"
bl_label = "Disable Editing Extrusion Profile" bl_label = "Disable Editing Extrusion Profile"
@@ -468,10 +592,6 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
else: else:
position = Matrix() position = Matrix()
z_values = [v[2] for v in obj.bound_box]
origin = obj.matrix_world @ Vector((0, 0, max(z_values)))
normal = obj.matrix_world.to_quaternion()
self.vertices = [] self.vertices = []
self.edges = [] self.edges = []
self.arcs = [] self.arcs = []
@@ -514,7 +634,7 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
global_point = position @ Vector(self.convert_unit_to_si(point.Coordinates)).to_3d() global_point = position @ Vector(self.convert_unit_to_si(point.Coordinates)).to_3d()
self.vertices.append(global_point) self.vertices.append(global_point)
self.edges.extend([(i, i + 1) for i in range(offset, len(self.vertices))]) self.edges.extend([(i, i + 1) for i in range(offset, len(self.vertices))])
self.edges[-1] = (len(self.vertices) - 1, offset) # Close the loop self.edges[-1] = (len(self.vertices) - 1, offset) # Close the loop
elif curve.is_a("IfcIndexedPolyCurve"): elif curve.is_a("IfcIndexedPolyCurve"):
is_arc = False is_arc = False
if curve.Segments: if curve.Segments:
@@ -539,9 +659,11 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
for local_point in curve.Points.CoordList: 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)
# Curves without segments are self closing
del self.vertices[-1]
self.edges.extend([(i, i + 1) for i in range(offset, len(self.vertices))]) self.edges.extend([(i, i + 1) for i in range(offset, len(self.vertices))])
self.edges[-1] = (len(self.vertices) - 1, offset) # Close the loop self.edges[-1] = (len(self.vertices) - 1, offset) # Close the loop
elif curve.is_a("IfcCircle"): elif curve.is_a("IfcCircle"):
center = self.convert_unit_to_si( center = self.convert_unit_to_si(
Matrix(ifcopenshell.util.placement.get_axis2placement(curve.Position).tolist()).col[3].to_3d() Matrix(ifcopenshell.util.placement.get_axis2placement(curve.Position).tolist()).col[3].to_3d()
@@ -589,8 +711,10 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
indices = helper.auto_detect_arbitrary_profile_with_voids(obj, obj.data) indices = helper.auto_detect_arbitrary_profile_with_voids(obj, obj.data)
if isinstance(indices, tuple) and indices[0] is False: # Ugly if isinstance(indices, tuple) and indices[0] is False: # Ugly
def msg(self, context): def msg(self, context):
self.layout.label(text="INVALID PROFILE: " + indices[1]) self.layout.label(text="INVALID PROFILE: " + indices[1])
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR") bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
DecorationsHandler.install(context) DecorationsHandler.install(context)
bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="EDIT")
@@ -630,7 +754,9 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
should_sync_changes_first=False, should_sync_changes_first=False,
) )
footprint_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "FootPrint", "SKETCH_VIEW") footprint_context = ifcopenshell.util.representation.get_context(
tool.Ifc.get(), "Plan", "FootPrint", "SKETCH_VIEW"
)
if footprint_context: if footprint_context:
curves = [profile.OuterCurve] curves = [profile.OuterCurve]
if profile.is_a("IfcArbitraryProfileDefWithVoids"): if profile.is_a("IfcArbitraryProfileDefWithVoids"):
@@ -638,7 +764,9 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
new_footprint = ifcopenshell.api.run( new_footprint = ifcopenshell.api.run(
"geometry.add_footprint_representation", tool.Ifc.get(), context=footprint_context, curves=curves "geometry.add_footprint_representation", tool.Ifc.get(), context=footprint_context, curves=curves
) )
old_footprint = ifcopenshell.util.representation.get_representation(element, "Plan", "FootPrint", "SKETCH_VIEW") old_footprint = ifcopenshell.util.representation.get_representation(
element, "Plan", "FootPrint", "SKETCH_VIEW"
)
if old_footprint: if old_footprint:
for inverse in tool.Ifc.get().get_inverse(old_footprint): for inverse in tool.Ifc.get().get_inverse(old_footprint):
ifcopenshell.util.element.replace_attribute(inverse, old_footprint, new_footprint) ifcopenshell.util.element.replace_attribute(inverse, old_footprint, new_footprint)
@@ -33,9 +33,6 @@ class BIM_PT_authoring(Panel):
row.operator("bim.align_wall", icon="ANCHOR_TOP", text="Ext.").align_type = "EXTERIOR" row.operator("bim.align_wall", icon="ANCHOR_TOP", text="Ext.").align_type = "EXTERIOR"
row.operator("bim.align_wall", icon="ANCHOR_CENTER", text="C/L").align_type = "CENTERLINE" row.operator("bim.align_wall", icon="ANCHOR_CENTER", text="C/L").align_type = "CENTERLINE"
row.operator("bim.align_wall", icon="ANCHOR_BOTTOM", text="Int.").align_type = "INTERIOR" row.operator("bim.align_wall", icon="ANCHOR_BOTTOM", text="Int.").align_type = "INTERIOR"
row = self.layout.row(align=True)
row.operator("bim.flip_wall", icon="ORIENTATION_NORMAL", text="Flip")
row.operator("bim.split_wall", icon="MOD_PHYSICS", text="Split")
class DisplayConstrTypesUI(Operator): class DisplayConstrTypesUI(Operator):