1+ """
2+ axisall
3+
4+ Author: Timothy Moore
5+
6+ Defines a class for handling axis-aligned two-dimensional lines
7+ segments. This class simplifies intermediary calculations in
8+ SAT and similiar algorithms.
9+
10+ These are 2dimensional axis-aligned objects
11+ https://en.wikipedia.org/wiki/Axis-aligned_object
12+ """
13+
14+ import math
15+
16+ class AxisAlignedLine (object ):
17+ """
18+ Define an axis aligned line.
19+
20+ This class provides functions related to axis aligned lines as well as
21+ acting as a convienent container for them. In this context, an axis
22+ aligned line is a two-dimensional line that is defined by an axis and
23+ length on that axis, rather than two points. When working with two lines
24+ defined as such that have the same axis, many calculations are
25+ simplified.
26+
27+ .. note::
28+
29+ Though it requires the same amount of memory as a simple representation of
30+ a 2 dimensional line (4 numerics), it cannot describe all types of lines.
31+ All lines that can be defined this way intersect (0, 0).
32+
33+ .. note::
34+
35+ `min` and `max` are referring to nearness to negative and positive infinity,
36+ respectively. The absolute value of `min` may be larger than that of `max`.
37+
38+ .. note::
39+
40+ AxisAlignedLines are an intermediary operation, so offsets should be baked
41+ into them.
42+
43+ :ivar axis: the axis this line is on
44+ :vartype axis: :class:`pygorithm.geometry.vector2.Vector2`
45+ :ivar min: the point closest to negative infinity
46+ :vartype min: :class:`numbers.Number`
47+ :ivar max: the point closest to positive infinity
48+ :vartype max: :class:`numbers.Number`
49+ """
50+
51+ def __init__ (self , axis , point1 , point2 ):
52+ """
53+ Construct an axis aligned line with the appropriate min and max.
54+
55+ :param axis: axis this line is on (for bookkeeping only, may be None)
56+ :type axis: :class:`pygorithm.geometry.vector2.Vector2`
57+ :param point1: one point on this line
58+ :type point1: :class:`numbers.Number`
59+ :param point2: a different point on this line
60+ :type point2: :class:`numbers.Number`
61+ """
62+
63+ self .axis = axis
64+ self .min = min (point1 , point2 )
65+ self .max = max (point1 , point2 )
66+
67+ @staticmethod
68+ def intersects (line1 , line2 ):
69+ """
70+ Determine if the two lines intersect
71+
72+ Determine if the two lines are touching, if they are overlapping, or if
73+ they are disjoint. Lines are touching if they share only one end point,
74+ whereas they are overlapping if they share infinitely many points.
75+
76+ .. note::
77+
78+ It is rarely faster to check intersection before finding intersection if
79+ you will need the minimum translation vector, since they do mostly
80+ the same operations.
81+
82+ .. tip::
83+
84+ This will never return ``True, True``
85+
86+ :param line1: the first line
87+ :type line1: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
88+ :param line2: the second line
89+ :type line2: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
90+ :returns: (touching, overlapping)
91+ :rtype: (bool, bool)
92+ """
93+
94+ if math .isclose (line1 .max , line2 .min ):
95+ return True , False
96+ elif math .isclose (line1 .min , line2 .max ):
97+ return True , False
98+ elif line1 .max < line2 .min :
99+ return False , False
100+ elif line1 .min > line2 .max :
101+ return False , False
102+
103+ return False , True
104+
105+ @staticmethod
106+ def find_intersection (line1 , line2 ):
107+ """
108+ Calculate the MTV between line1 and line2 to move line1
109+
110+ Determine if the two lines are touching and/or overlapping and then
111+ returns the minimum translation vector to move line 1 along axis. If the
112+ result is negative, it means line 1 should be moved in the opposite
113+ direction of the axis by the magnitude of the result.
114+
115+
116+ Returns `true, (None, touch_point_numeric, touch_point_numeric)` if the lines are touching
117+ and not overlapping.
118+
119+ .. note::
120+
121+ Ensure your program correctly handles `true, (None, numeric, numeric)`
122+
123+
124+ :param line1: the first line
125+ :type line1: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
126+ :param line2: the second line
127+ :type line2: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
128+ :returns: (touching, (mtv against 1, intersection min, intersection max))
129+ :rtype: (bool, (:class:`numbers.Number` or None, :class:`numbers.Number`, :class:`numbers.Number`) or None)
130+ """
131+
132+ if math .isclose (line1 .max , line2 .min ):
133+ return True , (None , line2 .min , line2 .min )
134+ elif math .isclose (line1 .min , line2 .max ):
135+ return True , (None , line1 .min , line1 .min )
136+ elif line1 .max < line2 .min or line2 .max < line1 .min :
137+ return False , None
138+ else :
139+ opt_1 = line2 .min - line1 .max
140+ opt_2 = line2 .max - line1 .min
141+
142+ res_min = max (line1 .min , line2 .min )
143+ res_max = min (line1 .max , line2 .max )
144+
145+ if abs (opt_1 ) < abs (opt_2 ):
146+ return True , (opt_1 , res_min , res_max )
147+ else :
148+ return True , (opt_2 , res_min , res_max )
149+
150+ @staticmethod
151+ def contains_point (line , point ):
152+ """
153+ Determine if the line contains the specified point.
154+
155+ The point must be defined the same way as min and max.
156+
157+ .. tip::
158+
159+ It is not possible for both returned booleans to be `True`.
160+
161+ :param line: the line
162+ :type line: :class:`pygorithm.geometry.axisall.AxisAlignedLine`
163+ :param point: the point
164+ :type point: :class:`numbers.Number`
165+ :returns: (if the point is an edge of the line, if the point is contained by the line)
166+ :rtype: (bool, bool)
167+ """
168+
169+ if math .isclose (line .min , point ) or math .isclose (line .max , point ):
170+ return True , False
171+ elif point < line .min or point > line .max :
172+ return False , False
173+ else :
174+ return False , True
175+
176+ def __repr__ (self ):
177+ """
178+ Create an unambiguous representation of this axis aligned
179+ line.
180+
181+ Example:
182+
183+ .. code-block:: python
184+
185+ from pygorithm.geometry import axisall
186+
187+ aal = axisall.AxisAlignedLine(None, 3, 5)
188+
189+ # prints AxisAlignedLine(axis=None, min=3, max=5)
190+ print(repr(aal))
191+
192+ :returns: un-ambiguous representation of this line
193+ :rtype: string
194+ """
195+
196+ return "AxisAlignedLine(axis={}, min={}, max={})" .format (repr (self .axis ), self .min , self .max )
197+
198+ def __str__ (self ):
199+ """
200+ Create a human-readable representation of this axis aligned line.
201+
202+ Example:
203+
204+ .. code-block:: python
205+
206+ from pygorithm.geometry import axisall
207+
208+ aal = axisall.AxisAlignedLine(None, 0.7071234, 0.7071234)
209+
210+ # prints axisall(along None from 0.707 to 0.707)
211+ print(aal)
212+
213+ :returns: human-readable representation of this line
214+ :rtype: string
215+ """
216+
217+ pretty_min = round (self .min * 1000 ) / 1000
218+ if pretty_min == math .floor (pretty_min ):
219+ pretty_min = math .floor (pretty_min )
220+
221+ pretty_max = round (self .max * 1000 ) / 1000
222+ if pretty_max == math .floor (pretty_max ):
223+ pretty_max = math .floor (pretty_max )
224+
225+ return "axisall(along {} from {} to {})" .format (str (self .axis ), pretty_min , pretty_max )
0 commit comments