mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 22:43:41 +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 = (
|
classes = (
|
||||||
operator.ColourByRelatedBuildingElement,
|
operator.ColourByRelatedBuildingElement,
|
||||||
operator.DisableEditingBoundary,
|
operator.DisableEditingBoundary,
|
||||||
|
operator.DisableEditingBoundaryGeometry,
|
||||||
operator.EditBoundaryAttributes,
|
operator.EditBoundaryAttributes,
|
||||||
|
operator.EditBoundaryGeometry,
|
||||||
operator.EnableEditingBoundary,
|
operator.EnableEditingBoundary,
|
||||||
|
operator.EnableEditingBoundaryGeometry,
|
||||||
|
operator.LoadBoundary,
|
||||||
operator.LoadProjectSpaceBoundaries,
|
operator.LoadProjectSpaceBoundaries,
|
||||||
operator.LoadSpaceBoundaries,
|
operator.LoadSpaceBoundaries,
|
||||||
operator.LoadBoundary,
|
|
||||||
operator.SelectRelatedElementBoundaries,
|
|
||||||
operator.SelectProjectBoundaries,
|
operator.SelectProjectBoundaries,
|
||||||
|
operator.SelectRelatedElementBoundaries,
|
||||||
operator.SelectRelatedElementTypeBoundaries,
|
operator.SelectRelatedElementTypeBoundaries,
|
||||||
operator.SelectSpaceBoundaries,
|
operator.SelectSpaceBoundaries,
|
||||||
operator.UpdateBoundaryGeometry,
|
operator.UpdateBoundaryGeometry,
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
import blenderbim.bim.import_ifc as import_ifc
|
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):
|
def get_boundaries_collection(blender_space):
|
||||||
@@ -38,6 +39,20 @@ def get_boundaries_collection(blender_space):
|
|||||||
return boundaries_collection
|
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:
|
class Loader:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ifc_file = None
|
self.ifc_file = None
|
||||||
@@ -376,3 +391,80 @@ class UpdateBoundaryGeometry(bpy.types.Operator):
|
|||||||
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
|
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
|
||||||
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
|
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
|
||||||
return {"FINISHED"}
|
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()
|
cls.bm.free()
|
||||||
return profile
|
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
|
@classmethod
|
||||||
def generate_occurrence_name(cls, element_type, ifc_class):
|
def generate_occurrence_name(cls, element_type, ifc_class):
|
||||||
props = bpy.context.scene.BIMModelProperties
|
props = bpy.context.scene.BIMModelProperties
|
||||||
@@ -218,6 +268,40 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
|
|
||||||
return obj
|
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
|
@classmethod
|
||||||
def import_curve(cls, obj, position, curve):
|
def import_curve(cls, obj, position, curve):
|
||||||
offset = len(cls.vertices)
|
offset = len(cls.vertices)
|
||||||
@@ -232,6 +316,10 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
cls.vertices.append(global_point)
|
cls.vertices.append(global_point)
|
||||||
cls.edges.extend([(i, i + 1) for i in range(offset, len(cls.vertices))])
|
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
|
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"):
|
elif curve.is_a("IfcIndexedPolyCurve"):
|
||||||
is_arc = False
|
is_arc = False
|
||||||
is_closed = False
|
is_closed = False
|
||||||
|
|||||||
Reference in New Issue
Block a user