Following a discussion in comments, there may be a point to consider: what does distance to edge means.
My first though was: the shortest distance to any point along the edge.
But you would prefer the distance as: the distance to the center point of the edge.
So two solutions below:
shortest distance to any point along the edge
A possible way is to use a BVHTree.
This tree usually queries for faces using find_nearest.
But we are looking here for edges and in consequence we can't use it directly (nearest face does not imply nearest edge and some edges may have no linked face).
The trick is to make polygons from edges and give it to the tree using FromPolygons to build the tree.
This is done below looping over edges and make a triangle from its two vertices.
import bpy
from mathutils import Vector
from mathutils.bvhtree import BVHTree
# Get the object and its mesh
obj = bpy.context.object
mesh = obj.data
matrix = obj.matrix_world
# Get vertices locations
vertices = [matrix @ v.co for v in mesh.vertices]
# Construct polygons from edges
polygons = [(e.vertices[0], e.vertices[1], e.vertices[0]) for e in mesh.edges]
# Create a BVH Tree from it
tree = BVHTree.FromPolygons( vertices, polygons, all_triangles = True )
# Search for a location
location = Vector( (0, 1, 1) )
# Query the tree
found_location, normal, index, distance = tree.find_nearest( location )
print( index )
Building the tree has a cost and generally we use it if several searches on the same tree. But as I tested it BVH is faster than direct calculation.
So it seems to be better to use a BVH tree.
Here is the code for min distance/index from a point (just for information: don't use it):
# Min distance calculation
def search_min_dist( mesh, location ):
min_dist = None
min_index = None
for index, edge in enumerate(mesh.edges):
e1 = mesh.vertices[edge.vertices[0]].co
e2 = mesh.vertices[edge.vertices[1]].co
length_squared = (e1 - e2).length_squared
if length_squared == 0.0:
# if edge is in fact one point
d = (location - e1).length_squared
else:
# parametric projection on e1 e2
p = (location - e1).dot(e2 - e1) / length_squared
if p > 1:
# if above e2
projected = e2
elif p < 0:
# if before e1
projected = e1
else:
# in between
projected = e1 + p * (e2-e1)
d = (location - projected).length_squared
if min_dist is None or d < min_dist:
min_dist = d
min_index = index
return min_index, sqrt(min_dist)
the distance to the center point of the edge
We can also use two approaches: using a Blender utility or direct calculation.
I've tested both and as this is equivalent in runtime, I give here a solution using Blender mathutils class KDTree.
KDTree is to work on points/locations in order to give nearest elements.
For our problem these points are the center of the segments.
So we can:
import bpy
import time
from math import sqrt
from mathutils import Vector
from mathutils.kdtree import KDTree
def search_min_dist_kd( obj, location ):
mesh = obj.data
matrix = obj.matrix_world
# Get vertices locations
vertices = [matrix @ v.co for v in mesh.vertices]
#Create the kd tree
kd = KDTree(len(vertices))
for e in mesh.edges:
i1 = e.vertices[0]
i2 = e.vertices[1]
median = (vertices[i1] + vertices[i2]) / 2.0
#populate it
kd.insert(median, e.index)
#initialize it
kd.balance()
#get closest median point
found_location, index, distance = kd.find(location)
return index, distance