Skip to content
Snippets Groups Projects
path_finder.py 12.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    
    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
    
    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
    
                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:
    
            handlers.remove(handler)
    
    
    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
    
                return {'CANCELLED'}
    
            if event.type in {'X', 'DEL'}:  # Cancel
    
                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':
    
                bpy.ops.curve.select_all(action='DESELECT')
    
    
            elif event.type == 'MOUSEMOVE':  #
    
                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__":