Export_3ds: Improved distance cue nodes setup
[blender-addons.git] / curve_tools / path_finder.py
blob1683f147ef4cb327c1f82ae3f6cd3be3734562f0
1 # SPDX-FileCopyrightText: 2019-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 'name': 'PathFinder',
7 'author': 'Spivak Vladimir (cwolf3d)',
8 'version': (0, 5, 1),
9 'blender': (3, 0, 0),
10 'location': 'Curve Tools addon. (N) Panel',
11 'description': 'PathFinder - quick search, selection, removal of splines',
12 'warning': '', # used for warning icon and text in addons panel
13 'doc_url': '',
14 'tracker_url': '',
15 'category': 'Curve',
18 import time
19 import threading
21 import gpu
22 from gpu_extras.batch import batch_for_shader
24 import bpy
25 from bpy.props import *
26 from bpy_extras import object_utils, view3d_utils
27 from mathutils import *
28 from math import *
30 from . import mathematics
31 from . import util
33 def get_bezier_points(spline, matrix_world):
34 point_list = []
35 len_bezier_points = len(spline.bezier_points)
36 if len_bezier_points > 1:
37 for i in range(0, len_bezier_points - 1):
38 point_list.extend([matrix_world @ spline.bezier_points[i].co])
39 for t in range(0, 100, 2):
40 h = mathematics.subdivide_cubic_bezier(spline.bezier_points[i].co,
41 spline.bezier_points[i].handle_right,
42 spline.bezier_points[i + 1].handle_left,
43 spline.bezier_points[i + 1].co,
44 t/100)
45 point_list.extend([matrix_world @ h[2]])
46 if spline.use_cyclic_u and len_bezier_points > 2:
47 point_list.extend([matrix_world @ spline.bezier_points[len_bezier_points - 1].co])
48 for t in range(0, 100, 2):
49 h = mathematics.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
50 spline.bezier_points[len_bezier_points - 1].handle_right,
51 spline.bezier_points[0].handle_left,
52 spline.bezier_points[0].co,
53 t/100)
54 point_list.extend([matrix_world @ h[2]])
55 point_list.extend([matrix_world @ spline.bezier_points[0].co])
57 return point_list
59 def get_points(spline, matrix_world):
60 point_list = []
61 len_points = len(spline.points)
62 if len_points > 1:
63 for i in range(0, len_points - 1):
64 point_list.extend([matrix_world @ Vector((spline.points[i].co.x, spline.points[i].co.y, spline.points[i].co.z))])
65 for t in range(0, 100, 2):
66 x = (spline.points[i].co.x + t / 100 * spline.points[i + 1].co.x) / (1 + t / 100)
67 y = (spline.points[i].co.y + t / 100 * spline.points[i + 1].co.y) / (1 + t / 100)
68 z = (spline.points[i].co.z + t / 100 * spline.points[i + 1].co.z) / (1 + t / 100)
69 point_list.extend([matrix_world @ Vector((x, y, z))])
70 if spline.use_cyclic_u and len_points > 2:
71 point_list.extend([matrix_world @ Vector((spline.points[len_points - 1].co.x, spline.points[len_points - 1].co.y, spline.points[len_points - 1].co.z))])
72 for t in range(0, 100, 2):
73 x = (spline.points[len_points - 1].co.x + t / 100 * spline.points[0].co.x) / (1 + t / 100)
74 y = (spline.points[len_points - 1].co.y + t / 100 * spline.points[0].co.y) / (1 + t / 100)
75 z = (spline.points[len_points - 1].co.z + t / 100 * spline.points[0].co.z) / (1 + t / 100)
76 point_list.extend([matrix_world @ Vector((x, y, z))])
77 point_list.extend([matrix_world @ Vector((spline.points[0].co.x, spline.points[0].co.y, spline.points[0].co.z))])
78 return point_list
80 def draw_bezier_points(self, context, spline, matrix_world, path_color, path_thickness):
82 points = get_bezier_points(spline, matrix_world)
84 shader = gpu.shader.from_builtin('UNIFORM_COLOR')
85 batch = batch_for_shader(shader, 'POINTS', {"pos": points})
87 shader.bind()
88 shader.uniform_float("color", path_color)
89 gpu.state.blend_set('ALPHA')
90 gpu.state.line_width_set(path_thickness)
91 batch.draw(shader)
93 def draw_points(self, context, spline, matrix_world, path_color, path_thickness):
95 points = get_points(spline, matrix_world)
97 shader = gpu.shader.from_builtin('UNIFORM_COLOR')
98 batch = batch_for_shader(shader, 'POINTS', {"pos": points})
100 shader.bind()
101 shader.uniform_float("color", path_color)
102 gpu.state.blend_set('ALPHA')
103 gpu.state.line_width_set(path_thickness)
104 batch.draw(shader)
106 def near(location3D, point, radius):
107 factor = 0
108 if point.x > (location3D.x - radius):
109 factor += 1
110 if point.x < (location3D.x + radius):
111 factor += 1
112 if point.y > (location3D.y - radius):
113 factor += 1
114 if point.y < (location3D.y + radius):
115 factor += 1
116 if point.z > (location3D.z - radius):
117 factor += 1
118 if point.z < (location3D.z + radius):
119 factor += 1
121 return factor
123 def click(self, context, event):
124 bpy.ops.object.mode_set(mode = 'EDIT')
125 bpy.context.view_layer.update()
126 for object in context.selected_objects:
127 matrix_world = object.matrix_world
128 if object.type == 'CURVE':
129 curvedata = object.data
131 radius = bpy.context.scene.curvetools.PathFinderRadius
133 for spline in curvedata.splines:
134 len_bezier_points = len(spline.bezier_points)
135 factor_max = 0
136 for i in range(0, len_bezier_points):
138 co = matrix_world @ spline.bezier_points[i].co
139 factor = near(self.location3D, co, radius)
140 if factor > factor_max:
141 factor_max = factor
143 if i < len_bezier_points - 1:
144 for t in range(0, 100, 2):
145 h = mathematics.subdivide_cubic_bezier(spline.bezier_points[i].co,
146 spline.bezier_points[i].handle_right,
147 spline.bezier_points[i + 1].handle_left,
148 spline.bezier_points[i + 1].co,
149 t/100)
150 co = matrix_world @ h[2]
151 factor = near(self.location3D, co, radius)
152 if factor > factor_max:
153 factor_max = factor
155 if spline.use_cyclic_u and len_bezier_points > 2:
156 for t in range(0, 100, 2):
157 h = mathematics.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
158 spline.bezier_points[len_bezier_points - 1].handle_right,
159 spline.bezier_points[0].handle_left,
160 spline.bezier_points[0].co,
161 t/100)
162 co = matrix_world @ h[2]
163 factor = near(self.location3D, co, radius)
164 if factor > factor_max:
165 factor_max = factor
167 if factor_max == 6:
168 args = (self, context, spline, matrix_world, self.path_color, self.path_thickness)
169 self.handlers.append(bpy.types.SpaceView3D.draw_handler_add(draw_bezier_points, args, 'WINDOW', 'POST_VIEW'))
171 for bezier_point in spline.bezier_points:
172 bezier_point.select_control_point = True
173 bezier_point.select_left_handle = True
174 bezier_point.select_right_handle = True
176 for spline in curvedata.splines:
177 len_points = len(spline.points)
178 factor_max = 0
179 for i in range(0, len_points):
180 co = matrix_world @ Vector((spline.points[i].co.x, spline.points[i].co.y, spline.points[i].co.z))
181 factor = near(self.location3D, co, radius)
182 if factor > factor_max:
183 factor_max = factor
185 if i < len_bezier_points - 1:
186 for t in range(0, 100, 2):
187 x = (spline.points[i].co.x + t / 100 * spline.points[i + 1].co.x) / (1 + t / 100)
188 y = (spline.points[i].co.y + t / 100 * spline.points[i + 1].co.y) / (1 + t / 100)
189 z = (spline.points[i].co.z + t / 100 * spline.points[i + 1].co.z) / (1 + t / 100)
190 co = matrix_world @ Vector((x, y, z))
191 factor = near(self.location3D, co, radius)
192 if factor > factor_max:
193 factor_max = factor
195 if spline.use_cyclic_u and len_points > 2:
196 for t in range(0, 100, 2):
197 x = (spline.points[len_points - 1].co.x + t / 100 * spline.points[0].co.x) / (1 + t / 100)
198 y = (spline.points[len_points - 1].co.y + t / 100 * spline.points[0].co.y) / (1 + t / 100)
199 z = (spline.points[len_points - 1].co.z + t / 100 * spline.points[0].co.z) / (1 + t / 100)
200 co = matrix_world @ Vector((x, y, z))
201 factor = near(self.location3D, co, radius)
202 if factor > factor_max:
203 factor_max = factor
205 if factor_max == 6:
206 args = (self, context, spline, matrix_world, self.path_color, self.path_thickness)
207 self.handlers.append(bpy.types.SpaceView3D.draw_handler_add(draw_points, args, 'WINDOW', 'POST_VIEW'))
209 for point in spline.points:
210 point.select = True
212 def remove_handler(handlers):
213 for handler in handlers:
214 try:
215 bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
216 except:
217 pass
218 for handler in handlers:
219 handlers.remove(handler)
221 class PathFinder(bpy.types.Operator):
222 bl_idname = "curvetools.pathfinder"
223 bl_label = "Path Finder"
224 bl_description = "Path Finder"
225 bl_options = {'REGISTER', 'UNDO'}
227 x: IntProperty(name="x", description="x")
228 y: IntProperty(name="y", description="y")
229 location3D: FloatVectorProperty(name = "",
230 description = "Start location",
231 default = (0.0, 0.0, 0.0),
232 subtype = 'XYZ')
234 handlers = []
236 def execute(self, context):
237 self.report({'INFO'}, "ESC or TAB - cancel")
238 bpy.ops.object.mode_set(mode = 'EDIT')
240 # color change in the panel
241 self.path_color = bpy.context.scene.curvetools.path_color
242 self.path_thickness = bpy.context.scene.curvetools.path_thickness
244 def modal(self, context, event):
245 context.area.tag_redraw()
247 if event.type in {'ESC', 'TAB'}: # Cancel
248 remove_handler(self.handlers)
249 return {'CANCELLED'}
251 if event.type in {'X', 'DEL'}: # Cancel
252 remove_handler(self.handlers)
253 bpy.ops.curve.delete(type='VERT')
254 return {'RUNNING_MODAL'}
256 elif event.alt and event.shift and event.type == 'LEFTMOUSE':
257 click(self, context, event)
259 elif event.alt and not event.shift and event.type == 'LEFTMOUSE':
260 remove_handler(self.handlers)
261 bpy.ops.curve.select_all(action='DESELECT')
262 click(self, context, event)
264 elif event.alt and event.type == 'RIGHTMOUSE':
265 remove_handler(self.handlers)
266 bpy.ops.curve.select_all(action='DESELECT')
267 click(self, context, event)
269 elif event.alt and not event.shift and event.shift and event.type == 'RIGHTMOUSE':
270 click(self, context, event)
272 elif event.type == 'A':
273 remove_handler(self.handlers)
274 bpy.ops.curve.select_all(action='DESELECT')
276 elif event.type == 'MOUSEMOVE': #
277 self.x = event.mouse_x
278 self.y = event.mouse_y
279 region = bpy.context.region
280 rv3d = bpy.context.space_data.region_3d
281 self.location3D = view3d_utils.region_2d_to_location_3d(
282 region,
283 rv3d,
284 (event.mouse_region_x, event.mouse_region_y),
285 (0.0, 0.0, 0.0)
288 return {'PASS_THROUGH'}
290 def invoke(self, context, event):
291 self.execute(context)
292 context.window_manager.modal_handler_add(self)
293 return {'RUNNING_MODAL'}
295 @classmethod
296 def poll(cls, context):
297 return util.Selected1OrMoreCurves()
299 def register():
300 for cls in classes:
301 bpy.utils.register_class(operators)
303 def unregister():
304 for cls in classes:
305 bpy.utils.unregister_class(operators)
307 if __name__ == "__main__":
308 register()
311 operators = [PathFinder]