Newer
Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and / or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
'name': 'PathFinder',
'author': 'Spivak Vladimir (cwolf3d)',
'version': (0, 5, 0),
'blender': (2, 80, 0),
'location': 'Curve Tools addon. (N) Panel',
'description': 'PathFinder - quick search, selection, removal of splines',
'warning': '', # used for warning icon and text in addons panel
'category': 'Curve',
}
import time
import threading
import gpu
import bgl
from gpu_extras.batch import batch_for_shader
import bpy
from bpy.props import *
from bpy_extras import object_utils, view3d_utils
from mathutils import *
from math import *
from . import mathematics
from . import util
def get_bezier_points(spline, matrix_world):
point_list = []
len_bezier_points = len(spline.bezier_points)
if len_bezier_points > 1:
for i in range(0, len_bezier_points - 1):
point_list.extend([matrix_world @ spline.bezier_points[i].co])
for t in range(0, 100, 2):
h = mathematics.subdivide_cubic_bezier(spline.bezier_points[i].co,
spline.bezier_points[i].handle_right,
spline.bezier_points[i + 1].handle_left,
spline.bezier_points[i + 1].co,
t/100)
point_list.extend([matrix_world @ h[2]])
if spline.use_cyclic_u and len_bezier_points > 2:
point_list.extend([matrix_world @ spline.bezier_points[len_bezier_points - 1].co])
for t in range(0, 100, 2):
h = mathematics.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
spline.bezier_points[len_bezier_points - 1].handle_right,
spline.bezier_points[0].handle_left,
spline.bezier_points[0].co,
t/100)
point_list.extend([matrix_world @ h[2]])
point_list.extend([matrix_world @ spline.bezier_points[0].co])
return point_list
def get_points(spline, matrix_world):
point_list = []
len_points = len(spline.points)
if len_points > 1:
for i in range(0, len_points - 1):
point_list.extend([matrix_world @ Vector((spline.points[i].co.x, spline.points[i].co.y, spline.points[i].co.z))])
for t in range(0, 100, 2):
x = (spline.points[i].co.x + t / 100 * spline.points[i + 1].co.x) / (1 + t / 100)
y = (spline.points[i].co.y + t / 100 * spline.points[i + 1].co.y) / (1 + t / 100)
z = (spline.points[i].co.z + t / 100 * spline.points[i + 1].co.z) / (1 + t / 100)
point_list.extend([matrix_world @ Vector((x, y, z))])
if spline.use_cyclic_u and len_points > 2:
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))])
for t in range(0, 100, 2):
x = (spline.points[len_points - 1].co.x + t / 100 * spline.points[0].co.x) / (1 + t / 100)
y = (spline.points[len_points - 1].co.y + t / 100 * spline.points[0].co.y) / (1 + t / 100)
z = (spline.points[len_points - 1].co.z + t / 100 * spline.points[0].co.z) / (1 + t / 100)
point_list.extend([matrix_world @ Vector((x, y, z))])
point_list.extend([matrix_world @ Vector((spline.points[0].co.x, spline.points[0].co.y, spline.points[0].co.z))])
return point_list
def draw_bezier_points(self, context, spline, matrix_world, path_color, path_thickness):
points = get_bezier_points(spline, matrix_world)
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'POINTS', {"pos": points})
shader.bind()
shader.uniform_float("color", path_color)
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(path_thickness)
batch.draw(shader)
def draw_points(self, context, spline, matrix_world, path_color, path_thickness):
points = get_points(spline, matrix_world)
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'POINTS', {"pos": points})
shader.bind()
shader.uniform_float("color", path_color)
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(path_thickness)
batch.draw(shader)
def near(location3D, point, radius):
factor = 0
if point.x > (location3D.x - radius):
factor += 1
if point.x < (location3D.x + radius):
factor += 1
if point.y > (location3D.y - radius):
factor += 1
if point.y < (location3D.y + radius):
factor += 1
if point.z > (location3D.z - radius):
factor += 1
if point.z < (location3D.z + radius):
factor += 1
return factor
def click(self, context, event):
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.context.view_layer.update()
for object in context.selected_objects:
matrix_world = object.matrix_world
if object.type == 'CURVE':
curvedata = object.data
radius = bpy.context.scene.curvetools.PathFinderRadius
for spline in curvedata.splines:
len_bezier_points = len(spline.bezier_points)
factor_max = 0
for i in range(0, len_bezier_points):
co = matrix_world @ spline.bezier_points[i].co
factor = near(self.location3D, co, radius)
if factor > factor_max:
factor_max = factor
if i < len_bezier_points - 1:
for t in range(0, 100, 2):
h = mathematics.subdivide_cubic_bezier(spline.bezier_points[i].co,
spline.bezier_points[i].handle_right,
spline.bezier_points[i + 1].handle_left,
spline.bezier_points[i + 1].co,
t/100)
co = matrix_world @ h[2]
factor = near(self.location3D, co, radius)
if factor > factor_max:
factor_max = factor
if spline.use_cyclic_u and len_bezier_points > 2:
for t in range(0, 100, 2):
h = mathematics.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
spline.bezier_points[len_bezier_points - 1].handle_right,
spline.bezier_points[0].handle_left,
spline.bezier_points[0].co,
t/100)
co = matrix_world @ h[2]
factor = near(self.location3D, co, radius)
if factor > factor_max:
factor_max = factor
if factor_max == 6:
args = (self, context, spline, matrix_world, self.path_color, self.path_thickness)
self.handlers.append(bpy.types.SpaceView3D.draw_handler_add(draw_bezier_points, args, 'WINDOW', 'POST_VIEW'))
for bezier_point in spline.bezier_points:
bezier_point.select_control_point = True
bezier_point.select_left_handle = True
bezier_point.select_right_handle = True
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
for spline in curvedata.splines:
len_points = len(spline.points)
factor_max = 0
for i in range(0, len_points):
co = matrix_world @ Vector((spline.points[i].co.x, spline.points[i].co.y, spline.points[i].co.z))
factor = near(self.location3D, co, radius)
if factor > factor_max:
factor_max = factor
if i < len_bezier_points - 1:
for t in range(0, 100, 2):
x = (spline.points[i].co.x + t / 100 * spline.points[i + 1].co.x) / (1 + t / 100)
y = (spline.points[i].co.y + t / 100 * spline.points[i + 1].co.y) / (1 + t / 100)
z = (spline.points[i].co.z + t / 100 * spline.points[i + 1].co.z) / (1 + t / 100)
co = matrix_world @ Vector((x, y, z))
factor = near(self.location3D, co, radius)
if factor > factor_max:
factor_max = factor
if spline.use_cyclic_u and len_points > 2:
for t in range(0, 100, 2):
x = (spline.points[len_points - 1].co.x + t / 100 * spline.points[0].co.x) / (1 + t / 100)
y = (spline.points[len_points - 1].co.y + t / 100 * spline.points[0].co.y) / (1 + t / 100)
z = (spline.points[len_points - 1].co.z + t / 100 * spline.points[0].co.z) / (1 + t / 100)
co = matrix_world @ Vector((x, y, z))
factor = near(self.location3D, co, radius)
if factor > factor_max:
factor_max = factor
if factor_max == 6:
args = (self, context, spline, matrix_world, self.path_color, self.path_thickness)
self.handlers.append(bpy.types.SpaceView3D.draw_handler_add(draw_points, args, 'WINDOW', 'POST_VIEW'))
for point in spline.points:
point.select = True
def remove_handler(handlers):
for handler in handlers:
try:
bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
except:
pass
for handler in handlers:
class PathFinder(bpy.types.Operator):
bl_idname = "curvetools.pathfinder"
bl_label = "Path Finder"
bl_description = "Path Finder"
bl_options = {'REGISTER', 'UNDO'}
x: IntProperty(name="x", description="x")
y: IntProperty(name="y", description="y")
location3D: FloatVectorProperty(name = "",
description = "Start location",
default = (0.0, 0.0, 0.0),
subtype = 'XYZ')
def execute(self, context):
self.report({'INFO'}, "ESC or TAB - cancel")
bpy.ops.object.mode_set(mode = 'EDIT')
# color change in the panel
self.path_color = bpy.context.scene.curvetools.path_color
self.path_thickness = bpy.context.scene.curvetools.path_thickness
def modal(self, context, event):
context.area.tag_redraw()
if event.type in {'ESC', 'TAB'}: # Cancel
remove_handler(self.handlers)
if event.type in {'X', 'DEL'}: # Cancel
remove_handler(self.handlers)
bpy.ops.curve.delete(type='VERT')
return {'RUNNING_MODAL'}
elif event.alt and event.shift and event.type == 'LEFTMOUSE':
click(self, context, event)
elif event.alt and not event.shift and event.type == 'LEFTMOUSE':
remove_handler(self.handlers)
bpy.ops.curve.select_all(action='DESELECT')
click(self, context, event)
elif event.alt and event.type == 'RIGHTMOUSE':
remove_handler(self.handlers)
bpy.ops.curve.select_all(action='DESELECT')
click(self, context, event)
elif event.alt and not event.shift and event.shift and event.type == 'RIGHTMOUSE':
click(self, context, event)
elif event.type == 'A':
remove_handler(self.handlers)
bpy.ops.curve.select_all(action='DESELECT')
self.x = event.mouse_x
self.y = event.mouse_y
region = bpy.context.region
rv3d = bpy.context.space_data.region_3d
self.location3D = view3d_utils.region_2d_to_location_3d(
region,
rv3d,
(event.mouse_region_x, event.mouse_region_y),
(0.0, 0.0, 0.0)
return {'PASS_THROUGH'}
def invoke(self, context, event):
self.execute(context)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
@classmethod
def poll(cls, context):
return util.Selected1OrMoreCurves()
for cls in classes:
bpy.utils.register_class(operators)
for cls in classes:
bpy.utils.unregister_class(operators)
if __name__ == "__main__":
operators = [PathFinder]