xocoatzin / pyeuclid

Automatically exported from code.google.com/p/pyeuclid
0 stars 0 forks source link

Distance from point to line produces exception instead of 0. #12

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Create a line and a colinear point.
problem_point = euclid.Point2(0, 30)
problem_line = euclid.Line2(euclid.Point2(0, 0), euclid.Point2(0, 50))
2. Attempt to find the distance from the point to the line.
distance = problem_point.distance(problem_line)

What is the expected output? 
0, at least approximately.

What do you see instead?

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import euclid
>>> problem_point = euclid.Point2(0.0, 30.0)
>>> problem_line = euclid.Line2(euclid.Point2(0.0, 0.0), euclid.Point2(0.0, 50.0
))
>>> problem_point.distance(problem_line)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Program Files\Python 2.7.1\lib\site-packages\euclid.py", line 1589, i
n distance
    c = self.connect(other)
  File "C:\Program Files\Python 2.7.1\lib\site-packages\euclid.py", line 1713, i
n connect
    return other._connect_point2(self)
  File "C:\Program Files\Python 2.7.1\lib\site-packages\euclid.py", line 1792, i
n _connect_point2
    return _connect_point2_line2(other, self)
  File "C:\Program Files\Python 2.7.1\lib\site-packages\euclid.py", line 1652, i
n _connect_point2_line2
    L.p.y + u * L.v.y))
  File "C:\Program Files\Python 2.7.1\lib\site-packages\euclid.py", line 1757, i
n __init__
    raise AttributeError, 'Line has zero-length vector'
AttributeError: Line has zero-length vector
>>>

What version of the product are you using? On what operating system?
Unexcepted behavior exists in latest revision located at 
http://code.google.com/p/pyeuclid/source/browse/trunk/euclid.py. Tested on 
python 2.7.1 running on Windows XP.

Please provide any additional information below.

I believe the error is the result of this code here:

    return LineSegment2(P, 
                        Point2(L.p.x + u * L.v.x,
                               L.p.y + u * L.v.y))

on lines 1666-1668.

That line segment appears to be the shortest line segment between the point and 
the line, but since the point is colinear with the line, its length would be 
zero and its constructor raises an exception. The Geometry class defines 
distance on line 1604 to return 0.0 if the connection returns None, but it 
isn't prepared to handle the exception, which is therefore allowed to propagate 
to the caller.

Not sure if this is really a bug. It seems like the distance function should 
return 0 in this case.

I don't have any practice writing real test cases ... but maybe this would help.

def test():
    import euclid
    point = euclid.Point2(0.0, 30.0)
    line = euclid.Line2(euclid.Point2(0.0, 0.0),  euclid.Point2(0.0, 50.0))
    try:
        distance = point.distance(line)
    except AttributeError:
        return False
    if (distance < EPSILON):
        return True
    else:
        return False

It returns False, failure, for me with the unmodified latest code.

When I replace _connect_point2_line2 with this (added some code to catch the 
exception and return None, indicating the inability to connect):

def _connect_point2_line2(P, L):
    d = L.v.magnitude_squared()
    assert d != 0
    u = ((P.x - L.p.x) * L.v.x + \
         (P.y - L.p.y) * L.v.y) / d
    if not L._u_in(u):
        u = max(min(u, 1.0), 0.0)
    try:
        return LineSegment2(P, 
                            Point2(L.p.x + u * L.v.x,
                                   L.p.y + u * L.v.y))
    except AttributeError:
        # LineSegment2 is complaining because it's of length 0.
        return None

test() returns True.

Original issue reported on code.google.com by asd454...@yahoo.com on 23 May 2011 at 1:51

GoogleCodeExporter commented 9 years ago
Whoops.

"problem_point = euclid.Point2(0, 30)
problem_line = euclid.Line2(euclid.Point2(0, 0), euclid.Point2(0, 50))"

In the steps to reproduce should be

"problem_point = euclid.Point2(0.0, 30.0)
problem_line = euclid.Line2(euclid.Point2(0.0, 0.0), euclid.Point2(0.0, 50.0))"

The direct copy and paste from my interpreter under "What do you see instead?" 
correctly uses floats instead of integers.

Sorry.

Original comment by asd454...@yahoo.com on 23 May 2011 at 1:55

schmittlema commented 8 years ago

So I may have a fix, more so a patch. I am new to fixing things and posting so let me know if I am real off here... This error only occurs when the point is inside the line so the distance between them is 0. It is creating a line and trying to measure the length of it, but euclid cannot create 0 length lines. To patch this I did the following and it seemed to work. Let me know you thoughts.

def distance(self, other): try: c = self.connect(other) except AttributeError: c = 0 if c: return c.length return 0.0