44
55public final class InternalClipper {
66
7+ private static final long MaxInt64 = 9223372036854775807L ;
8+ private static final long MaxCoord = MaxInt64 / 4 ;
9+ private static final double max_coord = MaxCoord ;
10+ private static final double min_coord = -MaxCoord ;
11+ private static final long Invalid64 = MaxInt64 ;
12+
13+ public static final double DEFAULT_ARC_TOLERANCE = 0.25 ;
714 private static final double FLOATING_POINT_TOLERANCE = 1E-12 ;
815 private static final double DEFAULT_MIN_EDGE_LENGTH = 0.1 ;
916
17+ private static final String PRECISION_RANGE_ERROR = "Error: Precision is out of range." ;
18+
19+ public static void CheckPrecision (int precision )
20+ {
21+ if (precision < -8 || precision > 8 )
22+ throw new IllegalArgumentException (PRECISION_RANGE_ERROR );
23+ }
24+
1025 private InternalClipper () {
1126 }
1227
@@ -30,75 +45,50 @@ public static double DotProduct(PointD vec1, PointD vec2) {
3045 return (vec1 .x * vec2 .x + vec1 .y * vec2 .y );
3146 }
3247
33- public static boolean GetIntersectPoint64 (Point64 ln1a , Point64 ln1b , Point64 ln2a , Point64 ln2b , /* out */ Point64 ip ) {
34- double m1 , b1 , m2 , b2 ;
35- if (ln1b .x == ln1a .x ) {
36- if (ln2b .x == ln2a .x ) {
37- return false ;
38- }
39- m2 = (double ) (ln2b .y - ln2a .y ) / (ln2b .x - ln2a .x );
40- b2 = ln2a .y - m2 * ln2a .x ;
41- ip .x = ln1a .x ;
42- ip .y = Math .round (m2 * ln1a .x + b2 );
43- } else if (ln2b .x == ln2a .x ) {
44- m1 = (double ) (ln1b .y - ln1a .y ) / (ln1b .x - ln1a .x );
45- b1 = ln1a .y - m1 * ln1a .x ;
46- ip .x = ln2a .x ;
47- ip .y = Math .round (m1 * ln2a .x + b1 );
48- } else {
49- m1 = (double ) (ln1b .y - ln1a .y ) / (ln1b .x - ln1a .x );
50- b1 = ln1a .y - m1 * ln1a .x ;
51- m2 = (double ) (ln2b .y - ln2a .y ) / (ln2b .x - ln2a .x );
52- b2 = ln2a .y - m2 * ln2a .x ;
53- if (Math .abs (m1 - m2 ) > FLOATING_POINT_TOLERANCE ) {
54- double x = (b2 - b1 ) / (m1 - m2 );
55- ip .x = Math .round (x );
56- ip .y = Math .round (m1 * x + b1 );
57- } else {
58- ip .x = Math .round ((ln1a .x + ln1b .x ) * 0.5 );
59- ip .y = Math .round ((ln1a .y + ln1b .y ) * 0.5 );
60- }
48+ public static long CheckCastInt64 (double val )
49+ {
50+ if ((val >= max_coord ) || (val <= min_coord )) return Invalid64 ;
51+ return (long )Math .rint (val );
52+ }
53+
54+ public static boolean GetIntersectPt (Point64 ln1a , Point64 ln1b , Point64 ln2a , Point64 ln2b , /*out*/ Point64 ip )
55+ {
56+ double dy1 = (ln1b .y - ln1a .y );
57+ double dx1 = (ln1b .x - ln1a .x );
58+ double dy2 = (ln2b .y - ln2a .y );
59+ double dx2 = (ln2b .x - ln2a .x );
60+ double cp = dy1 * dx2 - dy2 * dx1 ;
61+ if (cp == 0.0 ) {
62+ return false ;
6163 }
62- return true ;
64+ double qx = dx1 * ln1a .y - dy1 * ln1a .x ;
65+ double qy = dx2 * ln2a .y - dy2 * ln2a .x ;
66+ ip .x = CheckCastInt64 ((dx1 * qy - dx2 * qx ) / cp );
67+ ip .y = CheckCastInt64 ((dy1 * qy - dy2 * qx ) / cp );
68+ return (ip .x != Invalid64 && ip .y != Invalid64 );
6369 }
6470
6571 public static boolean GetIntersectPoint (Point64 ln1a , Point64 ln1b , Point64 ln2a , Point64 ln2b , /* out */ PointD ip ) {
66- double m1 , b1 , m2 , b2 ;
67- if (ln1b .x == ln1a .x ) {
68- if (ln2b .x == ln2a .x ) {
69- return false ;
70- }
71- m2 = (double ) (ln2b .y - ln2a .y ) / (ln2b .x - ln2a .x );
72- b2 = ln2a .y - m2 * ln2a .x ;
73- ip .x = ln1a .x ;
74- ip .y = m2 * ln1a .x + b2 ;
75- } else if (ln2b .x == ln2a .x ) {
76- m1 = (double ) (ln1b .y - ln1a .y ) / (ln1b .x - ln1a .x );
77- b1 = ln1a .y - m1 * ln1a .x ;
78- ip .x = ln2a .x ;
79- ip .y = m1 * ln2a .x + b1 ;
80- } else {
81- m1 = (double ) (ln1b .y - ln1a .y ) / (ln1b .x - ln1a .x );
82- b1 = ln1a .y - m1 * ln1a .x ;
83- m2 = (double ) (ln2b .y - ln2a .y ) / (ln2b .x - ln2a .x );
84- b2 = ln2a .y - m2 * ln2a .x ;
85- if (Math .abs (m1 - m2 ) > FLOATING_POINT_TOLERANCE ) {
86- ip .x = (b2 - b1 ) / (m1 - m2 );
87- ip .y = m1 * ip .x + b1 ;
88- } else {
89- ip .x = (ln1a .x + ln1b .x ) * 0.5 ;
90- ip .y = (ln1a .y + ln1b .y ) * 0.5 ;
91- }
72+ double dy1 = (ln1b .y - ln1a .y );
73+ double dx1 = (ln1b .x - ln1a .x );
74+ double dy2 = (ln2b .y - ln2a .y );
75+ double dx2 = (ln2b .x - ln2a .x );
76+ double q1 = dy1 * ln1a .x - dx1 * ln1a .y ;
77+ double q2 = dy2 * ln2a .x - dx2 * ln2a .y ;
78+ double cross_prod = dy1 * dx2 - dy2 * dx1 ;
79+ if (cross_prod == 0.0 ) {
80+ return false ;
9281 }
93-
82+ ip .x = (dx2 * q1 - dx1 * q2 ) / cross_prod ;
83+ ip .y = (dy2 * q1 - dy1 * q2 ) / cross_prod ;
9484 return true ;
9585 }
9686
97- public static boolean SegmentsIntersect (Point64 seg1a , Point64 seg1b , Point64 seg2a , Point64 seg2b ) {
98- return SegmentsIntersect (seg1a , seg1b , seg2a , seg2b , false );
87+ public static boolean SegsIntersect (Point64 seg1a , Point64 seg1b , Point64 seg2a , Point64 seg2b ) {
88+ return SegsIntersect (seg1a , seg1b , seg2a , seg2b , false );
9989 }
10090
101- public static boolean SegmentsIntersect (Point64 seg1a , Point64 seg1b , Point64 seg2a , Point64 seg2b , boolean inclusive ) {
91+ public static boolean SegsIntersect (Point64 seg1a , Point64 seg1b , Point64 seg2a , Point64 seg2b , boolean inclusive ) {
10292 if (inclusive ) {
10393 double res1 = CrossProduct (seg1a , seg2a , seg2b );
10494 double res2 = CrossProduct (seg1b , seg2a , seg2b );
@@ -113,75 +103,93 @@ public static boolean SegmentsIntersect(Point64 seg1a, Point64 seg1b, Point64 se
113103 // ensure NOT collinear
114104 return (res1 != 0 || res2 != 0 || res3 != 0 || res4 != 0 );
115105 } else {
116- double dx1 = seg1a .x - seg1b .x ;
117- double dy1 = seg1a .y - seg1b .y ;
118- double dx2 = seg2a .x - seg2b .x ;
119- double dy2 = seg2a .y - seg2b .y ;
120- return (((dy1 * (seg2a .x - seg1a .x ) - dx1 * (seg2a .y - seg1a .y ))
121- * (dy1 * (seg2b .x - seg1a .x ) - dx1 * (seg2b .y - seg1a .y )) < 0 )
122- && ((dy2 * (seg1a .x - seg2a .x ) - dx2 * (seg1a .y - seg2a .y ))
123- * (dy2 * (seg1b .x - seg2a .x ) - dx2 * (seg1b .y - seg2a .y )) < 0 ));
106+ return (CrossProduct (seg1a , seg2a , seg2b ) *
107+ CrossProduct (seg1b , seg2a , seg2b ) < 0 ) &&
108+ (CrossProduct (seg2a , seg1a , seg1b ) *
109+ CrossProduct (seg2b , seg1a , seg1b ) < 0 );
124110 }
125111 }
126112
127- public static PointInPolygonResult PointInPolygon (Point64 pt , Path64 polygon ) {
128- int len = polygon .size (), i = len - 1 ;
113+ public static Point64 GetClosestPtOnSegment (Point64 offPt , Point64 seg1 , Point64 seg2 )
114+ {
115+ if (seg1 .x == seg2 .x && seg1 .y == seg2 .y ) return seg1 ;
116+ double dx = (seg2 .x - seg1 .x );
117+ double dy = (seg2 .y - seg1 .y );
118+ double q = ((offPt .x - seg1 .x ) * dx +
119+ (offPt .y - seg1 .y ) * dy ) / ((dx *dx ) + (dy *dy ));
120+ if (q < 0 ) q = 0 ; else if (q > 1 ) q = 1 ;
121+ return new Point64 (
122+ seg1 .x + Math .rint (q * dx ), seg1 .y + Math .rint (q * dy ));
123+ }
129124
125+ public static PointInPolygonResult PointInPolygon (Point64 pt , Path64 polygon ) {
126+ int len = polygon .size (), start = 0 ;
130127 if (len < 3 ) {
131128 return PointInPolygonResult .IsOutside ;
132129 }
133130
134- while (i >= 0 && polygon .get (i ).y == pt .y ) {
135- --i ;
136- }
137- if (i < 0 ) {
131+ while (start < len && polygon .get (start ).y == pt .y ) start ++;
132+ if (start == len ) {
138133 return PointInPolygonResult .IsOutside ;
139134 }
140135
141- int val = 0 ;
142- boolean isAbove = polygon .get (i ).y < pt .y ;
143- i = 0 ;
136+ double d ;
137+ boolean isAbove = polygon .get (start ).y < pt .y , startingAbove = isAbove ;
138+ int val = 0 , i = start + 1 , end = len ;
139+ while (true ) {
140+ if (i == end ) {
141+ if (end == 0 || start == 0 ) {
142+ break ;
143+ }
144+ end = start ;
145+ i = 0 ;
146+ }
144147
145- while (i < len ) {
146148 if (isAbove ) {
147- while (i < len && polygon .get (i ).y < pt .y ) {
149+ while (i < end && polygon .get (i ).y < pt .y ) {
148150 i ++;
149151 }
150- if (i == len ) {
151- break ;
152+ if (i == end ) {
153+ continue ;
152154 }
153155 } else {
154- while (i < len && polygon .get (i ).y > pt .y ) {
156+ while (i < end && polygon .get (i ).y > pt .y ) {
155157 i ++;
156158 }
157- if (i == len ) {
158- break ;
159+ if (i == end ) {
160+ continue ;
159161 }
160162 }
161163
162- Point64 prev ;
163-
164- Point64 curr = polygon .get (i );
164+ Point64 curr = polygon .get (i ), prev ;
165165 if (i > 0 ) {
166166 prev = polygon .get (i - 1 );
167167 } else {
168168 prev = polygon .get (len - 1 );
169169 }
170170
171- if (curr .y == pt .y ) {
172- if (curr .x == pt .x || (curr .y == prev .y && ((pt .x < prev .x ) != (pt .x < curr .x )))) {
171+ if (curr .y == pt .y )
172+ {
173+ if (curr .x == pt .x || (curr .y == prev .y &&
174+ ((pt .x < prev .x ) != (pt .x < curr .x )))) {
173175 return PointInPolygonResult .IsOn ;
174176 }
175177 i ++;
178+ if (i == start ) {
179+ break ;
180+ }
176181 continue ;
177182 }
178183
179- if (pt .x < curr .x && pt .x < prev .x ) {
184+ if (pt .x < curr .x && pt .x < prev .x )
185+ {
180186 // we're only interested in edges crossing on the left
181- } else if (pt .x > prev .x && pt .x > curr .x ) {
187+ }
188+ else if (pt .x > prev .x && pt .x > curr .x )
189+ {
182190 val = 1 - val ; // toggle val
183191 } else {
184- double d = CrossProduct (prev , curr , pt );
192+ d = CrossProduct (prev , curr , pt );
185193 if (d == 0 ) {
186194 return PointInPolygonResult .IsOn ;
187195 }
@@ -192,6 +200,24 @@ public static PointInPolygonResult PointInPolygon(Point64 pt, Path64 polygon) {
192200 isAbove = !isAbove ;
193201 i ++;
194202 }
203+
204+ if (isAbove != startingAbove ) {
205+ if (i == len ) {
206+ i = 0 ;
207+ }
208+ if (i == 0 ) {
209+ d = CrossProduct (polygon .get (len - 1 ), polygon .get (0 ), pt );
210+ } else {
211+ d = CrossProduct (polygon .get (i - 1 ), polygon .get (i ), pt );
212+ }
213+ if (d == 0 ) {
214+ return PointInPolygonResult .IsOn ;
215+ }
216+ if ((d < 0 ) == isAbove ) {
217+ val = 1 - val ;
218+ }
219+ }
220+
195221 if (val == 0 ) {
196222 return PointInPolygonResult .IsOutside ;
197223 }
0 commit comments