class Event { constructor(x, y, isLeft, index) { this.x = x; this.y = y; this.isLeft = isLeft; this.index = index; } // Order events by y-coordinate, then by x-coordinate compareTo(e) { if (this.y === e.y) return this.x - e.x; return this.y - e.y; } } function onSegment(p, q, r) { // Given three collinear points p, q, r // the function checks if point // q lies on line segment 'pr' if (q[0] <= Math.max(p[0], r[0]) && q[0] >= Math.min(p[0], r[0]) && q[1] <= Math.max(p[1], r[1]) && q[1] >= Math.min(p[1], r[1])) return true; return false; } function orientation(p, q, r) { // To find orientation of ordered triplet (p, q, r). // Returns 0 if collinear, 1 if clockwise, 2 if counterclockwise. let val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]); // collinear if (val === 0) return 0; // clock- or counterclockwise return (val > 0) ? 1 : 2; } function doIntersect(s1, s2) { // to fin dif two segments intersect let p1 = s1[0], q1 = s1[1], p2 = s2[0], q2 = s2[1]; let o1 = orientation(p1, q1, p2); let o2 = orientation(p1, q1, q2); let o3 = orientation(p2, q2, p1); let o4 = orientation(p2, q2, q1); // General case if (o1 !== o2 && o3 !== o4) return true; // Special cases if (o1 === 0 && onSegment(p1, p2, q1)) return true; if (o2 === 0 && onSegment(p1, q2, q1)) return true; if (o3 === 0 && onSegment(p2, p1, q2)) return true; if (o4 === 0 && onSegment(p2, q1, q2)) return true; return false; } function pred(arr, pos) { // Returns the predecessor iterator in set s. return pos > 0 ? arr[pos - 1] : null; } function succ(arr, pos) { // Returns the successor iterator in set s. return (pos + 1 < arr.length) ? arr[pos + 1] : null; } function lowerBound(arr, elem) { // Binary search to find the first element not less than elem. let low = 0, high = arr.length; while (low < high) { let mid = Math.floor((low + high) / 2); if (arr[mid].compareTo(elem) < 0) low = mid + 1; else high = mid; } return low; } function findIndex(arr, elem) { // Finds index of elem in arr. for (let i = 0; i < arr.length; i++) { if (arr[i].x === elem.x && arr[i].y === elem.y && arr[i].isLeft === elem.isLeft && arr[i].index === elem.index) return i; } return -1; } function isIntersect(lines) { // Returns the number of intersections found between segments. // To avoid duplicate intersection reports. let mp = {}; let events = []; let n = lines.length; for (let i = 0; i < n; ++i) { // Create events for the left and right endpoints. events.push(new Event(lines[i][0][0], lines[i][0][1], true, i)); events.push(new Event(lines[i][1][0], lines[i][1][1], false, i)); } // Sort events according to x-coordinate. events.sort((e1, e2) => e1.x - e2.x); // Set for storing active segments. let active = []; let ans = 0; // Process all events. for (let i = 0; i < 2 * n; i++) { let curr = events[i]; let index = curr.index; if (curr.isLeft) { // For the left endpoint, get neighbors in the active set. let pos = lowerBound(active, curr); let next = pos < active.length ? active[pos] : null; let prev = pred(active, pos); // Check for intersection with the next segment. if (next !== null && doIntersect(lines[next.index], lines[index])) { let key = (next.index + 1).toString() + " " + (index + 1).toString(); if (!(key in mp)) { mp[key] = 1; ans++; } } // Check for intersection with the previous segment. if (prev !== null && doIntersect(lines[prev.index], lines[index])) { let key = (prev.index + 1).toString() + " " + (index + 1).toString(); if (!(key in mp)) { mp[key] = 1; ans++; } } // To avoid counting duplicates if the same segment is both neighbor. if (prev !== null && next !== null && next.index === prev.index) ans--; active.splice(lowerBound(active, curr), 0, curr); } else { // For the right endpoint, find the matching left endpoint in the active set. let temp = new Event(lines[index][0][0], lines[index][0][1], true, index); let pos = findIndex(active, temp); let next = succ(active, pos); let prev = pred(active, pos); // If both neighbors exist, check if they intersect. if (next !== null && prev !== null) { let key1 = (next.index + 1).toString() + " " + (prev.index + 1).toString(); let key2 = (prev.index + 1).toString() + " " + (next.index + 1).toString(); if (!(key1 in mp) && !(key2 in mp) && doIntersect(lines[prev.index], lines[next.index])) ans++; mp[key1] = 1; } active.splice(pos, 1); } } for (let key in mp) { console.log("Line: " + key); } return ans; } let lines = [ [ [1, 5], [4, 5] ], [ [2, 5], [10, 1] ], [ [3, 2], [10, 3] ], [ [6, 4], [9, 4] ], [ [7, 1], [8, 1] ] ]; console.log(isIntersect(lines));