11import unittest
22import math
3+ import random
34
45from pygorithm .geometry import (
56 rect_broad_phase ,
@@ -213,6 +214,193 @@ def test_magnitude(self):
213214 magn = vec1 .magnitude ()
214215 self .assertEqual (5 , magn )
215216
217+ class TestLine2 (unittest .TestCase ):
218+ def setUp (self ):
219+ random .seed ()
220+
221+ self .vec_origin = vector2 .Vector2 (0 , 0 )
222+ self .vec_1_1 = vector2 .Vector2 (1 , 1 )
223+ self .vec_2_1 = vector2 .Vector2 (2 , 1 )
224+ self .vec_1_2 = vector2 .Vector2 (1 , 2 )
225+ self .vec_3_4 = vector2 .Vector2 (3 , 4 )
226+ self .vec_neg_1_neg_1 = vector2 .Vector2 (- 1 , - 1 )
227+
228+ self .line_origin_1_1 = line2 .Line2 (self .vec_origin , self .vec_1_1 )
229+ self .line_1_1_3_4 = line2 .Line2 (self .vec_1_1 , self .vec_3_4 )
230+ self .line_1_1_2_1 = line2 .Line2 (self .vec_1_1 , self .vec_2_1 )
231+ self .line_1_1_1_2 = line2 .Line2 (self .vec_1_1 , self .vec_1_2 )
232+
233+ def test_constructor (self ):
234+ _line = self .line_origin_1_1
235+
236+ self .assertIsNotNone (_line .start )
237+ self .assertIsNotNone (_line .end )
238+
239+ self .assertEqual (0 , _line .start .x )
240+ self .assertEqual (0 , _line .start .y )
241+ self .assertEqual (1 , _line .end .x )
242+ self .assertEqual (1 , _line .end .y )
243+
244+ with self .assertRaises (ValueError ):
245+ _line2 = line2 .Line2 (self .vec_origin , self .vec_origin )
246+
247+ def test_delta (self ):
248+ self .assertEqual (1 , self .line_origin_1_1 .delta .x )
249+ self .assertEqual (1 , self .line_origin_1_1 .delta .y )
250+ self .assertEqual (2 , self .line_1_1_3_4 .delta .x )
251+ self .assertEqual (3 , self .line_1_1_3_4 .delta .y )
252+
253+ def test_axis (self ):
254+ self .assertAlmostEqual (0.70710678118 , self .line_origin_1_1 .axis .x )
255+ self .assertAlmostEqual (0.70710678118 , self .line_origin_1_1 .axis .y )
256+ self .assertAlmostEqual (0.55470019622 , self .line_1_1_3_4 .axis .x )
257+ self .assertAlmostEqual (0.83205029433 , self .line_1_1_3_4 .axis .y )
258+ self .assertEqual (1 , self .line_1_1_2_1 .axis .x )
259+ self .assertEqual (0 , self .line_1_1_2_1 .axis .y )
260+ self .assertEqual (0 , self .line_1_1_1_2 .axis .x )
261+ self .assertEqual (1 , self .line_1_1_1_2 .axis .y )
262+
263+ def test_normal (self ):
264+ self .assertAlmostEqual (- 0.70710678118 , self .line_origin_1_1 .normal .x )
265+ self .assertAlmostEqual (0.70710678118 , self .line_origin_1_1 .normal .y )
266+ self .assertAlmostEqual (- 0.83205029433 , self .line_1_1_3_4 .normal .x )
267+ self .assertAlmostEqual (0.55470019622 , self .line_1_1_3_4 .normal .y )
268+ self .assertEqual (0 , self .line_1_1_2_1 .normal .x )
269+ self .assertEqual (1 , self .line_1_1_2_1 .normal .y )
270+ self .assertEqual (- 1 , self .line_1_1_1_2 .normal .x )
271+ self .assertEqual (0 , self .line_1_1_1_2 .normal .y )
272+
273+ def test_magnitude_squared (self ):
274+ self .assertAlmostEqual (2 , self .line_origin_1_1 .magnitude_squared )
275+ self .assertAlmostEqual (13 , self .line_1_1_3_4 .magnitude_squared )
276+ self .assertEqual (1 , self .line_1_1_2_1 .magnitude_squared )
277+ self .assertEqual (1 , self .line_1_1_1_2 .magnitude_squared )
278+
279+ def test_magnitude (self ):
280+ self .assertAlmostEqual (1.41421356237 , self .line_origin_1_1 .magnitude )
281+ self .assertAlmostEqual (3.60555127546 , self .line_1_1_3_4 .magnitude )
282+ self .assertEqual (1 , self .line_1_1_2_1 .magnitude )
283+ self .assertEqual (1 , self .line_1_1_1_2 .magnitude )
284+
285+ def test_line_boundaries_x (self ): # min_x, min_y, max_x, max_y
286+ _line = line2 .Line2 (vector2 .Vector2 (- 2 , 3 ), vector2 .Vector2 (1 , - 1 ))
287+ self .assertEqual (- 2 , _line .min_x )
288+ self .assertEqual (1 , _line .max_x )
289+ self .assertEqual (- 1 , _line .min_y )
290+ self .assertEqual (3 , _line .max_y )
291+
292+ def test_slope (self ):
293+ self .assertEqual (1 , self .line_origin_1_1 .slope )
294+ self .assertAlmostEqual (1.5 , self .line_1_1_3_4 .slope )
295+ self .assertEqual (0 , self .line_1_1_1_2 .slope )
296+ self .assertEqual (float ('+inf' ), self .line_1_1_2_1 .slope )
297+
298+ def test_y_intercept (self ):
299+ self .assertEqual (0 , self .line_origin_1_1 .y_intercept )
300+ self .assertAlmostEqual (- 0.5 , self .line_1_1_3_4 .y_intercept )
301+ self .assertTrue (math .isnan (self .line_1_1_1_2 .y_intercept ))
302+ self .assertEqul (1 , self .line_1_1_2_1 .y_intercept )
303+
304+ def test_horizontal (self ):
305+ self .assertFalse (self .line_origin_1_1 .horizontal )
306+ self .assertFalse (self .line_1_1_3_4 .horizontal )
307+ self .assertFalse (self .line_1_1_1_2 .horizontal )
308+ self .assertTrue (self .line_1_1_2_1 .horizontal )
309+
310+ def test_vertical (self ):
311+ self .assertFalse (self .line_origin_1_1 .vertical )
312+ self .assertFalse (self .line_1_1_3_4 .vertical )
313+ self .assertTrue (self .line_1_1_1_2 .vertical )
314+ self .assertFalse (self .line_1_1_2_1 .vertical )
315+
316+ def test_repr (self ):
317+ self .assertEqual ('line2(start=vector2(x=1, y=1), end=vector2(x=3, y=4))' , repr (self .line_1_1_3_4 ))
318+
319+ def test_str (self ):
320+ self .assertEqual ('<1, 1> -> <3, 4>' , str (self .line_1_1_3_4 ))
321+
322+ def test_calculate_y_intercept (self ):
323+ self .assertAlmostEqual (0.66666666667 , self .line_1_1_3_4 .calculate_y_intercept (self .vec_1_1 ))
324+
325+ def test_are_parallel (self ):
326+ self .assertFalse (line2 .Line2 .are_parallel (self .line_origin_1_1 , self .line_1_1_3_4 ))
327+
328+ _line = line2 .Line2 (vector2 .Vector2 (5 , 4 ), vector2 .Vector2 (3 , 1 ))
329+ self .assertTrue (line2 .Line2 .are_parallel (self .line_1_1_3_4 , _line ))
330+
331+ @staticmethod
332+ def _find_intr_fuzzer (v1 , v2 , v3 , v4 , exp_touching , exp_overlap , exp_intr , number_fuzzes = 3 ):
333+ for i in range (number_fuzzes ):
334+ offset1 = vector2 .Vector2 (random .randrange (- 1000 , 1000 , 0.01 ), random .randrange (- 1000 , 1000 , 0.01 ))
335+ offset2 = vector2 .Vector2 (random .randrange (- 1000 , 1000 , 0.01 ), random .randrange (- 1000 , 1000 , 0.01 ))
336+
337+ _line1 = line2 .Line2 (v1 - offset1 , v2 - offset1 )
338+ _line2 = line2 .Line2 (v3 - offset2 , v4 - offset2 )
339+
340+ help_msg = 'v1={}, v2={}, offset1={}\n _line1={}\n v3={}, v4={}, offset2={}\n _line2={}' .format (repr (v1 ), \
341+ repr (v2 ), repr (offset1 ), repr (_line1 ), repr (v3 ), repr (v4 ), repr (offset2 ), repr (_line2 ))
342+
343+ touching , overlap , intr = line2 .Line2 .find_intersection (_line1 , _line2 , offset1 , offset2 )
344+ self .assertEqual (exp_touching , touching , help_msg )
345+ self .assertEqual (exp_overlap , overlap , help_msg )
346+
347+ if exp_intr is None :
348+ self .assertIsNone (intr , help_msg )
349+ else :
350+ self .assertIsNotNone (intr , help_msg )
351+
352+ if isinstance (exp_intr , vector2 .Vector2 ):
353+ self .assertIsInstance (intr , vector2 .Vector2 , help_msg )
354+
355+ self .assertAlmostEqual (exp_intr .x , intr .x )
356+ self .assertAlmostEqual (exp_intr .y , intr .y )
357+ else :
358+ self .assertIsInstance (exp_intr , line2 .Line2 , help_msg )
359+ self .assertIsInstance (intr , line2 .Line2 , help_msg )
360+
361+ self .assertAlmostEqual (exp_intr .start .x , intr .start .x )
362+ self .assertAlmostEqual (exp_intr .start .y , intr .start .y )
363+ self .assertAlmostEqual (exp_intr .end .x , intr .end .x )
364+ self .assertAlmostEqual (exp_intr .end .y , itnr .end .y )
365+
366+
367+ def test_find_intersection_non_parallel_no_intersection (self ):
368+ self ._find_intr_fuzzer (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 ),
369+ vector2 .Vector2 (5 , 4 ), vector2 .Vector2 (7 , 3 ),
370+ False , False , None )
371+
372+ def test_find_intersection_parallel_no_intersection (self ):
373+ self ._find_intr_fuzzer (vector2 .Vector2 (1 , 1 ), vector2 .Vector2 (3 , 3 ),
374+ vector2 .Vector2 (2 , 1 ), vector2 .Vector2 (4 , 3 ),
375+ False , False , None )
376+
377+ def test_find_intersection_non_parallel_intersect_at_edge (self ):
378+ self ._find_intr_fuzzer (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 ),
379+ vector2 .Vector2 (1 , 6 ), vector2 .Vector2 (5 , 2 ),
380+ True , False , vector2 .Vector2 (3 , 4 ))
381+
382+ def test_find_intersection_non_parallel_intersect_not_edge (self ):
383+ self ._find_intr_fuzzer (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 ),
384+ vector2 .Vector2 (3.5 , 7 ), vector2 .Vector2 (4.5 , 4 ),
385+ False , True , vector2 .Vector2 (4.125 , 5.125 ))
386+
387+ def test_find_intersection_parallel_intersect_at_edge (self ):
388+ self ._find_intr_fuzzer (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 ),
389+ vector2 .Vector2 (5 , 6 ), vector2 .Vector2 (7 , 8 ),
390+ True , False , vector2 .Vector2 (5 , 6 ))
391+
392+ def test_find_intersection_parallel_intersect_overlap (self ):
393+ self ._find_intr_fuzzer (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 ),
394+ vector2 .Vector2 (4 , 5 ), vector2 .Vector2 (7 , 8 ),
395+ False , True , line2 .Line2 (vector2 .Vector2 (4 , 5 ), vector2 .Vector2 (5 , 6 )))
396+
397+ def test_find_intersection_parallel_overlap_compeletely (self ):
398+ self ._find_intr_fuzzer (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 ),
399+ vector2 .Vector2 (2 , 3 ), vector2 .Vector2 (7 , 8 ),
400+ False , True , line2 .Line2 (vector2 .Vector2 (3 , 4 ), vector2 .Vector2 (5 , 6 )))
401+
402+
403+
216404
217405if __name__ == '__main__' :
218406 unittest .main ()
0 commit comments