Skip to content

Commit 39a44fc

Browse files
committed
Axis-aligned line and line2 tests
Currently I'm running py 3.4.4 which doesn't have math.isclose. I'm not sure the best place to put a similar method, right now I've replicated it to the two locations that need it. Polygon will also need it. I'll try to update python before pushing this upstream * pygorithm/geometry/axisall.py - Implement everything, change intersects return values and find_intersection return values (and update docs) * pygorithm/geometry/line2.py - Implement everything. find_intersection could be broken up but the subfunctions would need a lot of parameters or it would repeat a lot of math * tests/test_geometry.py - fix incorrect tests, use random.uniform instead of random.randrange
1 parent 7a13d05 commit 39a44fc

File tree

3 files changed

+412
-55
lines changed

3 files changed

+412
-55
lines changed

pygorithm/geometry/axisall.py

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
https://en.wikipedia.org/wiki/Axis-aligned_object
1212
"""
1313

14+
import math
15+
1416
class AxisAlignedLine(object):
1517
"""
1618
Define an axis aligned line.
@@ -50,32 +52,54 @@ def __init__(self, axis, point1, point2):
5052
"""
5153
Construct an axis aligned line with the appropriate min and max.
5254
53-
:param axis: axis this line is on
55+
:param axis: axis this line is on (for bookkeeping only, may be None)
5456
:type axis: :class:`pygorithm.geometry.vector2.Vector2`
5557
:param point1: one point on this line
5658
:type point1: :class:`numbers.Number`
5759
:param point2: a different point on this line
5860
:type point2: :class:`numbers.Number`
5961
"""
6062

61-
pass
63+
self.axis = axis
64+
self.min = min(point1, point2)
65+
self.max = max(point1, point2)
66+
67+
@staticmethod
68+
def _approx(a, b):
69+
"""
70+
Same as math.isclose but supports python < 3.5
71+
72+
:param a: first numeric
73+
:type a: :class:`numbers.Number`
74+
:param b: second numeric
75+
:type b: :class:`numbers.Number`
76+
:returns: if the are close
77+
:rtype: bool
78+
"""
79+
80+
if hasattr(math, 'isclose'):
81+
return math.isclose(a, b)
82+
return abs(a - b) <= 1e-09 * max(abs(a), abs(b))
6283

6384
@staticmethod
6485
def intersects(line1, line2):
6586
"""
6687
Determine if the two lines intersect
6788
68-
Determine if the two lines are touching and if they are, if
69-
they are overlapping. Lines are touching if they share only
70-
one end point, whereas they are overlapping if they share
71-
infinitely many points.
89+
Determine if the two lines are touching, if they are overlapping, or if
90+
they are disjoint. Lines are touching if they share only one end point,
91+
whereas they are overlapping if they share infinitely many points.
7292
7393
.. note::
7494
7595
It is rarely faster to check intersection before finding intersection if
7696
you will need the minimum translation vector, since they do mostly
7797
the same operations.
7898
99+
.. tip::
100+
101+
This will never return ``True, True``
102+
79103
:param line1: the first line
80104
:type line1: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
81105
:param line2: the second line
@@ -84,7 +108,16 @@ def intersects(line1, line2):
84108
:rtype: (bool, bool)
85109
"""
86110

87-
pass
111+
if AxisAlignedLine._approx(line1.max, line2.min):
112+
return True, False
113+
elif AxisAlignedLine._approx(line1.min, line2.max):
114+
return True, False
115+
elif line1.max < line2.min:
116+
return False, False
117+
elif line1.min > line2.max:
118+
return False, False
119+
120+
return False, True
88121

89122
@staticmethod
90123
def find_intersection(line1, line2):
@@ -97,22 +130,39 @@ def find_intersection(line1, line2):
97130
direction of the axis by the magnitude of the result.
98131
99132
100-
Returns `true, None` if the lines are touching.
133+
Returns `true, (None, touch_point_numeric, touch_point_numeric)` if the lines are touching
134+
and not overlapping.
101135
102136
.. note::
103137
104-
Ensure your program correctly handles `true, None`
138+
Ensure your program correctly handles `true, (None, numeric, numeric)`
105139
106140
107141
:param line1: the first line
108142
:type line1: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
109143
:param line2: the second line
110144
:type line2: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
111-
:returns: (touching, mtv against 1)
112-
:rtype: (bool, :class:`numbers.Number` or None)
145+
:returns: (touching, (mtv against 1, intersection min, intersection max))
146+
:rtype: (bool, (:class:`numbers.Number` or None, :class:`numbers.Number`, :class:`numbers.Number`) or None)
113147
"""
114148

115-
pass
149+
if AxisAlignedLine._approx(line1.max, line2.min):
150+
return True, (None, line2.min, line2.min)
151+
elif AxisAlignedLine._approx(line1.min, line2.max):
152+
return True, (None, line1.min, line1.min)
153+
elif line1.max < line2.min or line2.max < line1.min:
154+
return False, None
155+
else:
156+
opt_1 = line2.min - line1.max
157+
opt_2 = line2.max - line1.min
158+
159+
res_min = max(line1.min, line2.min)
160+
res_max = min(line1.max, line2.max)
161+
162+
if abs(opt_1) < abs(opt_2):
163+
return True, (opt_1, res_min, res_max)
164+
else:
165+
return True, (opt_2, res_min, res_max)
116166

117167
@staticmethod
118168
def contains_point(line, point):
@@ -132,4 +182,10 @@ def contains_point(line, point):
132182
:returns: (if the point is an edge of the line, if the point is contained by the line)
133183
:rtype: (bool, bool)
134184
"""
135-
pass
185+
186+
if AxisAlignedLine._approx(line.min, point) or AxisAlignedLine._approx(line.max, point):
187+
return True, False
188+
elif point < line.min or point > line.max:
189+
return False, False
190+
else:
191+
return False, True

0 commit comments

Comments
 (0)