fix rebase conflict

This commit is contained in:
Bruno Perdigão
2024-09-08 12:10:01 -03:00
committed by Bruno Perdigão
parent cc7171060f
commit 8dc77735a5
7 changed files with 388 additions and 275 deletions
+20 -202
View File
@@ -302,8 +302,8 @@ class PolylineDecorator:
is_installed = False
handlers = []
mouse_pos = None
input_panel = None
input_type = None
input_ui = None
angle_snap_mat = None
angle_snap_loc = None
use_default_container = False
@@ -319,7 +319,7 @@ class PolylineDecorator:
SpaceView3D.draw_handler_add(handler.draw_on_screen_menu, (context,), "WINDOW", "POST_PIXEL")
)
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_measurements, (context,), "WINDOW", "POST_PIXEL"))
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_input_panel, (context,), "WINDOW", "POST_PIXEL"))
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_input_ui, (context,), "WINDOW", "POST_PIXEL"))
cls.handlers.append(SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW"))
cls.is_installed = True
@@ -337,8 +337,8 @@ class PolylineDecorator:
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
@classmethod
def set_input_panel(cls, input_panel, input_type):
cls.input_panel = input_panel
def set_input_ui(cls, input_ui, input_type):
cls.input_ui = input_ui
cls.input_type = input_type
@classmethod
@@ -354,11 +354,6 @@ class PolylineDecorator:
def set_use_default_container(cls, value=False):
cls.use_default_container = value
@classmethod
def set_plane(cls, plane_origin, plane_normal):
cls.plane_origin = plane_origin
cls.plane_normal = plane_normal
@classmethod
def set_instructions(cls, instructions):
cls.instructions = instructions
@@ -367,169 +362,6 @@ class PolylineDecorator:
def set_snap_info(cls, snap_info):
cls.snap_info = snap_info
@classmethod
def calculate_distance_and_angle(cls, context, is_input_on):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
last_point_data = polyline_data[len(polyline_data) - 1]
except:
default_container_elevation = 0
last_point_data = None
snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0]
if last_point_data:
last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z))
else:
last_point = Vector((0, 0, 0))
if is_input_on:
if cls.use_default_container:
snap_vector = Vector(
(float(cls.input_panel["X"]), float(cls.input_panel["Y"]), default_container_elevation)
)
else:
snap_vector = Vector(
(float(cls.input_panel["X"]), float(cls.input_panel["Y"]), float(cls.input_panel["Z"]))
)
else:
if cls.use_default_container:
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
else:
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
second_to_last_point = None
if len(polyline_data) > 1:
second_to_last_point_data = polyline_data[len(polyline_data) - 2]
second_to_last_point = Vector(
(second_to_last_point_data.x, second_to_last_point_data.y, second_to_last_point_data.z)
)
else:
# Creates a fake "second to last" point away from the first point but in the same x axis
# this allows to calculate the angle relative to x axis when there is only one point
second_to_last_point = Vector((last_point.x + 1000, last_point.y, last_point.z))
distance = (snap_vector - last_point).length
if distance > 0:
angle = tool.Cad.angle_3_vectors(
second_to_last_point, last_point, snap_vector, new_angle=None, degrees=True
)
# Round angle to the nearest 0.05
angle = round(angle / 0.05) * 0.05
if cls.input_panel:
cls.input_panel["X"] = str(round(snap_vector.x, 3))
cls.input_panel["Y"] = str(round(snap_vector.y, 3))
if "Z" in list(cls.input_panel.keys()):
cls.input_panel["Z"] = str(round(snap_vector.z, 3))
cls.input_panel["D"] = str(round(distance, 3))
cls.input_panel["A"] = str(round(angle, 3))
return cls.input_panel
return cls.input_panel
@classmethod
def calculate_area(cls, context):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
except:
return cls.input_panel
if len(polyline_data) < 3:
return cls.input_panel
points = []
for data in polyline_data:
points.append(Vector((data.x, data.y, data.z)))
if points[0] == points[-1]:
points = points[1:]
# TODO move this to CAD
# Calculate the normal vector of the plane formed by the first three vertices
v1, v2, v3 = points[:3]
normal = (v2 - v1).cross(v3 - v1).normalized()
# Check if all points are coplanar
is_coplanar = True
tolerance = 1e-6 # Adjust this value as needed
for v in points:
if abs((v - v1).dot(normal)) > tolerance:
is_coplanar = False
if is_coplanar:
area = 0
for i in range(len(points)):
j = (i + 1) % len(points)
area += points[i].cross(points[j]).dot(normal)
area = abs(area) / 2
else:
area = 0
if "AREA" in list(cls.input_panel.keys()):
cls.input_panel["AREA"] = str(round(area, 4))
return cls.input_panel
@classmethod
def calculate_x_y_and_z(cls, context):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
last_point_data = polyline_data[len(polyline_data) - 1]
last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z))
except:
default_container_elevation = 0
last_point = Vector((0, 0, 0))
snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0]
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
if cls.use_default_container:
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
else:
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
if len(polyline_data) > 1:
second_to_last_point_data = polyline_data[len(polyline_data) - 2]
second_to_last_point = Vector(
(second_to_last_point_data.x, second_to_last_point_data.y, second_to_last_point_data.z)
)
else:
# Creates a fake "second to last" point away from the first point but in the same x axis
# this allows to calculate the angle relative to x axis when there is only one point
second_to_last_point = Vector((last_point.x + 1000, last_point.y, last_point.z))
distance = float(cls.input_panel["D"])
if distance < 0 or distance > 0:
angle = radians(float(cls.input_panel["A"]))
rot_vector = tool.Cad.angle_3_vectors(second_to_last_point, last_point, snap_vector, angle, degrees=True)
coords = rot_vector * distance + last_point
x = coords[0]
y = coords[1]
z = coords[2]
if cls.input_panel:
cls.input_panel["X"] = str(round(x, 3))
cls.input_panel["Y"] = str(round(y, 3))
if "Z" in list(cls.input_panel.keys()):
cls.input_panel["Z"] = str(round(z, 3))
return cls.input_panel
cls.input_panel["X"] = str(round(last_point.x, 3))
cls.input_panel["Y"] = str(round(last_point.y, 3))
if "Z" in list(cls.input_panel.keys()):
cls.input_panel["Z"] = str(round(last_point.z, 3))
return cls.input_panel
def draw_batch(self, shader_type, content_pos, color, indices=None):
shader = self.line_shader if shader_type == "LINES" else self.shader
@@ -537,21 +369,8 @@ class PolylineDecorator:
shader.uniform_float("color", color)
batch.draw(shader)
@classmethod
def format_input_panel_units(cls, context, value):
unit_system = tool.Drawing.get_unit_system()
if unit_system == "IMPERIAL":
precision = context.scene.DocProperties.imperial_precision
factor = 3.28084
else:
precision = None
factor = 1
if context.scene.unit_settings.length_unit == "MILLIMETERS":
factor = 1000
return format_distance(value * factor, precision=precision, suppress_zero_inches=True, in_unit_length=True)
def draw_input_panel(self, context):
def draw_input_ui(self, context):
texts = {
"D": "Distance: ",
"A": "Angle: ",
@@ -571,22 +390,22 @@ class PolylineDecorator:
color_highlight = self.addon_prefs.decorator_color_special
offset = 20
new_line = 20
for i, (key, value) in enumerate(self.input_panel.items()):
for i, (key, field_name) in enumerate(texts.items()):
if key != "A" and key != self.input_type:
value = float(value)
formatted_value = self.format_input_panel_units(context, value)
if key != self.input_type:
formatted_value = self.input_ui.get_formatted_value(key)
else:
formatted_value = value
formatted_value = self.input_ui.get_text_value(key)
if key not in list(texts.keys()):
if formatted_value is None:
continue
if key == self.input_type:
blf.color(self.font_id, *color_highlight)
else:
blf.color(self.font_id, *color)
blf.position(self.font_id, self.mouse_pos[0] + offset, self.mouse_pos[1] - (new_line * i), 0)
blf.draw(self.font_id, texts[key] + formatted_value)
blf.draw(self.font_id, field_name + formatted_value)
def draw_measurements(self, context):
region = context.region
@@ -608,9 +427,7 @@ class PolylineDecorator:
pos_dim = (Vector(measurement_prop[i].position) + Vector(measurement_prop[i - 1].position)) / 2
coords_dim = view3d_utils.location_3d_to_region_2d(region, rv3d, pos_dim)
value = measurement_prop[i].dim
value = float(value)
formatted_value = self.format_input_panel_units(context, value)
formatted_value = measurement_prop[i].dim
blf.position(self.font_id, coords_dim[0], coords_dim[1], 0)
blf.draw(self.font_id, "d: " + formatted_value)
@@ -622,6 +439,7 @@ class PolylineDecorator:
blf.position(self.font_id, coords_angle[0], coords_angle[1], 0)
blf.draw(self.font_id, "a: " + measurement_prop[i].angle)
def draw_on_screen_menu(self, context):
region = context.region
@@ -708,12 +526,12 @@ class PolylineDecorator:
pass
# Area highlight
if "AREA" in list(self.input_panel.keys()):
if self.input_panel["AREA"] and float(self.input_panel["AREA"]) > 0:
edges = []
for i in range(1, len(polyline_points) - 1):
edges.append((0, i, i + 1))
self.draw_batch("TRIS", polyline_points, (0, 1, 0, 0.1), edges)
# if "AREA" in list(self.input_panel.keys()): # TODO Change to input_ui
# if self.input_panel["AREA"] and float(self.input_panel["AREA"]) > 0: # TODO Change to input_ui
# edges = []
# for i in range(1, len(polyline_points) - 1):
# edges.append((0, i, i + 1))
# self.draw_batch("TRIS", polyline_points, (0, 1, 0, 0.1), edges)
# Mouse points
if snap_prop.snap_type in ["Face", "Plane"]:
+39 -35
View File
@@ -324,9 +324,9 @@ class DrawPolylineWall(bpy.types.Operator):
self.number_is_negative = False
self.is_input_on = False
self.input_options = ["D", "A", "X", "Y"]
self.input_type = "OFF"
self.input_type = None
self.input_value_xy = [None, None]
self.input_panel = {"D": "", "A": "", "X": "", "Y": ""}
self.input_ui = tool.Polyline.create_input_ui()
self.snap_angle = None
self.snapping_points = []
self.instructions = """TAB: Cycle Input
@@ -340,18 +340,18 @@ class DrawPolylineWall(bpy.types.Operator):
def recalculate_inputs(self, context):
if self.number_input:
is_valid, self.number_output = tool.Snap.validate_input(self.number_output, self.input_type)
self.input_panel[self.input_type] = self.number_output
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"}:
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
elif self.input_type in {"D", "A"}:
self.input_panel = PolylineDecorator.calculate_x_y_and_z(context)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
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_panel[self.input_type] = self.number_output
self.input_ui.set_value(self.input_type, self.number_output)
tool.Blender.update_viewport()
return is_valid
@@ -387,8 +387,8 @@ class DrawPolylineWall(bpy.types.Operator):
if event.type == "MOUSEMOVE" or event.type == "INBETWEEN_MOUSEMOVE":
self.mousemove_count += 1
self.is_input_on = False
self.input_type = "OFF"
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
self.input_type = None
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Snap.clear_snapping_ref()
tool.Blender.update_viewport()
else:
@@ -403,7 +403,7 @@ class DrawPolylineWall(bpy.types.Operator):
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)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Blender.update_viewport()
return {"RUNNING_MODAL"}
@@ -412,7 +412,7 @@ class DrawPolylineWall(bpy.types.Operator):
tool.Blender.update_viewport()
if event.value == "RELEASE" and event.type == "LEFTMOUSE":
tool.Snap.insert_polyline_point(self.input_panel)
tool.Snap.insert_polyline_point(self.input_ui)
tool.Blender.update_viewport()
if event.value == "PRESS" and event.type == "X":
@@ -425,7 +425,7 @@ class DrawPolylineWall(bpy.types.Operator):
if event.value == "PRESS" and event.type == "C":
tool.Snap.close_polyline()
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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":
@@ -434,15 +434,15 @@ class DrawPolylineWall(bpy.types.Operator):
size = len(self.input_options)
self.input_type = self.input_options[((index + 1) % size)]
self.number_input = self.input_panel[self.input_type]
self.number_input = self.input_ui.get_text_value(self.input_type)
self.number_input = list(self.number_input)
self.number_output = "".join(self.number_input)
if self.input_type != "A":
self.number_output = PolylineDecorator.format_input_panel_units(context, float(self.number_output))
self.number_output = self.input_ui.get_formatted_value(self.input_type)
self.input_panel[self.input_type] = self.number_output
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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":
@@ -450,13 +450,14 @@ class DrawPolylineWall(bpy.types.Operator):
self.is_input_on = True
self.input_type = "D"
self.number_input = self.input_panel[self.input_type]
self.number_input = self.input_ui.get_text_value(self.input_type)
print("NI", self.number_input)
self.number_input = list(self.number_input)
self.number_output = "".join(self.number_input)
self.number_output = PolylineDecorator.format_input_panel_units(context, float(self.number_output))
self.input_panel[self.input_type] = self.number_output
self.number_output = self.input_ui.get_formatted_value(self.input_type)
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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:
@@ -464,7 +465,8 @@ class DrawPolylineWall(bpy.types.Operator):
self.is_input_on = True
self.input_type = "D"
self.number_input = []
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
print("WALL", self.input_type)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Blender.update_viewport()
if event.value == "RELEASE" and event.type in {"D", "A"}:
@@ -472,8 +474,8 @@ class DrawPolylineWall(bpy.types.Operator):
self.is_input_on = True
self.input_type = event.type
self.number_input = []
self.input_panel[self.input_type] = ""
PolylineDecorator.set_input_panel(self.input_panel, self.input_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:
@@ -486,16 +488,16 @@ class DrawPolylineWall(bpy.types.Operator):
self.number_output = "".join(self.number_input)
if not self.number_input:
self.number_output = "0"
self.input_panel[self.input_type] = self.number_output
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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()
else:
self.number_input.append(event.ascii)
self.number_output = "".join(self.number_input)
if self.number_input:
self.input_panel[self.input_type] = self.number_output
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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"}:
@@ -508,18 +510,18 @@ class DrawPolylineWall(bpy.types.Operator):
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_panel)
tool.Snap.insert_polyline_point(self.input_ui)
self.is_input_on = False
self.input_type = "OFF"
self.input_type = None
self.number_input = []
self.number_output = ""
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Blender.update_viewport()
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)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Blender.update_viewport()
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
@@ -529,8 +531,8 @@ class DrawPolylineWall(bpy.types.Operator):
if event.value == "RELEASE" and event.type in {"ESC"}:
self.recalculate_inputs(context)
self.is_input_on = False
self.input_type = "OFF"
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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"}:
@@ -547,16 +549,18 @@ class DrawPolylineWall(bpy.types.Operator):
PolylineDecorator.install(context)
tool.Snap.set_use_default_container(True)
PolylineDecorator.set_use_default_container(True)
tool.Polyline.set_use_default_container(True) # <---
tool.Snap.set_snap_plane_method("XY")
PolylineDecorator.set_instructions(self.instructions)
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
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"}
@@ -2310,7 +2310,7 @@ class MeasureTool(bpy.types.Operator):
self.input_options = ["D", "A", "X", "Y", "Z"]
self.input_type = None
self.input_value_xy = [None, None]
self.input_panel = {"D": "", "A": "", "X": "", "Y": "", "Z": ""}
self.input_ui = tool.Polyline.create_input_ui(init_z=True)
self.snap_angle = None
self.snapping_points = []
self.instructions = """TAB: Cycle Input
@@ -2325,18 +2325,18 @@ class MeasureTool(bpy.types.Operator):
def recalculate_inputs(self, context):
if self.number_input:
is_valid, self.number_output = tool.Snap.validate_input(self.number_output, self.input_type)
self.input_panel[self.input_type] = self.number_output
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"}:
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
elif self.input_type in {"D", "A"}:
self.input_panel = PolylineDecorator.calculate_x_y_and_z(context)
# self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
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_panel[self.input_type] = self.number_output
self.input_ui.set_value(self.input_type, self.number_output)
tool.Blender.update_viewport()
return is_valid
@@ -2347,7 +2347,7 @@ class MeasureTool(bpy.types.Operator):
self.mousemove_count += 1
self.is_input_on = False
self.input_type = None
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Snap.clear_snapping_ref()
tool.Blender.update_viewport()
else:
@@ -2362,7 +2362,7 @@ class MeasureTool(bpy.types.Operator):
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)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Blender.update_viewport()
return {"RUNNING_MODAL"}
@@ -2371,7 +2371,7 @@ class MeasureTool(bpy.types.Operator):
tool.Blender.update_viewport()
if event.value == "RELEASE" and event.type == "LEFTMOUSE":
tool.Snap.insert_polyline_point(self.input_panel)
tool.Snap.insert_polyline_point(self.input_ui)
tool.Blender.update_viewport()
if event.value == "PRESS" and event.type == "X":
@@ -2388,7 +2388,7 @@ class MeasureTool(bpy.types.Operator):
if event.value == "PRESS" and event.type == "C":
tool.Snap.close_polyline()
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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":
@@ -2397,15 +2397,15 @@ class MeasureTool(bpy.types.Operator):
size = len(self.input_options)
self.input_type = self.input_options[((index + 1) % size)]
self.number_input = self.input_panel[self.input_type]
self.number_input = self.input_ui.get_text_value(self.input_type)
self.number_input = list(self.number_input)
self.number_output = "".join(self.number_input)
if self.input_type != "A":
self.number_output = PolylineDecorator.format_input_panel_units(context, float(self.number_output))
self.number_output = self.input_ui.get_formatted_value(self.input_type)
self.input_panel[self.input_type] = self.number_output
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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":
@@ -2413,13 +2413,13 @@ class MeasureTool(bpy.types.Operator):
self.is_input_on = True
self.input_type = "D"
self.number_input = self.input_panel[self.input_type]
self.number_input = self.input_ui.get_text_value(self.input_type)
self.number_input = list(self.number_input)
self.number_output = "".join(self.number_input)
self.number_output = PolylineDecorator.format_input_panel_units(context, float(self.number_output))
self.input_panel[self.input_type] = self.number_output
self.number_output = self.input_ui.get_formatted_value(self.input_type)
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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:
@@ -2427,7 +2427,7 @@ class MeasureTool(bpy.types.Operator):
self.is_input_on = True
self.input_type = "D"
self.number_input = []
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Blender.update_viewport()
if event.value == "RELEASE" and event.type in {"D", "A"}:
@@ -2435,8 +2435,8 @@ class MeasureTool(bpy.types.Operator):
self.is_input_on = True
self.input_type = event.type
self.number_input = []
self.input_panel[self.input_type] = ""
PolylineDecorator.set_input_panel(self.input_panel, self.input_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:
@@ -2447,16 +2447,18 @@ class MeasureTool(bpy.types.Operator):
else:
self.number_input = self.number_input[:-1]
self.number_output = "".join(self.number_input)
self.input_panel[self.input_type] = self.number_output
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
if not self.number_input:
self.number_output = "0"
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()
else:
self.number_input.append(event.ascii)
self.number_output = "".join(self.number_input)
if self.number_input:
self.input_panel[self.input_type] = self.number_output
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
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"}:
@@ -2468,18 +2470,18 @@ class MeasureTool(bpy.types.Operator):
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_panel)
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_panel(self.input_panel, self.input_type)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Blender.update_viewport()
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)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Blender.update_viewport()
if event.shift and event.value == "PRESS" and event.type == "X":
@@ -2511,7 +2513,7 @@ class MeasureTool(bpy.types.Operator):
self.recalculate_inputs(context)
self.is_input_on = False
self.input_type = None
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
tool.Blender.update_viewport()
else:
if event.value == "RELEASE" and event.type in {"ESC"}:
@@ -2529,17 +2531,18 @@ class MeasureTool(bpy.types.Operator):
PolylineDecorator.install(context)
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_panel(self.input_panel, self.input_type)
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)
self.input_panel = PolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
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"}
+3
View File
@@ -580,6 +580,9 @@ class Nest:
class Patch:
def run_migrate_patch(cls, infile, outfile, schema): pass
@interface
class Polyline:
pass
@interface
class Owner:
+1
View File
@@ -44,6 +44,7 @@ from bonsai.tool.model import Model
from bonsai.tool.nest import Nest
from bonsai.tool.owner import Owner
from bonsai.tool.patch import Patch
from bonsai.tool.polyline import Polyline
from bonsai.tool.project import Project
from bonsai.tool.profile import Profile
from bonsai.tool.pset import Pset
+284
View File
@@ -0,0 +1,284 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2022 Cyril Waechter <cyril@biminsight.ch>
#
# 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 bonsai.core.tool
import bonsai.tool as tool
from bonsai.bim.module.drawing.helper import format_distance
from dataclasses import dataclass, field
from math import sin, cos, radians, degrees, atan2, acos
from mathutils import Vector, Matrix
from typing import Optional
@dataclass
class PolylineUI:
_D: str = ""
_A: str = ""
_X: str = ""
_Y: str = ""
_Z: Optional[str] = None
_AREA: Optional[str] = None
init_z: bool = False
init_area: bool = False
def __post_init__(self):
if self.init_z:
self._Z = ""
if self.init_area:
self._AREA = ""
def set_value(self, attribute_name, value):
if isinstance(value, float):
value = round(value, 3)
value = str(value)
setattr(self, f"_{attribute_name}", value)
def get_text_value(self, attribute_name):
return getattr(self, f"_{attribute_name}")
def get_number_value(self, attribute_name):
value = getattr(self, f"_{attribute_name}")
if value:
return float(value)
else:
return value
def get_formatted_value(self, attribute_name):
value = self.get_number_value(attribute_name)
context = bpy.context
if value is None:
return None
if attribute_name == 'A':
return self.get_text_value(attribute_name)
else:
return self.format_input_ui_units(context, value)
def format_input_ui_units(cls, context, value):
unit_system = tool.Drawing.get_unit_system()
if unit_system == "IMPERIAL":
precision = context.scene.DocProperties.imperial_precision
factor = 3.28084
else:
precision = None
factor = 1
if context.scene.unit_settings.length_unit == "MILLIMETERS":
factor = 1000
return format_distance(value * factor, precision=precision, suppress_zero_inches=True, in_unit_length=True)
class Polyline(bonsai.core.tool.Polyline):
@classmethod
def create_input_ui(cls, init_z=False, init_area=False):
return PolylineUI(init_z=init_z, init_area=init_area)
@classmethod
def set_mouse_position(cls, event):
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
@classmethod
def set_angle_axis_line(cls, start, end):
cls.axis_start = start
cls.axis_end = end
@classmethod
def set_axis_rectangle(cls, corners):
cls.axis_rectangle = [*corners]
@classmethod
def set_use_default_container(cls, value=False):
cls.use_default_container = value
@classmethod
def set_plane(cls, plane_origin, plane_normal):
cls.plane_origin = plane_origin
cls.plane_normal = plane_normal
@classmethod
def set_instructions(cls, instructions):
cls.instructions = instructions
@classmethod
def set_snap_info(cls, snap_info):
cls.snap_info = snap_info
@classmethod
def calculate_distance_and_angle(cls, context, is_input_on, input_ui):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
last_point_data = polyline_data[len(polyline_data) - 1]
except:
default_container_elevation = 0
last_point_data = None
snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0]
if last_point_data:
last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z))
else:
last_point = Vector((0, 0, 0))
if is_input_on:
if cls.use_default_container:
snap_vector = Vector(
(input_ui.get_number_value("X"), input_ui.get_number_value("Y"), default_container_elevation)
)
else:
snap_vector = Vector(
(input_ui.get_number_value("X"), input_ui.get_number_value("Y"), input_ui.get_number_value("Z"))
)
else:
if cls.use_default_container:
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
else:
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
second_to_last_point = None
if len(polyline_data) > 1:
second_to_last_point_data = polyline_data[len(polyline_data) - 2]
second_to_last_point = Vector(
(second_to_last_point_data.x, second_to_last_point_data.y, second_to_last_point_data.z)
)
else:
# Creates a fake "second to last" point away from the first point but in the same x axis
# this allows to calculate the angle relative to x axis when there is only one point
second_to_last_point = Vector((last_point.x + 1000, last_point.y, last_point.z))
distance = (snap_vector - last_point).length
if distance > 0:
angle = tool.Cad.angle_3_vectors(
second_to_last_point, last_point, snap_vector, new_angle=None, degrees=True
)
# Round angle to the nearest 0.05
angle = round(angle / 0.05) * 0.05
if input_ui:
input_ui.set_value("X", snap_vector.x)
input_ui.set_value("Y", snap_vector.y)
if input_ui.get_number_value("Z") is not None:
input_ui.set_value("Z", snap_vector.z)
input_ui.set_value("D", distance)
input_ui.set_value("A", angle)
return
return
@classmethod
def calculate_area(cls, context, input_ui):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
except:
return input_ui
if len(polyline_data) < 3:
return input_ui
points = []
for data in polyline_data:
points.append(Vector((data.x, data.y, data.z)))
if points[0] == points[-1]:
points = points[1:]
# TODO move this to CAD
# Calculate the normal vector of the plane formed by the first three vertices
v1, v2, v3 = points[:3]
normal = (v2 - v1).cross(v3 - v1).normalized()
# Check if all points are coplanar
is_coplanar = True
tolerance = 1e-6 # Adjust this value as needed
for v in points:
if abs((v - v1).dot(normal)) > tolerance:
is_coplanar = False
if is_coplanar:
area = 0
for i in range(len(points)):
j = (i + 1) % len(points)
area += points[i].cross(points[j]).dot(normal)
area = abs(area) / 2
else:
area = 0
if input_ui.get_text_value("A") is not None:
input_ui.set_value("A", area)
return
@classmethod
def calculate_x_y_and_z(cls, context, input_ui):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
last_point_data = polyline_data[len(polyline_data) - 1]
last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z))
except:
default_container_elevation = 0
last_point = Vector((0, 0, 0))
snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0]
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
if cls.use_default_container:
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
else:
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
if len(polyline_data) > 1:
second_to_last_point_data = polyline_data[len(polyline_data) - 2]
second_to_last_point = Vector(
(second_to_last_point_data.x, second_to_last_point_data.y, second_to_last_point_data.z)
)
else:
# Creates a fake "second to last" point away from the first point but in the same x axis
# this allows to calculate the angle relative to x axis when there is only one point
second_to_last_point = Vector((last_point.x + 1000, last_point.y, last_point.z))
distance = input_ui.get_number_value("D")
if distance < 0 or distance > 0:
angle = radians(input_ui.get_number_value("A"))
rot_vector = tool.Cad.angle_3_vectors(second_to_last_point, last_point, snap_vector, angle, degrees=True)
coords = rot_vector * distance + last_point
x = coords[0]
y = coords[1]
z = coords[2]
if input_ui:
input_ui.set_value("X", x)
input_ui.set_value("Y", y)
if input_ui.get_number_value("Z") is not None:
input_ui.set_value("Z", z)
return
input_ui.set_value("X", last_point.x)
input_ui.set_value("Y", last_point.y)
if input_ui.get_number_value("Z") is not None:
input_ui.set_value("Z", last_point.z)
return
+7 -7
View File
@@ -70,6 +70,7 @@ class Snap(bonsai.core.tool.Snap):
return snap_point
# TODO Remove this function
@classmethod
def select_snap_point(cls, snap_points, hit, threshold):
shortest_distance = None
@@ -125,15 +126,15 @@ class Snap(bonsai.core.tool.Snap):
bpy.context.scene.BIMModelProperties.snap_mouse_ref.clear()
@classmethod
def insert_polyline_point(cls, input_panel):
x = float(input_panel["X"])
y = float(input_panel["Y"])
def insert_polyline_point(cls, input_ui):
x = input_ui.get_number_value("X")
y = input_ui.get_number_value("Y")
try:
z = float(input_panel["Z"])
z = input_ui.get_number_value("Z")
except:
z = Vector((0, 0, 0))
d = input_panel["D"]
a = input_panel["A"]
d = input_ui.get_formatted_value("D")
a = input_ui.get_formatted_value("A")
snap_vertex = bpy.context.scene.BIMModelProperties.snap_mouse_point[0]
if cls.use_default_container:
@@ -405,7 +406,6 @@ class Snap(bonsai.core.tool.Snap):
elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
plane_origin, plane_normal = select_plane_method()
PolylineDecorator.set_plane(plane_origin, plane_normal)
intersection = tool.Raycast.ray_cast_to_plane(context, event, plane_origin, plane_normal)
axis_start = None