animation_animall: remove workaround for T68666
[blender-addons.git] / curve_tools / PathFinder.py
blob4df49c41f81d188ab847ff76374078654ba170a6
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and / or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 'name': 'PathFinder',
21 'author': 'Spivak Vladimir (cwolf3d)',
22 'version': (0, 5, 0),
23 'blender': (2, 80, 0),
24 'location': 'Curve Tools addon. (N) Panel',
25 'description': 'PathFinder - quick search, selection, removal of splines',
26 'warning': '', # used for warning icon and text in addons panel
27 'wiki_url': '',
28 'tracker_url': '',
29 'category': 'Curve'}
31 import time
32 import threading
34 import gpu
35 import bgl
36 from gpu_extras.batch import batch_for_shader
38 import bpy
39 from bpy.props import *
40 from bpy_extras import object_utils, view3d_utils
41 from mathutils import *
42 from math import *
44 from . import Properties
45 from . import Curves
46 from . import CurveIntersections
47 from . import Util
48 from . import Surfaces
49 from . import Math
51 def get_bezier_points(spline, matrix_world):
52 point_list = []
53 len_bezier_points = len(spline.bezier_points)
54 if len_bezier_points > 1:
55 for i in range(0, len_bezier_points - 1):
56 point_list.extend([matrix_world @ spline.bezier_points[i].co])
57 for t in range(0, 100, 2):
58 h = Math.subdivide_cubic_bezier(spline.bezier_points[i].co,
59 spline.bezier_points[i].handle_right,
60 spline.bezier_points[i + 1].handle_left,
61 spline.bezier_points[i + 1].co,
62 t/100)
63 point_list.extend([matrix_world @ h[2]])
64 if spline.use_cyclic_u and len_bezier_points > 2:
65 point_list.extend([matrix_world @ spline.bezier_points[len_bezier_points - 1].co])
66 for t in range(0, 100, 2):
67 h = Math.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
68 spline.bezier_points[len_bezier_points - 1].handle_right,
69 spline.bezier_points[0].handle_left,
70 spline.bezier_points[0].co,
71 t/100)
72 point_list.extend([matrix_world @ h[2]])
73 point_list.extend([matrix_world @ spline.bezier_points[0].co])
75 return point_list
77 def get_points(spline, matrix_world):
78 point_list = []
79 len_points = len(spline.points)
80 if len_points > 1:
81 for i in range(0, len_points - 1):
82 point_list.extend([matrix_world @ Vector((spline.points[i].co.x, spline.points[i].co.y, spline.points[i].co.z))])
83 for t in range(0, 100, 2):
84 x = (spline.points[i].co.x + t / 100 * spline.points[i + 1].co.x) / (1 + t / 100)
85 y = (spline.points[i].co.y + t / 100 * spline.points[i + 1].co.y) / (1 + t / 100)
86 z = (spline.points[i].co.z + t / 100 * spline.points[i + 1].co.z) / (1 + t / 100)
87 point_list.extend([matrix_world @ Vector((x, y, z))])
88 if spline.use_cyclic_u and len_points > 2:
89 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))])
90 for t in range(0, 100, 2):
91 x = (spline.points[len_points - 1].co.x + t / 100 * spline.points[0].co.x) / (1 + t / 100)
92 y = (spline.points[len_points - 1].co.y + t / 100 * spline.points[0].co.y) / (1 + t / 100)
93 z = (spline.points[len_points - 1].co.z + t / 100 * spline.points[0].co.z) / (1 + t / 100)
94 point_list.extend([matrix_world @ Vector((x, y, z))])
95 point_list.extend([matrix_world @ Vector((spline.points[0].co.x, spline.points[0].co.y, spline.points[0].co.z))])
96 return point_list
98 def draw_bezier_points(self, context, spline, matrix_world, path_color, path_thickness):
100 points = get_bezier_points(spline, matrix_world)
102 shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
103 batch = batch_for_shader(shader, 'LINES', {"pos": points})
105 shader.bind()
106 shader.uniform_float("color", path_color)
107 bgl.glEnable(bgl.GL_BLEND)
108 bgl.glLineWidth(path_thickness)
109 batch.draw(shader)
111 def draw_points(self, context, spline, matrix_world, path_color, path_thickness):
113 points = get_points(spline, matrix_world)
115 shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
116 batch = batch_for_shader(shader, 'LINES', {"pos": points})
118 shader.bind()
119 shader.uniform_float("color", path_color)
120 bgl.glEnable(bgl.GL_BLEND)
121 bgl.glLineWidth(path_thickness)
122 batch.draw(shader)
124 def near(location3D, point, radius):
125 factor = 0
126 if point.x > (location3D.x - radius):
127 factor += 1
128 if point.x < (location3D.x + radius):
129 factor += 1
130 if point.y > (location3D.y - radius):
131 factor += 1
132 if point.y < (location3D.y + radius):
133 factor += 1
134 if point.z > (location3D.z - radius):
135 factor += 1
136 if point.z < (location3D.z + radius):
137 factor += 1
139 return factor
141 def click(self, context, event):
142 bpy.ops.object.mode_set(mode = 'EDIT')
143 bpy.context.view_layer.update()
144 for object in context.selected_objects:
145 matrix_world = object.matrix_world
146 if object.type == 'CURVE':
147 curvedata = object.data
149 radius = bpy.context.scene.curvetools.PathFinderRadius
151 for spline in curvedata.splines:
152 len_bezier_points = len(spline.bezier_points)
153 factor_max = 0
154 for i in range(0, len_bezier_points):
156 co = matrix_world @ spline.bezier_points[i].co
157 factor = near(self.location3D, co, radius)
158 if factor > factor_max:
159 factor_max = factor
161 if i < len_bezier_points - 1:
162 for t in range(0, 100, 2):
163 h = Math.subdivide_cubic_bezier(spline.bezier_points[i].co,
164 spline.bezier_points[i].handle_right,
165 spline.bezier_points[i + 1].handle_left,
166 spline.bezier_points[i + 1].co,
167 t/100)
168 co = matrix_world @ h[2]
169 factor = near(self.location3D, co, radius)
170 if factor > factor_max:
171 factor_max = factor
173 if spline.use_cyclic_u and len_bezier_points > 2:
174 for t in range(0, 100, 2):
175 h = Math.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
176 spline.bezier_points[len_bezier_points - 1].handle_right,
177 spline.bezier_points[0].handle_left,
178 spline.bezier_points[0].co,
179 t/100)
180 co = matrix_world @ h[2]
181 factor = near(self.location3D, co, radius)
182 if factor > factor_max:
183 factor_max = factor
185 if factor_max == 6:
186 args = (self, context, spline, matrix_world, self.path_color, self.path_thickness)
187 self.handlers.append(bpy.types.SpaceView3D.draw_handler_add(draw_bezier_points, args, 'WINDOW', 'POST_VIEW'))
189 for bezier_point in spline.bezier_points:
190 bezier_point.select_control_point = True
191 bezier_point.select_left_handle = True
192 bezier_point.select_right_handle = True
194 for spline in curvedata.splines:
195 len_points = len(spline.points)
196 factor_max = 0
197 for i in range(0, len_points):
198 co = matrix_world @ Vector((spline.points[i].co.x, spline.points[i].co.y, spline.points[i].co.z))
199 factor = near(self.location3D, co, radius)
200 if factor > factor_max:
201 factor_max = factor
203 if i < len_bezier_points - 1:
204 for t in range(0, 100, 2):
205 x = (spline.points[i].co.x + t / 100 * spline.points[i + 1].co.x) / (1 + t / 100)
206 y = (spline.points[i].co.y + t / 100 * spline.points[i + 1].co.y) / (1 + t / 100)
207 z = (spline.points[i].co.z + t / 100 * spline.points[i + 1].co.z) / (1 + t / 100)
208 co = matrix_world @ Vector((x, y, z))
209 factor = near(self.location3D, co, radius)
210 if factor > factor_max:
211 factor_max = factor
213 if spline.use_cyclic_u and len_points > 2:
214 for t in range(0, 100, 2):
215 x = (spline.points[len_points - 1].co.x + t / 100 * spline.points[0].co.x) / (1 + t / 100)
216 y = (spline.points[len_points - 1].co.y + t / 100 * spline.points[0].co.y) / (1 + t / 100)
217 z = (spline.points[len_points - 1].co.z + t / 100 * spline.points[0].co.z) / (1 + t / 100)
218 co = matrix_world @ Vector((x, y, z))
219 factor = near(self.location3D, co, radius)
220 if factor > factor_max:
221 factor_max = factor
223 if factor_max == 6:
224 args = (self, context, spline, matrix_world, self.path_color, self.path_thickness)
225 self.handlers.append(bpy.types.SpaceView3D.draw_handler_add(draw_points, args, 'WINDOW', 'POST_VIEW'))
227 for point in spline.points:
228 point.select = True
230 def remove_handler(handlers):
231 for handler in handlers:
232 try:
233 bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
234 except:
235 pass
236 for handler in handlers:
237 handlers.remove(handler)
239 class PathFinder(bpy.types.Operator):
240 bl_idname = "curvetools.pathfinder"
241 bl_label = "Path Finder"
242 bl_description = "Path Finder"
243 bl_options = {'REGISTER', 'UNDO'}
245 x: IntProperty(name="x", description="x")
246 y: IntProperty(name="y", description="y")
247 location3D: FloatVectorProperty(name = "",
248 description = "Start location",
249 default = (0.0, 0.0, 0.0),
250 subtype = 'XYZ')
252 handlers = []
254 def execute(self, context):
255 self.report({'INFO'}, "ESC or TAB - cancel")
256 bpy.ops.object.mode_set(mode = 'EDIT')
258 # color change in the panel
259 self.path_color = bpy.context.scene.curvetools.path_color
260 self.path_thickness = bpy.context.scene.curvetools.path_thickness
262 def modal(self, context, event):
263 context.area.tag_redraw()
265 if event.type in {'ESC', 'TAB'}: # Cancel
266 remove_handler(self.handlers)
267 return {'CANCELLED'}
269 if event.type in {'X', 'DEL'}: # Cancel
270 remove_handler(self.handlers)
271 bpy.ops.curve.delete(type='VERT')
272 return {'RUNNING_MODAL'}
274 elif event.alt and not event.shift and event.type == 'LEFTMOUSE':
275 remove_handler(self.handlers)
276 bpy.ops.curve.select_all(action='DESELECT')
277 click(self, context, event)
279 elif event.alt and event.shift and event.type == 'LEFTMOUSE':
280 click(self, context, event)
282 elif event.alt and event.type == 'RIGHTMOUSE':
283 remove_handler(self.handlers)
284 bpy.ops.curve.select_all(action='DESELECT')
285 click(self, context, event)
287 elif event.alt and not event.shift and event.shift and event.type == 'RIGHTMOUSE':
288 click(self, context, event)
290 elif event.type == 'A':
291 remove_handler(self.handlers)
292 bpy.ops.curve.select_all(action='DESELECT')
294 elif event.type == 'MOUSEMOVE': #
295 self.x = event.mouse_x
296 self.y = event.mouse_y
297 region = bpy.context.region
298 rv3d = bpy.context.space_data.region_3d
299 self.location3D = view3d_utils.region_2d_to_location_3d(
300 region,
301 rv3d,
302 (event.mouse_region_x, event.mouse_region_y),
303 (0.0, 0.0, 0.0)
306 return {'PASS_THROUGH'}
308 def invoke(self, context, event):
309 self.execute(context)
310 context.window_manager.modal_handler_add(self)
311 return {'RUNNING_MODAL'}
313 @classmethod
314 def poll(cls, context):
315 return (context.object is not None and
316 context.object.type == 'CURVE')
318 def register():
319 bpy.utils.register_class(PathFinder)
321 def unregister():
322 bpy.utils.unregister_class(PathFinder)
324 if __name__ == "__main__":
325 register()