Skip to content

Commit fd1450f

Browse files
committed
Implement moving point / stationary line
Implements calculate_one_moving_one_stationary_line in extrapolated intersection, and adds "az" tests. * imgs/test_geometry/test_extrapolated_intersection/(many) - Add az test visualizations * pygorithm/geometry/extrapolated_intersection.py - - Implement calculate_one_moving_one_stationary_line * tests/test_geometry.py - Add az* tests.
1 parent 1cbec4b commit fd1450f

7 files changed

+194
-4
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from utils import create_newfig, create_moving_polygon, create_still_polygon, run_or_export
2+
3+
func_code = 'az'
4+
func_name = 'test_one_moving_many_stationary_touching'
5+
6+
def setup_fig01():
7+
fig, ax, renderer = create_newfig('{}01'.format(func_code), xlim=(-1, 12), ylim=(-1, 12))
8+
create_moving_polygon(fig, ax, renderer, ((3, 3, 'botleft'), (4, 3), (4, 4), (3, 4)), (3, 4), 'none')
9+
create_still_polygon(fig, ax, renderer, ((6, 3, 'botleft'), (7, 3), (7, 4), (6, 4)), 'none')
10+
create_still_polygon(fig, ax, renderer, ((3, 6, 'botleft'), (3, 7), (4, 7), (4, 6)), 'none')
11+
create_still_polygon(fig, ax, renderer, ((4, 10), (6, 11), (6, 8), (2, 7)))
12+
13+
return fig, ax, '{}01_{}'.format(func_code, func_name)
14+
15+
def setup_fig02():
16+
fig, ax, renderer = create_newfig('{}02'.format(func_code), xlim=(-3, 9), ylim=(-10, 5))
17+
create_moving_polygon(fig, ax, renderer, ((-1, -9.5), (-1, -5.5), (3, -5.5), (4, -7.5)), (4, 6))
18+
create_still_polygon(fig, ax, renderer, ((6, -6), (8, -7), (7, -9)))
19+
create_still_polygon(fig, ax, renderer, ((0, 2), (2, 3), (1, 1)))
20+
create_still_polygon(fig, ax, renderer, ((-2, -2, 'botleft'), (-2, -1), (-1, -1), (-1, -2)), 'none')
21+
create_still_polygon(fig, ax, renderer, ((8, -4, 'botleft'), (8, -3), (7, -3), (7, -4)), 'none')
22+
23+
return fig, ax, '{}02_{}'.format(func_code, func_name)
24+
25+
def setup_fig03():
26+
fig, ax, renderer = create_newfig('{}03'.format(func_code), xlim=(-1, 21), ylim=(-1, 15))
27+
create_moving_polygon(fig, ax, renderer, ((18.5, 3), (17.5, 3), (17.5, 5), (19.5, 5)), (-12.5, 2))
28+
create_still_polygon(fig, ax, renderer, ((18, 13), (20, 14), (18.5, 11)))
29+
create_still_polygon(fig, ax, renderer, ((5, 5), (6, 2), (3, 3), (2, 4)))
30+
31+
return fig, ax, '{}03_{}'.format(func_code, func_name)
32+
33+
def setup_fig04():
34+
fig, ax, renderer = create_newfig('{}04'.format(func_code), xlim=(-9, 7), ylim=(-4, 6))
35+
create_moving_polygon(fig, ax, renderer, ((-6, 2), (-6, 1), (-8, 0), (-8, 2)), (5, 1))
36+
create_still_polygon(fig, ax, renderer, ((-7, 3, 'botleft'), (-7, 4), (-6, 4), (-6, 3)), 'none')
37+
create_still_polygon(fig, ax, renderer, ((-6, 3, 'botleft'), (-6, 4), (-5, 4), (-5, 3)), 'none')
38+
create_still_polygon(fig, ax, renderer, ((-5, 3, 'botleft'), (-5, 4), (-4, 4), (-4, 3)), 'none')
39+
create_still_polygon(fig, ax, renderer, ((-4, 3, 'botleft'), (-4, 4), (-3, 4), (-3, 3)), 'none')
40+
41+
42+
return fig, ax, '{}04_{}'.format(func_code, func_name)
43+
44+
run_or_export(setup_fig01, setup_fig02, setup_fig03, setup_fig04)

pygorithm/geometry/extrapolated_intersection.py

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,85 @@
1717
1818
"""
1919

20+
from pygorithm.geometry import (vector2, line2, polygon2, axisall)
21+
import math
22+
23+
def __calc_one_moving_one_stat_vertical_velocity(point, velocity, line, offset):
24+
line_as_axisall = axisall.AxisAlignedLine(None, line.min_x + offset.x, line.max_x + offset.x)
25+
outer, inner = axisall.AxisAlignedLine.contains_point(line_as_axisall, point.x)
26+
if not outer and not inner:
27+
return False, None
28+
29+
if line.vertical:
30+
if velocity.y > 0:
31+
if point.y < line.min_y + offset.y:
32+
dist = line.min_y + offset.y - point.y
33+
return True, dist
34+
else:
35+
return False, None
36+
else:
37+
if point.y > line.max_y + offset.y:
38+
dist = point.y - line.max_y - offset.y
39+
return True, dist
40+
else:
41+
return False, None
42+
43+
line_yintr = line.calculate_y_intercept(offset)
44+
liney_at_pointx = line.slope * point.x + line_yintr
45+
46+
point_to_line = liney_at_pointx - point.y
47+
if not math.isclose(math.copysign(1, point_to_line), math.copysign(1, velocity.y)):
48+
return False, None
49+
50+
return True, abs(point.y - liney_at_pointx)
51+
52+
def __calc_one_moving_one_stat_vertical_line(point, velocity, line, offset):
53+
# nonvertical velocity, point not on line
54+
55+
if math.isclose(point.x, line.start.x, abs_tol = 1e-07):
56+
# it's impossible that the point intersects a vertical line that it
57+
# is at the start of except at the point, but that's checked previously
58+
return False, None
59+
60+
if velocity.x > 0 and (line.start.x + offset.x) < point.x:
61+
return False, None
62+
if velocity.x < 0 and (line.start.x + offset.x) > point.x:
63+
return False, None
64+
65+
point_slope = velocity.y / velocity.x
66+
point_yintr = point.y - point_slope * point.x
67+
68+
point_y_at_line_x = point_slope * (line.start.x + offset.x) + point_yintr
69+
70+
line_as_axisall = axisall.AxisAlignedLine(None, line.min_y + offset.y, line.max_y + offset.y)
71+
outer, inner = axisall.AxisAlignedLine.contains_point(line_as_axisall, point_y_at_line_x)
72+
if not outer and not inner:
73+
return False, None
74+
75+
dist = (vector2.Vector2(line.start.x + offset.x, point_y_at_line_x) - point).magnitude()
76+
return True, dist
77+
78+
def __calc_one_moving_one_stat_parallel(point, velocity, line, offset, point_slope):
79+
# vel not vertical, line not vertical, point not on line, line is parallel to velocity
80+
81+
if not math.isclose(line.slope, point_slope, abs_tol=1e-07):
82+
return False, None
83+
84+
line_as_axisall = axisall.AxisAlignedLine(line.axis, (line.start + offset).dot(line.axis), (line.end + offset).dot(line.axis))
85+
point_on_axis = point.dot(line.axis)
86+
vel_on_axis_sign = math.copysign(1, velocity.dot(line.axis))
87+
88+
if point_on_axis < line_as_axisall.min:
89+
if vel_on_axis_sign < 0:
90+
return False, None
91+
else:
92+
return True, line_as_axisall.min - point_on_axis
93+
else:
94+
if vel_on_axis_sign > 0:
95+
return False, None
96+
else:
97+
return True, point_on_axis - line_as_axisall.max
98+
2099
def calculate_one_moving_point_and_one_stationary_line(point, velocity, line, offset):
21100
"""
22101
Determine if the point moving at velocity will intersect the line.
@@ -41,7 +120,40 @@ def calculate_one_moving_point_and_one_stationary_line(point, velocity, line, of
41120
:returns: if the point will intersect the line, distance until intersection
42121
:rtype: bool, :class:`numbers.Number` or None
43122
"""
44-
return False, -1
123+
if offset is None:
124+
offset = vector2.Vector2(0, 0)
125+
126+
if line2.Line2.contains_point(line, point, offset):
127+
return True, 0
128+
129+
if math.isclose(velocity.x, 0, abs_tol=1e-07):
130+
return __calc_one_moving_one_stat_vertical_velocity(point, velocity, line, offset)
131+
if line.vertical:
132+
return __calc_one_moving_one_stat_vertical_line(point, velocity, line, offset)
133+
134+
point_slope = velocity.y / velocity.x
135+
if math.isclose(line.slope, point_slope, abs_tol=1e-07):
136+
return __calc_one_moving_one_stat_parallel(point, velocity, line, offset, point_slope)
137+
138+
point_yintr = point.y - point_slope * point.x
139+
line_yintr = line.calculate_y_intercept(offset)
140+
141+
# m1x + b1 = m2x + b2
142+
# x = b2 - b1 / m1 - m2
143+
intr_x = (line_yintr - point_yintr) / (point_slope - line.slope)
144+
intr_y = point_slope * intr_x + point_yintr
145+
intr_vec = vector2.Vector2(intr_x, intr_y)
146+
if not line2.Line2.contains_point(line, intr_vec, offset):
147+
return False, None
148+
149+
point_to_intr = intr_vec - point
150+
vel_normal = velocity.normalize()
151+
ptintr_dot_veln = point_to_intr.dot(vel_normal)
152+
153+
if ptintr_dot_veln < 0:
154+
return False, None
155+
156+
return True, ptintr_dot_veln
45157

46158
def calculate_one_moving_line_and_one_stationary_line(line1, offset1, velocity1, _line2, offset2):
47159
"""

tests/test_geometry.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,9 +1584,9 @@ def test_point_line_intr_later(self):
15841584
self.assertAlmostEqual(3, dist, msg=repr(offset))
15851585

15861586
# ad04
1587-
intr, dist, offset = fn(self.pt(6, 4), self.pt(-3, -1).normalize(), self.ln(self.pt(1, 1), self.pt(5, 3)))
1587+
intr, dist, offset = fn(self.pt(6, 4), self.pt(-3, -2).normalize(), self.ln(self.pt(1, 1), self.pt(5, 3)))
15881588
self.assertTrue(intr, msg=repr(offset))
1589-
self.assertAlmostEqual(self.pt(-3, -1).magnitude(), dist, msg=repr(offset))
1589+
self.assertAlmostEqual(self.pt(-3, -2).magnitude(), dist, msg=repr(offset))
15901590

15911591

15921592
# calculate_one_moving_line_and_one_stationary_line
@@ -2133,7 +2133,41 @@ def test_one_moving_many_stationary_no_intr(self):
21332133
self.assertFalse(intr, msg=msg)
21342134

21352135
def test_one_moving_many_stationary_touching(self):
2136-
pass
2136+
fn = self._calc_one_moving_many_stat_fuzzer
2137+
2138+
# az01
2139+
intr, msg = fn(((3, 3), (4, 3), (4, 4), (3, 4)), (3, 4), [
2140+
((6, 3), (7, 3), (7, 4), (6, 4)),
2141+
((3, 6), (3, 7), (4, 7), (4, 6)),
2142+
((4, 10), (6, 11), (6, 8), (2, 7))
2143+
])
2144+
self.assertFalse(intr, msg=msg)
2145+
2146+
# az02
2147+
intr, msg = fn(((-1, -9.5), (-1, -5.5), (3, -5.5), (4, -7.5)), (4, 6), [
2148+
((6, -6), (8, -7), (7, -9)),
2149+
((0, 2), (2, 3), (1, 1)),
2150+
((-2, -2), (-2, -1), (-1, -1), (-1, -2)),
2151+
((8, -4), (8, -3), (7, -3), (7, -4))
2152+
])
2153+
self.assertFalse(intr, msg=msg)
2154+
2155+
# az03
2156+
intr, msg = fn(((18.5, 3), (17.5, 3), (17.5, 5), (19.5, 5)), (-12.5, 2), [
2157+
((18, 13), (20, 14), (18.5, 11)),
2158+
((5, 5), (6, 2), (3, 3), (2, 4))
2159+
])
2160+
self.assertFalse(intr, msg=msg)
2161+
2162+
# az04
2163+
intr, msg = fn(((-6, 2), (-6, 1), (-8, 0), (-8, 2)), (-4, 6), [
2164+
((-7, 3), (-7, 4), (-6, 4), (-6, 3)),
2165+
((-6, 3), (-6, 4), (-5, 4), (-5, 3)),
2166+
((-5, 3), (-5, 4), (-4, 4), (-4, 3)),
2167+
((-4, 3), (-4, 4), (-3, 4), (-3, 3))
2168+
])
2169+
self.assertFalse(intr, msg=msg)
2170+
21372171
def test_one_moving_many_stationary_intr_at_start(self):
21382172
pass
21392173
def test_one_moving_many_stationary_intr_later(self):

0 commit comments

Comments
 (0)