mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Refactor snapping functions and allow to choose snapping point.
By pressing "M", the user can now cycle through active snapping points when there are many options close to each other. Still lacks a text to give information about the active point.
This commit is contained in:
@@ -324,6 +324,7 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
self.input_value_xy = [None, None]
|
||||
self.input_panel = {"D": "", "A": "", "X": "", "Y": ""}
|
||||
self.snap_angle = None
|
||||
self.snapping_points = []
|
||||
|
||||
def recalculate_inputs(self, context):
|
||||
if self.number_input:
|
||||
@@ -378,7 +379,7 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
self.is_input_on = False
|
||||
self.input_type = "OFF"
|
||||
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
|
||||
tool.Snap.clear_snaping_ref()
|
||||
tool.Snap.clear_snapping_ref()
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
self.mousemove_count = 0
|
||||
@@ -389,7 +390,8 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
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)
|
||||
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.Blender.update_viewport()
|
||||
@@ -480,6 +482,10 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
PolylineDecorator.set_input_panel(self.input_panel, 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)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
@@ -508,7 +514,8 @@ class DrawPolylineWall(bpy.types.Operator):
|
||||
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)
|
||||
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.Blender.update_viewport()
|
||||
|
||||
@@ -2333,6 +2333,7 @@ class MeasureTool(bpy.types.Operator):
|
||||
self.input_value_xy = [None, None]
|
||||
self.input_panel = {"D": "", "A": "", "X": "", "Y": "", "Z": ""}
|
||||
self.snap_angle = None
|
||||
self.snapping_points = []
|
||||
|
||||
def recalculate_inputs(self, context):
|
||||
if self.number_input:
|
||||
@@ -2361,7 +2362,7 @@ class MeasureTool(bpy.types.Operator):
|
||||
self.is_input_on = False
|
||||
self.input_type = "OFF"
|
||||
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
|
||||
tool.Snap.clear_snaping_ref()
|
||||
tool.Snap.clear_snapping_ref()
|
||||
tool.Blender.update_viewport()
|
||||
else:
|
||||
self.mousemove_count = 0
|
||||
@@ -2372,7 +2373,8 @@ class MeasureTool(bpy.types.Operator):
|
||||
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)
|
||||
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)
|
||||
PolylineDecorator.set_input_panel(self.input_panel, self.input_type)
|
||||
@@ -2456,6 +2458,10 @@ class MeasureTool(bpy.types.Operator):
|
||||
PolylineDecorator.set_input_panel(self.input_panel, 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)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if event.shift and event.value == "PRESS" and event.type == "X":
|
||||
tool.Snap.set_use_default_container(False)
|
||||
PolylineDecorator.set_use_default_container(False)
|
||||
@@ -2499,7 +2505,8 @@ class MeasureTool(bpy.types.Operator):
|
||||
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)
|
||||
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.Blender.update_viewport()
|
||||
|
||||
@@ -106,12 +106,14 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
|
||||
@classmethod
|
||||
def ray_cast_by_proximity(cls, context, event, obj, mesh=None):
|
||||
region = context.region
|
||||
rv3d = context.region_data
|
||||
mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||
ray_origin, ray_target, ray_direction = cls.get_viewport_ray_data(context, event)
|
||||
points = []
|
||||
|
||||
try:
|
||||
loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction)
|
||||
@@ -119,9 +121,9 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
loc = Vector((0, 0, 0))
|
||||
|
||||
bm = bmesh.new()
|
||||
if mesh is None:
|
||||
if mesh is None: # Object with faces
|
||||
bm.from_mesh(obj.data)
|
||||
else:
|
||||
else: # Object without faces
|
||||
verts = [bm.verts.new(obj.data.vertices[i].co) for i in mesh.vertices]
|
||||
bm.faces.new(verts)
|
||||
|
||||
@@ -134,14 +136,14 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
intersection, _ = mathutils.geometry.intersect_point_line(division_point, ray_target, loc)
|
||||
distance = (division_point - intersection).length
|
||||
if distance < 0.2:
|
||||
return division_point, "Edge Center"
|
||||
points.append((division_point, "Edge Center"))
|
||||
|
||||
for vertex in bm.verts:
|
||||
world_vertex = obj.matrix_world @ vertex.co
|
||||
intersection, _ = mathutils.geometry.intersect_point_line(world_vertex, ray_target, loc)
|
||||
distance = (world_vertex - intersection).length
|
||||
if distance < 0.2:
|
||||
return world_vertex, "Vertex"
|
||||
points.append((world_vertex, "Vertex"))
|
||||
|
||||
for edge in bm.edges:
|
||||
v1 = edge.verts[0].co
|
||||
@@ -151,11 +153,36 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
intersection = mathutils.geometry.intersect_line_line(ray_target, loc, world_v1, world_v2)
|
||||
distance = (intersection[0] - intersection[1]).length
|
||||
if distance < 0.2:
|
||||
return intersection[1], "Edge"
|
||||
points.append((intersection[1], "Edge"))
|
||||
|
||||
return None, None
|
||||
return points
|
||||
|
||||
|
||||
@classmethod
|
||||
def ray_cast_to_polyline(cls, context, event):
|
||||
region = context.region
|
||||
rv3d = context.region_data
|
||||
mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||
ray_origin, ray_target, ray_direction = cls.get_viewport_ray_data(context, event)
|
||||
|
||||
try:
|
||||
loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction)
|
||||
except:
|
||||
loc = Vector((0, 0, 0))
|
||||
|
||||
polyline_data = bpy.context.scene.BIMModelProperties.polyline_point
|
||||
polyline_points = []
|
||||
for point_data in polyline_data:
|
||||
point = Vector((point_data.x, point_data.y, point_data.z))
|
||||
|
||||
|
||||
intersection, _ = mathutils.geometry.intersect_point_line(point, ray_target, loc)
|
||||
distance = (point - intersection).length
|
||||
if distance < 0.2:
|
||||
polyline_points.append((point, "Vertex"))
|
||||
|
||||
return polyline_points
|
||||
|
||||
@classmethod
|
||||
def ray_cast_to_plane(cls, context, event, plane_origin, plane_normal):
|
||||
region = context.region
|
||||
|
||||
@@ -113,7 +113,7 @@ class Snap(bonsai.core.tool.Snap):
|
||||
snap_vertex.snap_type = snap_type
|
||||
|
||||
@classmethod
|
||||
def clear_snaping_ref(cls):
|
||||
def clear_snapping_ref(cls):
|
||||
bpy.context.scene.BIMModelProperties.snap_mouse_ref.clear()
|
||||
|
||||
@classmethod
|
||||
@@ -294,10 +294,11 @@ class Snap(bonsai.core.tool.Snap):
|
||||
# cls.update_snaping_point(snap_point[0], snap_point[1])
|
||||
|
||||
@classmethod
|
||||
def snaping_movement(cls, context, event, objs_2d_bbox):
|
||||
def detect_snapping_points(cls, context, event, objs_2d_bbox):
|
||||
region = context.region
|
||||
rv3d = context.region_data
|
||||
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||
detected_snaps = []
|
||||
|
||||
offset = 15
|
||||
snap_threshold = 0.3
|
||||
@@ -376,12 +377,38 @@ class Snap(bonsai.core.tool.Snap):
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
|
||||
ray_origin, ray_target, ray_direction = tool.Raycast.get_viewport_ray_data(context, event)
|
||||
|
||||
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)
|
||||
# Obj
|
||||
snap_obj, hit, face_index = cast_rays_and_get_best_object(objs_to_raycast)
|
||||
if hit is not None:
|
||||
detected_snaps.append({"Object": (snap_obj, hit, face_index)})
|
||||
|
||||
# Edge-Vertex
|
||||
for obj in objs_to_raycast:
|
||||
options = tool.Raycast.ray_cast_by_proximity(context, event, obj)
|
||||
snap_obj = obj
|
||||
if options:
|
||||
detected_snaps.append({"Edge-Vertex": (snap_obj, options)})
|
||||
break
|
||||
# Polyline
|
||||
try:
|
||||
polyline_data = bpy.context.scene.BIMModelProperties.polyline_point
|
||||
last_polyline_point = polyline_data[len(polyline_data) - 1]
|
||||
except:
|
||||
last_polyline_point = None
|
||||
snap_points = tool.Raycast.ray_cast_to_polyline(context, event)
|
||||
# snap_point = cls.select_snap_point(snap_points, intersection, snap_threshold)
|
||||
if snap_points:
|
||||
detected_snaps.append({"Polyline": snap_points})
|
||||
|
||||
# Axis and Plane
|
||||
elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
|
||||
|
||||
plane_origin, plane_normal = select_plane_method()
|
||||
@@ -391,67 +418,79 @@ class Snap(bonsai.core.tool.Snap):
|
||||
axis_start = None
|
||||
axis_end = None
|
||||
if cls.snap_plane_method in {"XY", "XZ", "YZ"}:
|
||||
# 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
|
||||
rot_intersection, snap_angle, axis_start, axis_end = cls.snap_on_axis(intersection, cls.snap_angle)
|
||||
if rot_intersection:
|
||||
detected_snaps.append({"Axis": rot_intersection})
|
||||
|
||||
ray_origin, ray_target, ray_direction = tool.Raycast.get_viewport_ray_data(context, event)
|
||||
detected_snaps.append({"Plane": intersection})
|
||||
|
||||
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 item in detected_snaps:
|
||||
|
||||
# Try to get object using object raycast method
|
||||
# If it fails use the raycast by proximity
|
||||
snap_obj, hit, face_index = cast_rays_and_get_best_object(objs_to_raycast)
|
||||
if snap_obj is None:
|
||||
for obj in objs_to_raycast:
|
||||
hit, hit_type = tool.Raycast.ray_cast_by_proximity(context, event, obj)
|
||||
snap_obj = obj
|
||||
if hit is not None:
|
||||
break
|
||||
return detected_snaps
|
||||
|
||||
if snap_obj is not None:
|
||||
# Objects with faces
|
||||
if face_index is not None:
|
||||
snap_point = cls.get_snap_points_on_raycasted_face(context, event, snap_obj, face_index)
|
||||
if snap_point[0] is None:
|
||||
cls.update_snaping_point(hit, "Face")
|
||||
return
|
||||
# Objects with only edges or verts
|
||||
else:
|
||||
if hit is not None:
|
||||
snap_point = (hit, hit_type)
|
||||
|
||||
@classmethod
|
||||
def select_snapping_points(cls, context, event, detected_snaps):
|
||||
snapping_points = []
|
||||
for origin in detected_snaps:
|
||||
|
||||
if "Object" in list(origin.keys()):
|
||||
snap_obj, hit, face_index = origin['Object']
|
||||
|
||||
matrix = snap_obj.matrix_world.copy()
|
||||
face = snap_obj.data.polygons[face_index]
|
||||
verts = []
|
||||
for i in face.vertices:
|
||||
verts.append(matrix @ snap_obj.data.vertices[i].co)
|
||||
|
||||
options = tool.Raycast.ray_cast_by_proximity(context, event, snap_obj, face)
|
||||
if not options:
|
||||
snapping_points.append((hit, "Face"))
|
||||
else:
|
||||
snap_point = None
|
||||
for op in options:
|
||||
snapping_points.append(op)
|
||||
|
||||
# If there are no objects to snap to, it will try snaping in the polyline
|
||||
else:
|
||||
snap_points = cls.get_snap_points_on_polyline()
|
||||
snap_point = cls.select_snap_point(snap_points, intersection, snap_threshold)
|
||||
cls.update_snaping_point(snapping_points[0][0], snapping_points[0][1])
|
||||
|
||||
if snap_point:
|
||||
if event.shift and axis_start:
|
||||
snap_result, snap_type = cls.mix_snap_and_axis(snap_point, axis_start, axis_end, elevation)
|
||||
cls.update_snaping_point(snap_result, snap_type)
|
||||
cls.update_snaping_ref(snap_point[0], snap_point[1])
|
||||
return
|
||||
else:
|
||||
cls.update_snaping_point(snap_point[0], snap_point[1])
|
||||
return
|
||||
# If theres no snapping point from objects and polyline
|
||||
# It will snap to the plane
|
||||
if rot_intersection:
|
||||
cls.update_snaping_point(rot_intersection, "Axis")
|
||||
else:
|
||||
cls.update_snaping_point(intersection, "Plane")
|
||||
return snapping_points
|
||||
|
||||
elif "Edge-Vertex" in list(origin.keys()):
|
||||
snap_obj, options = origin['Edge-Vertex']
|
||||
for op in options:
|
||||
snapping_points.append(op)
|
||||
|
||||
cls.update_snaping_point(snapping_points[0][0], snapping_points[0][1])
|
||||
return snapping_points
|
||||
|
||||
elif "Polyline" in list(origin.keys()):
|
||||
options = origin['Polyline']
|
||||
for op in options:
|
||||
snapping_points.append(op)
|
||||
|
||||
cls.update_snaping_point(snapping_points[0][0], snapping_points[0][1])
|
||||
return snapping_points
|
||||
|
||||
elif "Axis" in list(origin.keys()):
|
||||
intersection = origin['Axis']
|
||||
snapping_points.append((intersection, "Axis"))
|
||||
|
||||
cls.update_snaping_point(snapping_points[0][0], snapping_points[0][1])
|
||||
return snapping_points
|
||||
|
||||
elif "Plane" in list(origin.keys()):
|
||||
intersection = origin['Plane']
|
||||
snapping_points.append((intersection, "Plane"))
|
||||
|
||||
cls.update_snaping_point(snapping_points[0][0], snapping_points[0][1])
|
||||
return snapping_points
|
||||
|
||||
|
||||
@classmethod
|
||||
def modify_snapping_point_selection(cls, snapping_points):
|
||||
shifted_list = snapping_points[1:] + snapping_points[:1]
|
||||
cls.update_snaping_point(shifted_list[0][0], shifted_list[0][1])
|
||||
return shifted_list
|
||||
|
||||
|
||||
@classmethod
|
||||
def validate_input(cls, input_number, input_type):
|
||||
|
||||
Reference in New Issue
Block a user