Skip to content

Commit 49de7a5

Browse files
committed
refactor: remove classes and improve complexity
1 parent c2f9b32 commit 49de7a5

File tree

4 files changed

+358
-302
lines changed

4 files changed

+358
-302
lines changed

Geometry/FindIntersections.js

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/**
2+
* @typedef {Object} Point
3+
* @property {number} x - The x-coordinate of the point.
4+
* @property {number} y - The y-coordinate of the point.
5+
*/
6+
7+
/**
8+
* @typedef {Object} Segment
9+
* @property {Point} start - The start point of the segment.
10+
* @property {Point} end - The end point of the segment.
11+
*/
12+
13+
/**
14+
* @typedef {Object} Event
15+
* @property {Point} point - The point where the event occurs.
16+
* @property {Array<string>} types - Types of the event ('left', 'right', or 'intersection').
17+
* @property {Array<Segment>} segments - Segments associated with this event.
18+
*/
19+
20+
/**
21+
* @typedef {Object} Intersection
22+
* @property {Segment} segment1 - First segment of the intersection.
23+
* @property {Segment} segment2 - Second segment of the intersection.
24+
* @property {Point} point - The point of intersection.
25+
*/
26+
27+
/**
28+
* Creates a new point.
29+
* @param {number} x - The x-coordinate.
30+
* @param {number} y - The y-coordinate.
31+
* @returns {Point} The created point.
32+
*/
33+
export const createPoint = (x, y) => ({ x, y })
34+
35+
/**
36+
* Creates a new event.
37+
* @param {Point} point - The point where the event occurs.
38+
* @param {Array<string>} types - Types of the event.
39+
* @param {Array<Segment>} segments - Segments associated with this event.
40+
* @returns {Event} The created event.
41+
*/
42+
export const createEvent = (point, types, segments) => ({
43+
point,
44+
types,
45+
segments
46+
})
47+
48+
/**
49+
* Compares two points lexicographically.
50+
* @param {Point} a - The first point.
51+
* @param {Point} b - The second point.
52+
* @returns {number} Negative if a < b, positive if a > b, zero if equal.
53+
*/
54+
export const comparePoints = (a, b) => {
55+
if (a.x !== b.x) return a.x - b.x
56+
return a.y - b.y
57+
}
58+
59+
/**
60+
* Compares two segments based on their y-coordinate at the current x-coordinate of the sweep line.
61+
* @param {Segment} a - The first segment.
62+
* @param {Segment} b - The second segment.
63+
* @returns {number} Negative if a < b, positive if a > b, zero if equal.
64+
*/
65+
export const compareSegmentsY = (a, b) => a.start.y - b.start.y
66+
67+
/**
68+
* Calculates the intersection point of two line segments.
69+
* @param {Segment} seg1 - First segment.
70+
* @param {Segment} seg2 - Second segment.
71+
* @returns {Point|null} The intersection point, or null if segments are parallel.
72+
*/
73+
export const calculateIntersectionPoint = (seg1, seg2) => {
74+
const x1 = seg1.start.x,
75+
y1 = seg1.start.y
76+
const x2 = seg1.end.x,
77+
y2 = seg1.end.y
78+
const x3 = seg2.start.x,
79+
y3 = seg2.start.y
80+
const x4 = seg2.end.x,
81+
y4 = seg2.end.y
82+
83+
const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
84+
if (Math.abs(denom) < Number.EPSILON) return null // parallel lines
85+
86+
const x =
87+
((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom
88+
const y =
89+
((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
90+
91+
return createPoint(x, y)
92+
}
93+
94+
/**
95+
* Handles potential intersection between two segments.
96+
* @param {Segment} seg1 - First segment.
97+
* @param {Segment} seg2 - Second segment.
98+
* @param {Array<Event>} events - Array of events to update.
99+
* @param {Array<Intersection>} intersections - Array of intersections to update.
100+
*/
101+
export const handlePotentialIntersection = (
102+
seg1,
103+
seg2,
104+
events,
105+
intersections
106+
) => {
107+
const intersectionPoint = calculateIntersectionPoint(seg1, seg2)
108+
if (intersectionPoint) {
109+
events.push(createEvent(intersectionPoint, ['intersection'], [seg1, seg2]))
110+
intersections.push({
111+
segment1: seg1,
112+
segment2: seg2,
113+
point: intersectionPoint
114+
})
115+
}
116+
}
117+
118+
/**
119+
* Finds all intersections among a set of line segments using the Bentley-Ottmann algorithm.
120+
* @param {Array<Segment>} segments - Array of line segments to check for intersections.
121+
* @returns {Array<Intersection>} Array of all intersections found.
122+
*/
123+
const findIntersections = (segments) => {
124+
const events = []
125+
const intersections = []
126+
let sweepLineStatus = []
127+
128+
// Create initial events
129+
segments.forEach((segment) => {
130+
events.push(createEvent(segment.start, ['left'], [segment]))
131+
events.push(createEvent(segment.end, ['right'], [segment]))
132+
})
133+
134+
// Sort events
135+
events.sort((a, b) => {
136+
const pointCompare = comparePoints(a.point, b.point)
137+
if (pointCompare !== 0) return pointCompare
138+
139+
// If points are the same, prioritize: intersection > left > right
140+
const typePriority = { intersection: 0, left: 1, right: 2 }
141+
return (
142+
Math.min(...a.types.map((t) => typePriority[t])) -
143+
Math.min(...b.types.map((t) => typePriority[t]))
144+
)
145+
})
146+
147+
// Process events
148+
events.forEach((event) => {
149+
if (event.types.includes('left')) {
150+
event.segments.forEach((segment) => {
151+
sweepLineStatus.push(segment)
152+
sweepLineStatus.sort(compareSegmentsY)
153+
const index = sweepLineStatus.indexOf(segment)
154+
const lower = index > 0 ? sweepLineStatus[index - 1] : null
155+
const upper =
156+
index < sweepLineStatus.length - 1 ? sweepLineStatus[index + 1] : null
157+
if (lower)
158+
handlePotentialIntersection(segment, lower, events, intersections)
159+
if (upper)
160+
handlePotentialIntersection(segment, upper, events, intersections)
161+
})
162+
}
163+
164+
if (event.types.includes('right')) {
165+
event.segments.forEach((segment) => {
166+
const index = sweepLineStatus.indexOf(segment)
167+
const lower = index > 0 ? sweepLineStatus[index - 1] : null
168+
const upper =
169+
index < sweepLineStatus.length - 1 ? sweepLineStatus[index + 1] : null
170+
sweepLineStatus = sweepLineStatus.filter((s) => s !== segment)
171+
if (lower && upper)
172+
handlePotentialIntersection(lower, upper, events, intersections)
173+
})
174+
}
175+
176+
if (event.types.includes('intersection')) {
177+
// Re-check all pairs of segments at this x-coordinate for intersections
178+
const segmentsAtX = sweepLineStatus.filter(
179+
(s) =>
180+
Math.min(s.start.x, s.end.x) <= event.point.x &&
181+
Math.max(s.start.x, s.end.x) >= event.point.x
182+
)
183+
for (let i = 0; i < segmentsAtX.length; i++) {
184+
for (let j = i + 1; j < segmentsAtX.length; j++) {
185+
handlePotentialIntersection(
186+
segmentsAtX[i],
187+
segmentsAtX[j],
188+
events,
189+
intersections
190+
)
191+
}
192+
}
193+
}
194+
})
195+
196+
return intersections
197+
}
198+
199+
export default findIntersections

Geometry/PlaneSweep.js

-144
This file was deleted.

0 commit comments

Comments
 (0)