0
$\begingroup$

I make efforts to study kdTree but get the wrong result. I return indexes that I did not expect.

Please tell me what my mistake is.

Video example - http://take.ms/UX1nm

import bpy
import bmesh
import bgl
import blf
from bpy.types import Operator
from bpy_extras import view3d_utils
from mathutils import Vector, Matrix, kdtree
def GetCoordMouse(self, context, event):
    '''Get Coordinate Mouse in 3d view'''
    #scene = context.scene
    region = context.region
    rv3d = context.region_data
    coord = event.mouse_region_x, event.mouse_region_y
    #rv3d.view_rotation * Vector((0.0, 0.0, 0.0))
    view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
    loc = view3d_utils.region_2d_to_location_3d(region, rv3d, coord, view_vector)

    act_obj = context.active_object
    matrix_obj = act_obj.matrix_world

    loc = matrix_obj.inverted() * loc
    return loc
class TetsKD(Operator):
    bl_idname = "view3d.test_kd"
    bl_label = "Test KD"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls,context):
        #return context.mode == "EDIT_MESH" and context.object is not None and context.object.type == "MESH"
        return context.space_data.type == "VIEW_3D"
# ______________________________INVOKE________________________________#
> 
    def invoke(self,context, event):
        if context.space_data.type == 'VIEW_3D':
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}
#______________________________MODAL________________________________#

    def modal(self, context, event):
        if event.type == 'MOUSEMOVE':
            #self.me = context.active_object.data
            self.me = context.object.data
            #self.bm = bmesh.new()
            self.bm = bmesh.from_edit_mesh(self.me)

            #self.bm.from_mesh(self.me)

            self.size = len(self.bm.verts)

            self.kd = kdtree.KDTree(self.size)

            for i, vtx in enumerate(self.bm.verts):
                self.kd.insert(vtx.co, i)
            self.kd.balance()

            self.loc = GetCoordMouse(self, context, event)

            dist = self.kd.find_range(self.loc, 2.0)

            print("index = ", dist[1])
            return {'RUNNING_MODAL'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            return {'FINISHED'}

        self.report({'WARNING'}, "Active space must be a View3d")

        return {'RUNNING_MODAL'}

def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()
$\endgroup$
2
  • $\begingroup$ It works fine for me, except I do test "if dist:" and get "dist[0]" (not dist[1]) $\endgroup$
    – lemon
    Apr 8, 2017 at 20:14
  • $\begingroup$ The video shows that when I put my mouse on an index of 0 or 3 then I do not get their indexes $\endgroup$ Apr 8, 2017 at 20:57

1 Answer 1

1
$\begingroup$

The fact that I used the mouse coordinates which have not been correctly calculated depth. I solved this problem with the help of rey cast . I take instead of the mouse, position the cursor of the blender

enter image description here

import bpy
import bmesh
import bgl
import blf
from bpy.types import Operator
from bpy_extras import view3d_utils
from mathutils import Vector, Matrix, kdtree


def main(context, event):
    """Run this function on left mouse, execute the ray cast"""
    # get the context arguments
    scene = context.scene
    region = context.region
    rv3d = context.region_data
    coord = event.mouse_region_x, event.mouse_region_y

    # get the ray from the viewport and mouse
    view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
    ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)

    ray_target = ray_origin + view_vector

    def visible_objects_and_duplis():
        """Loop over (object, matrix) pairs (mesh only)"""

        for obj in context.visible_objects:
            if obj.type == 'MESH':
                yield (obj, obj.matrix_world.copy())

            if obj.dupli_type != 'NONE':
                obj.dupli_list_create(scene)
                for dob in obj.dupli_list:
                    obj_dupli = dob.object
                    if obj_dupli.type == 'MESH':
                        yield (obj_dupli, dob.matrix.copy())

            obj.dupli_list_clear()

    def obj_ray_cast(obj, matrix):
        """Wrapper for ray casting that moves the ray into object space"""

        # get the ray relative to the object
        matrix_inv = matrix.inverted()
        ray_origin_obj = matrix_inv * ray_origin
        ray_target_obj = matrix_inv * ray_target
        ray_direction_obj = ray_target_obj - ray_origin_obj

        # cast the ray
        success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)

        if success:
            return location, normal, face_index
        else:
            return None, None, None

    def KD(face_index):
        # self.me = context.active_object.data
        me = context.active_object.data
        #bm = bmesh.new()
        # bm = bmesh.from_mesh(me)

        bm.from_mesh(me)
        bm.faces.ensure_lookup_table()
        print(face_index)
        print(len(bm.faces[face_index].verts))
        size = len(bm.faces[face_index].verts)
        bm.faces.ensure_lookup_table()
        kd = kdtree.KDTree(size)

        for i, vtx in enumerate(bm.faces[face_index].verts):
            kd.insert(vtx.co, i)
        kd.balance()

        loc = context.scene.cursor_location

        print(loc)

        dist = kd.find_range(loc, 1)
        print("Mouse Local = ", loc)
        # print("index = ", dist[0])
        for i in dist:
            for j in i:
                context.scene.cursor_location = j
                break
            break
        bm.free()

        return dist

    # cast rays and find the closest object
    best_length_squared = -1.0
    best_obj = None

    for obj, matrix in visible_objects_and_duplis():
        if obj.type == 'MESH':
            hit, normal, face_index = obj_ray_cast(obj, matrix)
            if hit is not None:
                hit_world = matrix * hit
                scene.cursor_location = hit_world
                length_squared = (hit_world - ray_origin).length_squared
                if best_obj is None or length_squared < best_length_squared:
                    best_length_squared = length_squared
                    best_obj = obj

                # now we have the object under the mouse cursor,
                # we could do lots of stuff but for the example just select.
    a = KD(face_index)
    if best_obj is not None:
        best_obj.select = True
        context.scene.objects.active = best_obj


class ViewOperatorRayCast(bpy.types.Operator):
    """Modal object selection with a ray cast"""
    bl_idname = "view3d.modal_operator_raycast"
    bl_label = "RayCast View Operator"

    def modal(self, context, event):
        if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
            # allow navigation
            return {'PASS_THROUGH'}
        elif event.type == 'LEFTMOUSE':
            main(context, event)
            return {'RUNNING_MODAL'}
        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.space_data.type == 'VIEW_3D':
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}


def register():
    bpy.utils.register_class(ViewOperatorRayCast)


def unregister():
    bpy.utils.unregister_class(ViewOperatorRayCast)


if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .