mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 01:11:40 +00:00
WIP: started refactoring to develop the measure tool with most of the logic from polyline wall
This commit is contained in:
committed by
Bruno Perdigão
parent
736b50c6fa
commit
d1d9db3ed8
File diff suppressed because it is too large
Load Diff
@@ -303,6 +303,7 @@ class WallPolylineDecorator:
|
||||
input_type = None
|
||||
angle_snap_mat = None
|
||||
angle_snap_loc = None
|
||||
use_default_container = False
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
@@ -336,6 +337,10 @@ class WallPolylineDecorator:
|
||||
cls.axis_start = start
|
||||
cls.axis_end = end
|
||||
|
||||
@classmethod
|
||||
def set_use_default_container(cls, value=False):
|
||||
cls.use_default_container = value
|
||||
|
||||
@classmethod
|
||||
def calculate_distance_and_angle(cls, context, is_input_on):
|
||||
try:
|
||||
@@ -358,7 +363,10 @@ class WallPolylineDecorator:
|
||||
(float(cls.input_panel["X"]), float(cls.input_panel["Y"]), default_container_elevation)
|
||||
)
|
||||
else:
|
||||
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
|
||||
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))
|
||||
|
||||
distance = (snap_vector - last_point).length
|
||||
x_axis_edge = (
|
||||
@@ -374,6 +382,8 @@ class WallPolylineDecorator:
|
||||
if cls.input_panel:
|
||||
cls.input_panel["X"] = str(round(snap_vector.x, 4))
|
||||
cls.input_panel["Y"] = str(round(snap_vector.y, 4))
|
||||
if "Z" in list(cls.input_panel.keys()):
|
||||
cls.input_panel["Z"] = str(round(snap_vector.z, 4))
|
||||
cls.input_panel["D"] = str(round(distance, 4))
|
||||
cls.input_panel["A"] = str(round(angle, 4))
|
||||
|
||||
@@ -412,7 +422,7 @@ class WallPolylineDecorator:
|
||||
batch.draw(shader)
|
||||
|
||||
def draw_text(self, context):
|
||||
texts = ["X coord:", "Y coord:", "Distance:", "Angle:"]
|
||||
texts = {"X": "X coord:", "Y": "Y coord:", "Z": "Z coord:", "D": "Distance:", "A": "Angle:"}
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
self.font_id = 0
|
||||
blf.size(self.font_id, 12)
|
||||
@@ -422,14 +432,16 @@ class WallPolylineDecorator:
|
||||
color_highlight = self.addon_prefs.decorator_color_special
|
||||
offset = 20
|
||||
new_line = 20
|
||||
keys = list(self.input_panel.keys())
|
||||
for i, text in enumerate(texts):
|
||||
if keys[i] == self.input_type:
|
||||
for i, (key, value) in enumerate(self.input_panel.items()):
|
||||
if key not in list(texts.keys()):
|
||||
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] + offset - (new_line * i), 0)
|
||||
blf.draw(self.font_id, text + self.input_panel[keys[i]])
|
||||
blf.draw(self.font_id, texts[key] + value)
|
||||
|
||||
|
||||
def __call__(self, context):
|
||||
|
||||
@@ -461,15 +473,16 @@ class WallPolylineDecorator:
|
||||
else:
|
||||
self.draw_batch("POINTS", mouse_point, (1.0, 0.6, 0.0, 1.0))
|
||||
|
||||
# When a point is above the plane it projects the point
|
||||
# to the plane and creates a line
|
||||
projection_point = []
|
||||
if snap_prop.snap_type != "Plane" and snap_prop.z != 0:
|
||||
self.line_shader.uniform_float("lineWidth", 1.0)
|
||||
projection_point = [Vector((snap_prop.x, snap_prop.y, default_container_elevation))]
|
||||
self.draw_batch("POINTS", projection_point, decorator_color_unselected)
|
||||
edges = [[0, 1]]
|
||||
self.draw_batch("LINES", mouse_point + projection_point, (1.0, 0.6, 0.0, 1.0), edges)
|
||||
if self.use_default_container:
|
||||
# When a point is above the plane it projects the point
|
||||
# to the plane and creates a line
|
||||
if snap_prop.snap_type != "Plane" and snap_prop.z != 0:
|
||||
self.line_shader.uniform_float("lineWidth", 1.0)
|
||||
projection_point = [Vector((snap_prop.x, snap_prop.y, default_container_elevation))]
|
||||
self.draw_batch("POINTS", projection_point, decorator_color_unselected)
|
||||
edges = [[0, 1]]
|
||||
self.draw_batch("LINES", mouse_point + projection_point, (1.0, 0.6, 0.0, 1.0), edges)
|
||||
|
||||
# Polyline with selected points
|
||||
polyline_data = context.scene.BIMModelProperties.polyline_point
|
||||
@@ -490,9 +503,11 @@ class WallPolylineDecorator:
|
||||
# Line between last polyline point and mouse
|
||||
edges = [[0, 1]]
|
||||
if polyline_points:
|
||||
if snap_prop.snap_type != "Plane" and snap_prop.z != 0 and projection_point:
|
||||
print("PP", polyline_points)
|
||||
if snap_prop.snap_type != "Plane" and projection_point:
|
||||
self.draw_batch("LINES", [polyline_points[-1]] + projection_point, decorator_color_unselected, edges)
|
||||
else:
|
||||
print("FOI")
|
||||
self.draw_batch("LINES", [polyline_points[-1]] + mouse_point, decorator_color_unselected, edges)
|
||||
|
||||
# Line for angle axis snap
|
||||
|
||||
@@ -285,8 +285,6 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
bl_label = "Draw Polyline Wall"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
objs_bvhs = []
|
||||
viewport_box = []
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -308,132 +306,132 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
self.input_panel = {"X": "", "Y": "", "D": "", "A": ""}
|
||||
self.snap_angle = None
|
||||
|
||||
def snaping_movement(self, context, event):
|
||||
region = context.region
|
||||
rv3d = context.region_data
|
||||
self.mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||
# def snaping_movement(self, context, event):
|
||||
# region = context.region
|
||||
# rv3d = context.region_data
|
||||
# self.mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||
|
||||
offset = 5
|
||||
mouse_offset = (
|
||||
(-offset, offset),
|
||||
(0, offset),
|
||||
(offset, offset),
|
||||
(-offset, 0),
|
||||
(0, 0),
|
||||
(offset, 0),
|
||||
(-offset, -offset),
|
||||
(0, -offset),
|
||||
(offset, -offset),
|
||||
)
|
||||
# offset = 5
|
||||
# mouse_offset = (
|
||||
# (-offset, offset),
|
||||
# (0, offset),
|
||||
# (offset, offset),
|
||||
# (-offset, 0),
|
||||
# (0, 0),
|
||||
# (offset, 0),
|
||||
# (-offset, -offset),
|
||||
# (0, -offset),
|
||||
# (offset, -offset),
|
||||
# )
|
||||
|
||||
# Plane to intersect. Default Container
|
||||
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
|
||||
plane_origin = Vector((0, 0, default_container_elevation))
|
||||
plane_normal = Vector((0, 0, 1))
|
||||
# # Plane to intersect. Default Container
|
||||
# default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
|
||||
# plane_origin = Vector((0, 0, default_container_elevation))
|
||||
# plane_normal = Vector((0, 0, 1))
|
||||
|
||||
def cast_rays_and_get_best_object():
|
||||
best_length_squared = 1.0
|
||||
best_obj = None
|
||||
best_hit = None
|
||||
best_face_index = None
|
||||
# def cast_rays_and_get_best_object():
|
||||
# best_length_squared = 1.0
|
||||
# best_obj = None
|
||||
# best_hit = None
|
||||
# best_face_index = None
|
||||
|
||||
objs_to_raycast = []
|
||||
# objs_to_raycast = []
|
||||
|
||||
for obj, bbox_2d in self.objs_2d_bbox:
|
||||
if obj.type == "MESH" and bbox_2d:
|
||||
if tool.Raycast.in_view_2d_bounding_box(self.mouse_pos, bbox_2d):
|
||||
objs_to_raycast.append(obj)
|
||||
# for obj, bbox_2d in self.objs_2d_bbox:
|
||||
# if obj.type == "MESH" and bbox_2d:
|
||||
# if tool.Raycast.in_view_2d_bounding_box(self.mouse_pos, bbox_2d):
|
||||
# objs_to_raycast.append(obj)
|
||||
|
||||
for obj in objs_to_raycast:
|
||||
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
if hit is None:
|
||||
# Tried original mouse position. Now it will try the offsets.
|
||||
original_mouse_pos = self.mouse_pos
|
||||
for value in mouse_offset:
|
||||
self.mouse_pos = tuple(x + y for x, y in zip(original_mouse_pos, value))
|
||||
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
if hit:
|
||||
break
|
||||
self.mouse_pos = original_mouse_pos
|
||||
if hit is not None:
|
||||
hit_world = obj.original.matrix_world @ hit
|
||||
length_squared = (hit_world - ray_origin).length_squared
|
||||
if best_obj is None or length_squared < best_length_squared:
|
||||
best_length_squared = length_squared
|
||||
best_obj = obj
|
||||
best_hit = hit_world
|
||||
best_face_index = face_index
|
||||
# for obj in objs_to_raycast:
|
||||
# hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
# if hit is None:
|
||||
# # Tried original mouse position. Now it will try the offsets.
|
||||
# original_mouse_pos = self.mouse_pos
|
||||
# for value in mouse_offset:
|
||||
# self.mouse_pos = tuple(x + y for x, y in zip(original_mouse_pos, value))
|
||||
# hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
# if hit:
|
||||
# break
|
||||
# self.mouse_pos = original_mouse_pos
|
||||
# if hit is not None:
|
||||
# hit_world = obj.original.matrix_world @ hit
|
||||
# length_squared = (hit_world - ray_origin).length_squared
|
||||
# if best_obj is None or length_squared < best_length_squared:
|
||||
# best_length_squared = length_squared
|
||||
# best_obj = obj
|
||||
# best_hit = hit_world
|
||||
# best_face_index = face_index
|
||||
|
||||
if best_obj is not None:
|
||||
return best_obj, best_hit, best_face_index
|
||||
# if best_obj is not None:
|
||||
# return best_obj, best_hit, best_face_index
|
||||
|
||||
else:
|
||||
return None, None, None
|
||||
# else:
|
||||
# return None, None, None
|
||||
|
||||
snap_threshold = 0.3
|
||||
ray_origin, ray_target, ray_direction = tool.Raycast.get_viewport_ray_data(context, event)
|
||||
obj, hit, face_index = cast_rays_and_get_best_object()
|
||||
intersection = tool.Raycast.ray_cast_to_plane(context, event, plane_origin, plane_normal)
|
||||
# snap_threshold = 0.3
|
||||
# ray_origin, ray_target, ray_direction = tool.Raycast.get_viewport_ray_data(context, event)
|
||||
# obj, hit, face_index = cast_rays_and_get_best_object()
|
||||
# intersection = tool.Raycast.ray_cast_to_plane(context, event, plane_origin, plane_normal)
|
||||
|
||||
# Locks snap into an angle axis
|
||||
if event.shift:
|
||||
rot_intersection, _, axis_start, axis_end = tool.Snap.snap_on_axis(intersection, self.snap_angle)
|
||||
else:
|
||||
self.snap_angle = None
|
||||
rot_intersection, self.snap_angle, _, _ = tool.Snap.snap_on_axis(intersection)
|
||||
# # Locks snap into an angle axis
|
||||
# if event.shift:
|
||||
# rot_intersection, _, axis_start, axis_end = tool.Snap.snap_on_axis(intersection, self.snap_angle)
|
||||
# else:
|
||||
# self.snap_angle = None
|
||||
# rot_intersection, self.snap_angle, _, _ = tool.Snap.snap_on_axis(intersection)
|
||||
|
||||
if obj is not None:
|
||||
# if obj is not None:
|
||||
|
||||
snap_points = tool.Snap.get_snap_points_on_raycasted_obj(obj, face_index)
|
||||
snap_point = tool.Snap.select_snap_point(snap_points, hit, snap_threshold)
|
||||
# snap_points = tool.Snap.get_snap_points_on_raycasted_obj(obj, face_index)
|
||||
# snap_point = tool.Snap.select_snap_point(snap_points, hit, snap_threshold)
|
||||
|
||||
if snap_point:
|
||||
# Creates a mixed snap point between the locked axis and
|
||||
# the object snap
|
||||
# TODO Use ALT key to give the user the option to choose between the two results.
|
||||
# TODO Create decorator for this
|
||||
try:
|
||||
snap_point_vector = Vector((snap_point[0].x, snap_point[0].y, snap_point[0].z))
|
||||
snap_point_axis_1 = (
|
||||
Vector((snap_point[0].x + 1000, snap_point[0].y, default_container_elevation)),
|
||||
Vector((snap_point[0].x - 1000, snap_point[0].y, default_container_elevation)),
|
||||
)
|
||||
snap_point_axis_2 = (
|
||||
Vector((snap_point[0].x, snap_point[0].y + 1000, default_container_elevation)),
|
||||
Vector((snap_point[0].x, snap_point[0].y - 1000, default_container_elevation)),
|
||||
)
|
||||
snap_angle_axis = (axis_start, axis_end)
|
||||
result_1 = tool.Cad.intersect_edges(snap_angle_axis, snap_point_axis_1)
|
||||
result_1 = Vector((result_1[0].x, result_1[0].y, default_container_elevation))
|
||||
distance_1 = (result_1 - snap_point_vector).length
|
||||
# if snap_point:
|
||||
# # Creates a mixed snap point between the locked axis and
|
||||
# # the object snap
|
||||
# # TODO Use ALT key to give the user the option to choose between the two results.
|
||||
# # TODO Create decorator for this
|
||||
# try:
|
||||
# snap_point_vector = Vector((snap_point[0].x, snap_point[0].y, snap_point[0].z))
|
||||
# snap_point_axis_1 = (
|
||||
# Vector((snap_point[0].x + 1000, snap_point[0].y, default_container_elevation)),
|
||||
# Vector((snap_point[0].x - 1000, snap_point[0].y, default_container_elevation)),
|
||||
# )
|
||||
# snap_point_axis_2 = (
|
||||
# Vector((snap_point[0].x, snap_point[0].y + 1000, default_container_elevation)),
|
||||
# Vector((snap_point[0].x, snap_point[0].y - 1000, default_container_elevation)),
|
||||
# )
|
||||
# snap_angle_axis = (axis_start, axis_end)
|
||||
# result_1 = tool.Cad.intersect_edges(snap_angle_axis, snap_point_axis_1)
|
||||
# result_1 = Vector((result_1[0].x, result_1[0].y, default_container_elevation))
|
||||
# distance_1 = (result_1 - snap_point_vector).length
|
||||
|
||||
result_2 = tool.Cad.intersect_edges(snap_angle_axis, snap_point_axis_2)
|
||||
result_2 = Vector((result_2[0].x, result_2[0].y, default_container_elevation))
|
||||
distance_2 = (result_2 - snap_point_vector).length
|
||||
# result_2 = tool.Cad.intersect_edges(snap_angle_axis, snap_point_axis_2)
|
||||
# result_2 = Vector((result_2[0].x, result_2[0].y, default_container_elevation))
|
||||
# distance_2 = (result_2 - snap_point_vector).length
|
||||
|
||||
if distance_1 < distance_2:
|
||||
best_result = result_1
|
||||
else:
|
||||
best_result = result_2
|
||||
# if distance_1 < distance_2:
|
||||
# best_result = result_1
|
||||
# else:
|
||||
# best_result = result_2
|
||||
|
||||
tool.Snap.update_snaping_point(best_result, "Axis")
|
||||
# tool.Snap.update_snaping_point(best_result, "Axis")
|
||||
|
||||
except Exception as e:
|
||||
tool.Snap.update_snaping_point(snap_point[0], snap_point[1])
|
||||
# except Exception as e:
|
||||
# tool.Snap.update_snaping_point(snap_point[0], snap_point[1])
|
||||
|
||||
else:
|
||||
tool.Snap.update_snaping_point(hit, "Face")
|
||||
# else:
|
||||
# tool.Snap.update_snaping_point(hit, "Face")
|
||||
|
||||
else:
|
||||
snap_points = tool.Snap.get_snap_points_on_polyline()
|
||||
snap_point = tool.Snap.select_snap_point(snap_points, intersection, snap_threshold)
|
||||
# else:
|
||||
# snap_points = tool.Snap.get_snap_points_on_polyline()
|
||||
# snap_point = tool.Snap.select_snap_point(snap_points, intersection, snap_threshold)
|
||||
|
||||
if snap_point:
|
||||
tool.Snap.update_snaping_point(snap_point[0], snap_point[1])
|
||||
elif rot_intersection:
|
||||
tool.Snap.update_snaping_point(rot_intersection, "Axis")
|
||||
else:
|
||||
tool.Snap.update_snaping_point(intersection, "Plane")
|
||||
# if snap_point:
|
||||
# tool.Snap.update_snaping_point(snap_point[0], snap_point[1])
|
||||
# elif rot_intersection:
|
||||
# tool.Snap.update_snaping_point(rot_intersection, "Axis")
|
||||
# else:
|
||||
# tool.Snap.update_snaping_point(intersection, "Plane")
|
||||
|
||||
def validate_input(self, input_number):
|
||||
grammar = """
|
||||
@@ -540,7 +538,7 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
self.objs_2d_bbox.append(tool.Raycast.get_objects_2d_bounding_boxes(context, obj))
|
||||
|
||||
if self.mousemove_count > 3:
|
||||
self.snaping_movement(context, event)
|
||||
tool.Snap.snaping_movement(context, event, self.objs_2d_bbox)
|
||||
WallPolylineDecorator.set_mouse_position(event)
|
||||
self.input_panel = WallPolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
|
||||
tool.Blender.update_viewport()
|
||||
@@ -660,11 +658,14 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
def invoke(self, context, event):
|
||||
if context.space_data.type == "VIEW_3D":
|
||||
WallPolylineDecorator.install(context)
|
||||
tool.Snap.set_use_default_container(True)
|
||||
WallPolylineDecorator.set_use_default_container(True)
|
||||
tool.Snap.set_snap_plane_method('XY')
|
||||
WallPolylineDecorator.set_input_panel(self.input_panel, 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_objects_2d_bounding_boxes(context, obj))
|
||||
self.snaping_movement(context, event)
|
||||
tool.Snap.snaping_movement(context, event, self.objs_2d_bbox)
|
||||
WallPolylineDecorator.set_mouse_position(event)
|
||||
self.input_panel = WallPolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
@@ -43,6 +43,7 @@ classes = (
|
||||
operator.LoadLinkedProject,
|
||||
operator.LoadProject,
|
||||
operator.LoadProjectElements,
|
||||
operator.MeasureTool,
|
||||
operator.NewProject,
|
||||
operator.QueryLinkedElement,
|
||||
operator.RefreshClippingPlanes,
|
||||
|
||||
@@ -52,6 +52,7 @@ from bpy.app.handlers import persistent
|
||||
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 WallPolylineDecorator
|
||||
from typing import Union
|
||||
|
||||
|
||||
@@ -2286,3 +2287,92 @@ if bpy.app.version >= (4, 1, 0):
|
||||
@classmethod
|
||||
def poll_drop(cls, context):
|
||||
return True
|
||||
|
||||
|
||||
class MeasureTool(bpy.types.Operator):
|
||||
bl_idname = "bim.measure_tool"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = "Measure Tool"
|
||||
|
||||
|
||||
@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.is_input_on = False
|
||||
self.input_options = ["X", "Y", "D", "A"]
|
||||
self.input_type = "OFF"
|
||||
self.input_value_xy = [None, None]
|
||||
self.input_panel = {"X": "", "Y": "", "Z": "", "D": "", "A": ""}
|
||||
self.snap_angle = None
|
||||
|
||||
|
||||
def modal(self, context, event):
|
||||
|
||||
if event.type == "MOUSEMOVE":
|
||||
self.mousemove_count += 1
|
||||
self.input_type = "OFF"
|
||||
WallPolylineDecorator.set_input_panel(self.input_panel, self.input_type)
|
||||
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_objects_2d_bounding_boxes(context, obj))
|
||||
|
||||
if self.mousemove_count > 3:
|
||||
tool.Snap.snaping_movement(context, event, self.objs_2d_bbox)
|
||||
WallPolylineDecorator.set_mouse_position(event)
|
||||
self.input_panel = WallPolylineDecorator.calculate_distance_and_angle(context, False)
|
||||
tool.Blender.update_viewport()
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
if event.value == "RELEASE" and event.type == "LEFTMOUSE":
|
||||
tool.Snap.insert_polyline_point()
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
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 in {"RIGHTMOUSE", "ESC"}:
|
||||
WallPolylineDecorator.uninstall()
|
||||
tool.Snap.clear_polyline()
|
||||
tool.Blender.update_viewport()
|
||||
return {"FINISHED"}
|
||||
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
if context.space_data.type == "VIEW_3D":
|
||||
WallPolylineDecorator.install(context)
|
||||
tool.Snap.set_use_default_container(False)
|
||||
WallPolylineDecorator.set_use_default_container(False)
|
||||
tool.Snap.set_snap_plane_method('No Plane')
|
||||
WallPolylineDecorator.set_input_panel(self.input_panel, 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_objects_2d_bounding_boxes(context, obj))
|
||||
tool.Snap.snaping_movement(context, event, self.objs_2d_bbox)
|
||||
WallPolylineDecorator.set_mouse_position(event)
|
||||
self.input_panel = WallPolylineDecorator.calculate_distance_and_angle(context, self.is_input_on)
|
||||
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"}
|
||||
|
||||
@@ -37,6 +37,7 @@ class ExploreTool(bpy.types.WorkSpaceTool):
|
||||
("bim.explore_hotkey", {"type": "C", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_C")]}),
|
||||
("bim.explore_hotkey", {"type": "F", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_F")]}),
|
||||
("bim.explore_hotkey", {"type": "C", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_C")]}),
|
||||
("bim.explore_hotkey", {"type": "M", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_M")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
@@ -57,6 +58,9 @@ class ExploreTool(bpy.types.WorkSpaceTool):
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_ALT")
|
||||
row.label(text="Disable Culling" if LinksData.enable_culling else "Enable Culling", icon="EVENT_C")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Measure Tool", icon="EVENT_M")
|
||||
|
||||
|
||||
class ExploreHotkey(bpy.types.Operator):
|
||||
@@ -88,3 +92,6 @@ class ExploreHotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.disable_culling()
|
||||
else:
|
||||
bpy.ops.bim.enable_culling("INVOKE_DEFAULT")
|
||||
|
||||
def hotkey_S_M(self):
|
||||
bpy.ops.bim.measure_tool("INVOKE_DEFAULT")
|
||||
|
||||
@@ -26,6 +26,19 @@ from mathutils import Matrix, Vector
|
||||
|
||||
|
||||
class Snap(bonsai.core.tool.Snap):
|
||||
mouse_pos = None
|
||||
snap_angle = None
|
||||
use_default_container = False
|
||||
snap_plane_method = "No Plane"
|
||||
|
||||
@classmethod
|
||||
def set_use_default_container(cls, value=True):
|
||||
cls.use_default_container = value
|
||||
|
||||
@classmethod
|
||||
def set_snap_plane_method(cls, value=True):
|
||||
cls.snap_plane_method = value
|
||||
|
||||
@classmethod
|
||||
def get_snap_points_on_raycasted_obj(cls, obj, face_index):
|
||||
snap_points = {}
|
||||
@@ -91,18 +104,21 @@ class Snap(bonsai.core.tool.Snap):
|
||||
snap_vertex.snap_type = snap_type
|
||||
|
||||
@classmethod
|
||||
def insert_polyline_point(cls, x=None, y=None):
|
||||
def insert_polyline_point(cls, x=None, y=None, z=None):
|
||||
snap_vertex = bpy.context.scene.BIMModelProperties.snap_mouse_point[0]
|
||||
polyline_point = bpy.context.scene.BIMModelProperties.polyline_point.add()
|
||||
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
|
||||
if cls.use_default_container:
|
||||
elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
|
||||
else:
|
||||
elevation = snap_vertex.z
|
||||
if x is not None and y is not None:
|
||||
polyline_point.x = x
|
||||
polyline_point.y = y
|
||||
polyline_point.z = default_container_elevation
|
||||
polyline_point.z = elevation
|
||||
else:
|
||||
polyline_point.x = snap_vertex.x
|
||||
polyline_point.y = snap_vertex.y
|
||||
polyline_point.z = default_container_elevation
|
||||
polyline_point.z = elevation
|
||||
|
||||
@classmethod
|
||||
def close_polyline(cls):
|
||||
@@ -170,3 +186,144 @@ class Snap(bonsai.core.tool.Snap):
|
||||
return snap_intersection, axis, start, end
|
||||
|
||||
return None, None, None, None
|
||||
|
||||
@classmethod
|
||||
def snaping_movement(cls, context, event, objs_2d_bbox):
|
||||
region = context.region
|
||||
rv3d = context.region_data
|
||||
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||
|
||||
offset = 5
|
||||
mouse_offset = (
|
||||
(-offset, offset),
|
||||
(0, offset),
|
||||
(offset, offset),
|
||||
(-offset, 0),
|
||||
(0, 0),
|
||||
(offset, 0),
|
||||
(-offset, -offset),
|
||||
(0, -offset),
|
||||
(offset, -offset),
|
||||
)
|
||||
|
||||
# TODO Create option for different planes. RANDOM, XY, YZ, XZ. This should change the code in the bottom
|
||||
if cls.snap_plane_method == "No Plane":
|
||||
camera_rotation = rv3d.view_rotation
|
||||
plane_origin = Vector((0, 0, 0))
|
||||
plane_normal = Vector((0, 1, 1, 1)) * Vector((camera_rotation))
|
||||
|
||||
elif cls.snap_plane_method == "XY":
|
||||
if cls.use_default_container:
|
||||
elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
|
||||
else:
|
||||
elevation = 0
|
||||
plane_origin = Vector((0, 0, elevation))
|
||||
plane_normal = Vector((0, 0, 1))
|
||||
|
||||
def cast_rays_and_get_best_object():
|
||||
best_length_squared = 1.0
|
||||
best_obj = None
|
||||
best_hit = None
|
||||
best_face_index = None
|
||||
|
||||
objs_to_raycast = []
|
||||
|
||||
for obj, bbox_2d in objs_2d_bbox:
|
||||
if obj.type == "MESH" and bbox_2d:
|
||||
if tool.Raycast.in_view_2d_bounding_box(cls.mouse_pos, bbox_2d):
|
||||
objs_to_raycast.append(obj)
|
||||
|
||||
for obj in objs_to_raycast:
|
||||
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
if hit is None:
|
||||
# Tried original mouse position. Now it will try the offsets.
|
||||
original_mouse_pos = cls.mouse_pos
|
||||
for value in mouse_offset:
|
||||
cls.mouse_pos = tuple(x + y for x, y in zip(original_mouse_pos, value))
|
||||
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
if hit:
|
||||
break
|
||||
cls.mouse_pos = original_mouse_pos
|
||||
if hit is not None:
|
||||
hit_world = obj.original.matrix_world @ hit
|
||||
length_squared = (hit_world - ray_origin).length_squared
|
||||
if best_obj is None or length_squared < best_length_squared:
|
||||
best_length_squared = length_squared
|
||||
best_obj = obj
|
||||
best_hit = hit_world
|
||||
best_face_index = face_index
|
||||
|
||||
if best_obj is not None:
|
||||
return best_obj, best_hit, best_face_index
|
||||
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
snap_threshold = 0.3
|
||||
ray_origin, ray_target, ray_direction = tool.Raycast.get_viewport_ray_data(context, event)
|
||||
obj, hit, face_index = cast_rays_and_get_best_object()
|
||||
intersection = tool.Raycast.ray_cast_to_plane(context, event, plane_origin, plane_normal)
|
||||
|
||||
if cls.use_default_container:
|
||||
# Locks snap into an angle axis
|
||||
if event.shift:
|
||||
rot_intersection, _, axis_start, axis_end = cls.snap_on_axis(intersection, cls.snap_angle)
|
||||
else:
|
||||
cls.snap_angle = None
|
||||
rot_intersection, cls.snap_angle, _, _ = cls.snap_on_axis(intersection)
|
||||
else:
|
||||
rot_intersection = None
|
||||
|
||||
if obj is not None:
|
||||
|
||||
snap_points = cls.get_snap_points_on_raycasted_obj(obj, face_index)
|
||||
snap_point = cls.select_snap_point(snap_points, hit, snap_threshold)
|
||||
|
||||
if snap_point:
|
||||
# Creates a mixed snap point between the locked axis and
|
||||
# the object snap
|
||||
# TODO Use ALT key to give the user the option to choose between the two results.
|
||||
# TODO Create decorator for this
|
||||
# try:
|
||||
# snap_point_vector = Vector((snap_point[0].x, snap_point[0].y, snap_point[0].z))
|
||||
# snap_point_axis_1 = (
|
||||
# Vector((snap_point[0].x + 1000, snap_point[0].y, default_container_elevation)),
|
||||
# Vector((snap_point[0].x - 1000, snap_point[0].y, default_container_elevation)),
|
||||
# )
|
||||
# snap_point_axis_2 = (
|
||||
# Vector((snap_point[0].x, snap_point[0].y + 1000, default_container_elevation)),
|
||||
# Vector((snap_point[0].x, snap_point[0].y - 1000, default_container_elevation)),
|
||||
# )
|
||||
# snap_angle_axis = (axis_start, axis_end)
|
||||
# result_1 = tool.Cad.intersect_edges(snap_angle_axis, snap_point_axis_1)
|
||||
# result_1 = Vector((result_1[0].x, result_1[0].y, default_container_elevation))
|
||||
# distance_1 = (result_1 - snap_point_vector).length
|
||||
|
||||
# result_2 = tool.Cad.intersect_edges(snap_angle_axis, snap_point_axis_2)
|
||||
# result_2 = Vector((result_2[0].x, result_2[0].y, default_container_elevation))
|
||||
# distance_2 = (result_2 - snap_point_vector).length
|
||||
|
||||
# if distance_1 < distance_2:
|
||||
# best_result = result_1
|
||||
# else:
|
||||
# best_result = result_2
|
||||
|
||||
# cls.update_snaping_point(best_result, "Axis")
|
||||
|
||||
# except Exception as e:
|
||||
# cls.update_snaping_point(snap_point[0], snap_point[1])
|
||||
cls.update_snaping_point(snap_point[0], snap_point[1])
|
||||
|
||||
else:
|
||||
cls.update_snaping_point(hit, "Face")
|
||||
|
||||
else:
|
||||
snap_points = cls.get_snap_points_on_polyline()
|
||||
snap_point = cls.select_snap_point(snap_points, intersection, snap_threshold)
|
||||
|
||||
if snap_point:
|
||||
cls.update_snaping_point(snap_point[0], snap_point[1])
|
||||
elif rot_intersection:
|
||||
cls.update_snaping_point(rot_intersection, "Axis")
|
||||
else:
|
||||
cls.update_snaping_point(intersection, "Plane")
|
||||
|
||||
Reference in New Issue
Block a user