22import { getHighPrecisionSnapInterval } from '../paths/mapshaper-snapping' ;
33import { debug } from '../utils/mapshaper-logging' ;
44import { distance2D , distanceSq , pointSegDistSq2 } from '../geom/mapshaper-basic-geom' ;
5- import Big from "big.js" ;
5+ import { fromScaledStr , toScaledStr , findBigIntScaleFactor } from '../geom/mapshaper-bigint-utils' ;
6+ //import { findCrossIntersection_big } from '../geom/mapshaper-segment-geom-big';
67
78// Find the intersection between two 2D segments
89// Returns 0, 1 or 2 [x, y] locations as null, [x, y], or [x1, y1, x2, y2]
@@ -38,18 +39,31 @@ export function segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy, epsArg) {
3839 return touches || cross || null ;
3940}
4041
41-
4242function findCrossIntersection ( ax , ay , bx , by , cx , cy , dx , dy , eps ) {
4343 var p ;
44- if ( eps > 0 && ! segmentHit_fast ( ax , ay , bx , by , cx , cy , dx , dy ) ) return null ;
45- else if ( eps === 0 && ! segmentHit_big ( ax , ay , bx , by , cx , cy , dx , dy ) ) return null ;
44+ // The normal-precision hit function works for all inputs when eps > 0 because
45+ // the geometries that cause the ordinary function fails are detected as
46+ // 'touches' or endpoint hits (at least this was true in all the real-world
47+ // data samples that were tested).
48+ //
49+ if ( eps > 0 && ! segmentHit_fast ( ax , ay , bx , by , cx , cy , dx , dy ) ) {
50+ return null ;
51+ } else if ( eps === 0 && ! segmentHit_robust ( ax , ay , bx , by , cx , cy , dx , dy ) ) {
52+ return null ;
53+ }
4654
47- // in a typical layer with many intersections, robust is preferred in
48- // most (>90%) segment intersections in order to keep the positional
49- // error within a small interval (e.g. 50% of eps)
55+ // in a typical layer with many intersections along shared polygon boundaries,
56+ // robust is preferred in most (>90%) segment intersections in order to keep
57+ // the positional error within a small interval (e.g. 50% of eps)
5058 //
5159 if ( useRobustCross ( ax , ay , bx , by , cx , cy , dx , dy ) ) {
5260 p = findCrossIntersection_robust ( ax , ay , bx , by , cx , cy , dx , dy ) ;
61+ // var p2 = findCrossIntersection_big(ax, ay, bx, by, cx, cy, dx, dy);
62+ // var dx = p[0] - p2[0];
63+ // var dy = p[1] - p2[1];
64+ // if (dx != 0 || dy != 0) {
65+ // console.log(dx, dy)
66+ // }
5367 } else {
5468 p = findCrossIntersection_fast ( ax , ay , bx , by , cx , cy , dx , dy ) ;
5569 }
@@ -69,41 +83,6 @@ function findCrossIntersection(ax, ay, bx, by, cx, cy, dx, dy, eps) {
6983 return p ;
7084}
7185
72- // Find the intersection point of two segments that cross each other,
73- // or return null if the segments do not cross.
74- // Assumes endpoint intersections have already been detected
75- function findCrossIntersection_robust ( ax , ay , bx , by , cx , cy , dx , dy ) {
76- var ax_big = Big ( ax ) ;
77- var ay_big = Big ( ay ) ;
78- var bx_big = Big ( bx ) ;
79- var by_big = Big ( by ) ;
80- var cx_big = Big ( cx ) ;
81- var cy_big = Big ( cy ) ;
82- var dx_big = Big ( dx ) ;
83- var dy_big = Big ( dy ) ;
84- var v1x = bx_big . minus ( ax_big ) ;
85- var v1y = by_big . minus ( ay_big ) ;
86- var den_big = determinant2D_big ( v1x , v1y , dx_big . minus ( cx ) , dy_big . minus ( cy ) ) ;
87- if ( den_big . eq ( 0 ) ) {
88- debug ( "DIV0 error (should have been caught upstream)" ) ;
89- // console.log("hit?", segmentHit_big(ax, ay, bx, by, cx, cy, dx, dy))
90- // console.log('Seg 1', getSegFeature(ax, ay, bx, by, true))
91- // console.log('Seg 2', getSegFeature(cx, cy, dx, dy, false))
92- return null ;
93- }
94- // perform division using regular math, which does not reduce overall
95- // precision in test data (big.js division is very slow)
96- // tests show identical result to:
97- // orient2D_big(cx_big, cy_big, dx_big, dy_big, ax_big, ay_big).div(den_big)
98- var m = orient2D_big ( cx_big , cy_big , dx_big , dy_big , ax_big ,
99- ay_big ) . toNumber ( ) / den_big . toNumber ( ) ;
100- var m_big = Big ( m ) ;
101- var x_big = ax_big . plus ( m_big . times ( v1x ) . round ( 16 ) ) ;
102- var y_big = ay_big . plus ( m_big . times ( v1y ) . round ( 16 ) ) ;
103- var p = [ x_big . toNumber ( ) , y_big . toNumber ( ) ] ;
104- return p ;
105- }
106-
10786function findCrossIntersection_fast ( ax , ay , bx , by , cx , cy , dx , dy ) {
10887 var den = determinant2D ( bx - ax , by - ay , dx - cx , dy - cy ) ;
10988 var m = orient2D ( cx , cy , dx , dy , ax , ay ) / den ;
@@ -118,6 +97,33 @@ function findCrossIntersection_fast(ax, ay, bx, by, cx, cy, dx, dy) {
11897 return p ;
11998}
12099
100+ // this function, using BigInt, is 3-4x faster than the version using big.js
101+ export function findCrossIntersection_robust ( ax , ay , bx , by , cx , cy , dx , dy ) {
102+ var d = findBigIntScaleFactor ( ax , ay , bx , by , cx , cy , dx , dy ) ;
103+ var d2 = 16 ; // scale numerator of integer division by this many decimal digits
104+ var k_bi = 10000000000000000n ; // matches d2
105+ var ax_bi = BigInt ( toScaledStr ( ax , d ) ) ;
106+ var ay_bi = BigInt ( toScaledStr ( ay , d ) ) ;
107+ var bx_bi = BigInt ( toScaledStr ( bx , d ) ) ;
108+ var by_bi = BigInt ( toScaledStr ( by , d ) ) ;
109+ var cx_bi = BigInt ( toScaledStr ( cx , d ) ) ;
110+ var cy_bi = BigInt ( toScaledStr ( cy , d ) ) ;
111+ var dx_bi = BigInt ( toScaledStr ( dx , d ) ) ;
112+ var dy_bi = BigInt ( toScaledStr ( dy , d ) ) ;
113+ var den = determinant2D ( bx_bi - ax_bi , by_bi - ay_bi , dx_bi - cx_bi , dy_bi - cy_bi ) ;
114+ if ( den === 0n ) {
115+ debug ( 'DIV0 error - should have been identified as collinear "touch" intersection.' ) ;
116+ return null ;
117+ }
118+ var num = orient2D ( cx_bi , cy_bi , dx_bi , dy_bi , ax_bi , ay_bi ) * k_bi ;
119+ var m_bi = num / den ;
120+ var x_bi = ax_bi * k_bi + m_bi * ( bx_bi - ax_bi ) ;
121+ var y_bi = ay_bi * k_bi + m_bi * ( by_bi - ay_bi ) ;
122+ var x = fromScaledStr ( x_bi . toString ( ) , d + d2 ) ;
123+ var y = fromScaledStr ( y_bi . toString ( ) , d + d2 ) ;
124+ return [ x , y ] ;
125+ }
126+
121127function useRobustCross ( ax , ay , bx , by , cx , cy , dx , dy ) {
122128 // angle and seg length ratio thresholds were found by comparing
123129 // fast and robust outputs on sample data
@@ -129,7 +135,7 @@ function useRobustCross(ax, ay, bx, by, cx, cy, dx, dy) {
129135 return false ;
130136}
131137
132- // Returns smaller unsigned angle between two segments
138+ // Returns smaller of two angles between two segments (unsigned)
133139function innerAngle ( ax , ay , bx , by , cx , cy , dx , dy ) {
134140 var v1x = bx - ax ;
135141 var v1y = by - ay ;
@@ -278,18 +284,6 @@ function clampIntersectionPoint(p, ax, ay, bx, by, cx, cy, dx, dy) {
278284 p [ 1 ] = y ;
279285}
280286
281- // function getSegFeature(x1, y1, x2, y2, hot) {
282- // return JSON.stringify({
283- // type: "Feature",
284- // properties: {
285- // stroke: hot ? "orange" : "blue"
286- // },
287- // geometry: {
288- // type: "LineString",
289- // coordinates: [[x1, y1], [x2, y2]]
290- // }
291- // });
292- // }
293287
294288// a: coordinate of point
295289// b: endpoint coordinate of segment
@@ -322,10 +316,6 @@ function determinant2D(a, b, c, d) {
322316 return a * d - b * c ;
323317}
324318
325- function determinant2D_big ( a , b , c , d ) {
326- return a . times ( d ) . minus ( b . times ( c ) ) ;
327- }
328-
329319// returns a positive value if the points a, b, and c are arranged in
330320// counterclockwise order, a negative value if the points are in clockwise
331321// order, and zero if the points are collinear.
@@ -334,22 +324,31 @@ export function orient2D(ax, ay, bx, by, cx, cy) {
334324 return determinant2D ( ax - cx , ay - cy , bx - cx , by - cy ) ;
335325}
336326
337- export function orient2D_big ( ax , ay , bx , by , cx , cy ) {
338- var a = ( ax . minus ( cx ) ) . times ( by . minus ( cy ) ) ;
339- var b = ( ay . minus ( cy ) ) . times ( bx . minus ( cx ) ) ;
340- return a . minus ( b ) ;
327+ export function orient2D_robust ( ax , ay , bx , by , cx , cy ) {
328+ var d = findBigIntScaleFactor ( ax , ay , bx , by , cx , cy ) ;
329+ var ax_bi = BigInt ( toScaledStr ( ax , d ) ) ;
330+ var ay_bi = BigInt ( toScaledStr ( ay , d ) ) ;
331+ var bx_bi = BigInt ( toScaledStr ( bx , d ) ) ;
332+ var by_bi = BigInt ( toScaledStr ( by , d ) ) ;
333+ var cx_bi = BigInt ( toScaledStr ( cx , d ) ) ;
334+ var cy_bi = BigInt ( toScaledStr ( cy , d ) ) ;
335+ var o2d_bi = orient2D ( ax_bi , ay_bi , bx_bi , by_bi , cx_bi , cy_bi ) ;
336+ return fromScaledStr ( o2d_bi . toString ( ) , d ) ;
341337}
342338
343- export function orient2D_big2 ( ax , ay , bx , by , cx , cy ) {
344- return orient2D_big ( Big ( ax ) , Big ( ay ) , Big ( bx ) , Big ( by ) , Big ( cx ) , Big ( cy ) ) ;
339+ function segmentHit_robust ( ax , ay , bx , by , cx , cy , dx , dy ) {
340+ var d = findBigIntScaleFactor ( ax , ay , bx , by , cx , cy , dx , dy ) ;
341+ var ax_bi = BigInt ( toScaledStr ( ax , d ) ) ;
342+ var ay_bi = BigInt ( toScaledStr ( ay , d ) ) ;
343+ var bx_bi = BigInt ( toScaledStr ( bx , d ) ) ;
344+ var by_bi = BigInt ( toScaledStr ( by , d ) ) ;
345+ var cx_bi = BigInt ( toScaledStr ( cx , d ) ) ;
346+ var cy_bi = BigInt ( toScaledStr ( cy , d ) ) ;
347+ var dx_bi = BigInt ( toScaledStr ( dx , d ) ) ;
348+ var dy_bi = BigInt ( toScaledStr ( dy , d ) ) ;
349+ return segmentHit_fast ( ax_bi , ay_bi , bx_bi , by_bi , cx_bi , cy_bi , dx_bi , dy_bi ) ;
345350}
346351
347-
348- // export function orient2D_v2(ax, ay, bx, by, cx, cy) {
349- // return -orient2D_robust(ax, ay, bx, by, cx, cy);
350- // }
351-
352-
353352// Source: Sedgewick, _Algorithms in C_
354353// (Other functions were tried that were more sensitive to floating point errors
355354// than this function)
@@ -360,25 +359,6 @@ export function segmentHit_fast(ax, ay, bx, by, cx, cy, dx, dy) {
360359 orient2D ( cx , cy , dx , dy , bx , by ) <= 0 ;
361360}
362361
363- export function segmentHit_big ( ax , ay , bx , by , cx , cy , dx , dy ) {
364- return orient2D_big ( ax , ay , bx , by , cx , cy ) . times (
365- orient2D_big ( ax , ay , bx , by , dx , dy ) ) . lte ( 0 ) &&
366- orient2D_big ( cx , cy , dx , dy , ax , ay ) . times (
367- orient2D_big ( cx , cy , dx , dy , bx , by ) ) . lte ( 0 ) ;
368- }
369-
370- export function segmentHit_big2 ( ax , ay , bx , by , cx , cy , dx , dy ) {
371- return segmentHit_big ( Big ( ax ) , Big ( ay ) , Big ( bx ) , Big ( by ) ,
372- Big ( cx ) , Big ( cy ) , Big ( dx ) , Big ( dy ) ) ;
373- }
374-
375- // export function segmentHit_robust(ax, ay, bx, by, cx, cy, dx, dy) {
376- // return -orient2D_robust(ax, ay, bx, by, cx, cy) *
377- // -orient2D_robust(ax, ay, bx, by, dx, dy) <= 0 &&
378- // -orient2D_robust(cx, cy, dx, dy, ax, ay) *
379- // -orient2D_robust(cx, cy, dx, dy, bx, by) <= 0;
380- // }
381-
382362// Useful for determining if a segment that intersects another segment is
383363// entering or leaving an enclosed buffer area
384364// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
0 commit comments