mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-28 15:53:00 +00:00
Major refactor in polyline tool to use a base class for other operators.
Polyline Wall and Measure tool now share most of the logic by inheriting from a base class PolylineOperator. This should facilitate the implementation of other tools that use polyline.
This commit is contained in:
committed by
Bruno Perdigão
parent
1a587dea7d
commit
c06929e1c7
@@ -0,0 +1,337 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2024 Bruno Perdigão <contact@brunopo.com>
|
||||
#
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# Bonsai is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Bonsai 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import copy
|
||||
import math
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.type
|
||||
import mathutils.geometry
|
||||
import bonsai.core.type
|
||||
import bonsai.core.root
|
||||
import bonsai.core.geometry
|
||||
import bonsai.core.model as core
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from math import pi, sin, cos, degrees
|
||||
from mathutils import Vector, Matrix
|
||||
from bonsai.bim.module.model.opening import FilledOpeningGenerator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||
from typing import Optional
|
||||
from lark import Lark, Transformer
|
||||
|
||||
|
||||
class PolylineOperator:
|
||||
# TODO Fill doc strings
|
||||
""" """
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.space_data.type == "VIEW_3D"
|
||||
|
||||
def __init__(self):
|
||||
self.mousemove_count = 0
|
||||
self.action_count = 0
|
||||
self.visible_objs = []
|
||||
self.objs_2d_bbox = []
|
||||
self.number_options = {
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
" ",
|
||||
".",
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"'",
|
||||
'"',
|
||||
"=",
|
||||
}
|
||||
self.number_input = []
|
||||
self.number_output = ""
|
||||
self.number_is_negative = False
|
||||
self.input_options = ["D", "A", "X", "Y"]
|
||||
self.input_type = None
|
||||
self.input_type = None
|
||||
self.input_value_xy = [None, None]
|
||||
self.input_ui = tool.Polyline.create_input_ui()
|
||||
self.is_typing = False
|
||||
self.snap_angle = None
|
||||
self.snapping_points = []
|
||||
self.instructions = """TAB: Cycle Input
|
||||
M: Modify Snap Point
|
||||
C: Close
|
||||
Backspace: Remove
|
||||
X Y: Axis
|
||||
Shift: Lock axis
|
||||
"""
|
||||
self.tool_state = tool.Polyline.create_tool_state()
|
||||
|
||||
def recalculate_inputs(self, context):
|
||||
if self.number_input:
|
||||
is_valid, self.number_output = tool.Polyline.validate_input(self.number_output, self.input_type)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
if not is_valid:
|
||||
self.report({"WARNING"}, "The number typed is not valid.")
|
||||
return is_valid
|
||||
else:
|
||||
if self.input_type in {"X", "Y"}:
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
elif self.input_type in {"D", "A"}:
|
||||
tool.Polyline.calculate_x_y_and_z(context, self.input_ui, self.tool_state)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
else:
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
tool.Blender.update_viewport()
|
||||
return is_valid
|
||||
|
||||
def choose_axis(self, event, x=True, y=True, z=False):
|
||||
if x:
|
||||
if event.value == "PRESS" and event.type == "X":
|
||||
self.tool_state.axis_method = "X" if self.tool_state.axis_method != event.type else None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if y:
|
||||
if event.value == "PRESS" and event.type == "Y":
|
||||
self.tool_state.axis_method = "Y" if self.tool_state.axis_method != event.type else None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
if z:
|
||||
if event.value == "PRESS" and event.type == "Z":
|
||||
self.tool_state.axis_method = "Z" if self.tool_state.axis_method != event.type else None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def choose_plane(self, event, x=True, y=True, z=True):
|
||||
if x:
|
||||
if event.shift and event.value == "PRESS" and event.type == "X":
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = "YZ"
|
||||
self.tool_state.axis_method = None
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if y:
|
||||
if event.shift and event.value == "PRESS" and event.type == "Y":
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = "XZ"
|
||||
self.tool_state.axis_method = None
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if z:
|
||||
if event.shift and event.value == "PRESS" and event.type == "Z":
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = "XY"
|
||||
self.tool_state.axis_method = None
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def handle_keyboard_input(self, context, event):
|
||||
|
||||
if self.tool_state.is_input_on and event.value == "PRESS" and event.type == "TAB":
|
||||
self.recalculate_inputs(context)
|
||||
index = self.input_options.index(self.input_type)
|
||||
size = len(self.input_options)
|
||||
self.input_type = self.input_options[((index + 1) % size)]
|
||||
self.tool_state.input_type = self.input_options[((index + 1) % size)]
|
||||
self.tool_state.mode = "Select"
|
||||
self.is_typing = False
|
||||
self.number_input = self.input_ui.get_formatted_value(self.input_type)
|
||||
self.number_input = list(self.number_input)
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.tool_state.is_input_on and event.value == "RELEASE" and event.type == "TAB":
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Select"
|
||||
self.tool_state.is_input_on = True
|
||||
self.input_type = "D"
|
||||
self.tool_state.input_type = "D"
|
||||
self.is_typing = False
|
||||
self.number_input = self.input_ui.get_formatted_value(self.input_type)
|
||||
self.number_input = list(self.number_input)
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.tool_state.is_input_on and event.ascii in self.number_options:
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Edit"
|
||||
self.tool_state.is_input_on = True
|
||||
self.input_type = "D"
|
||||
self.tool_state.input_type = "D"
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "RELEASE" and event.type in {"D", "A"}:
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Edit"
|
||||
self.tool_state.is_input_on = True
|
||||
self.input_type = event.type
|
||||
self.tool_state.input_type = event.type
|
||||
self.input_ui.set_value(self.input_type, "")
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if self.input_type in self.input_options:
|
||||
if (event.ascii in self.number_options) or (event.value == "RELEASE" and event.type == "BACK_SPACE"):
|
||||
if not self.tool_state.mode == "Edit" and not (event.ascii == "=" or event.type == "BACK_SPACE"):
|
||||
self.number_input = []
|
||||
|
||||
if event.type == "BACK_SPACE":
|
||||
if len(self.number_input) <= 1:
|
||||
self.number_input = []
|
||||
else:
|
||||
self.number_input.pop(-1)
|
||||
elif event.ascii == "=":
|
||||
if self.number_input[0] == "=":
|
||||
self.number_input.pop(0)
|
||||
else:
|
||||
self.number_input.insert(0, "=")
|
||||
else:
|
||||
self.number_input.append(event.ascii)
|
||||
|
||||
if not self.number_input:
|
||||
self.number_output = "0"
|
||||
|
||||
self.tool_state.mode = "Edit"
|
||||
self.is_typing = True
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def handle_inserting_polyline(self, context, event):
|
||||
if event.value == "RELEASE" and event.type == "LEFTMOUSE":
|
||||
tool.Snap.insert_polyline_point(self.input_ui)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "C":
|
||||
tool.Snap.close_polyline()
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if (
|
||||
self.tool_state.is_input_on
|
||||
and event.value == "RELEASE"
|
||||
and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}
|
||||
):
|
||||
is_valid = self.recalculate_inputs(context)
|
||||
if is_valid:
|
||||
tool.Snap.insert_polyline_point(self.input_ui)
|
||||
self.tool_state.mode = "Mouse"
|
||||
self.tool_state.is_input_on = False
|
||||
self.input_type = None
|
||||
self.tool_state.input_type = None
|
||||
self.number_input = []
|
||||
self.number_output = ""
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def handle_snap_selection(self, context, event):
|
||||
if event.value == "PRESS" and event.type == "M":
|
||||
self.snapping_points = tool.Snap.modify_snapping_point_selection(self.snapping_points)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def handle_cancelation(self, context, event):
|
||||
if self.tool_state.is_input_on:
|
||||
if event.value == "RELEASE" and event.type in {"ESC"}:
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Mouse"
|
||||
self.tool_state.is_input_on = False
|
||||
self.input_type = None
|
||||
self.tool_state.input_type = None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
if event.value == "RELEASE" and event.type in {"ESC"}:
|
||||
self.tool_state.axis_method = None
|
||||
PolylineDecorator.uninstall()
|
||||
tool.Snap.clear_polyline()
|
||||
tool.Blender.update_viewport()
|
||||
return {"CANCELLED"}
|
||||
|
||||
def handle_mouse_move(self, context, event):
|
||||
if not self.tool_state.is_input_on:
|
||||
if event.type == "MOUSEMOVE" or event.type == "INBETWEEN_MOUSEMOVE":
|
||||
self.mousemove_count += 1
|
||||
self.tool_state.mode = "Mouse"
|
||||
self.tool_state.is_input_on = False
|
||||
self.input_type = None
|
||||
self.tool_state.input_type = None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Snap.clear_snapping_ref()
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
self.mousemove_count = 0
|
||||
|
||||
if self.mousemove_count == 2:
|
||||
self.objs_2d_bbox = []
|
||||
for obj in self.visible_objs:
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
|
||||
|
||||
if self.mousemove_count > 3:
|
||||
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox, self.tool_state)
|
||||
self.snapping_points = tool.Snap.select_snapping_points(context, event, self.tool_state, detected_snaps)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
tool.Blender.update_viewport()
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
if event.value == "RELEASE" and event.type == "BACK_SPACE":
|
||||
tool.Snap.remove_last_polyline_point()
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
PolylineDecorator.install(context)
|
||||
tool.Snap.clear_snapping_point()
|
||||
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.axis_method = None
|
||||
self.tool_state.plane_method = None
|
||||
self.tool_state.mode = "Mouse"
|
||||
tool.Snap.set_tool_state(self.tool_state)
|
||||
self.visible_objs = tool.Raycast.get_visible_objects(context)
|
||||
for obj in self.visible_objs:
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
|
||||
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox, self.tool_state)
|
||||
self.snapping_points = tool.Snap.select_snapping_points(context, event, self.tool_state, detected_snaps)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
context.window_manager.modal_handler_add(self)
|
||||
# return {"RUNNING_MODAL"}
|
||||
@@ -38,6 +38,7 @@ from math import pi, sin, cos, degrees
|
||||
from mathutils import Vector, Matrix
|
||||
from bonsai.bim.module.model.opening import FilledOpeningGenerator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||
from typing import Optional
|
||||
from lark import Lark, Transformer
|
||||
|
||||
@@ -284,7 +285,7 @@ def recalculate_dumb_wall_origin(wall, new_origin=None):
|
||||
child.matrix_parent_inverse = wall.matrix_world.inverted()
|
||||
|
||||
|
||||
class DrawPolylineWall(bpy.types.Operator):
|
||||
class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
|
||||
bl_idname = "bim.draw_polyline_wall"
|
||||
bl_label = "Draw Polyline Wall"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -294,68 +295,7 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
return context.space_data.type == "VIEW_3D"
|
||||
|
||||
def __init__(self):
|
||||
self.mousemove_count = 0
|
||||
self.action_count = 0
|
||||
self.visible_objs = []
|
||||
self.objs_2d_bbox = []
|
||||
self.number_options = {
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
" ",
|
||||
".",
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"'",
|
||||
'"',
|
||||
"=",
|
||||
}
|
||||
self.number_input = []
|
||||
self.number_output = ""
|
||||
self.number_is_negative = False
|
||||
self.input_options = ["D", "A", "X", "Y"]
|
||||
self.input_type = None
|
||||
self.input_type = None
|
||||
self.input_value_xy = [None, None]
|
||||
self.input_ui = tool.Polyline.create_input_ui()
|
||||
self.is_typing = False
|
||||
self.snap_angle = None
|
||||
self.snapping_points = []
|
||||
self.instructions = """TAB: Cycle Input
|
||||
M: Modify Snap Point
|
||||
C: Close
|
||||
Backspace: Remove
|
||||
X Y: Axis
|
||||
Shift: Lock axis
|
||||
"""
|
||||
self.tool_state = tool.Polyline.create_tool_state()
|
||||
|
||||
def recalculate_inputs(self, context):
|
||||
if self.number_input:
|
||||
is_valid, self.number_output = tool.Polyline.validate_input(self.number_output, self.input_type)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
if not is_valid:
|
||||
self.report({"WARNING"}, "The number typed is not valid.")
|
||||
return is_valid
|
||||
else:
|
||||
if self.input_type in {"X", "Y"}:
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
elif self.input_type in {"D", "A"}:
|
||||
tool.Polyline.calculate_x_y_and_z(context, self.input_ui, self.tool_state)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
else:
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
tool.Blender.update_viewport()
|
||||
return is_valid
|
||||
super().__init__()
|
||||
|
||||
# TODO This is creating a hack in generate function from DumbWallGenerator
|
||||
# Come up with a better solution
|
||||
@@ -384,131 +324,14 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
DumbWallJoiner().join_V(wall1["obj"], wall2["obj"])
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
if not self.tool_state.is_input_on:
|
||||
if event.type == "MOUSEMOVE" or event.type == "INBETWEEN_MOUSEMOVE":
|
||||
self.mousemove_count += 1
|
||||
self.tool_state.mode = "Mouse"
|
||||
self.tool_state.is_input_on = False
|
||||
self.input_type = None
|
||||
self.tool_state.input_type = None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Snap.clear_snapping_ref()
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
self.mousemove_count = 0
|
||||
self.handle_mouse_move(context, event)
|
||||
|
||||
if self.mousemove_count == 2:
|
||||
self.objs_2d_bbox = []
|
||||
for obj in self.visible_objs:
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
|
||||
self.choose_axis(event)
|
||||
|
||||
if self.mousemove_count > 3:
|
||||
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox, self.tool_state)
|
||||
self.snapping_points = tool.Snap.select_snapping_points(context, event, self.tool_state, detected_snaps)
|
||||
print(self.snapping_points)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
tool.Blender.update_viewport()
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
if event.value == "RELEASE" and event.type == "BACK_SPACE":
|
||||
tool.Snap.remove_last_polyline_point()
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "RELEASE" and event.type == "LEFTMOUSE":
|
||||
tool.Snap.insert_polyline_point(self.input_ui)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "X":
|
||||
self.tool_state.axis_method = "X" if self.tool_state.axis_method != event.type else None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "Y":
|
||||
self.tool_state.axis_method = "Y" if self.tool_state.axis_method != event.type else None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "C":
|
||||
tool.Snap.close_polyline()
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if self.tool_state.is_input_on and event.value == "PRESS" and event.type == "TAB":
|
||||
self.recalculate_inputs(context)
|
||||
index = self.input_options.index(self.input_type)
|
||||
size = len(self.input_options)
|
||||
self.input_type = self.input_options[((index + 1) % size)]
|
||||
self.tool_state.input_type = self.input_options[((index + 1) % size)]
|
||||
self.tool_state.mode = "Select"
|
||||
self.is_typing = False
|
||||
self.number_input = self.input_ui.get_formatted_value(self.input_type)
|
||||
self.number_input = list(self.number_input)
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.tool_state.is_input_on and event.value == "RELEASE" and event.type == "TAB":
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Select"
|
||||
self.tool_state.is_input_on = True
|
||||
self.input_type = "D"
|
||||
self.tool_state.input_type = "D"
|
||||
self.is_typing = False
|
||||
self.number_input = self.input_ui.get_formatted_value(self.input_type)
|
||||
self.number_input = list(self.number_input)
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.tool_state.is_input_on and event.ascii in self.number_options:
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Edit"
|
||||
self.tool_state.is_input_on = True
|
||||
self.input_type = "D"
|
||||
self.tool_state.input_type = "D"
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "RELEASE" and event.type in {"D", "A"}:
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Edit"
|
||||
self.tool_state.is_input_on = True
|
||||
self.input_type = event.type
|
||||
self.tool_state.input_type = event.type
|
||||
self.input_ui.set_value(self.input_type, "")
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if self.input_type in self.input_options:
|
||||
if (event.ascii in self.number_options) or (event.value == "RELEASE" and event.type == "BACK_SPACE"):
|
||||
if not self.tool_state.mode == "Edit" and not (event.ascii == "=" or event.type == "BACK_SPACE"):
|
||||
self.number_input = []
|
||||
|
||||
if event.type == "BACK_SPACE":
|
||||
if len(self.number_input) <= 1:
|
||||
self.number_input = []
|
||||
else:
|
||||
self.number_input.pop(-1)
|
||||
elif event.ascii == "=":
|
||||
if self.number_input[0] == "=":
|
||||
self.number_input.pop(0)
|
||||
else:
|
||||
self.number_input.insert(0, "=")
|
||||
else:
|
||||
self.number_input.append(event.ascii)
|
||||
|
||||
if not self.number_input:
|
||||
self.number_output = "0"
|
||||
|
||||
self.tool_state.mode = "Edit"
|
||||
self.is_typing = True
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
self.handle_snap_selection(context, event)
|
||||
|
||||
if not self.tool_state.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}:
|
||||
self.create_walls_from_polyline(context)
|
||||
@@ -517,72 +340,21 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
tool.Blender.update_viewport()
|
||||
return {"FINISHED"}
|
||||
|
||||
if self.tool_state.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}:
|
||||
is_valid = self.recalculate_inputs(context)
|
||||
if is_valid:
|
||||
tool.Snap.insert_polyline_point(self.input_ui)
|
||||
self.tool_state.mode = "Mouse"
|
||||
self.tool_state.is_input_on = False
|
||||
self.input_type = None
|
||||
self.tool_state.input_type = None
|
||||
self.number_input = []
|
||||
self.number_output = ""
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
self.handle_keyboard_input(context, event)
|
||||
|
||||
if event.value == "PRESS" and event.type == "M":
|
||||
self.snapping_points = tool.Snap.modify_snapping_point_selection(self.snapping_points)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
self.handle_inserting_polyline(context, event)
|
||||
|
||||
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
|
||||
return {"PASS_THROUGH"}
|
||||
result = self.handle_cancelation(context, event)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
if self.tool_state.is_input_on:
|
||||
if event.value == "RELEASE" and event.type in {"ESC"}:
|
||||
self.recalculate_inputs(context)
|
||||
self.tool_state.mode = "Mouse"
|
||||
self.tool_state.is_input_on = False
|
||||
self.input_type = None
|
||||
self.tool_state.input_type = None
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
if event.value == "RELEASE" and event.type in {"ESC"}:
|
||||
self.tool_state.axis_method = None
|
||||
PolylineDecorator.uninstall()
|
||||
tool.Snap.clear_polyline()
|
||||
tool.Blender.update_viewport()
|
||||
return {"CANCELLED"}
|
||||
|
||||
print(">>> ", self.tool_state.mode)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
if context.space_data.type == "VIEW_3D":
|
||||
PolylineDecorator.install(context)
|
||||
tool.Snap.clear_snapping_point()
|
||||
|
||||
self.tool_state.use_default_container = True
|
||||
self.tool_state.axis_method = None
|
||||
self.tool_state.plane_method = "XY"
|
||||
self.tool_state.mode = "Mouse"
|
||||
tool.Snap.set_tool_state(self.tool_state)
|
||||
self.visible_objs = tool.Raycast.get_visible_objects(context)
|
||||
for obj in self.visible_objs:
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
|
||||
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox, self.tool_state)
|
||||
self.snapping_points = tool.Snap.select_snapping_points(context, event, self.tool_state, detected_snaps)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
else:
|
||||
self.report({"WARNING"}, "Active space must be a View3d")
|
||||
return {"CANCELLED"}
|
||||
super().invoke(context, event)
|
||||
self.tool_state.use_default_container = True
|
||||
self.tool_state.plane_method = "XY"
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class DumbWallAligner:
|
||||
|
||||
@@ -53,6 +53,7 @@ from ifcopenshell.geom import ShapeElementType
|
||||
from bonsai.bim.module.project.data import LinksData
|
||||
from bonsai.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||
from typing import Union
|
||||
|
||||
|
||||
@@ -2268,7 +2269,7 @@ if bpy.app.version >= (4, 1, 0):
|
||||
return True
|
||||
|
||||
|
||||
class MeasureTool(bpy.types.Operator):
|
||||
class MeasureTool(bpy.types.Operator, PolylineOperator):
|
||||
bl_idname = "bim.measure_tool"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = "Measure Tool"
|
||||
@@ -2278,42 +2279,9 @@ class MeasureTool(bpy.types.Operator):
|
||||
return context.space_data.type == "VIEW_3D"
|
||||
|
||||
def __init__(self):
|
||||
self.mousemove_count = 0
|
||||
self.action_count = 0
|
||||
self.visible_objs = []
|
||||
self.objs_2d_bbox = []
|
||||
self.number_options = {
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
" ",
|
||||
".",
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"'",
|
||||
'"',
|
||||
"=",
|
||||
}
|
||||
self.number_input = []
|
||||
self.number_output = ""
|
||||
self.number_is_negative = False
|
||||
self.is_input_on = False
|
||||
self.input_options = ["D", "A", "X", "Y", "Z"]
|
||||
self.input_type = None
|
||||
self.input_value_xy = [None, None]
|
||||
super().__init__()
|
||||
self.input_ui = tool.Polyline.create_input_ui(init_z=True)
|
||||
self.is_typing = False
|
||||
self.snap_angle = None
|
||||
self.snapping_points = []
|
||||
self.input_options = ["D", "A", "X", "Y", "Z"]
|
||||
self.instructions = """TAB: Cycle Input
|
||||
M: Modify Snap Point
|
||||
C: Close
|
||||
@@ -2323,228 +2291,34 @@ class MeasureTool(bpy.types.Operator):
|
||||
Shift: Lock axis
|
||||
"""
|
||||
|
||||
def recalculate_inputs(self, context):
|
||||
if self.number_input:
|
||||
is_valid, self.number_output = tool.Polyline.validate_input(self.number_output, self.input_type)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
if not is_valid:
|
||||
self.report({"WARNING"}, "The number typed is not valid.")
|
||||
return is_valid
|
||||
else:
|
||||
if self.input_type in {"X", "Y", "Z"}:
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
|
||||
elif self.input_type in {"D", "A"}:
|
||||
tool.Polyline.calculate_x_y_and_z(context, self.input_ui)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
|
||||
else:
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
tool.Blender.update_viewport()
|
||||
return is_valid
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
if not self.is_input_on:
|
||||
if event.type == "MOUSEMOVE" or event.type == "INBETWEEN_MOUSEMOVE":
|
||||
self.mousemove_count += 1
|
||||
self.is_input_on = False
|
||||
self.input_type = None
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Snap.clear_snapping_ref()
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
self.mousemove_count = 0
|
||||
self.handle_mouse_move(context, event)
|
||||
|
||||
if self.mousemove_count == 2:
|
||||
self.objs_2d_bbox = []
|
||||
for obj in self.visible_objs:
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
|
||||
self.choose_axis(event, z=True)
|
||||
|
||||
if self.mousemove_count > 3:
|
||||
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox)
|
||||
self.snapping_points = tool.Snap.select_snapping_points(context, event, detected_snaps)
|
||||
PolylineDecorator.set_mouse_position(event)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
|
||||
tool.Blender.update_viewport()
|
||||
return {"RUNNING_MODAL"}
|
||||
self.choose_plane(event)
|
||||
|
||||
if event.value == "RELEASE" and event.type == "BACK_SPACE":
|
||||
tool.Snap.remove_last_polyline_point()
|
||||
tool.Blender.update_viewport()
|
||||
self.handle_snap_selection(context, event)
|
||||
|
||||
if event.value == "RELEASE" and event.type == "LEFTMOUSE":
|
||||
tool.Snap.insert_polyline_point(self.input_ui)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "X":
|
||||
tool.Snap.set_snap_axis_method("X")
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "Y":
|
||||
tool.Snap.set_snap_axis_method("Y")
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "Z":
|
||||
tool.Snap.set_snap_axis_method("Z")
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "PRESS" and event.type == "C":
|
||||
tool.Snap.close_polyline()
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if self.is_input_on and event.value == "PRESS" and event.type == "TAB":
|
||||
self.recalculate_inputs(context)
|
||||
index = self.input_options.index(self.input_type)
|
||||
size = len(self.input_options)
|
||||
self.input_type = self.input_options[((index + 1) % size)]
|
||||
self.is_typing = False
|
||||
self.number_input = self.input_ui.get_formatted_value(self.input_type)
|
||||
self.number_input = list(self.number_input)
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.is_input_on and event.value == "RELEASE" and event.type == "TAB":
|
||||
self.recalculate_inputs(context)
|
||||
self.is_input_on = True
|
||||
self.input_type = "D"
|
||||
self.is_typing = False
|
||||
self.number_input = self.input_ui.get_formatted_value(self.input_type)
|
||||
self.number_input = list(self.number_input)
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.is_input_on and event.ascii in self.number_options:
|
||||
self.recalculate_inputs(context)
|
||||
self.is_input_on = True
|
||||
self.input_type = "D"
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.value == "RELEASE" and event.type in {"D", "A"}:
|
||||
self.recalculate_inputs(context)
|
||||
self.is_input_on = True
|
||||
self.input_type = event.type
|
||||
self.input_ui.set_value(self.input_type, "")
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if self.input_type in self.input_options:
|
||||
if (event.ascii in self.number_options) or (event.value == "RELEASE" and event.type == "BACK_SPACE"):
|
||||
if not self.is_typing and not (event.ascii == "=" or event.type == "BACK_SPACE"):
|
||||
self.number_input = []
|
||||
|
||||
if event.type == "BACK_SPACE":
|
||||
if len(self.number_input) <= 1:
|
||||
self.number_input = []
|
||||
else:
|
||||
self.number_input.pop(-1)
|
||||
elif event.ascii == "=":
|
||||
if self.number_input[0] == "=":
|
||||
self.number_input.pop(0)
|
||||
else:
|
||||
self.number_input.insert(0, "=")
|
||||
else:
|
||||
self.number_input.append(event.ascii)
|
||||
|
||||
if not self.number_input:
|
||||
self.number_output = "0"
|
||||
|
||||
self.is_typing = True
|
||||
self.number_output = "".join(self.number_input)
|
||||
self.input_ui.set_value(self.input_type, self.number_output)
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if not self.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}:
|
||||
if not self.tool_state.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}:
|
||||
PolylineDecorator.uninstall()
|
||||
tool.Snap.clear_polyline()
|
||||
tool.Blender.update_viewport()
|
||||
return {"FINISHED"}
|
||||
|
||||
if self.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}:
|
||||
is_valid = self.recalculate_inputs(context)
|
||||
if is_valid:
|
||||
tool.Snap.insert_polyline_point(self.input_ui)
|
||||
self.is_input_on = False
|
||||
self.input_type = None
|
||||
self.number_input = []
|
||||
self.number_output = ""
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
self.handle_keyboard_input(context, event)
|
||||
|
||||
if event.value == "PRESS" and event.type == "M":
|
||||
self.snapping_points = tool.Snap.modify_snapping_point_selection(self.snapping_points)
|
||||
PolylineDecorator.set_mouse_position(event)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
|
||||
tool.Blender.update_viewport()
|
||||
self.handle_inserting_polyline(context, event)
|
||||
|
||||
if event.shift and event.value == "PRESS" and event.type == "X":
|
||||
tool.Snap.set_use_default_container(False)
|
||||
PolylineDecorator.set_use_default_container(False)
|
||||
tool.Snap.cycle_snap_plane_method("YZ")
|
||||
tool.Snap.set_snap_axis_method(None)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.shift and event.value == "PRESS" and event.type == "Y":
|
||||
tool.Snap.set_use_default_container(False)
|
||||
PolylineDecorator.set_use_default_container(False)
|
||||
tool.Snap.cycle_snap_plane_method("XZ")
|
||||
tool.Snap.set_snap_axis_method(None)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.shift and event.value == "PRESS" and event.type == "Z":
|
||||
tool.Snap.set_use_default_container(False)
|
||||
PolylineDecorator.set_use_default_container(False)
|
||||
tool.Snap.cycle_snap_plane_method("XY")
|
||||
tool.Snap.set_snap_axis_method(None)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
if self.is_input_on:
|
||||
if event.value == "RELEASE" and event.type in {"ESC"}:
|
||||
self.recalculate_inputs(context)
|
||||
self.is_input_on = False
|
||||
self.input_type = None
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
if event.value == "RELEASE" and event.type in {"ESC"}:
|
||||
tool.Snap.set_snap_plane_method(None)
|
||||
tool.Snap.set_snap_axis_method(None)
|
||||
PolylineDecorator.uninstall()
|
||||
tool.Snap.clear_polyline()
|
||||
tool.Blender.update_viewport()
|
||||
return {"CANCELLED"}
|
||||
result = self.handle_cancelation(context, event)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
if context.space_data.type == "VIEW_3D":
|
||||
PolylineDecorator.install(context)
|
||||
tool.Snap.clear_snapping_point()
|
||||
tool.Snap.set_use_default_container(False)
|
||||
PolylineDecorator.set_use_default_container(False)
|
||||
tool.Polyline.set_use_default_container(False)
|
||||
tool.Snap.set_snap_plane_method(None)
|
||||
tool.Snap.set_snap_axis_method(None)
|
||||
PolylineDecorator.set_instructions(self.instructions)
|
||||
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
|
||||
self.visible_objs = tool.Raycast.get_visible_objects(context)
|
||||
for obj in self.visible_objs:
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
|
||||
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox)
|
||||
self.snapping_points = tool.Snap.select_snapping_points(context, event, detected_snaps)
|
||||
PolylineDecorator.set_mouse_position(event)
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
|
||||
tool.Blender.update_viewport()
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
else:
|
||||
self.report({"WARNING"}, "Active space must be a View3d")
|
||||
return {"CANCELLED"}
|
||||
super().invoke(context, event)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
@@ -118,10 +118,10 @@ class Snap(bonsai.core.tool.Snap):
|
||||
def insert_polyline_point(cls, input_ui):
|
||||
x = input_ui.get_number_value("X")
|
||||
y = input_ui.get_number_value("Y")
|
||||
try:
|
||||
if input_ui.get_number_value("Z") is not None:
|
||||
z = input_ui.get_number_value("Z")
|
||||
except:
|
||||
z = Vector((0, 0, 0))
|
||||
else:
|
||||
z = 0
|
||||
d = input_ui.get_formatted_value("D")
|
||||
a = input_ui.get_formatted_value("A")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user