Skip to content

Commit

Permalink
Fix: ccw() floating point representation error.
Browse files Browse the repository at this point in the history
Due to the way floating point numbers are represented, there is only
15-17 significant decimal digits of precision, so in some cases ccw
would evaluate 3 points as not collinear, when they should be.

This is fixed by rounding the area calculation to a fixed tolerance.
I have set this to 13. In the future may allow users to set this
through the API.

Resolves: #19
See also: #20
  • Loading branch information
TaipanRex committed Jul 4, 2017
1 parent 0e1a6b0 commit 4e288ea
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 4 deletions.
4 changes: 2 additions & 2 deletions pyvisgraph/visible_vertices.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from pyvisgraph.graph import Point

INF = 10000

COLIN_TOLERANCE = 13

def visible_vertices(point, graph, origin=None, destination=None, scan='full'):
"""Returns list of Points in graph visible by point.
Expand Down Expand Up @@ -283,7 +283,7 @@ def angle2(point_a, point_b, point_c):

def ccw(A, B, C):
"""Return 1 if counter clockwise, -1 if clock wise, 0 if collinear """
area = (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x)
area = round((B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x), COLIN_TOLERANCE)
if area > 0: return 1
if area < 0: return -1
return 0
Expand Down
24 changes: 22 additions & 2 deletions tests/test_pvg.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from pyvisgraph.visible_vertices import edge_intersect, point_edge_distance
from pyvisgraph.visible_vertices import visible_vertices, angle
from pyvisgraph.visible_vertices import intersect_point, edge_distance
from math import pi, degrees
from math import pi, degrees, cos, sin
import pyvisgraph as vg

'''
Expand Down Expand Up @@ -119,6 +119,7 @@ def test_point_edge_distance_function():
assert point_edge_distance(point_f, point_g, edge3) == 1.4142135623730951
assert point_edge_distance(point_h, point_g, edge3) == 0.9428090415820635


def test_point_in_polygon():
g = vg.VisGraph()
point_a = Point(0,0)
Expand All @@ -128,6 +129,7 @@ def test_point_in_polygon():
g.build([[point_a, point_b, point_c]])
assert g.point_in_polygon(point_d) != -1


class TestClosestPoint:

def setup_method(self, method):
Expand Down Expand Up @@ -157,9 +159,9 @@ def test_closest_point_edge_point(self):
p = Point(1,0.9)
pid = g.point_in_polygon(p)
cp = g.closest_point(p, pid, length=0.001)
print(cp)
assert g.point_in_polygon(cp) == -1


class TestCollinear:

def setup_method(self, method):
Expand Down Expand Up @@ -199,3 +201,21 @@ def test_collin4(self):
[Point(2,4)]])
visible = visible_vertices(Point(2,1), graph, None, None)
assert visible == [Point(3,1), Point(2,2), Point(1,1)]

def test_collin5(self):
r = 0.2 # Radius of polygon
n = 4 # Sides of polygon
c = Point(1.0, 1.0) # Center of polygon
verts = []
for i in range(n):
verts.append(Point(r * cos(2*pi * i/n - pi/4) + c.x,
r * sin(2*pi * i/n - pi/4) + c.y))
g = vg.VisGraph()
g.build([verts])
s = Point(0, 0)
t = Point(1.7, 1.7)
shortest = g.shortest_path(s, t)
visible = visible_vertices(t, g.graph, s, None)
assert verts[3] not in visible
assert verts[1] not in shortest
assert verts[3] not in shortest

0 comments on commit 4e288ea

Please sign in to comment.