--- a/orpg/mapper/map_utils.py	2007-11-01 22:21:17.000000000 +0000
+++ b/orpg/mapper/map_utils.py	2007-11-02 11:43:41.000000000 +0000
@@ -8,15 +8,32 @@
 
 import math
 
+#-----------------------------------------------------------------------
+# distance_between()
+# Returns the distance between two points
+#-----------------------------------------------------------------------
+def distance_between( x1, y1, x2, y2 ):
+    "Returns the distance between two points"
+    dx = x2 - x1
+    dy = y2 - y1
+    return math.sqrt( dx*dx + dy*dy )
 
 #-----------------------------------------------------------------------
 # proximity_test()
-# Tests a given point against a line segment to see if it is within x units
-# Author: Snowdog
-# Date: 6/2/2005
+# Tests if 'test_point' (T) is close (within 'threshold' units) to the
+# line segment 'start_point' to 'end_point' (PQ).
+#
+# The closest point (R) to T on the line PQ is given by:
+#    R = P + u (Q - P)
+# TR is perpendicular to PQ so:
+#    (T - R) dot (Q - P) = 0
+# Solving these two equations gives the equation for u (see below).
+#
+# If u < 0 or u > 1 then R is not within the line segment and we simply
+# test against point P or Q.
 #-----------------------------------------------------------------------
 def proximity_test( start_point, end_point, test_point, threshold ):
-    "accepts three tuples (x,y) for the points and an int for the threshold. returns 1/0"
+    "Test if a point is close to a line segment"
     x1,y1 = start_point
     x2,y2 = end_point
     xt,yt = test_point
@@ -28,97 +45,19 @@
     xt = float(xt)
     yt = float(yt)
 
-    #check for 0 length line
-    if x1 == x2 and y1 == y2: return 0
-
-
-    m = 0
-    mt = 0
-    try:
-        #calculate line segment slope
-        m = (y2-y1)/(x2-x1)
-
-        #calculate the slope of a line tangent to the given line segment
-        mt = -1.0 / m
-
-    except ZeroDivisionError:
-        #division by zero means its a vertical line
-        #so we will handle that specifically
-        return proximity_vline_test( x1, y1, y2, xt, yt, threshold )
-
-    x = x1
-    y = yt
-
-    #calculate y intercept (i.e. line offset) for the line segment
-    b = (m * -1.0 * x1) + y1
-
-    #calculate y intercept (i.e. line offset) for the tangent line through (xt,yt)
-    bt = (mt * -1.0 * xt) + yt
-
-    #find the point where the tangent line and given line would cross
-    x = (bt-b)/(m-mt)
-    y = m*x+ b
-
-    #configure min/max x y values for segment
-    minx = x1
-    maxx = x2
-    if x2 < x1:
-        minx = x2
-        maxx = x1
-
-    if minx <= x <= maxx:
-        #test against tangent point
-        if proximity_point_test( xt, yt, x, y ) <= threshold: return 1
-
+    # Coincident points?
+    if x1 == x2 and y1 == y2:
+        d = distance_between(xt, yt, x1, y1)
     else:
-        #test against line end point 1
-        if proximity_point_test( xt, yt, x1, y1 ) <= threshold: return 1
-
-        #test against line end point 2
-        if proximity_point_test( xt, yt, x2, y2 ) <= threshold: return 1
-
-    #default no-intersect
-    return 0
-
-
-
-
-#-----------------------------------------------------------------------
-# proximity_point_test()
-# returns the distance between two points
-# added 6/2/05 by Snowdog
-#-----------------------------------------------------------------------
-def proximity_point_test( x1, y1, x2, y2 ):
-    "Calculates the distance between points"
-    d = math.sqrt( math.pow((x2 - x1),2) + math.pow((y2 - y1),2))
-    return d
-
-
-
-#-----------------------------------------------------------------------
-# proximity_vline_test()
-# checks for proximity to a vertical line
-# (special case check where line slope DNE)
-# added 6/2/05 by Snowdog
-#-----------------------------------------------------------------------
-def proximity_vline_test( x, y1, y2, xt, yt, threshold ):
-    "given a vertical line segment (at x between y1 and y2) is the point (xt,xy) within 'threshold' units"
-
-    #if point not close enough to line segment on x-axis return False
-    if xt < (x - threshold) or x > (x + threshold): return 0
-
-    #check if y cood of point falls within vertical bounds of line segment
-    if y1 <= y2:
-        if y1 <= yt and yt <= y2: return 1
-    else:
-        if y2 <= yt and yt <= y1: return 1
-
-    #if we get this far then xt is in bounds but yt is out.
-    #figure out the distance from (xt,yt) to each of the line segment
-    #end points. if either point is within threshold units then the point
-    #is within proximity of the line
-
-    d1 = proximity_point_test( xt, yt, x, y1 )
-    d2 = proximity_point_test( xt, yt, x, y2 )
-    if d1 <= threshold or d2 <= threshold : return 1
-    return 0
+        dx = x2 - x1
+        dy = y2 - y1
+        u = ((xt - x1) * dx + (yt - y1) * dy) / (dx*dx + dy*dy)
+        if u < 0:
+            d = distance_between(xt, yt, x1, y1)
+        elif u > 1:
+            d = distance_between(xt, yt, x2, y2)
+        else:
+            xr = x1 + u * dx
+            yr = y1 + u * dy
+            d = distance_between(xt, yt, xr, yr)
+    return d <= threshold
