I have 3 points (A, B and X) and a distance (d). I need to make a function that tests if point X is closer than distance d to any point on the line segment AB.
The question is firstly, is my solution correct and then to come up with a better (faster) solution.
My first pass is as follows
AX = X-A
BX = X-B
AB = A-B
// closer than d to A (done squared to avoid needing to compute the sqrt in mag)
If d^2 > AX.mag^2 return true
// closer than d to B
If d^2 > BX.mag^2 return true
// "beyond" B
If (dot(BX,AB) < 0) return false
// "beyond" A
If (dot(AX,AB) > 0) return false
// find component of BX perpendicular to AB
Return (BX.mag)^2 - (dot(AB,BX)/AB.mag)^2 < d^2
This code will end up being run for a large set of P's and a large set of A/B/d triplets with the intent of finding all P's that pass for at least one A/B/d so I suspect that there is a way to reduce overall the cost based on that but I haven't looked into that yet.
(BTW: I am aware that some reordering, some temporary values and some algebraic identities could decrease the cost of the above. I just omitted them for clarity.)
EDIT: this is a 2D problem (but solution that generalizes to 3D would be cool
Edit: On further reflection, I expect the hit rate to be around 50%. The X point can be formed in a nested hierarchy so I expect to be able to prune large subtrees as all-pass and all-fail. The A/B/d limiting the triplets will be more of a trick.
Edit: d is in the same order of magnitude as AB.
edit: Artelius posted a nice solution. I'm not sure I understand exactly what he's getting at as I got off on a tangent before I fully understood it. Anyway another thought came to mind as a result:
- First Artelius' bit, pre-cacluate a matrix that will place AB centered ate the origin and aligned with the X-axis. (2 adds, 4 muls and 2 adds)
- fold it all into the 1st quadrant (2 abs)
- scale in X&Y to make the central portion of the zone into a unit square (2 mul)
- test if the point is in that square (2 test) is so quit
- test the end cap (go back to the unscaled values
- translate in x to place the end at the origin (1 add)
- square and add (2 mul, 1 add)
- compare to d^2 (1 cmp)
I'm fairly sure this beats my solution.
(if nothing better comes along sone Artelius gets the "prize" :)