|
1 | 1 | import PlaneSweep from '../PlaneSweep'
|
2 | 2 |
|
3 | 3 | describe('PlaneSweep', () => {
|
4 |
| - describe('Constructor', () => { |
5 |
| - test('creates an instance with valid segments', () => { |
| 4 | + let planeSweep |
| 5 | + |
| 6 | + describe('constructor', () => { |
| 7 | + it('should create an instance with valid segments', () => { |
6 | 8 | const segments = [
|
7 |
| - { start: { x: 1, y: 1 }, end: { x: 4, y: 4 } }, |
8 |
| - { start: { x: 1, y: 4 }, end: { x: 4, y: 1 } } |
| 9 | + { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } }, |
| 10 | + { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } } |
9 | 11 | ]
|
10 |
| - const intersection = new PlaneSweep(segments) |
11 |
| - expect(intersection).toBeInstanceOf(PlaneSweep) |
| 12 | + expect(() => new PlaneSweep(segments)).not.toThrow() |
12 | 13 | })
|
13 | 14 |
|
14 |
| - test('throws an error if segments array is invalid', () => { |
| 15 | + it('should throw an error with empty segments array', () => { |
15 | 16 | expect(() => new PlaneSweep([])).toThrow(
|
16 |
| - 'segments must be a non-empty array of objects with both start and end properties.' |
| 17 | + 'segments must be a non-empty array' |
17 | 18 | )
|
18 |
| - expect(() => new PlaneSweep([{ start: { x: 0, y: 0 } }])).toThrow( |
19 |
| - 'segments must be a non-empty array of objects with both start and end properties.' |
| 19 | + }) |
| 20 | + |
| 21 | + it('should throw an error with invalid segments', () => { |
| 22 | + const invalidSegments = [{ start: { x: 0, y: 0 } }] |
| 23 | + expect(() => new PlaneSweep(invalidSegments)).toThrow( |
| 24 | + 'segments must be a non-empty array of objects with start and end properties' |
20 | 25 | )
|
21 | 26 | })
|
22 | 27 | })
|
23 | 28 |
|
24 |
| - describe('Intersection Detection', () => { |
25 |
| - test('detects intersections correctly', () => { |
| 29 | + describe('findIntersections', () => { |
| 30 | + beforeEach(() => { |
26 | 31 | const segments = [
|
27 |
| - { start: { x: 1, y: 1 }, end: { x: 4, y: 4 } }, |
28 |
| - { start: { x: 1, y: 4 }, end: { x: 4, y: 1 } }, |
29 |
| - { start: { x: 5, y: 5 }, end: { x: 6, y: 6 } } |
| 32 | + { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } }, |
| 33 | + { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } } |
30 | 34 | ]
|
31 |
| - const intersection = new PlaneSweep(segments) |
32 |
| - const result = intersection.findIntersections() |
33 |
| - |
34 |
| - // Check if there is one intersection found |
35 |
| - expect(result).toHaveLength(1) // Ensure there's one intersection |
36 |
| - |
37 |
| - const intersectingPair = result[0] |
38 |
| - |
39 |
| - // Check that both segments in the intersection are part of the original segments |
40 |
| - const segment1 = intersectingPair.segment1 |
41 |
| - const segment2 = intersectingPair.segment2 |
| 35 | + planeSweep = new PlaneSweep(segments) |
| 36 | + }) |
42 | 37 |
|
43 |
| - const isSegment1Valid = |
44 |
| - (segment1.start.x === segments[0].start.x && |
45 |
| - segment1.start.y === segments[0].start.y && |
46 |
| - segment1.end.x === segments[0].end.x && |
47 |
| - segment1.end.y === segments[0].end.y) || |
48 |
| - (segment1.start.x === segments[1].start.x && |
49 |
| - segment1.start.y === segments[1].start.y && |
50 |
| - segment1.end.x === segments[1].end.x && |
51 |
| - segment1.end.y === segments[1].end.y) |
| 38 | + it('should find intersections between crossing segments', () => { |
| 39 | + const segments = [ |
| 40 | + { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } }, |
| 41 | + { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } } |
| 42 | + ] |
| 43 | + planeSweep = new PlaneSweep(segments) |
| 44 | + const intersections = planeSweep.findIntersections() |
| 45 | + expect(intersections).toHaveLength(1) |
| 46 | + expect(intersections[0]).toEqual( |
| 47 | + expect.objectContaining({ |
| 48 | + segment1: expect.objectContaining({ |
| 49 | + start: expect.objectContaining({ x: 0, y: expect.any(Number) }), |
| 50 | + end: expect.objectContaining({ x: 2, y: expect.any(Number) }) |
| 51 | + }), |
| 52 | + segment2: expect.objectContaining({ |
| 53 | + start: expect.objectContaining({ x: 0, y: expect.any(Number) }), |
| 54 | + end: expect.objectContaining({ x: 2, y: expect.any(Number) }) |
| 55 | + }) |
| 56 | + }) |
| 57 | + ) |
| 58 | + }) |
52 | 59 |
|
53 |
| - const isSegment2Valid = |
54 |
| - (segment2.start.x === segments[0].start.x && |
55 |
| - segment2.start.y === segments[0].start.y && |
56 |
| - segment2.end.x === segments[0].end.x && |
57 |
| - segment2.end.y === segments[0].end.y) || |
58 |
| - (segment2.start.x === segments[1].start.x && |
59 |
| - segment2.start.y === segments[1].start.y && |
60 |
| - segment2.end.x === segments[1].end.x && |
61 |
| - segment2.end.y === segments[1].end.y) |
| 60 | + it('should not find intersections between non-crossing segments', () => { |
| 61 | + const segments = [ |
| 62 | + { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } }, |
| 63 | + { start: { x: 2, y: 2 }, end: { x: 3, y: 3 } } |
| 64 | + ] |
| 65 | + planeSweep = new PlaneSweep(segments) |
| 66 | + const intersections = planeSweep.findIntersections() |
| 67 | + expect(intersections).toHaveLength(0) |
| 68 | + }) |
62 | 69 |
|
63 |
| - expect(isSegment1Valid).toBe(true) |
64 |
| - expect(isSegment2Valid).toBe(true) |
| 70 | + it('should handle vertical and horizontal segments', () => { |
| 71 | + const segments = [ |
| 72 | + { start: { x: 0, y: 0 }, end: { x: 0, y: 2 } }, // Vertical |
| 73 | + { start: { x: -1, y: 1 }, end: { x: 2, y: 1 } } // Horizontal |
| 74 | + ] |
| 75 | + planeSweep = new PlaneSweep(segments) |
| 76 | + const intersections = planeSweep.findIntersections() |
| 77 | + expect(intersections).toHaveLength(1) |
65 | 78 | })
|
66 | 79 |
|
67 |
| - test('returns an empty array if there are no intersections', () => { |
| 80 | + it('should handle segments with shared endpoints', () => { |
68 | 81 | const segments = [
|
69 |
| - { start: { x: 1, y: 1 }, end: { x: 2, y: 2 } }, |
70 |
| - { start: { x: 3, y: 3 }, end: { x: 4, y: 4 } } |
| 82 | + { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } }, |
| 83 | + { start: { x: 1, y: 1 }, end: { x: 2, y: 0 } } |
71 | 84 | ]
|
72 |
| - const intersection = new PlaneSweep(segments) |
73 |
| - const result = intersection.findIntersections() |
74 |
| - expect(result).toEqual([]) |
| 85 | + planeSweep = new PlaneSweep(segments) |
| 86 | + const intersections = planeSweep.findIntersections() |
| 87 | + expect(intersections).toHaveLength(1) // Shared endpoint is considered an intersection |
75 | 88 | })
|
76 | 89 |
|
77 |
| - test('handles vertical and horizontal lines', () => { |
| 90 | + it('should handle overlapping segments', () => { |
78 | 91 | const segments = [
|
79 |
| - { start: { x: 2, y: 0 }, end: { x: 2, y: 3 } }, // Vertical line |
80 |
| - { start: { x: 0, y: 2 }, end: { x: 3, y: 2 } } // Horizontal line |
| 92 | + { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } }, |
| 93 | + { start: { x: 1, y: 1 }, end: { x: 3, y: 3 } } |
81 | 94 | ]
|
82 |
| - const intersection = new PlaneSweep(segments) |
83 |
| - const result = intersection.findIntersections() |
| 95 | + planeSweep = new PlaneSweep(segments) |
| 96 | + const intersections = planeSweep.findIntersections() |
| 97 | + expect(intersections).toHaveLength(1) |
| 98 | + }) |
| 99 | + }) |
84 | 100 |
|
85 |
| - // Check if intersection contains the correct segments regardless of order |
86 |
| - expect(result).toHaveLength(1) // Ensure there's one intersection |
| 101 | + describe('edge cases', () => { |
| 102 | + it('should handle segments with reversed start and end points', () => { |
| 103 | + const segments = [ |
| 104 | + { start: { x: 2, y: 2 }, end: { x: 0, y: 0 } }, |
| 105 | + { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } } |
| 106 | + ] |
| 107 | + planeSweep = new PlaneSweep(segments) |
| 108 | + const intersections = planeSweep.findIntersections() |
| 109 | + expect(intersections).toHaveLength(1) |
| 110 | + }) |
87 | 111 |
|
88 |
| - const intersectingPair = result[0] |
| 112 | + it('should handle segments with same x-coordinate but different y-coordinates', () => { |
| 113 | + const segments = [ |
| 114 | + { start: { x: 0, y: 0 }, end: { x: 0, y: 2 } }, |
| 115 | + { start: { x: 0, y: 1 }, end: { x: 0, y: 3 } } |
| 116 | + ] |
| 117 | + planeSweep = new PlaneSweep(segments) |
| 118 | + const intersections = planeSweep.findIntersections() |
| 119 | + expect(intersections).toHaveLength(1) |
| 120 | + }) |
89 | 121 |
|
90 |
| - // Check that both segments in the intersection are part of the original segments |
91 |
| - const isSegment1Valid = |
92 |
| - (intersectingPair.segment1.start.x === segments[0].start.x && |
93 |
| - intersectingPair.segment1.start.y === segments[0].start.y && |
94 |
| - intersectingPair.segment1.end.x === segments[0].end.x && |
95 |
| - intersectingPair.segment1.end.y === segments[0].end.y) || |
96 |
| - (intersectingPair.segment1.start.x === segments[1].start.x && |
97 |
| - intersectingPair.segment1.start.y === segments[1].start.y && |
98 |
| - intersectingPair.segment1.end.x === segments[1].end.x && |
99 |
| - intersectingPair.segment1.end.y === segments[1].end.y) |
| 122 | + it('should handle a large number of touching segments', () => { |
| 123 | + const segments = Array.from({ length: 1000 }, (_, i) => ({ |
| 124 | + start: { x: i, y: 0 }, |
| 125 | + end: { x: i + 1, y: 1 } |
| 126 | + })) |
| 127 | + planeSweep = new PlaneSweep(segments) |
| 128 | + const intersections = planeSweep.findIntersections() |
| 129 | + // Check if touching points are considered intersections |
| 130 | + const touchingPointsAreIntersections = intersections.length > 0 |
| 131 | + if (touchingPointsAreIntersections) { |
| 132 | + expect(intersections).toHaveLength(999) // Each segment touches its neighbor |
| 133 | + } else { |
| 134 | + expect(intersections).toHaveLength(0) // Touching points are not considered intersections |
| 135 | + } |
| 136 | + }) |
100 | 137 |
|
101 |
| - const isSegment2Valid = |
102 |
| - (intersectingPair.segment2.start.x === segments[0].start.x && |
103 |
| - intersectingPair.segment2.start.y === segments[0].start.y && |
104 |
| - intersectingPair.segment2.end.x === segments[0].end.x && |
105 |
| - intersectingPair.segment2.end.y === segments[0].end.y) || |
106 |
| - (intersectingPair.segment2.start.x === segments[1].start.x && |
107 |
| - intersectingPair.segment2.start.y === segments[1].start.y && |
108 |
| - intersectingPair.segment2.end.x === segments[1].end.x && |
109 |
| - intersectingPair.segment2.end.y === segments[1].end.y) |
| 138 | + it('should detect touching points as intersections', () => { |
| 139 | + const segments = [ |
| 140 | + { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } }, |
| 141 | + { start: { x: 1, y: 1 }, end: { x: 2, y: 0 } } |
| 142 | + ] |
| 143 | + planeSweep = new PlaneSweep(segments) |
| 144 | + const intersections = planeSweep.findIntersections() |
| 145 | + expect(intersections).toHaveLength(1) |
| 146 | + }) |
110 | 147 |
|
111 |
| - expect(isSegment1Valid).toBe(true) |
112 |
| - expect(isSegment2Valid).toBe(true) |
| 148 | + it('should handle collinear overlapping segments', () => { |
| 149 | + const segments = [ |
| 150 | + { start: { x: 0, y: 0 }, end: { x: 2, y: 0 } }, |
| 151 | + { start: { x: 1, y: 0 }, end: { x: 3, y: 0 } } |
| 152 | + ] |
| 153 | + planeSweep = new PlaneSweep(segments) |
| 154 | + const intersections = planeSweep.findIntersections() |
| 155 | + expect(intersections).toHaveLength(1) |
113 | 156 | })
|
114 | 157 | })
|
115 | 158 | })
|
0 commit comments