mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Add profile-style (like slab editing) enable editing, edit, and disable editing operators for energy boundary surface geometry.
This commit is contained in:
@@ -22,13 +22,16 @@ from . import ui, operator, prop
|
||||
classes = (
|
||||
operator.ColourByRelatedBuildingElement,
|
||||
operator.DisableEditingBoundary,
|
||||
operator.DisableEditingBoundaryGeometry,
|
||||
operator.EditBoundaryAttributes,
|
||||
operator.EditBoundaryGeometry,
|
||||
operator.EnableEditingBoundary,
|
||||
operator.EnableEditingBoundaryGeometry,
|
||||
operator.LoadBoundary,
|
||||
operator.LoadProjectSpaceBoundaries,
|
||||
operator.LoadSpaceBoundaries,
|
||||
operator.LoadBoundary,
|
||||
operator.SelectRelatedElementBoundaries,
|
||||
operator.SelectProjectBoundaries,
|
||||
operator.SelectRelatedElementBoundaries,
|
||||
operator.SelectRelatedElementTypeBoundaries,
|
||||
operator.SelectSpaceBoundaries,
|
||||
operator.UpdateBoundaryGeometry,
|
||||
|
||||
@@ -24,8 +24,9 @@ import ifcopenshell.api
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.bim.import_ifc as import_ifc
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
|
||||
|
||||
def get_boundaries_collection(blender_space):
|
||||
@@ -38,6 +39,20 @@ def get_boundaries_collection(blender_space):
|
||||
return boundaries_collection
|
||||
|
||||
|
||||
def disable_editing_boundary_geometry(context):
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
old_mesh = obj.data
|
||||
loader = Loader()
|
||||
obj.data = loader.create_mesh(element)
|
||||
tool.Geometry.delete_data(old_mesh)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class Loader:
|
||||
def __init__(self):
|
||||
self.ifc_file = None
|
||||
@@ -376,3 +391,80 @@ class UpdateBoundaryGeometry(bpy.types.Operator):
|
||||
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
|
||||
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_boundary_geometry"
|
||||
bl_label = "Enable Editing Boundary Geometry"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
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)
|
||||
|
||||
if element.ConnectionGeometry.is_a("IfcConnectionSurfaceGeometry"):
|
||||
surface = element.ConnectionGeometry.SurfaceOnRelatingElement
|
||||
tool.Model.import_surface(surface, obj)
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_boundary_geometry(context))
|
||||
if not bpy.app.background:
|
||||
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_boundary_geometry"
|
||||
bl_label = "Edit Boundary Geometry"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
if element.ConnectionGeometry.is_a("IfcConnectionSurfaceGeometry"):
|
||||
surface = tool.Model.export_surface(obj)
|
||||
|
||||
if not surface:
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="INVALID PROFILE")
|
||||
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
ProfileDecorator.install(
|
||||
context, exit_edit_mode_callback=lambda: disable_editing_boundary_geometry(context)
|
||||
)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
return
|
||||
|
||||
old_surface = element.ConnectionGeometry.SurfaceOnRelatingElement
|
||||
for inverse in tool.Ifc.get().get_inverse(old_surface):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_surface, surface)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_surface)
|
||||
|
||||
old_mesh = obj.data
|
||||
loader = Loader()
|
||||
obj.data = loader.create_mesh(element)
|
||||
tool.Geometry.delete_data(old_mesh)
|
||||
|
||||
|
||||
class DisableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_boundary_geometry"
|
||||
bl_label = "Disable Editing Boundary Geometry"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
return disable_editing_boundary_geometry(context)
|
||||
|
||||
@@ -115,6 +115,56 @@ class Model(blenderbim.core.tool.Model):
|
||||
cls.bm.free()
|
||||
return profile
|
||||
|
||||
@classmethod
|
||||
def export_surface(cls, obj):
|
||||
p1, p2, p3 = [v.co.copy() for v in obj.data.vertices[0:3]]
|
||||
|
||||
edge1 = p2 - p1
|
||||
edge2 = p3 - p1
|
||||
normal = edge1.cross(edge2)
|
||||
z_axis = normal.normalized()
|
||||
x_axis = p2 - p1
|
||||
x_axis.normalize()
|
||||
y_axis = z_axis.cross(x_axis)
|
||||
|
||||
position = Matrix()
|
||||
position.col[0][:3] = x_axis
|
||||
position.col[1][:3] = y_axis
|
||||
position.col[2][:3] = z_axis
|
||||
position.col[3][:3] = p1
|
||||
|
||||
cls.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
helper = Helper(tool.Ifc.get())
|
||||
indices = helper.auto_detect_arbitrary_profile_with_voids(obj, obj.data)
|
||||
|
||||
if isinstance(indices, tuple) and indices[0] is False: # Ugly
|
||||
return
|
||||
|
||||
cls.bm = bmesh.new()
|
||||
cls.bm.from_mesh(obj.data)
|
||||
cls.bm.verts.ensure_lookup_table()
|
||||
cls.bm.edges.ensure_lookup_table()
|
||||
|
||||
surface = tool.Ifc.get().createIfcCurveBoundedPlane()
|
||||
surface.BasisSurface = tool.Ifc.get().createIfcPlane(tool.Ifc.get().createIfcAxis2Placement3D(
|
||||
tool.Ifc.get().createIfcCartesianPoint([o / cls.unit_scale for o in p1]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
|
||||
))
|
||||
|
||||
if tool.Ifc.get().schema != "IFC2X3":
|
||||
cls.points = cls.export_points(position, indices["points"])
|
||||
|
||||
surface.OuterBoundary = cls.export_curve(position, indices["profile"])
|
||||
results = []
|
||||
for inner_curve in indices["inner_curves"]:
|
||||
results.append(cls.export_curve(position, inner_curve))
|
||||
surface.InnerBoundaries = results
|
||||
|
||||
cls.bm.free()
|
||||
return surface
|
||||
|
||||
@classmethod
|
||||
def generate_occurrence_name(cls, element_type, ifc_class):
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
@@ -218,6 +268,40 @@ class Model(blenderbim.core.tool.Model):
|
||||
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_surface(cls, surface, obj=None):
|
||||
cls.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
cls.vertices = []
|
||||
cls.edges = []
|
||||
cls.arcs = []
|
||||
cls.circles = []
|
||||
|
||||
if surface.is_a("IfcCurveBoundedPlane"):
|
||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist())
|
||||
cls.import_curve(obj, position, surface.OuterBoundary)
|
||||
for inner_boundary in surface.InnerBoundaries:
|
||||
cls.import_curve(obj, position, inner_boundary)
|
||||
|
||||
mesh = bpy.data.meshes.new("Surface")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.subshape_type = "PROFILE"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Surface", mesh)
|
||||
else:
|
||||
obj.data = mesh
|
||||
|
||||
for arc in cls.arcs:
|
||||
group = obj.vertex_groups.new(name="IFCARCINDEX")
|
||||
group.add(arc, 1, "REPLACE")
|
||||
|
||||
for circle in cls.circles:
|
||||
group = obj.vertex_groups.new(name="IFCCIRCLE")
|
||||
group.add(circle, 1, "REPLACE")
|
||||
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_curve(cls, obj, position, curve):
|
||||
offset = len(cls.vertices)
|
||||
@@ -232,6 +316,10 @@ class Model(blenderbim.core.tool.Model):
|
||||
cls.vertices.append(global_point)
|
||||
cls.edges.extend([(i, i + 1) for i in range(offset, len(cls.vertices))])
|
||||
cls.edges[-1] = (len(cls.vertices) - 1, offset) # Close the loop
|
||||
elif curve.is_a("IfcCompositeCurve"):
|
||||
# This is a first pass incomplete implementation only for simple polylines, and misses many details.
|
||||
for segment in curve.Segments:
|
||||
cls.import_curve(obj, position, segment.ParentCurve)
|
||||
elif curve.is_a("IfcIndexedPolyCurve"):
|
||||
is_arc = False
|
||||
is_closed = False
|
||||
|
||||
Reference in New Issue
Block a user