mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Raycast and Snap refactor. Snap now works with objects with no faces
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bmesh
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
@@ -105,6 +106,56 @@ class Raycast(bonsai.core.tool.Raycast):
|
|||||||
else:
|
else:
|
||||||
return None, None, None
|
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)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction)
|
||||||
|
except:
|
||||||
|
loc = Vector((0, 0, 0))
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
if mesh is None:
|
||||||
|
bm.from_mesh(obj.data)
|
||||||
|
else:
|
||||||
|
verts = [bm.verts.new(obj.data.vertices[i].co) for i in mesh.vertices]
|
||||||
|
bm.faces.new(verts)
|
||||||
|
|
||||||
|
for edge in bm.edges:
|
||||||
|
v1 = edge.verts[0].co
|
||||||
|
v2 = edge.verts[1].co
|
||||||
|
world_v1 = obj.matrix_world @ v1
|
||||||
|
world_v2 = obj.matrix_world @ v2
|
||||||
|
division_point = (world_v1 + world_v2) / 2 # TODO Make it work for different divisions
|
||||||
|
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"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
for edge in bm.edges:
|
||||||
|
v1 = edge.verts[0].co
|
||||||
|
v2 = edge.verts[1].co
|
||||||
|
world_v1 = obj.matrix_world @ v1
|
||||||
|
world_v2 = obj.matrix_world @ v2
|
||||||
|
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"
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ray_cast_to_plane(cls, context, event, plane_origin, plane_normal):
|
def ray_cast_to_plane(cls, context, event, plane_origin, plane_normal):
|
||||||
region = context.region
|
region = context.region
|
||||||
|
|||||||
@@ -41,23 +41,19 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
cls.snap_plane_method = value
|
cls.snap_plane_method = value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_snap_points_on_raycasted_obj(cls, obj, face_index):
|
def get_snap_points_on_raycasted_face(cls, context, event, obj, face_index):
|
||||||
snap_points = {}
|
|
||||||
matrix = obj.matrix_world.copy()
|
matrix = obj.matrix_world.copy()
|
||||||
face = obj.data.polygons[face_index]
|
face = obj.data.polygons[face_index]
|
||||||
vertices = []
|
verts = []
|
||||||
for i in face.vertices:
|
for i in face.vertices:
|
||||||
vertices.append(matrix @ obj.data.vertices[i].co)
|
verts.append(matrix @ obj.data.vertices[i].co)
|
||||||
|
|
||||||
edges_center = []
|
hit, hit_type = tool.Raycast.ray_cast_by_proximity(context, event, obj, face)
|
||||||
for v1, v2 in zip(vertices, vertices[1:] + [vertices[0]]):
|
snap_point = (hit, hit_type)
|
||||||
middle_dist = (v2 - v1) / 2
|
if hit is None:
|
||||||
edges_center.append(v1 + middle_dist)
|
return (None, None)
|
||||||
|
|
||||||
snap_points.update({tuple(vertex): "Vertices" for vertex in vertices})
|
return snap_point
|
||||||
snap_points.update({tuple(edge_center): "Edge Center" for edge_center in edges_center})
|
|
||||||
|
|
||||||
return snap_points
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_snap_points_on_polyline(cls):
|
def get_snap_points_on_polyline(cls):
|
||||||
@@ -286,7 +282,8 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
rv3d = context.region_data
|
rv3d = context.region_data
|
||||||
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
|
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
|
||||||
|
|
||||||
offset = 5
|
offset = 15
|
||||||
|
snap_threshold = 0.3
|
||||||
mouse_offset = (
|
mouse_offset = (
|
||||||
(-offset, offset),
|
(-offset, offset),
|
||||||
(0, offset),
|
(0, offset),
|
||||||
@@ -329,19 +326,12 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
|
|
||||||
return plane_origin, plane_normal
|
return plane_origin, plane_normal
|
||||||
|
|
||||||
def cast_rays_and_get_best_object():
|
def cast_rays_and_get_best_object(objs_to_raycast):
|
||||||
best_length_squared = 1.0
|
best_length_squared = 1.0
|
||||||
best_obj = None
|
best_obj = None
|
||||||
best_hit = None
|
best_hit = None
|
||||||
best_face_index = 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:
|
for obj in objs_to_raycast:
|
||||||
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||||
if hit is None:
|
if hit is None:
|
||||||
@@ -353,6 +343,7 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
if hit:
|
if hit:
|
||||||
break
|
break
|
||||||
cls.mouse_pos = original_mouse_pos
|
cls.mouse_pos = original_mouse_pos
|
||||||
|
|
||||||
if hit is not None:
|
if hit is not None:
|
||||||
hit_world = obj.original.matrix_world @ hit
|
hit_world = obj.original.matrix_world @ hit
|
||||||
length_squared = (hit_world - ray_origin).length_squared
|
length_squared = (hit_world - ray_origin).length_squared
|
||||||
@@ -368,9 +359,6 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
else:
|
else:
|
||||||
return None, None, None
|
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()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
polyline_data = bpy.context.scene.BIMModelProperties.polyline_point
|
polyline_data = bpy.context.scene.BIMModelProperties.polyline_point
|
||||||
@@ -396,26 +384,56 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
else:
|
else:
|
||||||
rot_intersection = None
|
rot_intersection = None
|
||||||
|
|
||||||
if obj is not None:
|
ray_origin, ray_target, ray_direction = tool.Raycast.get_viewport_ray_data(context, event)
|
||||||
|
|
||||||
snap_points = cls.get_snap_points_on_raycasted_obj(obj, face_index)
|
objs_to_raycast = []
|
||||||
snap_point = cls.select_snap_point(snap_points, hit, snap_threshold)
|
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)
|
||||||
|
|
||||||
if not snap_point:
|
# Try to get object using object raycast method
|
||||||
cls.update_snaping_point(hit, "Face")
|
# 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
snap_point = None
|
||||||
|
|
||||||
|
# If there are no objects to snap to, it will try snaping in the polyline
|
||||||
else:
|
else:
|
||||||
snap_points = cls.get_snap_points_on_polyline()
|
snap_points = cls.get_snap_points_on_polyline()
|
||||||
snap_point = cls.select_snap_point(snap_points, intersection, snap_threshold)
|
snap_point = cls.select_snap_point(snap_points, intersection, snap_threshold)
|
||||||
|
print("POLY", snap_points, snap_point)
|
||||||
|
|
||||||
if snap_point:
|
if snap_point:
|
||||||
if event.shift and axis_start:
|
if event.shift and axis_start:
|
||||||
snap_result, snap_type = cls.mix_snap_and_axis(snap_point, axis_start, axis_end, elevation)
|
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_point(snap_result, snap_type)
|
||||||
cls.update_snaping_ref(snap_point[0], snap_point[1])
|
cls.update_snaping_ref(snap_point[0], snap_point[1])
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
cls.update_snaping_point(snap_point[0], snap_point[1])
|
cls.update_snaping_point(snap_point[0], snap_point[1])
|
||||||
elif rot_intersection:
|
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")
|
cls.update_snaping_point(rot_intersection, "Axis")
|
||||||
else:
|
else:
|
||||||
cls.update_snaping_point(intersection, "Plane")
|
cls.update_snaping_point(intersection, "Plane")
|
||||||
|
|||||||
Reference in New Issue
Block a user