clipper2-ts 1.5.4-3.9a869ba
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +67 -0
- package/dist/Clipper.d.ts +114 -0
- package/dist/Clipper.d.ts.map +1 -0
- package/dist/Clipper.js +1134 -0
- package/dist/Clipper.js.map +1 -0
- package/dist/Core.d.ts +150 -0
- package/dist/Core.d.ts.map +1 -0
- package/dist/Core.js +645 -0
- package/dist/Core.js.map +1 -0
- package/dist/Engine.d.ts +337 -0
- package/dist/Engine.d.ts.map +1 -0
- package/dist/Engine.js +2972 -0
- package/dist/Engine.js.map +1 -0
- package/dist/Minkowski.d.ts +16 -0
- package/dist/Minkowski.d.ts.map +1 -0
- package/dist/Minkowski.js +131 -0
- package/dist/Minkowski.js.map +1 -0
- package/dist/Offset.d.ts +85 -0
- package/dist/Offset.d.ts.map +1 -0
- package/dist/Offset.js +649 -0
- package/dist/Offset.js.map +1 -0
- package/dist/RectClip.d.ts +80 -0
- package/dist/RectClip.d.ts.map +1 -0
- package/dist/RectClip.js +1009 -0
- package/dist/RectClip.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/src/Clipper.ts +1106 -0
- package/src/Core.ts +683 -0
- package/src/Engine.ts +3116 -0
- package/src/Minkowski.ts +153 -0
- package/src/Offset.ts +711 -0
- package/src/RectClip.ts +1028 -0
- package/src/index.ts +146 -0
package/dist/RectClip.js
ADDED
|
@@ -0,0 +1,1009 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*******************************************************************************
|
|
3
|
+
* Author : Angus Johnson *
|
|
4
|
+
* Date : 11 October 2025 *
|
|
5
|
+
* Website : https://www.angusj.com *
|
|
6
|
+
* Copyright : Angus Johnson 2010-2025 *
|
|
7
|
+
* Purpose : FAST rectangular clipping *
|
|
8
|
+
* License : https://www.boost.org/LICENSE_1_0.txt *
|
|
9
|
+
*******************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.RectClipLines64 = exports.RectClip64 = exports.OutPt2 = void 0;
|
|
12
|
+
const Core_1 = require("./Core");
|
|
13
|
+
class OutPt2 {
|
|
14
|
+
constructor(pt) {
|
|
15
|
+
this.next = null;
|
|
16
|
+
this.prev = null;
|
|
17
|
+
this.ownerIdx = 0;
|
|
18
|
+
this.edge = null;
|
|
19
|
+
this.pt = pt;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.OutPt2 = OutPt2;
|
|
23
|
+
var Location;
|
|
24
|
+
(function (Location) {
|
|
25
|
+
Location[Location["left"] = 0] = "left";
|
|
26
|
+
Location[Location["top"] = 1] = "top";
|
|
27
|
+
Location[Location["right"] = 2] = "right";
|
|
28
|
+
Location[Location["bottom"] = 3] = "bottom";
|
|
29
|
+
Location[Location["inside"] = 4] = "inside";
|
|
30
|
+
})(Location || (Location = {}));
|
|
31
|
+
class RectClip64 {
|
|
32
|
+
constructor(rect) {
|
|
33
|
+
this.pathBounds = { left: 0, top: 0, right: 0, bottom: 0 };
|
|
34
|
+
this.results = [];
|
|
35
|
+
this.edges = [];
|
|
36
|
+
this.currIdx = -1;
|
|
37
|
+
this.currIdx = -1;
|
|
38
|
+
this.rect = rect;
|
|
39
|
+
this.mp = Core_1.Rect64Utils.midPoint(rect);
|
|
40
|
+
this.rectPath = Core_1.Rect64Utils.asPath(this.rect);
|
|
41
|
+
this.results = [];
|
|
42
|
+
this.edges = [];
|
|
43
|
+
for (let i = 0; i < 8; i++) {
|
|
44
|
+
this.edges[i] = [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
add(pt, startingNewPath = false) {
|
|
48
|
+
// this method is only called by InternalExecute.
|
|
49
|
+
// Later splitting and rejoining won't create additional op's,
|
|
50
|
+
// though they will change the (non-storage) fResults count.
|
|
51
|
+
let currIdx = this.results.length;
|
|
52
|
+
let result;
|
|
53
|
+
if ((currIdx === 0) || startingNewPath) {
|
|
54
|
+
result = new OutPt2(pt);
|
|
55
|
+
this.results.push(result);
|
|
56
|
+
result.ownerIdx = currIdx;
|
|
57
|
+
result.prev = result;
|
|
58
|
+
result.next = result;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
currIdx--;
|
|
62
|
+
const prevOp = this.results[currIdx];
|
|
63
|
+
if (prevOp && Core_1.Point64Utils.equals(prevOp.pt, pt))
|
|
64
|
+
return prevOp;
|
|
65
|
+
result = new OutPt2(pt);
|
|
66
|
+
result.ownerIdx = currIdx;
|
|
67
|
+
result.next = prevOp.next;
|
|
68
|
+
prevOp.next.prev = result;
|
|
69
|
+
prevOp.next = result;
|
|
70
|
+
result.prev = prevOp;
|
|
71
|
+
this.results[currIdx] = result;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
static path1ContainsPath2(path1, path2) {
|
|
76
|
+
// nb: occasionally, due to rounding, path1 may
|
|
77
|
+
// appear (momentarily) inside or outside path2.
|
|
78
|
+
let ioCount = 0;
|
|
79
|
+
for (const pt of path2) {
|
|
80
|
+
const pip = Core_1.InternalClipper.pointInPolygon(pt, path1);
|
|
81
|
+
switch (pip) {
|
|
82
|
+
case Core_1.PointInPolygonResult.IsInside:
|
|
83
|
+
ioCount--;
|
|
84
|
+
break;
|
|
85
|
+
case Core_1.PointInPolygonResult.IsOutside:
|
|
86
|
+
ioCount++;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
if (Math.abs(ioCount) > 1)
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
return ioCount <= 0;
|
|
93
|
+
}
|
|
94
|
+
static isClockwise(prev, curr, prevPt, currPt, rectMidPoint) {
|
|
95
|
+
if (RectClip64.areOpposites(prev, curr)) {
|
|
96
|
+
return Core_1.InternalClipper.crossProduct(prevPt, rectMidPoint, currPt) < 0;
|
|
97
|
+
}
|
|
98
|
+
return RectClip64.headingClockwise(prev, curr);
|
|
99
|
+
}
|
|
100
|
+
static areOpposites(prev, curr) {
|
|
101
|
+
return Math.abs(prev - curr) === 2;
|
|
102
|
+
}
|
|
103
|
+
static headingClockwise(prev, curr) {
|
|
104
|
+
return (prev + 1) % 4 === curr;
|
|
105
|
+
}
|
|
106
|
+
static getAdjacentLocation(loc, isClockwise) {
|
|
107
|
+
const delta = isClockwise ? 1 : 3;
|
|
108
|
+
return (loc + delta) % 4;
|
|
109
|
+
}
|
|
110
|
+
static unlinkOp(op) {
|
|
111
|
+
if (op.next === op)
|
|
112
|
+
return null;
|
|
113
|
+
op.prev.next = op.next;
|
|
114
|
+
op.next.prev = op.prev;
|
|
115
|
+
return op.next;
|
|
116
|
+
}
|
|
117
|
+
static unlinkOpBack(op) {
|
|
118
|
+
if (op.next === op)
|
|
119
|
+
return null;
|
|
120
|
+
op.prev.next = op.next;
|
|
121
|
+
op.next.prev = op.prev;
|
|
122
|
+
return op.prev;
|
|
123
|
+
}
|
|
124
|
+
static getEdgesForPt(pt, rec) {
|
|
125
|
+
let result = 0;
|
|
126
|
+
if (pt.x === rec.left)
|
|
127
|
+
result = 1;
|
|
128
|
+
else if (pt.x === rec.right)
|
|
129
|
+
result = 4;
|
|
130
|
+
if (pt.y === rec.top)
|
|
131
|
+
result += 2;
|
|
132
|
+
else if (pt.y === rec.bottom)
|
|
133
|
+
result += 8;
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
static isHeadingClockwise(pt1, pt2, edgeIdx) {
|
|
137
|
+
switch (edgeIdx) {
|
|
138
|
+
case 0: return pt2.y < pt1.y;
|
|
139
|
+
case 1: return pt2.x > pt1.x;
|
|
140
|
+
case 2: return pt2.y > pt1.y;
|
|
141
|
+
default: return pt2.x < pt1.x;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
static hasHorzOverlap(left1, right1, left2, right2) {
|
|
145
|
+
return (left1.x < right2.x) && (right1.x > left2.x);
|
|
146
|
+
}
|
|
147
|
+
static hasVertOverlap(top1, bottom1, top2, bottom2) {
|
|
148
|
+
return (top1.y < bottom2.y) && (bottom1.y > top2.y);
|
|
149
|
+
}
|
|
150
|
+
static addToEdge(edge, op) {
|
|
151
|
+
if (op.edge !== null)
|
|
152
|
+
return;
|
|
153
|
+
op.edge = edge;
|
|
154
|
+
edge.push(op);
|
|
155
|
+
}
|
|
156
|
+
static uncoupleEdge(op) {
|
|
157
|
+
if (op.edge === null)
|
|
158
|
+
return;
|
|
159
|
+
for (let i = 0; i < op.edge.length; i++) {
|
|
160
|
+
const op2 = op.edge[i];
|
|
161
|
+
if (op2 === op) {
|
|
162
|
+
op.edge[i] = null;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
op.edge = null;
|
|
167
|
+
}
|
|
168
|
+
static setNewOwner(op, newIdx) {
|
|
169
|
+
op.ownerIdx = newIdx;
|
|
170
|
+
let op2 = op.next;
|
|
171
|
+
while (op2 !== op) {
|
|
172
|
+
op2.ownerIdx = newIdx;
|
|
173
|
+
op2 = op2.next;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
addCorner(prev, curr) {
|
|
177
|
+
this.add(RectClip64.headingClockwise(prev, curr) ?
|
|
178
|
+
this.rectPath[prev] : this.rectPath[curr]);
|
|
179
|
+
}
|
|
180
|
+
addCornerWithDirection(loc, isClockwise) {
|
|
181
|
+
if (isClockwise) {
|
|
182
|
+
this.add(this.rectPath[loc]);
|
|
183
|
+
return RectClip64.getAdjacentLocation(loc, true);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const newLoc = RectClip64.getAdjacentLocation(loc, false);
|
|
187
|
+
this.add(this.rectPath[newLoc]);
|
|
188
|
+
return newLoc;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
static getLocation(rec, pt) {
|
|
192
|
+
if (pt.x === rec.left && pt.y >= rec.top && pt.y <= rec.bottom) {
|
|
193
|
+
return { location: Location.left, isOnRect: true };
|
|
194
|
+
}
|
|
195
|
+
if (pt.x === rec.right && pt.y >= rec.top && pt.y <= rec.bottom) {
|
|
196
|
+
return { location: Location.right, isOnRect: true };
|
|
197
|
+
}
|
|
198
|
+
if (pt.y === rec.top && pt.x >= rec.left && pt.x <= rec.right) {
|
|
199
|
+
return { location: Location.top, isOnRect: true };
|
|
200
|
+
}
|
|
201
|
+
if (pt.y === rec.bottom && pt.x >= rec.left && pt.x <= rec.right) {
|
|
202
|
+
return { location: Location.bottom, isOnRect: true };
|
|
203
|
+
}
|
|
204
|
+
let location;
|
|
205
|
+
if (pt.x < rec.left)
|
|
206
|
+
location = Location.left;
|
|
207
|
+
else if (pt.x > rec.right)
|
|
208
|
+
location = Location.right;
|
|
209
|
+
else if (pt.y < rec.top)
|
|
210
|
+
location = Location.top;
|
|
211
|
+
else if (pt.y > rec.bottom)
|
|
212
|
+
location = Location.bottom;
|
|
213
|
+
else
|
|
214
|
+
location = Location.inside;
|
|
215
|
+
return { location, isOnRect: false };
|
|
216
|
+
}
|
|
217
|
+
static isHorizontal(pt1, pt2) {
|
|
218
|
+
return pt1.y === pt2.y;
|
|
219
|
+
}
|
|
220
|
+
static getSegmentIntersection(p1, p2, p3, p4) {
|
|
221
|
+
const res1 = Core_1.InternalClipper.crossProduct(p1, p3, p4);
|
|
222
|
+
const res2 = Core_1.InternalClipper.crossProduct(p2, p3, p4);
|
|
223
|
+
if (res1 === 0) {
|
|
224
|
+
const ip = p1;
|
|
225
|
+
if (res2 === 0)
|
|
226
|
+
return { intersects: false, point: ip }; // segments are collinear
|
|
227
|
+
if (Core_1.Point64Utils.equals(p1, p3) || Core_1.Point64Utils.equals(p1, p4))
|
|
228
|
+
return { intersects: true, point: ip };
|
|
229
|
+
if (RectClip64.isHorizontal(p3, p4)) {
|
|
230
|
+
return { intersects: (p1.x > p3.x) === (p1.x < p4.x), point: ip };
|
|
231
|
+
}
|
|
232
|
+
return { intersects: (p1.y > p3.y) === (p1.y < p4.y), point: ip };
|
|
233
|
+
}
|
|
234
|
+
if (res2 === 0) {
|
|
235
|
+
const ip = p2;
|
|
236
|
+
if (Core_1.Point64Utils.equals(p2, p3) || Core_1.Point64Utils.equals(p2, p4))
|
|
237
|
+
return { intersects: true, point: ip };
|
|
238
|
+
if (RectClip64.isHorizontal(p3, p4)) {
|
|
239
|
+
return { intersects: (p2.x > p3.x) === (p2.x < p4.x), point: ip };
|
|
240
|
+
}
|
|
241
|
+
return { intersects: (p2.y > p3.y) === (p2.y < p4.y), point: ip };
|
|
242
|
+
}
|
|
243
|
+
if ((res1 > 0) === (res2 > 0)) {
|
|
244
|
+
return { intersects: false, point: { x: 0, y: 0 } };
|
|
245
|
+
}
|
|
246
|
+
const res3 = Core_1.InternalClipper.crossProduct(p3, p1, p2);
|
|
247
|
+
const res4 = Core_1.InternalClipper.crossProduct(p4, p1, p2);
|
|
248
|
+
if (res3 === 0) {
|
|
249
|
+
const ip = p3;
|
|
250
|
+
if (Core_1.Point64Utils.equals(p3, p1) || Core_1.Point64Utils.equals(p3, p2))
|
|
251
|
+
return { intersects: true, point: ip };
|
|
252
|
+
if (RectClip64.isHorizontal(p1, p2)) {
|
|
253
|
+
return { intersects: (p3.x > p1.x) === (p3.x < p2.x), point: ip };
|
|
254
|
+
}
|
|
255
|
+
return { intersects: (p3.y > p1.y) === (p3.y < p2.y), point: ip };
|
|
256
|
+
}
|
|
257
|
+
if (res4 === 0) {
|
|
258
|
+
const ip = p4;
|
|
259
|
+
if (Core_1.Point64Utils.equals(p4, p1) || Core_1.Point64Utils.equals(p4, p2))
|
|
260
|
+
return { intersects: true, point: ip };
|
|
261
|
+
if (RectClip64.isHorizontal(p1, p2)) {
|
|
262
|
+
return { intersects: (p4.x > p1.x) === (p4.x < p2.x), point: ip };
|
|
263
|
+
}
|
|
264
|
+
return { intersects: (p4.y > p1.y) === (p4.y < p2.y), point: ip };
|
|
265
|
+
}
|
|
266
|
+
if ((res3 > 0) === (res4 > 0)) {
|
|
267
|
+
return { intersects: false, point: { x: 0, y: 0 } };
|
|
268
|
+
}
|
|
269
|
+
// segments must intersect to get here
|
|
270
|
+
return Core_1.InternalClipper.getLineIntersectPt(p1, p2, p3, p4);
|
|
271
|
+
}
|
|
272
|
+
static getIntersection(rectPath, p, p2, loc) {
|
|
273
|
+
// gets the pt of intersection between rectPath and segment(p, p2) that's closest to 'p'
|
|
274
|
+
// when result == false, loc will remain unchanged
|
|
275
|
+
let ip = { x: 0, y: 0 };
|
|
276
|
+
let newLocation = loc;
|
|
277
|
+
switch (loc) {
|
|
278
|
+
case Location.left:
|
|
279
|
+
{
|
|
280
|
+
const result1 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[3]);
|
|
281
|
+
if (result1.intersects) {
|
|
282
|
+
ip = result1.point;
|
|
283
|
+
return { intersects: true, point: ip, newLocation };
|
|
284
|
+
}
|
|
285
|
+
if (p.y < rectPath[0].y) {
|
|
286
|
+
const result2 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[1]);
|
|
287
|
+
if (result2.intersects) {
|
|
288
|
+
newLocation = Location.top;
|
|
289
|
+
return { intersects: true, point: result2.point, newLocation };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const result3 = RectClip64.getSegmentIntersection(p, p2, rectPath[2], rectPath[3]);
|
|
293
|
+
if (result3.intersects) {
|
|
294
|
+
newLocation = Location.bottom;
|
|
295
|
+
return { intersects: true, point: result3.point, newLocation };
|
|
296
|
+
}
|
|
297
|
+
return { intersects: false, point: ip, newLocation };
|
|
298
|
+
}
|
|
299
|
+
case Location.right:
|
|
300
|
+
{
|
|
301
|
+
const result1 = RectClip64.getSegmentIntersection(p, p2, rectPath[1], rectPath[2]);
|
|
302
|
+
if (result1.intersects) {
|
|
303
|
+
return { intersects: true, point: result1.point, newLocation };
|
|
304
|
+
}
|
|
305
|
+
if (p.y < rectPath[0].y) {
|
|
306
|
+
const result2 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[1]);
|
|
307
|
+
if (result2.intersects) {
|
|
308
|
+
newLocation = Location.top;
|
|
309
|
+
return { intersects: true, point: result2.point, newLocation };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const result3 = RectClip64.getSegmentIntersection(p, p2, rectPath[2], rectPath[3]);
|
|
313
|
+
if (result3.intersects) {
|
|
314
|
+
newLocation = Location.bottom;
|
|
315
|
+
return { intersects: true, point: result3.point, newLocation };
|
|
316
|
+
}
|
|
317
|
+
return { intersects: false, point: ip, newLocation };
|
|
318
|
+
}
|
|
319
|
+
case Location.top:
|
|
320
|
+
{
|
|
321
|
+
const result1 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[1]);
|
|
322
|
+
if (result1.intersects) {
|
|
323
|
+
return { intersects: true, point: result1.point, newLocation };
|
|
324
|
+
}
|
|
325
|
+
if (p.x < rectPath[0].x) {
|
|
326
|
+
const result2 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[3]);
|
|
327
|
+
if (result2.intersects) {
|
|
328
|
+
newLocation = Location.left;
|
|
329
|
+
return { intersects: true, point: result2.point, newLocation };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (p.x <= rectPath[1].x) {
|
|
333
|
+
return { intersects: false, point: ip, newLocation };
|
|
334
|
+
}
|
|
335
|
+
const result3 = RectClip64.getSegmentIntersection(p, p2, rectPath[1], rectPath[2]);
|
|
336
|
+
if (result3.intersects) {
|
|
337
|
+
newLocation = Location.right;
|
|
338
|
+
return { intersects: true, point: result3.point, newLocation };
|
|
339
|
+
}
|
|
340
|
+
return { intersects: false, point: ip, newLocation };
|
|
341
|
+
}
|
|
342
|
+
case Location.bottom:
|
|
343
|
+
{
|
|
344
|
+
const result1 = RectClip64.getSegmentIntersection(p, p2, rectPath[2], rectPath[3]);
|
|
345
|
+
if (result1.intersects) {
|
|
346
|
+
return { intersects: true, point: result1.point, newLocation };
|
|
347
|
+
}
|
|
348
|
+
if (p.x < rectPath[3].x) {
|
|
349
|
+
const result2 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[3]);
|
|
350
|
+
if (result2.intersects) {
|
|
351
|
+
newLocation = Location.left;
|
|
352
|
+
return { intersects: true, point: result2.point, newLocation };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (p.x <= rectPath[2].x) {
|
|
356
|
+
return { intersects: false, point: ip, newLocation };
|
|
357
|
+
}
|
|
358
|
+
const result3 = RectClip64.getSegmentIntersection(p, p2, rectPath[1], rectPath[2]);
|
|
359
|
+
if (result3.intersects) {
|
|
360
|
+
newLocation = Location.right;
|
|
361
|
+
return { intersects: true, point: result3.point, newLocation };
|
|
362
|
+
}
|
|
363
|
+
return { intersects: false, point: ip, newLocation };
|
|
364
|
+
}
|
|
365
|
+
default:
|
|
366
|
+
{
|
|
367
|
+
const result1 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[3]);
|
|
368
|
+
if (result1.intersects) {
|
|
369
|
+
newLocation = Location.left;
|
|
370
|
+
return { intersects: true, point: result1.point, newLocation };
|
|
371
|
+
}
|
|
372
|
+
const result2 = RectClip64.getSegmentIntersection(p, p2, rectPath[0], rectPath[1]);
|
|
373
|
+
if (result2.intersects) {
|
|
374
|
+
newLocation = Location.top;
|
|
375
|
+
return { intersects: true, point: result2.point, newLocation };
|
|
376
|
+
}
|
|
377
|
+
const result3 = RectClip64.getSegmentIntersection(p, p2, rectPath[1], rectPath[2]);
|
|
378
|
+
if (result3.intersects) {
|
|
379
|
+
newLocation = Location.right;
|
|
380
|
+
return { intersects: true, point: result3.point, newLocation };
|
|
381
|
+
}
|
|
382
|
+
const result4 = RectClip64.getSegmentIntersection(p, p2, rectPath[2], rectPath[3]);
|
|
383
|
+
if (result4.intersects) {
|
|
384
|
+
newLocation = Location.bottom;
|
|
385
|
+
return { intersects: true, point: result4.point, newLocation };
|
|
386
|
+
}
|
|
387
|
+
return { intersects: false, point: ip, newLocation };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
getNextLocation(path, loc, i, highI) {
|
|
392
|
+
let newI = i;
|
|
393
|
+
let newLoc = loc;
|
|
394
|
+
switch (loc) {
|
|
395
|
+
case Location.left:
|
|
396
|
+
while (newI <= highI && path[newI].x <= this.rect.left)
|
|
397
|
+
newI++;
|
|
398
|
+
if (newI > highI)
|
|
399
|
+
break;
|
|
400
|
+
if (path[newI].x >= this.rect.right)
|
|
401
|
+
newLoc = Location.right;
|
|
402
|
+
else if (path[newI].y <= this.rect.top)
|
|
403
|
+
newLoc = Location.top;
|
|
404
|
+
else if (path[newI].y >= this.rect.bottom)
|
|
405
|
+
newLoc = Location.bottom;
|
|
406
|
+
else
|
|
407
|
+
newLoc = Location.inside;
|
|
408
|
+
break;
|
|
409
|
+
case Location.top:
|
|
410
|
+
while (newI <= highI && path[newI].y <= this.rect.top)
|
|
411
|
+
newI++;
|
|
412
|
+
if (newI > highI)
|
|
413
|
+
break;
|
|
414
|
+
if (path[newI].y >= this.rect.bottom)
|
|
415
|
+
newLoc = Location.bottom;
|
|
416
|
+
else if (path[newI].x <= this.rect.left)
|
|
417
|
+
newLoc = Location.left;
|
|
418
|
+
else if (path[newI].x >= this.rect.right)
|
|
419
|
+
newLoc = Location.right;
|
|
420
|
+
else
|
|
421
|
+
newLoc = Location.inside;
|
|
422
|
+
break;
|
|
423
|
+
case Location.right:
|
|
424
|
+
while (newI <= highI && path[newI].x >= this.rect.right)
|
|
425
|
+
newI++;
|
|
426
|
+
if (newI > highI)
|
|
427
|
+
break;
|
|
428
|
+
if (path[newI].x <= this.rect.left)
|
|
429
|
+
newLoc = Location.left;
|
|
430
|
+
else if (path[newI].y <= this.rect.top)
|
|
431
|
+
newLoc = Location.top;
|
|
432
|
+
else if (path[newI].y >= this.rect.bottom)
|
|
433
|
+
newLoc = Location.bottom;
|
|
434
|
+
else
|
|
435
|
+
newLoc = Location.inside;
|
|
436
|
+
break;
|
|
437
|
+
case Location.bottom:
|
|
438
|
+
while (newI <= highI && path[newI].y >= this.rect.bottom)
|
|
439
|
+
newI++;
|
|
440
|
+
if (newI > highI)
|
|
441
|
+
break;
|
|
442
|
+
if (path[newI].y <= this.rect.top)
|
|
443
|
+
newLoc = Location.top;
|
|
444
|
+
else if (path[newI].x <= this.rect.left)
|
|
445
|
+
newLoc = Location.left;
|
|
446
|
+
else if (path[newI].x >= this.rect.right)
|
|
447
|
+
newLoc = Location.right;
|
|
448
|
+
else
|
|
449
|
+
newLoc = Location.inside;
|
|
450
|
+
break;
|
|
451
|
+
case Location.inside:
|
|
452
|
+
while (newI <= highI) {
|
|
453
|
+
if (path[newI].x < this.rect.left)
|
|
454
|
+
newLoc = Location.left;
|
|
455
|
+
else if (path[newI].x > this.rect.right)
|
|
456
|
+
newLoc = Location.right;
|
|
457
|
+
else if (path[newI].y > this.rect.bottom)
|
|
458
|
+
newLoc = Location.bottom;
|
|
459
|
+
else if (path[newI].y < this.rect.top)
|
|
460
|
+
newLoc = Location.top;
|
|
461
|
+
else {
|
|
462
|
+
this.add(path[newI]);
|
|
463
|
+
newI++;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
return { location: newLoc, index: newI };
|
|
471
|
+
}
|
|
472
|
+
static startLocsAreClockwise(startLocs) {
|
|
473
|
+
let result = 0;
|
|
474
|
+
for (let i = 1; i < startLocs.length; i++) {
|
|
475
|
+
const d = startLocs[i] - startLocs[i - 1];
|
|
476
|
+
switch (d) {
|
|
477
|
+
case -1:
|
|
478
|
+
result -= 1;
|
|
479
|
+
break;
|
|
480
|
+
case 1:
|
|
481
|
+
result += 1;
|
|
482
|
+
break;
|
|
483
|
+
case -3:
|
|
484
|
+
result += 1;
|
|
485
|
+
break;
|
|
486
|
+
case 3:
|
|
487
|
+
result -= 1;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return result > 0;
|
|
492
|
+
}
|
|
493
|
+
executeInternal(path) {
|
|
494
|
+
if (path.length < 3 || Core_1.Rect64Utils.isEmpty(this.rect))
|
|
495
|
+
return;
|
|
496
|
+
const startLocs = [];
|
|
497
|
+
let firstCross = Location.inside;
|
|
498
|
+
let crossingLoc = firstCross;
|
|
499
|
+
let prev = firstCross;
|
|
500
|
+
const highI = path.length - 1;
|
|
501
|
+
const lastLocResult = RectClip64.getLocation(this.rect, path[highI]);
|
|
502
|
+
let loc = lastLocResult.location;
|
|
503
|
+
if (lastLocResult.isOnRect) {
|
|
504
|
+
let i = highI - 1;
|
|
505
|
+
while (i >= 0) {
|
|
506
|
+
const prevLocResult = RectClip64.getLocation(this.rect, path[i]);
|
|
507
|
+
if (!prevLocResult.isOnRect) {
|
|
508
|
+
prev = prevLocResult.location;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
i--;
|
|
512
|
+
}
|
|
513
|
+
if (i < 0) {
|
|
514
|
+
for (const pt of path) {
|
|
515
|
+
this.add(pt);
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (prev === Location.inside)
|
|
520
|
+
loc = Location.inside;
|
|
521
|
+
}
|
|
522
|
+
const startingLoc = loc;
|
|
523
|
+
///////////////////////////////////////////////////
|
|
524
|
+
let i = 0;
|
|
525
|
+
while (i <= highI) {
|
|
526
|
+
prev = loc;
|
|
527
|
+
const prevCrossLoc = crossingLoc;
|
|
528
|
+
const nextLocResult = this.getNextLocation(path, loc, i, highI);
|
|
529
|
+
loc = nextLocResult.location;
|
|
530
|
+
i = nextLocResult.index;
|
|
531
|
+
if (i > highI)
|
|
532
|
+
break;
|
|
533
|
+
const prevPt = (i === 0) ? path[highI] : path[i - 1];
|
|
534
|
+
crossingLoc = loc;
|
|
535
|
+
const intersectionResult = RectClip64.getIntersection(this.rectPath, path[i], prevPt, crossingLoc);
|
|
536
|
+
if (!intersectionResult.intersects) {
|
|
537
|
+
// ie remaining outside
|
|
538
|
+
if (prevCrossLoc === Location.inside) {
|
|
539
|
+
const isClockw = RectClip64.isClockwise(prev, loc, prevPt, path[i], this.mp);
|
|
540
|
+
do {
|
|
541
|
+
startLocs.push(prev);
|
|
542
|
+
prev = RectClip64.getAdjacentLocation(prev, isClockw);
|
|
543
|
+
} while (prev !== loc);
|
|
544
|
+
crossingLoc = prevCrossLoc; // still not crossed
|
|
545
|
+
}
|
|
546
|
+
else if (prev !== Location.inside && prev !== loc) {
|
|
547
|
+
const isClockw = RectClip64.isClockwise(prev, loc, prevPt, path[i], this.mp);
|
|
548
|
+
do {
|
|
549
|
+
prev = this.addCornerWithDirection(prev, isClockw);
|
|
550
|
+
} while (prev !== loc);
|
|
551
|
+
}
|
|
552
|
+
++i;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
const ip = intersectionResult.point;
|
|
556
|
+
crossingLoc = intersectionResult.newLocation;
|
|
557
|
+
////////////////////////////////////////////////////
|
|
558
|
+
// we must be crossing the rect boundary to get here
|
|
559
|
+
////////////////////////////////////////////////////
|
|
560
|
+
if (loc === Location.inside) { // path must be entering rect
|
|
561
|
+
if (firstCross === Location.inside) {
|
|
562
|
+
firstCross = crossingLoc;
|
|
563
|
+
startLocs.push(prev);
|
|
564
|
+
}
|
|
565
|
+
else if (prev !== crossingLoc) {
|
|
566
|
+
const isClockw = RectClip64.isClockwise(prev, crossingLoc, prevPt, path[i], this.mp);
|
|
567
|
+
do {
|
|
568
|
+
prev = this.addCornerWithDirection(prev, isClockw);
|
|
569
|
+
} while (prev !== crossingLoc);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
else if (prev !== Location.inside) {
|
|
573
|
+
// passing right through rect. 'ip' here will be the second
|
|
574
|
+
// intersect pt but we'll also need the first intersect pt (ip2)
|
|
575
|
+
loc = prev;
|
|
576
|
+
const intersection2Result = RectClip64.getIntersection(this.rectPath, prevPt, path[i], loc);
|
|
577
|
+
const ip2 = intersection2Result.point;
|
|
578
|
+
if (prevCrossLoc !== Location.inside && prevCrossLoc !== loc) { //#597
|
|
579
|
+
this.addCorner(prevCrossLoc, loc);
|
|
580
|
+
}
|
|
581
|
+
if (firstCross === Location.inside) {
|
|
582
|
+
firstCross = loc;
|
|
583
|
+
startLocs.push(prev);
|
|
584
|
+
}
|
|
585
|
+
loc = crossingLoc;
|
|
586
|
+
this.add(ip2);
|
|
587
|
+
if (Core_1.Point64Utils.equals(ip, ip2)) {
|
|
588
|
+
// it's very likely that path[i] is on rect
|
|
589
|
+
const pathLocResult = RectClip64.getLocation(this.rect, path[i]);
|
|
590
|
+
loc = pathLocResult.location;
|
|
591
|
+
this.addCorner(crossingLoc, loc);
|
|
592
|
+
crossingLoc = loc;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
else { // path must be exiting rect
|
|
597
|
+
loc = crossingLoc;
|
|
598
|
+
if (firstCross === Location.inside) {
|
|
599
|
+
firstCross = crossingLoc;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
this.add(ip);
|
|
603
|
+
} //while i <= highI
|
|
604
|
+
///////////////////////////////////////////////////
|
|
605
|
+
if (firstCross === Location.inside) {
|
|
606
|
+
// path never intersects
|
|
607
|
+
if (startingLoc === Location.inside)
|
|
608
|
+
return;
|
|
609
|
+
if (!Core_1.Rect64Utils.containsRect(this.pathBounds, this.rect) ||
|
|
610
|
+
!RectClip64.path1ContainsPath2(path, this.rectPath))
|
|
611
|
+
return;
|
|
612
|
+
const startLocsClockwise = RectClip64.startLocsAreClockwise(startLocs);
|
|
613
|
+
for (let j = 0; j < 4; j++) {
|
|
614
|
+
const k = startLocsClockwise ? j : 3 - j; // ie reverse result path
|
|
615
|
+
this.add(this.rectPath[k]);
|
|
616
|
+
RectClip64.addToEdge(this.edges[k * 2], this.results[0]);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
else if (loc !== Location.inside &&
|
|
620
|
+
(loc !== firstCross || startLocs.length > 2)) {
|
|
621
|
+
if (startLocs.length > 0) {
|
|
622
|
+
prev = loc;
|
|
623
|
+
for (const loc2 of startLocs) {
|
|
624
|
+
if (prev === loc2)
|
|
625
|
+
continue;
|
|
626
|
+
prev = this.addCornerWithDirection(prev, RectClip64.headingClockwise(prev, loc2));
|
|
627
|
+
prev = loc2;
|
|
628
|
+
}
|
|
629
|
+
loc = prev;
|
|
630
|
+
}
|
|
631
|
+
if (loc !== firstCross) {
|
|
632
|
+
this.addCornerWithDirection(loc, RectClip64.headingClockwise(loc, firstCross));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
execute(paths) {
|
|
637
|
+
const result = [];
|
|
638
|
+
if (Core_1.Rect64Utils.isEmpty(this.rect))
|
|
639
|
+
return result;
|
|
640
|
+
for (const path of paths) {
|
|
641
|
+
if (path.length < 3)
|
|
642
|
+
continue;
|
|
643
|
+
this.pathBounds = Core_1.InternalClipper.getBounds(path);
|
|
644
|
+
if (!Core_1.Rect64Utils.intersects(this.rect, this.pathBounds)) {
|
|
645
|
+
continue; // the path must be completely outside rect
|
|
646
|
+
}
|
|
647
|
+
if (Core_1.Rect64Utils.containsRect(this.rect, this.pathBounds)) {
|
|
648
|
+
// the path must be completely inside rect
|
|
649
|
+
result.push(path);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
this.executeInternal(path);
|
|
653
|
+
this.checkEdges();
|
|
654
|
+
for (let i = 0; i < 4; ++i) {
|
|
655
|
+
this.tidyEdgePair(i, this.edges[i * 2], this.edges[i * 2 + 1]);
|
|
656
|
+
}
|
|
657
|
+
for (const op of this.results) {
|
|
658
|
+
const tmp = this.getPath(op);
|
|
659
|
+
if (tmp.length > 0)
|
|
660
|
+
result.push(tmp);
|
|
661
|
+
}
|
|
662
|
+
//clean up after every loop
|
|
663
|
+
this.results.length = 0;
|
|
664
|
+
for (let i = 0; i < 8; i++) {
|
|
665
|
+
this.edges[i].length = 0;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
checkEdges() {
|
|
671
|
+
for (let i = 0; i < this.results.length; i++) {
|
|
672
|
+
let op = this.results[i];
|
|
673
|
+
let op2 = op;
|
|
674
|
+
if (op === null)
|
|
675
|
+
continue;
|
|
676
|
+
do {
|
|
677
|
+
if (Core_1.InternalClipper.isCollinear(op2.prev.pt, op2.pt, op2.next.pt)) {
|
|
678
|
+
if (op2 === op) {
|
|
679
|
+
op2 = RectClip64.unlinkOpBack(op2);
|
|
680
|
+
if (op2 === null)
|
|
681
|
+
break;
|
|
682
|
+
op = op2.prev;
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
op2 = RectClip64.unlinkOpBack(op2);
|
|
686
|
+
if (op2 === null)
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
op2 = op2.next;
|
|
692
|
+
}
|
|
693
|
+
} while (op2 !== op);
|
|
694
|
+
if (op2 === null) {
|
|
695
|
+
this.results[i] = null;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
this.results[i] = op2; // safety first
|
|
699
|
+
let edgeSet1 = RectClip64.getEdgesForPt(op.prev.pt, this.rect);
|
|
700
|
+
op2 = op;
|
|
701
|
+
do {
|
|
702
|
+
const edgeSet2 = RectClip64.getEdgesForPt(op2.pt, this.rect);
|
|
703
|
+
if (edgeSet2 !== 0 && op2.edge === null) {
|
|
704
|
+
const combinedSet = (edgeSet1 & edgeSet2);
|
|
705
|
+
for (let j = 0; j < 4; ++j) {
|
|
706
|
+
if ((combinedSet & (1 << j)) === 0)
|
|
707
|
+
continue;
|
|
708
|
+
if (RectClip64.isHeadingClockwise(op2.prev.pt, op2.pt, j)) {
|
|
709
|
+
RectClip64.addToEdge(this.edges[j * 2], op2);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
RectClip64.addToEdge(this.edges[j * 2 + 1], op2);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
edgeSet1 = edgeSet2;
|
|
717
|
+
op2 = op2.next;
|
|
718
|
+
} while (op2 !== op);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
tidyEdgePair(idx, cw, ccw) {
|
|
722
|
+
if (ccw.length === 0)
|
|
723
|
+
return;
|
|
724
|
+
const isHorz = ((idx === 1) || (idx === 3));
|
|
725
|
+
const cwIsTowardLarger = ((idx === 1) || (idx === 2));
|
|
726
|
+
let i = 0, j = 0;
|
|
727
|
+
while (i < cw.length) {
|
|
728
|
+
let p1 = cw[i];
|
|
729
|
+
if (p1 === null || p1.next === p1.prev) {
|
|
730
|
+
cw[i++] = null;
|
|
731
|
+
j = 0;
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const jLim = ccw.length;
|
|
735
|
+
while (j < jLim && (ccw[j] === null || ccw[j].next === ccw[j].prev))
|
|
736
|
+
++j;
|
|
737
|
+
if (j === jLim) {
|
|
738
|
+
++i;
|
|
739
|
+
j = 0;
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
let p2;
|
|
743
|
+
let p1a;
|
|
744
|
+
let p2a;
|
|
745
|
+
if (cwIsTowardLarger) {
|
|
746
|
+
// p1 >>>> p1a;
|
|
747
|
+
// p2 <<<< p2a;
|
|
748
|
+
p1 = cw[i].prev;
|
|
749
|
+
p1a = cw[i];
|
|
750
|
+
p2 = ccw[j];
|
|
751
|
+
p2a = ccw[j].prev;
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
// p1 <<<< p1a;
|
|
755
|
+
// p2 >>>> p2a;
|
|
756
|
+
p1 = cw[i];
|
|
757
|
+
p1a = cw[i].prev;
|
|
758
|
+
p2 = ccw[j].prev;
|
|
759
|
+
p2a = ccw[j];
|
|
760
|
+
}
|
|
761
|
+
if ((isHorz && !RectClip64.hasHorzOverlap(p1.pt, p1a.pt, p2.pt, p2a.pt)) ||
|
|
762
|
+
(!isHorz && !RectClip64.hasVertOverlap(p1.pt, p1a.pt, p2.pt, p2a.pt))) {
|
|
763
|
+
++j;
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
// to get here we're either splitting or rejoining
|
|
767
|
+
const isRejoining = cw[i].ownerIdx !== ccw[j].ownerIdx;
|
|
768
|
+
if (isRejoining) {
|
|
769
|
+
this.results[p2.ownerIdx] = null;
|
|
770
|
+
RectClip64.setNewOwner(p2, p1.ownerIdx);
|
|
771
|
+
}
|
|
772
|
+
// do the split or re-join
|
|
773
|
+
if (cwIsTowardLarger) {
|
|
774
|
+
// p1 >> | >> p1a;
|
|
775
|
+
// p2 << | << p2a;
|
|
776
|
+
p1.next = p2;
|
|
777
|
+
p2.prev = p1;
|
|
778
|
+
p1a.prev = p2a;
|
|
779
|
+
p2a.next = p1a;
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
// p1 << | << p1a;
|
|
783
|
+
// p2 >> | >> p2a;
|
|
784
|
+
p1.prev = p2;
|
|
785
|
+
p2.next = p1;
|
|
786
|
+
p1a.next = p2a;
|
|
787
|
+
p2a.prev = p1a;
|
|
788
|
+
}
|
|
789
|
+
if (!isRejoining) {
|
|
790
|
+
const newIdx = this.results.length;
|
|
791
|
+
this.results.push(p1a);
|
|
792
|
+
RectClip64.setNewOwner(p1a, newIdx);
|
|
793
|
+
}
|
|
794
|
+
let op;
|
|
795
|
+
let op2;
|
|
796
|
+
if (cwIsTowardLarger) {
|
|
797
|
+
op = p2;
|
|
798
|
+
op2 = p1a;
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
op = p1;
|
|
802
|
+
op2 = p2a;
|
|
803
|
+
}
|
|
804
|
+
this.results[op.ownerIdx] = op;
|
|
805
|
+
this.results[op2.ownerIdx] = op2;
|
|
806
|
+
// and now lots of work to get ready for the next loop
|
|
807
|
+
let opIsLarger, op2IsLarger;
|
|
808
|
+
if (isHorz) { // X
|
|
809
|
+
opIsLarger = op.pt.x > op.prev.pt.x;
|
|
810
|
+
op2IsLarger = op2.pt.x > op2.prev.pt.x;
|
|
811
|
+
}
|
|
812
|
+
else { // Y
|
|
813
|
+
opIsLarger = op.pt.y > op.prev.pt.y;
|
|
814
|
+
op2IsLarger = op2.pt.y > op2.prev.pt.y;
|
|
815
|
+
}
|
|
816
|
+
if ((op.next === op.prev) || Core_1.Point64Utils.equals(op.pt, op.prev.pt)) {
|
|
817
|
+
if (op2IsLarger === cwIsTowardLarger) {
|
|
818
|
+
cw[i] = op2;
|
|
819
|
+
ccw[j++] = null;
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
ccw[j] = op2;
|
|
823
|
+
cw[i++] = null;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
else if ((op2.next === op2.prev) || Core_1.Point64Utils.equals(op2.pt, op2.prev.pt)) {
|
|
827
|
+
if (opIsLarger === cwIsTowardLarger) {
|
|
828
|
+
cw[i] = op;
|
|
829
|
+
ccw[j++] = null;
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
ccw[j] = op;
|
|
833
|
+
cw[i++] = null;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
else if (opIsLarger === op2IsLarger) {
|
|
837
|
+
if (opIsLarger === cwIsTowardLarger) {
|
|
838
|
+
cw[i] = op;
|
|
839
|
+
RectClip64.uncoupleEdge(op2);
|
|
840
|
+
RectClip64.addToEdge(cw, op2);
|
|
841
|
+
ccw[j++] = null;
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
cw[i++] = null;
|
|
845
|
+
ccw[j] = op2;
|
|
846
|
+
RectClip64.uncoupleEdge(op);
|
|
847
|
+
RectClip64.addToEdge(ccw, op);
|
|
848
|
+
j = 0;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
if (opIsLarger === cwIsTowardLarger) {
|
|
853
|
+
cw[i] = op;
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
ccw[j] = op;
|
|
857
|
+
}
|
|
858
|
+
if (op2IsLarger === cwIsTowardLarger) {
|
|
859
|
+
cw[i] = op2;
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
ccw[j] = op2;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
getPath(op) {
|
|
868
|
+
const result = [];
|
|
869
|
+
if (op === null || op.prev === op.next)
|
|
870
|
+
return result;
|
|
871
|
+
let op2 = op.next;
|
|
872
|
+
while (op2 !== null && op2 !== op) {
|
|
873
|
+
if (Core_1.InternalClipper.isCollinear(op2.prev.pt, op2.pt, op2.next.pt)) {
|
|
874
|
+
op = op2.prev;
|
|
875
|
+
op2 = RectClip64.unlinkOp(op2);
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
op2 = op2.next;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (op2 === null)
|
|
882
|
+
return [];
|
|
883
|
+
result.push(op.pt);
|
|
884
|
+
op2 = op.next;
|
|
885
|
+
while (op2 !== op) {
|
|
886
|
+
result.push(op2.pt);
|
|
887
|
+
op2 = op2.next;
|
|
888
|
+
}
|
|
889
|
+
return result;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
exports.RectClip64 = RectClip64;
|
|
893
|
+
class RectClipLines64 extends RectClip64 {
|
|
894
|
+
constructor(rect) {
|
|
895
|
+
super(rect);
|
|
896
|
+
}
|
|
897
|
+
execute(paths) {
|
|
898
|
+
const result = [];
|
|
899
|
+
if (Core_1.Rect64Utils.isEmpty(this.rect))
|
|
900
|
+
return result;
|
|
901
|
+
for (const path of paths) {
|
|
902
|
+
if (path.length < 2)
|
|
903
|
+
continue;
|
|
904
|
+
this.pathBounds = Core_1.InternalClipper.getBounds(path);
|
|
905
|
+
if (!Core_1.Rect64Utils.intersects(this.rect, this.pathBounds)) {
|
|
906
|
+
continue; // the path must be completely outside rect
|
|
907
|
+
}
|
|
908
|
+
// Apart from that, we can't be sure whether the path
|
|
909
|
+
// is completely outside or completed inside or intersects
|
|
910
|
+
// rect, simply by comparing path bounds with rect.
|
|
911
|
+
this.executeInternalLines(path);
|
|
912
|
+
for (const op of this.results) {
|
|
913
|
+
const tmp = this.getPathLines(op);
|
|
914
|
+
if (tmp.length > 0)
|
|
915
|
+
result.push(tmp);
|
|
916
|
+
}
|
|
917
|
+
//clean up after every loop
|
|
918
|
+
this.results.length = 0;
|
|
919
|
+
for (let i = 0; i < 8; i++) {
|
|
920
|
+
this.edges[i].length = 0;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
getPathLines(op) {
|
|
926
|
+
const result = [];
|
|
927
|
+
if (op === null || op === op.next)
|
|
928
|
+
return result;
|
|
929
|
+
op = op.next; // starting at path beginning
|
|
930
|
+
result.push(op.pt);
|
|
931
|
+
let op2 = op.next;
|
|
932
|
+
while (op2 !== op) {
|
|
933
|
+
result.push(op2.pt);
|
|
934
|
+
op2 = op2.next;
|
|
935
|
+
}
|
|
936
|
+
return result;
|
|
937
|
+
}
|
|
938
|
+
executeInternalLines(path) {
|
|
939
|
+
this.results.length = 0;
|
|
940
|
+
if (path.length < 2 || Core_1.Rect64Utils.isEmpty(this.rect))
|
|
941
|
+
return;
|
|
942
|
+
let prev = Location.inside;
|
|
943
|
+
let i = 1;
|
|
944
|
+
const highI = path.length - 1;
|
|
945
|
+
const firstLocResult = RectClip64.getLocation(this.rect, path[0]);
|
|
946
|
+
let loc = firstLocResult.location;
|
|
947
|
+
if (firstLocResult.isOnRect) {
|
|
948
|
+
while (i <= highI) {
|
|
949
|
+
const prevLocResult = RectClip64.getLocation(this.rect, path[i]);
|
|
950
|
+
if (!prevLocResult.isOnRect) {
|
|
951
|
+
prev = prevLocResult.location;
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
i++;
|
|
955
|
+
}
|
|
956
|
+
if (i > highI) {
|
|
957
|
+
for (const pt of path) {
|
|
958
|
+
this.add(pt);
|
|
959
|
+
}
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
if (prev === Location.inside)
|
|
963
|
+
loc = Location.inside;
|
|
964
|
+
i = 1;
|
|
965
|
+
}
|
|
966
|
+
if (loc === Location.inside)
|
|
967
|
+
this.add(path[0]);
|
|
968
|
+
///////////////////////////////////////////////////
|
|
969
|
+
while (i <= highI) {
|
|
970
|
+
prev = loc;
|
|
971
|
+
const nextLocResult = this.getNextLocation(path, loc, i, highI);
|
|
972
|
+
loc = nextLocResult.location;
|
|
973
|
+
i = nextLocResult.index;
|
|
974
|
+
if (i > highI)
|
|
975
|
+
break;
|
|
976
|
+
const prevPt = path[i - 1];
|
|
977
|
+
let crossingLoc = loc;
|
|
978
|
+
const intersectionResult = RectClip64.getIntersection(this.rectPath, path[i], prevPt, crossingLoc);
|
|
979
|
+
if (!intersectionResult.intersects) {
|
|
980
|
+
// ie remaining outside (& crossingLoc still == loc)
|
|
981
|
+
++i;
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
const ip = intersectionResult.point;
|
|
985
|
+
crossingLoc = intersectionResult.newLocation;
|
|
986
|
+
////////////////////////////////////////////////////
|
|
987
|
+
// we must be crossing the rect boundary to get here
|
|
988
|
+
////////////////////////////////////////////////////
|
|
989
|
+
if (loc === Location.inside) { // path must be entering rect
|
|
990
|
+
this.add(ip, true);
|
|
991
|
+
}
|
|
992
|
+
else if (prev !== Location.inside) {
|
|
993
|
+
// passing right through rect. 'ip' here will be the second
|
|
994
|
+
// intersect pt but we'll also need the first intersect pt (ip2)
|
|
995
|
+
crossingLoc = prev;
|
|
996
|
+
const intersection2Result = RectClip64.getIntersection(this.rectPath, prevPt, path[i], crossingLoc);
|
|
997
|
+
const ip2 = intersection2Result.point;
|
|
998
|
+
this.add(ip2, true);
|
|
999
|
+
this.add(ip);
|
|
1000
|
+
}
|
|
1001
|
+
else { // path must be exiting rect
|
|
1002
|
+
this.add(ip);
|
|
1003
|
+
}
|
|
1004
|
+
} //while i <= highI
|
|
1005
|
+
///////////////////////////////////////////////////
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
exports.RectClipLines64 = RectClipLines64;
|
|
1009
|
+
//# sourceMappingURL=RectClip.js.map
|