From d4d210ef7ce748e1f081f518e01cd42da847e8ad Mon Sep 17 00:00:00 2001
From: saahil-mahato <saahilmahato11@gmail.com>
Date: Wed, 2 Oct 2024 10:41:30 +0545
Subject: [PATCH 1/3] feat: add plane sweep algorithm

---
 Geometry/PlaneSweep.js           | 131 +++++++++++++++++++++++++++++++
 Geometry/Test/PlaneSweep.test.js | 115 +++++++++++++++++++++++++++
 2 files changed, 246 insertions(+)
 create mode 100644 Geometry/PlaneSweep.js
 create mode 100644 Geometry/Test/PlaneSweep.test.js

diff --git a/Geometry/PlaneSweep.js b/Geometry/PlaneSweep.js
new file mode 100644
index 0000000000..3d2682639e
--- /dev/null
+++ b/Geometry/PlaneSweep.js
@@ -0,0 +1,131 @@
+/**
+ * This class implements a Line Segment Intersection algorithm using the Plane Sweep technique.
+ * It detects intersections between a set of line segments in a 2D plane.
+ * @see {@link https://en.wikipedia.org/wiki/Sweep_line_algorithm}
+ * @class
+ */
+export default class PlaneSweep {
+  /** @private */
+  #segments
+
+  /** @private */
+  #events
+
+  /** @private */
+  #activeSet
+
+  /**
+   * Creates a Line Segment Intersection instance.
+   * @constructor
+   * @param {Array<{start: {x: number, y: number}, end: {x: number, y: number}}> } segments - An array of line segments defined by start and end points.
+   * @throws {Error} Will throw an error if the segments array is empty or invalid.
+   */
+  constructor(segments) {
+    this.#validateSegments(segments)
+
+    this.#segments = segments
+    this.#events = []
+    this.#activeSet = new Set()
+    this.#initializeEvents()
+  }
+
+  /**
+   * Validates that the input is a non-empty array of segments.
+   * @private
+   * @param {Array} segments - The array of line segments to validate.
+   * @throws {Error} Will throw an error if the input is not a valid array of segments.
+   */
+  #validateSegments(segments) {
+    if (
+      !Array.isArray(segments) ||
+      segments.length === 0 ||
+      !segments.every((seg) => seg.start && seg.end)
+    ) {
+      throw new Error(
+        'segments must be a non-empty array of objects with both start and end properties.'
+      )
+    }
+  }
+
+  /**
+   * Initializes the event points for the sweep line algorithm.
+   * @private
+   */
+  #initializeEvents() {
+    for (const segment of this.#segments) {
+      const startEvent = { point: segment.start, type: 'start', segment }
+      const endEvent = { point: segment.end, type: 'end', segment }
+
+      // Ensure start is always before end in terms of x-coordinates
+      if (
+        startEvent.point.x > endEvent.point.x ||
+        (startEvent.point.x === endEvent.point.x &&
+          startEvent.point.y > endEvent.point.y)
+      ) {
+        this.#events.push(endEvent)
+        this.#events.push(startEvent)
+      } else {
+        this.#events.push(startEvent)
+        this.#events.push(endEvent)
+      }
+    }
+
+    // Sort events by x-coordinate, then by type (start before end)
+    this.#events.sort((a, b) => {
+      if (a.point.x === b.point.x) {
+        return a.type === 'start' ? -1 : 1
+      }
+      return a.point.x - b.point.x
+    })
+  }
+
+  /**
+   * Checks if two line segments intersect.
+   * @private
+   * @param {{start: {x: number, y: number}, end: {x: number, y: number}}} seg1 - The first segment.
+   * @param {{start: {x: number, y: number}, end: {x: number, y: number}}} seg2 - The second segment.
+   * @returns {boolean} True if the segments intersect, otherwise false.
+   */
+  #doSegmentsIntersect(seg1, seg2) {
+    const ccw = (A, B, C) =>
+      (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)
+    return (
+      ccw(seg1.start, seg2.start, seg2.end) !==
+        ccw(seg1.end, seg2.start, seg2.end) &&
+      ccw(seg1.start, seg1.end, seg2.start) !==
+        ccw(seg1.start, seg1.end, seg2.end)
+    )
+  }
+
+  /**
+   * Executes the Plane Sweep algorithm to find all intersections.
+   * @public
+   * @returns {Array<{segment1: *, segment2: *}>} An array of intersecting segment pairs.
+   */
+  findIntersections() {
+    const intersections = []
+
+    for (const event of this.#events) {
+      const { type, segment } = event
+
+      if (type === 'start') {
+        this.#activeSet.add(segment)
+
+        // Check for intersections with neighboring active segments
+        const neighbors = Array.from(this.#activeSet).filter(
+          (seg) => seg !== segment
+        )
+        for (const neighbor of neighbors) {
+          if (this.#doSegmentsIntersect(neighbor, segment)) {
+            intersections.push({ segment1: neighbor, segment2: segment })
+          }
+        }
+      } else if (type === 'end') {
+        // Remove the segment from the active set
+        this.#activeSet.delete(segment)
+      }
+    }
+
+    return intersections
+  }
+}
diff --git a/Geometry/Test/PlaneSweep.test.js b/Geometry/Test/PlaneSweep.test.js
new file mode 100644
index 0000000000..82681ee340
--- /dev/null
+++ b/Geometry/Test/PlaneSweep.test.js
@@ -0,0 +1,115 @@
+import PlaneSweep from '../PlaneSweep'
+
+describe('PlaneSweep', () => {
+  describe('Constructor', () => {
+    test('creates an instance with valid segments', () => {
+      const segments = [
+        { start: { x: 1, y: 1 }, end: { x: 4, y: 4 } },
+        { start: { x: 1, y: 4 }, end: { x: 4, y: 1 } }
+      ]
+      const intersection = new PlaneSweep(segments)
+      expect(intersection).toBeInstanceOf(PlaneSweep)
+    })
+
+    test('throws an error if segments array is invalid', () => {
+      expect(() => new PlaneSweep([])).toThrow(
+        'segments must be a non-empty array of objects with both start and end properties.'
+      )
+      expect(() => new PlaneSweep([{ start: { x: 0, y: 0 } }])).toThrow(
+        'segments must be a non-empty array of objects with both start and end properties.'
+      )
+    })
+  })
+
+  describe('Intersection Detection', () => {
+    test('detects intersections correctly', () => {
+      const segments = [
+        { start: { x: 1, y: 1 }, end: { x: 4, y: 4 } },
+        { start: { x: 1, y: 4 }, end: { x: 4, y: 1 } },
+        { start: { x: 5, y: 5 }, end: { x: 6, y: 6 } }
+      ]
+      const intersection = new PlaneSweep(segments)
+      const result = intersection.findIntersections()
+
+      // Check if there is one intersection found
+      expect(result).toHaveLength(1) // Ensure there's one intersection
+
+      const intersectingPair = result[0]
+
+      // Check that both segments in the intersection are part of the original segments
+      const segment1 = intersectingPair.segment1
+      const segment2 = intersectingPair.segment2
+
+      const isSegment1Valid =
+        (segment1.start.x === segments[0].start.x &&
+          segment1.start.y === segments[0].start.y &&
+          segment1.end.x === segments[0].end.x &&
+          segment1.end.y === segments[0].end.y) ||
+        (segment1.start.x === segments[1].start.x &&
+          segment1.start.y === segments[1].start.y &&
+          segment1.end.x === segments[1].end.x &&
+          segment1.end.y === segments[1].end.y)
+
+      const isSegment2Valid =
+        (segment2.start.x === segments[0].start.x &&
+          segment2.start.y === segments[0].start.y &&
+          segment2.end.x === segments[0].end.x &&
+          segment2.end.y === segments[0].end.y) ||
+        (segment2.start.x === segments[1].start.x &&
+          segment2.start.y === segments[1].start.y &&
+          segment2.end.x === segments[1].end.x &&
+          segment2.end.y === segments[1].end.y)
+
+      expect(isSegment1Valid).toBe(true)
+      expect(isSegment2Valid).toBe(true)
+    })
+
+    test('returns an empty array if there are no intersections', () => {
+      const segments = [
+        { start: { x: 1, y: 1 }, end: { x: 2, y: 2 } },
+        { start: { x: 3, y: 3 }, end: { x: 4, y: 4 } }
+      ]
+      const intersection = new PlaneSweep(segments)
+      const result = intersection.findIntersections()
+      expect(result).toEqual([])
+    })
+
+    test('handles vertical and horizontal lines', () => {
+      const segments = [
+        { start: { x: 2, y: 0 }, end: { x: 2, y: 3 } }, // Vertical line
+        { start: { x: 0, y: 2 }, end: { x: 3, y: 2 } } // Horizontal line
+      ]
+      const intersection = new PlaneSweep(segments)
+      const result = intersection.findIntersections()
+
+      // Check if intersection contains the correct segments regardless of order
+      expect(result).toHaveLength(1) // Ensure there's one intersection
+
+      const intersectingPair = result[0]
+
+      // Check that both segments in the intersection are part of the original segments
+      const isSegment1Valid =
+        (intersectingPair.segment1.start.x === segments[0].start.x &&
+          intersectingPair.segment1.start.y === segments[0].start.y &&
+          intersectingPair.segment1.end.x === segments[0].end.x &&
+          intersectingPair.segment1.end.y === segments[0].end.y) ||
+        (intersectingPair.segment1.start.x === segments[1].start.x &&
+          intersectingPair.segment1.start.y === segments[1].start.y &&
+          intersectingPair.segment1.end.x === segments[1].end.x &&
+          intersectingPair.segment1.end.y === segments[1].end.y)
+
+      const isSegment2Valid =
+        (intersectingPair.segment2.start.x === segments[0].start.x &&
+          intersectingPair.segment2.start.y === segments[0].start.y &&
+          intersectingPair.segment2.end.x === segments[0].end.x &&
+          intersectingPair.segment2.end.y === segments[0].end.y) ||
+        (intersectingPair.segment2.start.x === segments[1].start.x &&
+          intersectingPair.segment2.start.y === segments[1].start.y &&
+          intersectingPair.segment2.end.x === segments[1].end.x &&
+          intersectingPair.segment2.end.y === segments[1].end.y)
+
+      expect(isSegment1Valid).toBe(true)
+      expect(isSegment2Valid).toBe(true)
+    })
+  })
+})

From c2f9b32949cbddab2afd05df4522feca21ef0844 Mon Sep 17 00:00:00 2001
From: saahil-mahato <saahilmahato11@gmail.com>
Date: Wed, 2 Oct 2024 11:49:51 +0545
Subject: [PATCH 2/3] fix: add more edge cases

---
 Geometry/PlaneSweep.js           |  45 ++++---
 Geometry/Test/PlaneSweep.test.js | 205 +++++++++++++++++++------------
 2 files changed, 153 insertions(+), 97 deletions(-)

diff --git a/Geometry/PlaneSweep.js b/Geometry/PlaneSweep.js
index 3d2682639e..39ed5d6bb9 100644
--- a/Geometry/PlaneSweep.js
+++ b/Geometry/PlaneSweep.js
@@ -42,7 +42,7 @@ export default class PlaneSweep {
       !segments.every((seg) => seg.start && seg.end)
     ) {
       throw new Error(
-        'segments must be a non-empty array of objects with both start and end properties.'
+        'segments must be a non-empty array of objects with start and end properties.'
       )
     }
   }
@@ -79,22 +79,35 @@ export default class PlaneSweep {
     })
   }
 
-  /**
-   * Checks if two line segments intersect.
-   * @private
-   * @param {{start: {x: number, y: number}, end: {x: number, y: number}}} seg1 - The first segment.
-   * @param {{start: {x: number, y: number}, end: {x: number, y: number}}} seg2 - The second segment.
-   * @returns {boolean} True if the segments intersect, otherwise false.
-   */
   #doSegmentsIntersect(seg1, seg2) {
-    const ccw = (A, B, C) =>
-      (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)
-    return (
-      ccw(seg1.start, seg2.start, seg2.end) !==
-        ccw(seg1.end, seg2.start, seg2.end) &&
-      ccw(seg1.start, seg1.end, seg2.start) !==
-        ccw(seg1.start, seg1.end, seg2.end)
-    )
+    const ccw = (A, B, C) => {
+      const val = (C.y - A.y) * (B.x - A.x) - (B.y - A.y) * (C.x - A.x)
+      if (Math.abs(val) < Number.EPSILON) return 0 // Collinear
+      return val > 0 ? 1 : -1 // Clockwise or counterclockwise
+    }
+
+    const onSegment = (p, q, r) => {
+      return (
+        q.x <= Math.max(p.x, r.x) &&
+        q.x >= Math.min(p.x, r.x) &&
+        q.y <= Math.max(p.y, r.y) &&
+        q.y >= Math.min(p.y, r.y)
+      )
+    }
+
+    const o1 = ccw(seg1.start, seg1.end, seg2.start)
+    const o2 = ccw(seg1.start, seg1.end, seg2.end)
+    const o3 = ccw(seg2.start, seg2.end, seg1.start)
+    const o4 = ccw(seg2.start, seg2.end, seg1.end)
+
+    // General case of intersection
+    if (o1 !== o2 && o3 !== o4) return true
+
+    // Special cases: collinear segments that may touch
+    if (o1 === 0 && onSegment(seg1.start, seg2.start, seg1.end)) return true
+    if (o2 === 0 && onSegment(seg1.start, seg2.end, seg1.end)) return true
+    if (o3 === 0 && onSegment(seg2.start, seg1.start, seg2.end)) return true
+    return o4 === 0 && onSegment(seg2.start, seg1.end, seg2.end)
   }
 
   /**
diff --git a/Geometry/Test/PlaneSweep.test.js b/Geometry/Test/PlaneSweep.test.js
index 82681ee340..3e3b47e17d 100644
--- a/Geometry/Test/PlaneSweep.test.js
+++ b/Geometry/Test/PlaneSweep.test.js
@@ -1,115 +1,158 @@
 import PlaneSweep from '../PlaneSweep'
 
 describe('PlaneSweep', () => {
-  describe('Constructor', () => {
-    test('creates an instance with valid segments', () => {
+  let planeSweep
+
+  describe('constructor', () => {
+    it('should create an instance with valid segments', () => {
       const segments = [
-        { start: { x: 1, y: 1 }, end: { x: 4, y: 4 } },
-        { start: { x: 1, y: 4 }, end: { x: 4, y: 1 } }
+        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
+        { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } }
       ]
-      const intersection = new PlaneSweep(segments)
-      expect(intersection).toBeInstanceOf(PlaneSweep)
+      expect(() => new PlaneSweep(segments)).not.toThrow()
     })
 
-    test('throws an error if segments array is invalid', () => {
+    it('should throw an error with empty segments array', () => {
       expect(() => new PlaneSweep([])).toThrow(
-        'segments must be a non-empty array of objects with both start and end properties.'
+        'segments must be a non-empty array'
       )
-      expect(() => new PlaneSweep([{ start: { x: 0, y: 0 } }])).toThrow(
-        'segments must be a non-empty array of objects with both start and end properties.'
+    })
+
+    it('should throw an error with invalid segments', () => {
+      const invalidSegments = [{ start: { x: 0, y: 0 } }]
+      expect(() => new PlaneSweep(invalidSegments)).toThrow(
+        'segments must be a non-empty array of objects with start and end properties'
       )
     })
   })
 
-  describe('Intersection Detection', () => {
-    test('detects intersections correctly', () => {
+  describe('findIntersections', () => {
+    beforeEach(() => {
       const segments = [
-        { start: { x: 1, y: 1 }, end: { x: 4, y: 4 } },
-        { start: { x: 1, y: 4 }, end: { x: 4, y: 1 } },
-        { start: { x: 5, y: 5 }, end: { x: 6, y: 6 } }
+        { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } },
+        { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } }
       ]
-      const intersection = new PlaneSweep(segments)
-      const result = intersection.findIntersections()
-
-      // Check if there is one intersection found
-      expect(result).toHaveLength(1) // Ensure there's one intersection
-
-      const intersectingPair = result[0]
-
-      // Check that both segments in the intersection are part of the original segments
-      const segment1 = intersectingPair.segment1
-      const segment2 = intersectingPair.segment2
+      planeSweep = new PlaneSweep(segments)
+    })
 
-      const isSegment1Valid =
-        (segment1.start.x === segments[0].start.x &&
-          segment1.start.y === segments[0].start.y &&
-          segment1.end.x === segments[0].end.x &&
-          segment1.end.y === segments[0].end.y) ||
-        (segment1.start.x === segments[1].start.x &&
-          segment1.start.y === segments[1].start.y &&
-          segment1.end.x === segments[1].end.x &&
-          segment1.end.y === segments[1].end.y)
+    it('should find intersections between crossing segments', () => {
+      const segments = [
+        { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } },
+        { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } }
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
+      expect(intersections[0]).toEqual(
+        expect.objectContaining({
+          segment1: expect.objectContaining({
+            start: expect.objectContaining({ x: 0, y: expect.any(Number) }),
+            end: expect.objectContaining({ x: 2, y: expect.any(Number) })
+          }),
+          segment2: expect.objectContaining({
+            start: expect.objectContaining({ x: 0, y: expect.any(Number) }),
+            end: expect.objectContaining({ x: 2, y: expect.any(Number) })
+          })
+        })
+      )
+    })
 
-      const isSegment2Valid =
-        (segment2.start.x === segments[0].start.x &&
-          segment2.start.y === segments[0].start.y &&
-          segment2.end.x === segments[0].end.x &&
-          segment2.end.y === segments[0].end.y) ||
-        (segment2.start.x === segments[1].start.x &&
-          segment2.start.y === segments[1].start.y &&
-          segment2.end.x === segments[1].end.x &&
-          segment2.end.y === segments[1].end.y)
+    it('should not find intersections between non-crossing segments', () => {
+      const segments = [
+        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
+        { start: { x: 2, y: 2 }, end: { x: 3, y: 3 } }
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(0)
+    })
 
-      expect(isSegment1Valid).toBe(true)
-      expect(isSegment2Valid).toBe(true)
+    it('should handle vertical and horizontal segments', () => {
+      const segments = [
+        { start: { x: 0, y: 0 }, end: { x: 0, y: 2 } }, // Vertical
+        { start: { x: -1, y: 1 }, end: { x: 2, y: 1 } } // Horizontal
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
     })
 
-    test('returns an empty array if there are no intersections', () => {
+    it('should handle segments with shared endpoints', () => {
       const segments = [
-        { start: { x: 1, y: 1 }, end: { x: 2, y: 2 } },
-        { start: { x: 3, y: 3 }, end: { x: 4, y: 4 } }
+        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
+        { start: { x: 1, y: 1 }, end: { x: 2, y: 0 } }
       ]
-      const intersection = new PlaneSweep(segments)
-      const result = intersection.findIntersections()
-      expect(result).toEqual([])
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1) // Shared endpoint is considered an intersection
     })
 
-    test('handles vertical and horizontal lines', () => {
+    it('should handle overlapping segments', () => {
       const segments = [
-        { start: { x: 2, y: 0 }, end: { x: 2, y: 3 } }, // Vertical line
-        { start: { x: 0, y: 2 }, end: { x: 3, y: 2 } } // Horizontal line
+        { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } },
+        { start: { x: 1, y: 1 }, end: { x: 3, y: 3 } }
       ]
-      const intersection = new PlaneSweep(segments)
-      const result = intersection.findIntersections()
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
+    })
+  })
 
-      // Check if intersection contains the correct segments regardless of order
-      expect(result).toHaveLength(1) // Ensure there's one intersection
+  describe('edge cases', () => {
+    it('should handle segments with reversed start and end points', () => {
+      const segments = [
+        { start: { x: 2, y: 2 }, end: { x: 0, y: 0 } },
+        { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } }
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
+    })
 
-      const intersectingPair = result[0]
+    it('should handle segments with same x-coordinate but different y-coordinates', () => {
+      const segments = [
+        { start: { x: 0, y: 0 }, end: { x: 0, y: 2 } },
+        { start: { x: 0, y: 1 }, end: { x: 0, y: 3 } }
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
+    })
 
-      // Check that both segments in the intersection are part of the original segments
-      const isSegment1Valid =
-        (intersectingPair.segment1.start.x === segments[0].start.x &&
-          intersectingPair.segment1.start.y === segments[0].start.y &&
-          intersectingPair.segment1.end.x === segments[0].end.x &&
-          intersectingPair.segment1.end.y === segments[0].end.y) ||
-        (intersectingPair.segment1.start.x === segments[1].start.x &&
-          intersectingPair.segment1.start.y === segments[1].start.y &&
-          intersectingPair.segment1.end.x === segments[1].end.x &&
-          intersectingPair.segment1.end.y === segments[1].end.y)
+    it('should handle a large number of touching segments', () => {
+      const segments = Array.from({ length: 1000 }, (_, i) => ({
+        start: { x: i, y: 0 },
+        end: { x: i + 1, y: 1 }
+      }))
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      // Check if touching points are considered intersections
+      const touchingPointsAreIntersections = intersections.length > 0
+      if (touchingPointsAreIntersections) {
+        expect(intersections).toHaveLength(999) // Each segment touches its neighbor
+      } else {
+        expect(intersections).toHaveLength(0) // Touching points are not considered intersections
+      }
+    })
 
-      const isSegment2Valid =
-        (intersectingPair.segment2.start.x === segments[0].start.x &&
-          intersectingPair.segment2.start.y === segments[0].start.y &&
-          intersectingPair.segment2.end.x === segments[0].end.x &&
-          intersectingPair.segment2.end.y === segments[0].end.y) ||
-        (intersectingPair.segment2.start.x === segments[1].start.x &&
-          intersectingPair.segment2.start.y === segments[1].start.y &&
-          intersectingPair.segment2.end.x === segments[1].end.x &&
-          intersectingPair.segment2.end.y === segments[1].end.y)
+    it('should detect touching points as intersections', () => {
+      const segments = [
+        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
+        { start: { x: 1, y: 1 }, end: { x: 2, y: 0 } }
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
+    })
 
-      expect(isSegment1Valid).toBe(true)
-      expect(isSegment2Valid).toBe(true)
+    it('should handle collinear overlapping segments', () => {
+      const segments = [
+        { start: { x: 0, y: 0 }, end: { x: 2, y: 0 } },
+        { start: { x: 1, y: 0 }, end: { x: 3, y: 0 } }
+      ]
+      planeSweep = new PlaneSweep(segments)
+      const intersections = planeSweep.findIntersections()
+      expect(intersections).toHaveLength(1)
     })
   })
 })

From 49de7a5a4c078147701150a00874ffc0d5bed609 Mon Sep 17 00:00:00 2001
From: saahil-mahato <saahilmahato11@gmail.com>
Date: Tue, 8 Oct 2024 17:16:56 +0545
Subject: [PATCH 3/3] refactor: remove classes and improve complexity

---
 Geometry/FindIntersections.js           | 199 ++++++++++++++++++++++++
 Geometry/PlaneSweep.js                  | 144 -----------------
 Geometry/Test/FindIntersections.test.js | 159 +++++++++++++++++++
 Geometry/Test/PlaneSweep.test.js        | 158 -------------------
 4 files changed, 358 insertions(+), 302 deletions(-)
 create mode 100644 Geometry/FindIntersections.js
 delete mode 100644 Geometry/PlaneSweep.js
 create mode 100644 Geometry/Test/FindIntersections.test.js
 delete mode 100644 Geometry/Test/PlaneSweep.test.js

diff --git a/Geometry/FindIntersections.js b/Geometry/FindIntersections.js
new file mode 100644
index 0000000000..b250187c51
--- /dev/null
+++ b/Geometry/FindIntersections.js
@@ -0,0 +1,199 @@
+/**
+ * @typedef {Object} Point
+ * @property {number} x - The x-coordinate of the point.
+ * @property {number} y - The y-coordinate of the point.
+ */
+
+/**
+ * @typedef {Object} Segment
+ * @property {Point} start - The start point of the segment.
+ * @property {Point} end - The end point of the segment.
+ */
+
+/**
+ * @typedef {Object} Event
+ * @property {Point} point - The point where the event occurs.
+ * @property {Array<string>} types - Types of the event ('left', 'right', or 'intersection').
+ * @property {Array<Segment>} segments - Segments associated with this event.
+ */
+
+/**
+ * @typedef {Object} Intersection
+ * @property {Segment} segment1 - First segment of the intersection.
+ * @property {Segment} segment2 - Second segment of the intersection.
+ * @property {Point} point - The point of intersection.
+ */
+
+/**
+ * Creates a new point.
+ * @param {number} x - The x-coordinate.
+ * @param {number} y - The y-coordinate.
+ * @returns {Point} The created point.
+ */
+export const createPoint = (x, y) => ({ x, y })
+
+/**
+ * Creates a new event.
+ * @param {Point} point - The point where the event occurs.
+ * @param {Array<string>} types - Types of the event.
+ * @param {Array<Segment>} segments - Segments associated with this event.
+ * @returns {Event} The created event.
+ */
+export const createEvent = (point, types, segments) => ({
+  point,
+  types,
+  segments
+})
+
+/**
+ * Compares two points lexicographically.
+ * @param {Point} a - The first point.
+ * @param {Point} b - The second point.
+ * @returns {number} Negative if a < b, positive if a > b, zero if equal.
+ */
+export const comparePoints = (a, b) => {
+  if (a.x !== b.x) return a.x - b.x
+  return a.y - b.y
+}
+
+/**
+ * Compares two segments based on their y-coordinate at the current x-coordinate of the sweep line.
+ * @param {Segment} a - The first segment.
+ * @param {Segment} b - The second segment.
+ * @returns {number} Negative if a < b, positive if a > b, zero if equal.
+ */
+export const compareSegmentsY = (a, b) => a.start.y - b.start.y
+
+/**
+ * Calculates the intersection point of two line segments.
+ * @param {Segment} seg1 - First segment.
+ * @param {Segment} seg2 - Second segment.
+ * @returns {Point|null} The intersection point, or null if segments are parallel.
+ */
+export const calculateIntersectionPoint = (seg1, seg2) => {
+  const x1 = seg1.start.x,
+    y1 = seg1.start.y
+  const x2 = seg1.end.x,
+    y2 = seg1.end.y
+  const x3 = seg2.start.x,
+    y3 = seg2.start.y
+  const x4 = seg2.end.x,
+    y4 = seg2.end.y
+
+  const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
+  if (Math.abs(denom) < Number.EPSILON) return null // parallel lines
+
+  const x =
+    ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom
+  const y =
+    ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
+
+  return createPoint(x, y)
+}
+
+/**
+ * Handles potential intersection between two segments.
+ * @param {Segment} seg1 - First segment.
+ * @param {Segment} seg2 - Second segment.
+ * @param {Array<Event>} events - Array of events to update.
+ * @param {Array<Intersection>} intersections - Array of intersections to update.
+ */
+export const handlePotentialIntersection = (
+  seg1,
+  seg2,
+  events,
+  intersections
+) => {
+  const intersectionPoint = calculateIntersectionPoint(seg1, seg2)
+  if (intersectionPoint) {
+    events.push(createEvent(intersectionPoint, ['intersection'], [seg1, seg2]))
+    intersections.push({
+      segment1: seg1,
+      segment2: seg2,
+      point: intersectionPoint
+    })
+  }
+}
+
+/**
+ * Finds all intersections among a set of line segments using the Bentley-Ottmann algorithm.
+ * @param {Array<Segment>} segments - Array of line segments to check for intersections.
+ * @returns {Array<Intersection>} Array of all intersections found.
+ */
+const findIntersections = (segments) => {
+  const events = []
+  const intersections = []
+  let sweepLineStatus = []
+
+  // Create initial events
+  segments.forEach((segment) => {
+    events.push(createEvent(segment.start, ['left'], [segment]))
+    events.push(createEvent(segment.end, ['right'], [segment]))
+  })
+
+  // Sort events
+  events.sort((a, b) => {
+    const pointCompare = comparePoints(a.point, b.point)
+    if (pointCompare !== 0) return pointCompare
+
+    // If points are the same, prioritize: intersection > left > right
+    const typePriority = { intersection: 0, left: 1, right: 2 }
+    return (
+      Math.min(...a.types.map((t) => typePriority[t])) -
+      Math.min(...b.types.map((t) => typePriority[t]))
+    )
+  })
+
+  // Process events
+  events.forEach((event) => {
+    if (event.types.includes('left')) {
+      event.segments.forEach((segment) => {
+        sweepLineStatus.push(segment)
+        sweepLineStatus.sort(compareSegmentsY)
+        const index = sweepLineStatus.indexOf(segment)
+        const lower = index > 0 ? sweepLineStatus[index - 1] : null
+        const upper =
+          index < sweepLineStatus.length - 1 ? sweepLineStatus[index + 1] : null
+        if (lower)
+          handlePotentialIntersection(segment, lower, events, intersections)
+        if (upper)
+          handlePotentialIntersection(segment, upper, events, intersections)
+      })
+    }
+
+    if (event.types.includes('right')) {
+      event.segments.forEach((segment) => {
+        const index = sweepLineStatus.indexOf(segment)
+        const lower = index > 0 ? sweepLineStatus[index - 1] : null
+        const upper =
+          index < sweepLineStatus.length - 1 ? sweepLineStatus[index + 1] : null
+        sweepLineStatus = sweepLineStatus.filter((s) => s !== segment)
+        if (lower && upper)
+          handlePotentialIntersection(lower, upper, events, intersections)
+      })
+    }
+
+    if (event.types.includes('intersection')) {
+      // Re-check all pairs of segments at this x-coordinate for intersections
+      const segmentsAtX = sweepLineStatus.filter(
+        (s) =>
+          Math.min(s.start.x, s.end.x) <= event.point.x &&
+          Math.max(s.start.x, s.end.x) >= event.point.x
+      )
+      for (let i = 0; i < segmentsAtX.length; i++) {
+        for (let j = i + 1; j < segmentsAtX.length; j++) {
+          handlePotentialIntersection(
+            segmentsAtX[i],
+            segmentsAtX[j],
+            events,
+            intersections
+          )
+        }
+      }
+    }
+  })
+
+  return intersections
+}
+
+export default findIntersections
diff --git a/Geometry/PlaneSweep.js b/Geometry/PlaneSweep.js
deleted file mode 100644
index 39ed5d6bb9..0000000000
--- a/Geometry/PlaneSweep.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * This class implements a Line Segment Intersection algorithm using the Plane Sweep technique.
- * It detects intersections between a set of line segments in a 2D plane.
- * @see {@link https://en.wikipedia.org/wiki/Sweep_line_algorithm}
- * @class
- */
-export default class PlaneSweep {
-  /** @private */
-  #segments
-
-  /** @private */
-  #events
-
-  /** @private */
-  #activeSet
-
-  /**
-   * Creates a Line Segment Intersection instance.
-   * @constructor
-   * @param {Array<{start: {x: number, y: number}, end: {x: number, y: number}}> } segments - An array of line segments defined by start and end points.
-   * @throws {Error} Will throw an error if the segments array is empty or invalid.
-   */
-  constructor(segments) {
-    this.#validateSegments(segments)
-
-    this.#segments = segments
-    this.#events = []
-    this.#activeSet = new Set()
-    this.#initializeEvents()
-  }
-
-  /**
-   * Validates that the input is a non-empty array of segments.
-   * @private
-   * @param {Array} segments - The array of line segments to validate.
-   * @throws {Error} Will throw an error if the input is not a valid array of segments.
-   */
-  #validateSegments(segments) {
-    if (
-      !Array.isArray(segments) ||
-      segments.length === 0 ||
-      !segments.every((seg) => seg.start && seg.end)
-    ) {
-      throw new Error(
-        'segments must be a non-empty array of objects with start and end properties.'
-      )
-    }
-  }
-
-  /**
-   * Initializes the event points for the sweep line algorithm.
-   * @private
-   */
-  #initializeEvents() {
-    for (const segment of this.#segments) {
-      const startEvent = { point: segment.start, type: 'start', segment }
-      const endEvent = { point: segment.end, type: 'end', segment }
-
-      // Ensure start is always before end in terms of x-coordinates
-      if (
-        startEvent.point.x > endEvent.point.x ||
-        (startEvent.point.x === endEvent.point.x &&
-          startEvent.point.y > endEvent.point.y)
-      ) {
-        this.#events.push(endEvent)
-        this.#events.push(startEvent)
-      } else {
-        this.#events.push(startEvent)
-        this.#events.push(endEvent)
-      }
-    }
-
-    // Sort events by x-coordinate, then by type (start before end)
-    this.#events.sort((a, b) => {
-      if (a.point.x === b.point.x) {
-        return a.type === 'start' ? -1 : 1
-      }
-      return a.point.x - b.point.x
-    })
-  }
-
-  #doSegmentsIntersect(seg1, seg2) {
-    const ccw = (A, B, C) => {
-      const val = (C.y - A.y) * (B.x - A.x) - (B.y - A.y) * (C.x - A.x)
-      if (Math.abs(val) < Number.EPSILON) return 0 // Collinear
-      return val > 0 ? 1 : -1 // Clockwise or counterclockwise
-    }
-
-    const onSegment = (p, q, r) => {
-      return (
-        q.x <= Math.max(p.x, r.x) &&
-        q.x >= Math.min(p.x, r.x) &&
-        q.y <= Math.max(p.y, r.y) &&
-        q.y >= Math.min(p.y, r.y)
-      )
-    }
-
-    const o1 = ccw(seg1.start, seg1.end, seg2.start)
-    const o2 = ccw(seg1.start, seg1.end, seg2.end)
-    const o3 = ccw(seg2.start, seg2.end, seg1.start)
-    const o4 = ccw(seg2.start, seg2.end, seg1.end)
-
-    // General case of intersection
-    if (o1 !== o2 && o3 !== o4) return true
-
-    // Special cases: collinear segments that may touch
-    if (o1 === 0 && onSegment(seg1.start, seg2.start, seg1.end)) return true
-    if (o2 === 0 && onSegment(seg1.start, seg2.end, seg1.end)) return true
-    if (o3 === 0 && onSegment(seg2.start, seg1.start, seg2.end)) return true
-    return o4 === 0 && onSegment(seg2.start, seg1.end, seg2.end)
-  }
-
-  /**
-   * Executes the Plane Sweep algorithm to find all intersections.
-   * @public
-   * @returns {Array<{segment1: *, segment2: *}>} An array of intersecting segment pairs.
-   */
-  findIntersections() {
-    const intersections = []
-
-    for (const event of this.#events) {
-      const { type, segment } = event
-
-      if (type === 'start') {
-        this.#activeSet.add(segment)
-
-        // Check for intersections with neighboring active segments
-        const neighbors = Array.from(this.#activeSet).filter(
-          (seg) => seg !== segment
-        )
-        for (const neighbor of neighbors) {
-          if (this.#doSegmentsIntersect(neighbor, segment)) {
-            intersections.push({ segment1: neighbor, segment2: segment })
-          }
-        }
-      } else if (type === 'end') {
-        // Remove the segment from the active set
-        this.#activeSet.delete(segment)
-      }
-    }
-
-    return intersections
-  }
-}
diff --git a/Geometry/Test/FindIntersections.test.js b/Geometry/Test/FindIntersections.test.js
new file mode 100644
index 0000000000..e71dfcfa5f
--- /dev/null
+++ b/Geometry/Test/FindIntersections.test.js
@@ -0,0 +1,159 @@
+import findIntersections, {
+  createPoint,
+  createEvent,
+  comparePoints,
+  compareSegmentsY,
+  calculateIntersectionPoint,
+  handlePotentialIntersection
+} from '../FindIntersections'
+
+describe('Geometry Functions', () => {
+  describe('createPoint', () => {
+    it('should create a point with given coordinates', () => {
+      const point = createPoint(1, 2)
+      expect(point).toEqual({ x: 1, y: 2 })
+    })
+  })
+
+  describe('createEvent', () => {
+    it('should create an event with the given parameters', () => {
+      const point = createPoint(1, 2)
+      const event = createEvent(point, ['left'], [])
+      expect(event).toEqual({
+        point,
+        types: ['left'],
+        segments: []
+      })
+    })
+  })
+
+  describe('comparePoints', () => {
+    it('should return negative if first point is less than second', () => {
+      const a = createPoint(1, 2)
+      const b = createPoint(2, 3)
+      expect(comparePoints(a, b)).toBeLessThan(0)
+    })
+
+    it('should return positive if first point is greater than second', () => {
+      const a = createPoint(2, 3)
+      const b = createPoint(1, 2)
+      expect(comparePoints(a, b)).toBeGreaterThan(0)
+    })
+
+    it('should return zero if points are equal', () => {
+      const a = createPoint(1, 2)
+      const b = createPoint(1, 2)
+      expect(comparePoints(a, b)).toBe(0)
+    })
+  })
+
+  describe('compareSegmentsY', () => {
+    it('should compare segments based on their start y-coordinates', () => {
+      const seg1 = { start: { x: 0, y: 1 }, end: { x: 1, y: 1 } }
+      const seg2 = { start: { x: 0, y: 2 }, end: { x: 1, y: 2 } }
+      expect(compareSegmentsY(seg1, seg2)).toBe(-1)
+    })
+
+    it('should return zero if segments have the same start y-coordinate', () => {
+      const seg1 = { start: { x: 0, y: 1 }, end: { x: 1, y: 1 } }
+      const seg2 = { start: { x: -1, y: 1 }, end: { x: -2, y: 1 } }
+      expect(compareSegmentsY(seg1, seg2)).toBe(0)
+    })
+
+    it('should return positive if first segment is greater than second', () => {
+      const seg1 = { start: { x: 0, y: 3 }, end: { x: 1, y: 3 } }
+      const seg2 = { start: { x: -1, y: 2 }, end: { x: -2, y: 2 } }
+      expect(compareSegmentsY(seg1, seg2)).toBe(1)
+    })
+  })
+
+  describe('calculateIntersectionPoint', () => {
+    it('should return null for parallel segments', () => {
+      const seg1 = { start: createPoint(0, 0), end: createPoint(5, 5) }
+      const seg2 = { start: createPoint(0, 1), end: createPoint(5, 6) }
+      expect(calculateIntersectionPoint(seg1, seg2)).toBeNull()
+    })
+
+    it('should return intersection point for intersecting segments', () => {
+      const seg1 = { start: createPoint(0, 0), end: createPoint(5, 5) }
+      const seg2 = { start: createPoint(0, 5), end: createPoint(5, 0) }
+      const intersection = calculateIntersectionPoint(seg1, seg2)
+      expect(intersection).toEqual(createPoint(2.5, 2.5))
+    })
+
+    it('should handle vertical and horizontal lines correctly', () => {
+      const seg1 = { start: createPoint(0, -1), end: createPoint(0, 1) } // vertical line
+      const seg2 = { start: createPoint(-1, 0), end: createPoint(1, 0) } // horizontal line
+      const intersection = calculateIntersectionPoint(seg1, seg2)
+
+      expect(intersection.x).toBeCloseTo(0) // Check for close proximity to zero
+      expect(intersection.y).toBeCloseTo(0) // Check for close proximity to zero
+    })
+
+    it('should handle coincident segments correctly', () => {
+      const seg1 = { start: createPoint(0, -1), end: createPoint(0, -3) }
+      const seg2 = { start: createPoint(0, -3), end: createPoint(0, -4) }
+      expect(calculateIntersectionPoint(seg1, seg2)).toBeNull() // should not intersect as they are collinear but not overlapping
+    })
+
+    it('should handle edge cases for intersection calculations', () => {
+      const seg1 = { start: createPoint(-10, -10), end: createPoint(-10, -10) }
+      const seg2 = { start: createPoint(-10, -10), end: createPoint(-10, -10) }
+
+      const intersection = calculateIntersectionPoint(seg1, seg2)
+
+      expect(intersection).toBeNull() // Expect null since they are coincident lines.
+    })
+  })
+
+  describe('handlePotentialIntersection', () => {
+    it('should push intersection event and intersection data when segments intersect', () => {
+      const events = []
+      const intersections = []
+      const seg1 = { start: createPoint(0, 0), end: createPoint(5, 5) }
+      const seg2 = { start: createPoint(0, 5), end: createPoint(5, -5) }
+
+      handlePotentialIntersection(seg1, seg2, events, intersections)
+
+      expect(events.length).toBeGreaterThan(0)
+      expect(intersections.length).toBeGreaterThan(0)
+    })
+
+    it('should not push anything when segments do not intersect', () => {
+      const events = []
+      const intersections = []
+      const seg1 = { start: createPoint(0, -10), end: createPoint(-10, -20) }
+      const seg2 = { start: createPoint(-20, -30), end: createPoint(-30, -40) }
+
+      handlePotentialIntersection(seg1, seg2, events, intersections)
+
+      expect(events.length).toBe(0)
+      expect(intersections.length).toBe(0)
+    })
+  })
+
+  describe('findIntersections', () => {
+    it('should find intersections among segments correctly', () => {
+      const segments = [
+        { start: createPoint(0, 0), end: createPoint(5, 5) },
+        { start: createPoint(0, 5), end: createPoint(5, 0) },
+        { start: createPoint(6, 6), end: createPoint(7, 7) }
+      ]
+
+      const result = findIntersections(segments)
+
+      expect(result.length).toBeGreaterThanOrEqual(1) // There should be at least one intersection
+    })
+
+    it('should return an empty array when no intersections exist', () => {
+      const segments = [
+        { start: createPoint(10, 10), end: createPoint(20, 20) },
+        { start: createPoint(30, 30), end: createPoint(40, 40) }
+      ]
+
+      const result = findIntersections(segments)
+
+      expect(result).toEqual([]) // No intersections should be found
+    })
+  })
+})
diff --git a/Geometry/Test/PlaneSweep.test.js b/Geometry/Test/PlaneSweep.test.js
deleted file mode 100644
index 3e3b47e17d..0000000000
--- a/Geometry/Test/PlaneSweep.test.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import PlaneSweep from '../PlaneSweep'
-
-describe('PlaneSweep', () => {
-  let planeSweep
-
-  describe('constructor', () => {
-    it('should create an instance with valid segments', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
-        { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } }
-      ]
-      expect(() => new PlaneSweep(segments)).not.toThrow()
-    })
-
-    it('should throw an error with empty segments array', () => {
-      expect(() => new PlaneSweep([])).toThrow(
-        'segments must be a non-empty array'
-      )
-    })
-
-    it('should throw an error with invalid segments', () => {
-      const invalidSegments = [{ start: { x: 0, y: 0 } }]
-      expect(() => new PlaneSweep(invalidSegments)).toThrow(
-        'segments must be a non-empty array of objects with start and end properties'
-      )
-    })
-  })
-
-  describe('findIntersections', () => {
-    beforeEach(() => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } },
-        { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-    })
-
-    it('should find intersections between crossing segments', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } },
-        { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-      expect(intersections[0]).toEqual(
-        expect.objectContaining({
-          segment1: expect.objectContaining({
-            start: expect.objectContaining({ x: 0, y: expect.any(Number) }),
-            end: expect.objectContaining({ x: 2, y: expect.any(Number) })
-          }),
-          segment2: expect.objectContaining({
-            start: expect.objectContaining({ x: 0, y: expect.any(Number) }),
-            end: expect.objectContaining({ x: 2, y: expect.any(Number) })
-          })
-        })
-      )
-    })
-
-    it('should not find intersections between non-crossing segments', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
-        { start: { x: 2, y: 2 }, end: { x: 3, y: 3 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(0)
-    })
-
-    it('should handle vertical and horizontal segments', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 0, y: 2 } }, // Vertical
-        { start: { x: -1, y: 1 }, end: { x: 2, y: 1 } } // Horizontal
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-    })
-
-    it('should handle segments with shared endpoints', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
-        { start: { x: 1, y: 1 }, end: { x: 2, y: 0 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1) // Shared endpoint is considered an intersection
-    })
-
-    it('should handle overlapping segments', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 2, y: 2 } },
-        { start: { x: 1, y: 1 }, end: { x: 3, y: 3 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-    })
-  })
-
-  describe('edge cases', () => {
-    it('should handle segments with reversed start and end points', () => {
-      const segments = [
-        { start: { x: 2, y: 2 }, end: { x: 0, y: 0 } },
-        { start: { x: 0, y: 2 }, end: { x: 2, y: 0 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-    })
-
-    it('should handle segments with same x-coordinate but different y-coordinates', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 0, y: 2 } },
-        { start: { x: 0, y: 1 }, end: { x: 0, y: 3 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-    })
-
-    it('should handle a large number of touching segments', () => {
-      const segments = Array.from({ length: 1000 }, (_, i) => ({
-        start: { x: i, y: 0 },
-        end: { x: i + 1, y: 1 }
-      }))
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      // Check if touching points are considered intersections
-      const touchingPointsAreIntersections = intersections.length > 0
-      if (touchingPointsAreIntersections) {
-        expect(intersections).toHaveLength(999) // Each segment touches its neighbor
-      } else {
-        expect(intersections).toHaveLength(0) // Touching points are not considered intersections
-      }
-    })
-
-    it('should detect touching points as intersections', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
-        { start: { x: 1, y: 1 }, end: { x: 2, y: 0 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-    })
-
-    it('should handle collinear overlapping segments', () => {
-      const segments = [
-        { start: { x: 0, y: 0 }, end: { x: 2, y: 0 } },
-        { start: { x: 1, y: 0 }, end: { x: 3, y: 0 } }
-      ]
-      planeSweep = new PlaneSweep(segments)
-      const intersections = planeSweep.findIntersections()
-      expect(intersections).toHaveLength(1)
-    })
-  })
-})