New split wall tool

This commit is contained in:
Dion Moult
2021-05-27 16:33:34 +10:00
parent afdc1e6679
commit c2684f4fbf
5 changed files with 105 additions and 17 deletions
+9 -9
View File
@@ -55,6 +55,15 @@ def name_callback(obj, data):
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
def active_object_callback():
obj = bpy.context.active_object
for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
if IfcStore.id_map[obj.BIMObjectProperties.ifc_definition_id] != obj:
bpy.ops.bim.copy_class(obj=obj.name)
def subscribe_to(object, data_path, callback):
subscribe_to = object.path_resolve(data_path, False)
bpy.msgbus.subscribe_rna(
@@ -176,15 +185,6 @@ def create_application_organisation(ifc):
)
def active_object_callback():
obj = bpy.context.active_object
for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
if IfcStore.id_map[obj.BIMObjectProperties.ifc_definition_id] != obj:
bpy.ops.bim.copy_class(obj=obj.name)
@persistent
def setDefaultProperties(scene):
global global_subscription_owner
@@ -7,6 +7,7 @@ classes = (
operator.JoinWall,
operator.AlignWall,
operator.FlipWall,
operator.SplitWall,
prop.BIMModelProperties,
ui.BIM_PT_authoring,
ui.BIM_PT_authoring_architectural,
@@ -1,11 +1,12 @@
import bpy
import bmesh
import math
import ifcopenshell
import ifcopenshell.util.type
import ifcopenshell.util.unit
import mathutils.geometry
from blenderbim.bim.ifc import IfcStore
from math import pi
from math import pi, degrees
from mathutils import Vector, Matrix
from ifcopenshell.api.material.data import Data as MaterialData
@@ -144,6 +145,20 @@ class FlipWall(bpy.types.Operator):
return {"FINISHED"}
class SplitWall(bpy.types.Operator):
bl_idname = "bim.split_wall"
bl_label = "Split Wall"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
selected_objs = context.selected_objects
if len(selected_objs) == 0:
return {"FINISHED"}
for obj in selected_objs:
DumbWallSplitter(obj, bpy.context.scene.cursor.location).split()
return {"FINISHED"}
def recalculate_dumb_wall_origin(wall, new_origin=None):
if new_origin is None:
new_origin = wall.matrix_world @ Vector(wall.bound_box[0])
@@ -157,6 +172,68 @@ def recalculate_dumb_wall_origin(wall, new_origin=None):
wall.matrix_world.translation = new_origin
class DumbWallSplitter:
def __init__(self, wall, point):
self.wall = wall
self.point = point
def split(self):
recalculate_dumb_wall_origin(self.wall)
self.point = self.determine_split_point()
if not self.point:
return
new_wall = self.duplicate_wall()
self.snap_end_face_to_point(self.wall, "max")
self.snap_end_face_to_point(new_wall, "min")
def determine_split_point(self):
start = self.wall.matrix_world @ Vector(self.wall.bound_box[0])
end = self.wall.matrix_world @ Vector(self.wall.bound_box[4])
point, distance = mathutils.geometry.intersect_point_line(self.point, start, end)
if round(distance, 2) <= 0 or round(distance, 2) >= 1:
return # The split point is not on the wall
return point
def duplicate_wall(self):
new = self.wall.copy()
self.wall.users_collection[0].objects.link(new)
bpy.ops.bim.copy_class(obj=new.name)
return new
def snap_end_face_to_point(self, wall, which_end):
bm = bmesh.new()
bm.from_mesh(wall.data)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
min_face, max_face = self.get_wall_end_faces(wall, bm)
face = min_face if which_end == "min" else max_face
local_point = wall.matrix_world.inverted() @ self.point
for vert in face.verts:
vert.co.x = local_point.x
bm.to_mesh(wall.data)
wall.data.update()
bm.free()
IfcStore.edited_objs.add(wall)
# An end face is a quad that is on one end of the wall or the other. It must
# have at least one vertex on either extreme X-axis, and a non-insignificant
# X component of its face normal
def get_wall_end_faces(self, wall, bm):
min_face = None
max_face = None
min_x = min([v[0] for v in wall.bound_box])
max_x = max([v[0] for v in wall.bound_box])
bm.faces.ensure_lookup_table()
for f in bm.faces:
for v in f.verts:
if v.co.x == min_x and abs(f.normal.x) > 0.1:
min_face = f
elif v.co.x == max_x and abs(f.normal.x) > 0.1:
max_face = f
if min_face and max_face:
break
return min_face, max_face
class DumbWallFlipper:
# A flip switches the origin from the min XY corner to the max XY corner, and rotates the origin by 180.
def __init__(self, wall):
@@ -243,14 +320,22 @@ class DumbWallAligner:
self.wall.matrix_world.translation[1] = new_origin[1]
def align_rotation(self):
reference_rotation = self.reference_wall.rotation_euler[2] % (2 * pi)
self.wall.rotation_euler[2] = (pi * round(self.wall.rotation_euler[2] / pi)) + reference_rotation
reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
angle = reference.angle_signed(wall)
if round(degrees(angle) % 360) in (0, 180):
return
elif angle > (pi / 2):
self.wall.rotation_euler[2] -= pi - angle
else:
self.wall.rotation_euler[2] += angle
bpy.context.view_layer.update()
def is_rotation_flipped(self):
pi2 = pi * 2
wall_delta_to_nearest_360 = abs((pi2 * round(self.wall.rotation_euler[2] / pi2)) - self.wall.rotation_euler[2])
reference_delta_to_nearest_360 = abs((pi2 * round(self.reference_wall.rotation_euler[2] / pi2)) - self.reference_wall.rotation_euler[2])
return abs(reference_delta_to_nearest_360 - wall_delta_to_nearest_360) > 0.1
reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
angle = reference.angle_signed(wall)
return round(degrees(angle) % 360) == 180
class DumbWallJoiner:
@@ -38,6 +38,7 @@ class BIM_PT_authoring_architectural(Panel):
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 BIM_PT_misc_utilities(Panel):
@@ -20,6 +20,7 @@ class WallTool(WorkSpaceTool):
("bim.join_wall", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("join_type", "L")]}),
("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}),
("bim.flip_wall", {"type": "F", "value": "PRESS", "shift": True}, {"properties": []}),
("bim.split_wall", {"type": "S", "value": "PRESS", "shift": True}, {"properties": []}),
(
"bim.align_wall",
{"type": "X", "value": "PRESS", "shift": True},