views:

499

answers:

3

Hi everyone,

I'm searching for an algorithm to calculate the average distance between a point and a line segment in 3D. So given two points A(x1, y1, z1) and B(x2, y2, z2) that represent line segment AB, and a third point C(x3, y3, z3), what is the average distance between each point on AB to point C?

I'm also interested in the average distance between two line segments. So given segment AB and CD, what is the average distance from each point on AB to the closest point on CD?

I haven't had any luck with the web searches I've tried, so any suggestions would be appreciated.

Thanks.

+1  A: 

First, the distance between two points is the square root of the sum of the squares of the pairwise differences of the coordinates. (For example, the distance from (0,0,0) to (1,1,1) is sqrt(3) but this works for arbitrary points in any number of dimensions.) This distance is known as the l2-norm (lower-case L) or Euclidean norm. Write norm(A,B) for the distance between points A and B.

On to the interesting problem of average distances... (Note that finding the minimum distance from a point to a line or between line segments is a much more common problem. There was an answer here with good pointers for that problem but it seems it was deleted in the meantime.)

To find the average distance from a point C to a line segment AB, consider the distance to an arbitrary point between A and B, namely (1-k)A+kB where k ranges from 0 to 1. That's norm(C, (1-k)A+kB). So the average distance is the integral from k = 0 to 1 of norm(C, (1-k)A+kB).

Mathematica can do that integral for any specific A, B, and C.

Here's a Mathematica implementation:

avgd[A_,B_,C_] :=  Integrate[Sqrt@Dot[(1-k)*A+k*B-C, (1-k)*A+k*B-C], {k, 0, 1}]

The integrand can also be written Norm[(1-k)*A+k*B-C]. Either way, Mathematica can do it for specific points but can't integrate it symbolically, though apparently David got it to do so somehow. Here's David's example from the comments:

> avgd[{0, 0, 0}, {4, 0, 0}, {4, 3, 0}] // N

3.73594

For the problem of the average distance between two line segments, in theory I think this should work:

avgd[A_,B_,C_,D_] := Integrate[Norm[(1-k)A+k*B - (1-j)C - j*D], {k,0,1}, {j,0,1}]

But Mathematica seems to choke on that even for specific points, let alone symbolically.

dreeves
Interesting method, can you point me to an explanation/proof? (I'm just curious)
David Zaslavsky
...and now that I think about it, I believe something's wrong with that formula. Consider `A=(0,0,0)`, `B=(4,0,0)`, and either `C=(3.99999999,3,0)` or `C=(4.000000001,3,0)`. In the former case, your first formula (for when D lies on AB) yields average distance of 3.5, but in the second case your second formula yields distance 4, despite the fact that the two should be nearly identical. (My own analytical calculation gives 3.7359)
David Zaslavsky
Crap, I started to sketch a proof and found I was wrong! Stand by for update...
dreeves
Yes, thanks David. Editing my answer now.
dreeves
So long as youre editing it, I dont think you meant to say "Drop a perpendicular from A to the line that AB lies on"
Karl
I'm now fairly sure my answer is correct at least for the point-to-the-line problem, albeit not terribly helpful unless you use a programming language that can do integrals.
dreeves
Worst case you can always approximate the integrate using numerical quadrature, e.g. the Newton-Cotes formulas.
+1  A: 

If you mean what I think you mean by "average" (and "distance," i.e. the L2 norm mentioned by dreeves), here's a procedure that I think should work for finding the average distance between a point and a line segment. You'll need a function dot(A,B) which takes the dot product of two vectors.

// given vectors (points) A, B, C
K1 = dot(A-C,A-C)
K2 = 2*dot(B-A,A-C)
K3 = dot(B-A,B-A)
L1 = sqrt(K3*(K1+K2+K3))
L2 = sqrt(K3*K1)
N = 4*K3*L1 + 2*K2*(L1-L2) + (K2*K2-4*K1*K3)*log((K2+2*L2)/(2*K3+K2+2*L1))
D = N / (8*K3^1.5)

Assuming I've transcribed everything correctly, D will then be the average distance.

This is basically just pseudocode for evaluating the result of an integral that I did in Mathematica. There may be some neat computational shortcut for this but if there is, I don't know it. (And unless there is one, I'd question how much you really need to do this computation)

If you want to find the average distance from the closest point on a line segment CD to all points on AB, in most cases the closest point will be either C or D so you can just check both of those to see which is closer (probably using some minimum-distance calculation as referenced in other answers). The only exception is when CD and AB are parallel and you could run a perpendicular from one to the other, in which case you'd have to define your requirements more precisely.

If you wanted to find the average distance between all points on CD and all points on AB... it could be done with a double integral, though I shudder to think how complicated the resulting formula would be.

David Zaslavsky
I'm impressed, David! How did you get Mathematica to evaluate the integral with symbolic A,B,C? Unfortunately, one of us has made an error because when I compare your algorithm to Integrate[Norm[(1-k)A+kB-C], {k,0,1}] for specific A,B,C they don't match. Any ideas?
dreeves
PS: I'm now certain there's an error in David's algorithm, but I haven't figured out how to reproduce what he did in order to determine what the error is!
dreeves
Here's what I did: the line segment can be parametrized as `A+k(B-A)`, so I manually evaluated `(A+k(B-A)-C)^2`, getting `(A-C)^2+2k(B-A).(A-C)+k^2(B-A)^2`. I set `K1=(A-C)^2`,`K2=2(B-A).(A-C)`, and `K3=(B-A)^2` and asked Mathematica to `Integrate[Sqrt[K1 + K2 k + K3 k^2],{k,0,1}]`.
David Zaslavsky
ahh, I think I see what happened: I accidentally switched K1 and K3 when I was typing the formula up. That should be fixed now.
David Zaslavsky
Thanks David! I ran your formula through a bunch of random tests to verify it's correct. At least now I don't feel so bad for not figuring this out on my own. I think I'll try to find a simpler formula that gives a good approximation, if possible.
Fred
That's probably not a bad idea, although the kinds of approximations you can use would depend on your particular application. For instance, how does the minimum distance from your point to your line segment compare to the length of the segment? If one of those is much less than the other, I can think of some quick formulas that would work. If you edit some more detail about your program into this question, we can help you come up with an appropriate approximation.
David Zaslavsky
+1  A: 

Well, if analysis fails, reach for a computer and do a silly amount of calculation until you get a feel for the numbers ...

I too have a copy of Mathematica. To keep things simple, since a triangle must lie in a plane, I've worked the following in 2D space. To keep things extra simple, I specify a point at {0,0} and a line segment from {1,0} to {0,1}. The average distance from point to line must be, if it is meaningful, the average length of all the lines which could be drawn from {0.0} to anywhere on the line segment. Of course, there are an awful lot of such lines, so let's start with, say, 10. In Mathematica this might be computed as

Mean[Table[EuclideanDistance[{0, 0}, {1 - k, 0 + k}], {k, 0, 1, 10.0^-1}]]]

which gives 0.830255. The next step is obvious, make the number of lines I measure larger. In fact, let's make a table of averages as the exponent of 10.0 gets smaller (they're negative !). In Mathematica:

Table[Mean[Table[EuclideanDistance[{0, 0}, {1 - k, 0 + k}], {k, 0, 1, 
10.0^-i}]], {i, 0, 6}]

which produces:

{1, 0.830255, 0.813494, 0.811801, 0.811631, 0.811615, 0.811613}

Following this approach I re-worked @Dave's example (forget the third dimension):

Table[Mean[Table[EuclideanDistance[{0, 0}, {4, 0 + 3 k}], {k, 0, 1, 
10.0^-i}]], {i, 0, 6}]

which gives:

{9/2, 4.36354, 4.34991, 4.34854, 4.34841, 4.34839, 4.34839}

This does not agree with what @dreeves says @Dave's algorithm computes.

EDIT: OK, so I've wasted some more time on this. For the simple example I used in the first place, that is with a point at {0,0} and a line segment extending from {0,1} to {1,0} I define a function in Mathematica (as ever), like this:

fun2[k_] := EuclideanDistance[{0, 0}, {0 + k, 1 - k}]

Now, this is integratable. Mathematica gives:

   In[13]:= Integrate[fun2[k], {k, 0, 1}]

   Out[13]= 1/4 (2 + Sqrt[2] ArcSinh[1])

Or, if you'd rather have numbers, this:

In[14]:= NIntegrate[fun2[k], {k, 0, 1}]
Out[14]= 0.811613

which is what the purely numerical approach I took earlier gives.

I'm now going to get back to work, and leave it to you all to generalise this to an arbitrary triangle defined by a point and the end-points of a line segment.

High Performance Mark
For the last example, I think we're computing different distances. In my comments I was talking about the average distance between the side of length 4 and its opposing vertex, but it seems that you've calculated the average distance between the side of length 3 and its opposing vertex. That probably accounts for why the numbers don't match.
David Zaslavsky
@David -- yes, when I rework my calculations I get 3.73594 too.
High Performance Mark