@dra2020/baseclient 1.0.13 → 1.0.14
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 +21 -0
- package/README.md +37 -0
- package/dist/all/all.d.ts +36 -0
- package/dist/all/allclient.d.ts +18 -0
- package/dist/base.js +33010 -0
- package/dist/base.js.map +1 -0
- package/dist/baseclient.js +8991 -0
- package/dist/baseclient.js.map +1 -0
- package/dist/context/all.d.ts +1 -0
- package/dist/context/context.d.ts +13 -0
- package/dist/dbabstract/all.d.ts +1 -0
- package/dist/dbabstract/db.d.ts +83 -0
- package/dist/dbdynamo/all.d.ts +1 -0
- package/dist/dbdynamo/dbdynamo.d.ts +190 -0
- package/dist/filterexpr/all.d.ts +1 -0
- package/dist/filterexpr/filterexpr.d.ts +64 -0
- package/dist/fsm/all.d.ts +1 -0
- package/dist/fsm/fsm.d.ts +118 -0
- package/dist/fsmfile/all.d.ts +1 -0
- package/dist/fsmfile/fsmfile.d.ts +47 -0
- package/dist/jsonstream/all.d.ts +1 -0
- package/dist/jsonstream/jsonstream.d.ts +130 -0
- package/dist/lambda/all.d.ts +1 -0
- package/dist/lambda/env.d.ts +10 -0
- package/dist/lambda/lambda.d.ts +18 -0
- package/dist/logabstract/all.d.ts +1 -0
- package/dist/logabstract/log.d.ts +26 -0
- package/dist/logclient/all.d.ts +1 -0
- package/dist/logclient/log.d.ts +6 -0
- package/dist/logserver/all.d.ts +5 -0
- package/dist/logserver/log.d.ts +11 -0
- package/dist/logserver/logaccum.d.ts +154 -0
- package/dist/logserver/logblob.d.ts +24 -0
- package/dist/logserver/logconcat.d.ts +55 -0
- package/dist/logserver/logkey.d.ts +28 -0
- package/dist/memsqs/all.d.ts +4 -0
- package/dist/memsqs/client.d.ts +13 -0
- package/dist/memsqs/loopback.d.ts +11 -0
- package/dist/memsqs/orderedlist.d.ts +19 -0
- package/dist/memsqs/queue.d.ts +84 -0
- package/dist/memsqs/server.d.ts +37 -0
- package/dist/ot-editutil/all.d.ts +2 -0
- package/dist/ot-editutil/oteditutil.d.ts +14 -0
- package/dist/ot-editutil/otmaputil.d.ts +21 -0
- package/dist/ot-js/all.d.ts +9 -0
- package/dist/ot-js/otarray.d.ts +111 -0
- package/dist/ot-js/otclientengine.d.ts +38 -0
- package/dist/ot-js/otcomposite.d.ts +37 -0
- package/dist/ot-js/otcounter.d.ts +17 -0
- package/dist/ot-js/otengine.d.ts +22 -0
- package/dist/ot-js/otmap.d.ts +19 -0
- package/dist/ot-js/otserverengine.d.ts +38 -0
- package/dist/ot-js/otsession.d.ts +111 -0
- package/dist/ot-js/ottypes.d.ts +29 -0
- package/dist/poly/all.d.ts +15 -0
- package/dist/poly/blend.d.ts +1 -0
- package/dist/poly/boundbox.d.ts +16 -0
- package/dist/poly/cartesian.d.ts +5 -0
- package/dist/poly/graham-scan.d.ts +8 -0
- package/dist/poly/hash.d.ts +1 -0
- package/dist/poly/matrix.d.ts +24 -0
- package/dist/poly/minbound.d.ts +1 -0
- package/dist/poly/poly.d.ts +52 -0
- package/dist/poly/polybin.d.ts +5 -0
- package/dist/poly/polylabel.d.ts +7 -0
- package/dist/poly/polypack.d.ts +30 -0
- package/dist/poly/polyround.d.ts +1 -0
- package/dist/poly/polysimplify.d.ts +1 -0
- package/dist/poly/quad.d.ts +48 -0
- package/dist/poly/selfintersect.d.ts +1 -0
- package/dist/poly/shamos.d.ts +1 -0
- package/dist/poly/simplify.d.ts +2 -0
- package/dist/poly/topo.d.ts +46 -0
- package/dist/poly/union.d.ts +48 -0
- package/dist/storage/all.d.ts +4 -0
- package/dist/storage/datablob.d.ts +9 -0
- package/dist/storage/env.d.ts +10 -0
- package/dist/storage/splitsblob.d.ts +13 -0
- package/dist/storage/storage.d.ts +166 -0
- package/dist/storages3/all.d.ts +1 -0
- package/dist/storages3/s3.d.ts +62 -0
- package/dist/util/all.d.ts +5 -0
- package/dist/util/bintrie.d.ts +93 -0
- package/dist/util/countedhash.d.ts +19 -0
- package/dist/util/gradient.d.ts +15 -0
- package/dist/util/indexedarray.d.ts +15 -0
- package/dist/util/util.d.ts +68 -0
- package/docs/context.md +2 -0
- package/docs/dbabstract.md +2 -0
- package/docs/dbdynamo.md +2 -0
- package/docs/fsm.md +243 -0
- package/docs/fsmfile.md +2 -0
- package/docs/jsonstream.md +44 -0
- package/docs/lambda.md +2 -0
- package/docs/logabstract.md +2 -0
- package/docs/logclient.md +2 -0
- package/docs/logserver.md +2 -0
- package/docs/ot-editutil.md +2 -0
- package/docs/ot-js.md +95 -0
- package/docs/poly.md +103 -0
- package/docs/storage.md +2 -0
- package/docs/storages3.md +2 -0
- package/docs/util.md +2 -0
- package/lib/all/all.ts +41 -0
- package/lib/all/allclient.ts +19 -0
- package/lib/context/all.ts +1 -0
- package/lib/context/context.ts +82 -0
- package/lib/dbabstract/all.ts +1 -0
- package/lib/dbabstract/db.ts +246 -0
- package/lib/dbdynamo/all.ts +1 -0
- package/lib/dbdynamo/dbdynamo.ts +1551 -0
- package/lib/filterexpr/all.ts +1 -0
- package/lib/filterexpr/filterexpr.ts +625 -0
- package/lib/fsm/all.ts +1 -0
- package/lib/fsm/fsm.ts +549 -0
- package/lib/fsmfile/all.ts +1 -0
- package/lib/fsmfile/fsmfile.ts +236 -0
- package/lib/jsonstream/all.ts +1 -0
- package/lib/jsonstream/jsonstream.ts +940 -0
- package/lib/lambda/all.ts +1 -0
- package/lib/lambda/env.ts +13 -0
- package/lib/lambda/lambda.ts +120 -0
- package/lib/logabstract/all.ts +1 -0
- package/lib/logabstract/log.ts +55 -0
- package/lib/logclient/all.ts +1 -0
- package/lib/logclient/log.ts +105 -0
- package/lib/logserver/all.ts +5 -0
- package/lib/logserver/log.ts +565 -0
- package/lib/logserver/logaccum.ts +1445 -0
- package/lib/logserver/logblob.ts +84 -0
- package/lib/logserver/logconcat.ts +313 -0
- package/lib/logserver/logkey.ts +125 -0
- package/lib/memsqs/all.ts +4 -0
- package/lib/memsqs/client.ts +268 -0
- package/lib/memsqs/loopback.ts +64 -0
- package/lib/memsqs/orderedlist.ts +74 -0
- package/lib/memsqs/queue.ts +395 -0
- package/lib/memsqs/server.ts +262 -0
- package/lib/ot-editutil/all.ts +2 -0
- package/lib/ot-editutil/oteditutil.ts +180 -0
- package/lib/ot-editutil/otmaputil.ts +209 -0
- package/lib/ot-js/all.ts +9 -0
- package/lib/ot-js/otarray.ts +1168 -0
- package/lib/ot-js/otclientengine.ts +327 -0
- package/lib/ot-js/otcomposite.ts +247 -0
- package/lib/ot-js/otcounter.ts +145 -0
- package/lib/ot-js/otengine.ts +71 -0
- package/lib/ot-js/otmap.ts +144 -0
- package/lib/ot-js/otserverengine.ts +329 -0
- package/lib/ot-js/otsession.ts +199 -0
- package/lib/ot-js/ottypes.ts +98 -0
- package/lib/poly/all.ts +15 -0
- package/lib/poly/blend.ts +27 -0
- package/lib/poly/boundbox.ts +102 -0
- package/lib/poly/cartesian.ts +130 -0
- package/lib/poly/graham-scan.ts +401 -0
- package/lib/poly/hash.ts +15 -0
- package/lib/poly/matrix.ts +309 -0
- package/lib/poly/minbound.ts +211 -0
- package/lib/poly/poly.ts +767 -0
- package/lib/poly/polybin.ts +218 -0
- package/lib/poly/polylabel.ts +204 -0
- package/lib/poly/polypack.ts +458 -0
- package/lib/poly/polyround.ts +30 -0
- package/lib/poly/polysimplify.ts +24 -0
- package/lib/poly/quad.ts +272 -0
- package/lib/poly/selfintersect.ts +87 -0
- package/lib/poly/shamos.ts +297 -0
- package/lib/poly/simplify.ts +119 -0
- package/lib/poly/topo.ts +525 -0
- package/lib/poly/union.ts +371 -0
- package/lib/storage/all.ts +4 -0
- package/lib/storage/datablob.ts +36 -0
- package/lib/storage/env.ts +14 -0
- package/lib/storage/splitsblob.ts +63 -0
- package/lib/storage/storage.ts +604 -0
- package/lib/storages3/all.ts +1 -0
- package/lib/storages3/s3.ts +576 -0
- package/lib/util/all.ts +5 -0
- package/lib/util/bintrie.ts +603 -0
- package/lib/util/countedhash.ts +83 -0
- package/lib/util/gradient.ts +108 -0
- package/lib/util/indexedarray.ts +80 -0
- package/lib/util/util.ts +695 -0
- package/package.json +8 -8
package/lib/poly/quad.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// Public libraries
|
|
2
|
+
import * as PC from 'polygon-clipping';
|
|
3
|
+
|
|
4
|
+
// Shared libraries
|
|
5
|
+
import * as Util from '../util/all';
|
|
6
|
+
import * as FSM from '../fsm/all';
|
|
7
|
+
|
|
8
|
+
// Local libraries
|
|
9
|
+
import * as Poly from './poly';
|
|
10
|
+
import * as PP from './polypack';
|
|
11
|
+
import * as BB from './boundbox';
|
|
12
|
+
import * as PR from './polyround';
|
|
13
|
+
|
|
14
|
+
let _union: any = undefined;
|
|
15
|
+
let anyPC: any = PC;
|
|
16
|
+
if (anyPC.union) _union = anyPC.union;
|
|
17
|
+
if (anyPC.default && anyPC.default.union) _union = anyPC.default.union;
|
|
18
|
+
if (_union === undefined) throw 'Unable to load union function from polygon-clipping';
|
|
19
|
+
|
|
20
|
+
export interface WrappedPoly { box: BB.BoundBox, p: any };
|
|
21
|
+
|
|
22
|
+
export interface WorkDone
|
|
23
|
+
{
|
|
24
|
+
nUnion: number,
|
|
25
|
+
nDifference: number,
|
|
26
|
+
ms: number,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function featureCoords(feature: any): any
|
|
30
|
+
{
|
|
31
|
+
/*
|
|
32
|
+
if (feature.geometry !== undefined)
|
|
33
|
+
{
|
|
34
|
+
if (feature.geometry.packed !== undefined)
|
|
35
|
+
return PP.polyUnpack(feature.geometry.packed);
|
|
36
|
+
else
|
|
37
|
+
return feature.geometry.coordinates;
|
|
38
|
+
}
|
|
39
|
+
else if (feature.offset !== undefined)
|
|
40
|
+
return PP.polyUnpack(feature);
|
|
41
|
+
else
|
|
42
|
+
return feature;
|
|
43
|
+
*/
|
|
44
|
+
if (feature.geometry !== undefined)
|
|
45
|
+
{
|
|
46
|
+
if (feature.geometry.packed !== undefined)
|
|
47
|
+
return feature.geometry.packed;
|
|
48
|
+
else
|
|
49
|
+
return feature.geometry.coordinates;
|
|
50
|
+
}
|
|
51
|
+
else
|
|
52
|
+
return feature;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// This function takes an array of packed or unpacked polygon/multipolygons and
|
|
56
|
+
// returns an unpacked polygon/multipolygon coordinate array. Returns null if
|
|
57
|
+
// handed an empty array.
|
|
58
|
+
|
|
59
|
+
export function unionPolys(polys: any[]): any
|
|
60
|
+
{
|
|
61
|
+
if (polys == null) return null;
|
|
62
|
+
|
|
63
|
+
let cleanPolys: any[] = polys.filter((f: any) => f != null);
|
|
64
|
+
if (cleanPolys.length == 0) return null;
|
|
65
|
+
if (cleanPolys.length == 1) return PP.polyUnpack(cleanPolys[0]);
|
|
66
|
+
cleanPolys = cleanPolys.map( (f: any) => PP.polyUnpack(f) );
|
|
67
|
+
|
|
68
|
+
let poly: any = cleanPolys.pop();
|
|
69
|
+
return PR.polyRound(_union(poly, ...cleanPolys));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface QuadOptions
|
|
73
|
+
{
|
|
74
|
+
maxLeafCount?: number;
|
|
75
|
+
maxDepth?: number;
|
|
76
|
+
tickStep?: number;
|
|
77
|
+
}
|
|
78
|
+
export const DefaultQuadOptions: QuadOptions = { maxLeafCount: 20, maxDepth: 20, tickStep: 1 };
|
|
79
|
+
|
|
80
|
+
export interface TickCounter { ticks: number };
|
|
81
|
+
|
|
82
|
+
// Use geo orientation for t/b (that is, t > b)
|
|
83
|
+
class QuadLevel
|
|
84
|
+
{
|
|
85
|
+
options: QuadOptions;
|
|
86
|
+
level: number;
|
|
87
|
+
children: QuadLevel[];
|
|
88
|
+
features: any[];
|
|
89
|
+
box: BB.BoundBox;
|
|
90
|
+
asyncUnion: any;
|
|
91
|
+
|
|
92
|
+
constructor(options: QuadOptions, level: number, box: BB.BoundBox, features: WrappedPoly[])
|
|
93
|
+
{
|
|
94
|
+
this.options = options;
|
|
95
|
+
this.level = level;
|
|
96
|
+
this.box = box;
|
|
97
|
+
if (features.length <= options.maxLeafCount || this.level >= options.maxDepth)
|
|
98
|
+
{
|
|
99
|
+
if (this.level >= options.maxDepth)
|
|
100
|
+
throw `QuadTree: maximum depth of ${options.maxDepth} exceeded`;
|
|
101
|
+
this.features = features.map((wp: WrappedPoly) => wp.p);
|
|
102
|
+
this.children = null;
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
{
|
|
106
|
+
this.features = null;
|
|
107
|
+
let cx = BB.boundboxCX(box);
|
|
108
|
+
let cy = BB.boundboxCY(box);
|
|
109
|
+
|
|
110
|
+
let tl: BB.BoundBox = { left: box.left, top: box.top, right: cx, bottom: cy };
|
|
111
|
+
let tr: BB.BoundBox = { left: cx, top: box.top, right: box.right, bottom: cy };
|
|
112
|
+
let bl: BB.BoundBox = { left: box.left, top: cy, right: cx, bottom: box.bottom };
|
|
113
|
+
let br: BB.BoundBox = { left: cx, top: cy, right: box.right, bottom: box.bottom };
|
|
114
|
+
let tlFeatures = [];
|
|
115
|
+
let trFeatures = [];
|
|
116
|
+
let blFeatures = [];
|
|
117
|
+
let brFeatures = [];
|
|
118
|
+
|
|
119
|
+
for (let i: number = 0; i < features.length; i++)
|
|
120
|
+
{
|
|
121
|
+
let f = features[i];
|
|
122
|
+
|
|
123
|
+
if (this.featureInBox(tl, f))
|
|
124
|
+
tlFeatures.push(f);
|
|
125
|
+
else if (this.featureInBox(tr, f))
|
|
126
|
+
trFeatures.push(f);
|
|
127
|
+
else if (this.featureInBox(bl, f))
|
|
128
|
+
blFeatures.push(f);
|
|
129
|
+
else if (this.featureInBox(br, f))
|
|
130
|
+
brFeatures.push(f);
|
|
131
|
+
}
|
|
132
|
+
this.children = [];
|
|
133
|
+
this.children.push(new QuadLevel(options, level+1, tl, tlFeatures));
|
|
134
|
+
this.children.push(new QuadLevel(options, level+1, tr, trFeatures));
|
|
135
|
+
this.children.push(new QuadLevel(options, level+1, bl, blFeatures));
|
|
136
|
+
this.children.push(new QuadLevel(options, level+1, br, brFeatures));
|
|
137
|
+
this.asyncUnion = null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private featureInBox(box: BB.BoundBox, f: WrappedPoly): boolean
|
|
142
|
+
{
|
|
143
|
+
return BB.boundboxIntersects(box, f.box);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
union(): any
|
|
147
|
+
{
|
|
148
|
+
if (this.asyncUnion == null)
|
|
149
|
+
{
|
|
150
|
+
if (this.children == null)
|
|
151
|
+
this.asyncUnion = unionPolys(this.features);
|
|
152
|
+
else
|
|
153
|
+
this.asyncUnion = unionPolys(this.children.map((q: QuadLevel) => q.union()));
|
|
154
|
+
}
|
|
155
|
+
return this.asyncUnion;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
get isempty(): boolean
|
|
159
|
+
{
|
|
160
|
+
return this.children == null && this.features.length == 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
tickUnion(tickCounter: TickCounter): void
|
|
164
|
+
{
|
|
165
|
+
if (!this.isempty && this.asyncUnion == null)
|
|
166
|
+
{
|
|
167
|
+
if (this.children == null)
|
|
168
|
+
{
|
|
169
|
+
if (tickCounter.ticks != 0)
|
|
170
|
+
{
|
|
171
|
+
this.union();
|
|
172
|
+
tickCounter.ticks--;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
{
|
|
177
|
+
for (let i: number = 0; i < this.children.length && tickCounter.ticks != 0; i++)
|
|
178
|
+
this.children[i].tickUnion(tickCounter);
|
|
179
|
+
|
|
180
|
+
// If tickCounter hasn't gone to zero, we fully computed children, go ahead and compute union here.
|
|
181
|
+
if (tickCounter.ticks != 0)
|
|
182
|
+
{
|
|
183
|
+
this.union();
|
|
184
|
+
tickCounter.ticks--;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export class FsmQuadTree extends FSM.Fsm
|
|
192
|
+
{
|
|
193
|
+
quad: QuadLevel;
|
|
194
|
+
asyncUnion: any;
|
|
195
|
+
isempty: boolean;
|
|
196
|
+
options: Poly.TickOptions;
|
|
197
|
+
work: WorkDone;
|
|
198
|
+
|
|
199
|
+
constructor(env: FSM.FsmEnvironment, options?: Poly.TickOptions, col?: any)
|
|
200
|
+
{
|
|
201
|
+
super(env);
|
|
202
|
+
this.options = Util.shallowAssignImmutable(Poly.DefaultTickOptions, options);
|
|
203
|
+
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
204
|
+
this.initialize(col);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
initialize(col: any): void
|
|
208
|
+
{
|
|
209
|
+
this.quad = null;
|
|
210
|
+
this.asyncUnion = undefined;
|
|
211
|
+
this.isempty = true;
|
|
212
|
+
|
|
213
|
+
if (col != null)
|
|
214
|
+
{
|
|
215
|
+
let features: any = col.features ? col.features : col;
|
|
216
|
+
this.work.nUnion = features.length;
|
|
217
|
+
this.isempty = features.length == 0;
|
|
218
|
+
|
|
219
|
+
// Compute BoundBox for each feature
|
|
220
|
+
let wrapped: WrappedPoly[] = features.map((f: any) => { return { box: BB.boundbox(f), p: featureCoords(f) } });
|
|
221
|
+
|
|
222
|
+
let box = BB.boundbox(col);
|
|
223
|
+
this.quad = new QuadLevel(this.options, 0, box, wrapped);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.setState(this.isempty ? FSM.FSM_DONE : FSM.FSM_STARTING);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
cancel(): any
|
|
230
|
+
{
|
|
231
|
+
this.quad = null;
|
|
232
|
+
this.asyncUnion = undefined;
|
|
233
|
+
this.isempty = true;
|
|
234
|
+
this.setState(FSM.FSM_DONE);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
get result(): any
|
|
238
|
+
{
|
|
239
|
+
if (this.asyncUnion === undefined && this.quad)
|
|
240
|
+
this.asyncUnion = this.quad.union();
|
|
241
|
+
return this.asyncUnion;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
tick(): void
|
|
245
|
+
{
|
|
246
|
+
if (this.ready)
|
|
247
|
+
{
|
|
248
|
+
switch (this.state)
|
|
249
|
+
{
|
|
250
|
+
case FSM.FSM_STARTING:
|
|
251
|
+
this.setState(FSM.FSM_PENDING);
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
case FSM.FSM_PENDING:
|
|
255
|
+
let tickCounter: TickCounter = { ticks: this.options.tickStep }
|
|
256
|
+
let elapsed = new Util.Elapsed();
|
|
257
|
+
if (!this.isempty && this.asyncUnion === undefined)
|
|
258
|
+
{
|
|
259
|
+
this.quad.tickUnion(tickCounter);
|
|
260
|
+
if (tickCounter.ticks != 0)
|
|
261
|
+
this.result;
|
|
262
|
+
}
|
|
263
|
+
this.work.ms += elapsed.ms();
|
|
264
|
+
if (this.asyncUnion !== undefined)
|
|
265
|
+
this.setState(FSM.FSM_DONE);
|
|
266
|
+
else
|
|
267
|
+
this.setState(FSM.FSM_PENDING);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as Util from '../util/all';
|
|
2
|
+
|
|
3
|
+
import * as P from './poly';
|
|
4
|
+
import * as PP from './polypack';
|
|
5
|
+
|
|
6
|
+
// Given three colinear points p, q, r, the function checks if
|
|
7
|
+
// point q lies on line segment 'pr'
|
|
8
|
+
function onSegment(px: number, py: number, qx: number, qy: number, rx: number, ry: number): boolean
|
|
9
|
+
{
|
|
10
|
+
if (qx <= Math.max(px, rx) && qx >= Math.min(px, rx) &&
|
|
11
|
+
qy <= Math.max(py, ry) && qy >= Math.min(py, ry))
|
|
12
|
+
return true;
|
|
13
|
+
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// To find orientation of ordered triplet (p, q, r).
|
|
18
|
+
// The function returns following values
|
|
19
|
+
// 0 --> p, q and r are colinear
|
|
20
|
+
// 1 --> Clockwise
|
|
21
|
+
// 2 --> Counterclockwise
|
|
22
|
+
function orientation(px: number, py: number, qx: number, qy: number, rx: number, ry: number): number
|
|
23
|
+
{
|
|
24
|
+
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
|
|
25
|
+
// for details of below formula.
|
|
26
|
+
let val: number = (qy - py) * (rx - qx) -
|
|
27
|
+
(qx - px) * (ry - qy);
|
|
28
|
+
|
|
29
|
+
if (val == 0) return 0; // colinear
|
|
30
|
+
|
|
31
|
+
return (val > 0)? 1: 2; // clock or counterclock wise
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// The main function that returns true if line segment 'p1q1'
|
|
35
|
+
// and 'p2q2' intersect.
|
|
36
|
+
function doIntersect(p1x: number, p1y: number, q1x: number, q1y: number, p2x: number, p2y: number, q2x: number, q2y: number): boolean
|
|
37
|
+
{
|
|
38
|
+
// closing segment is not intersection
|
|
39
|
+
if (p1x - q2x + p1y - q2y === 0) return false;
|
|
40
|
+
|
|
41
|
+
// Find the four orientations needed for general and
|
|
42
|
+
// special cases
|
|
43
|
+
let o1 = orientation(p1x, p1y, q1x, q1y, p2x, p2y);
|
|
44
|
+
let o2 = orientation(p1x, p1y, q1x, q1y, q2x, q2y);
|
|
45
|
+
let o3 = orientation(p2x, p2y, q2x, q2y, p1x, p1y);
|
|
46
|
+
let o4 = orientation(p2x, p2y, q2x, q2y, q1x, q1y);
|
|
47
|
+
|
|
48
|
+
// General case
|
|
49
|
+
if (o1 != o2 && o3 != o4)
|
|
50
|
+
return true;
|
|
51
|
+
|
|
52
|
+
// Special Cases
|
|
53
|
+
// p1, q1 and p2 are colinear and p2 lies on segment p1q1
|
|
54
|
+
if (o1 == 0 && onSegment(p1x, p1y, p2x, p2y, q1x, q1y)) return true;
|
|
55
|
+
|
|
56
|
+
// p1, q1 and q2 are colinear and q2 lies on segment p1q1
|
|
57
|
+
if (o2 == 0 && onSegment(p1x, p1y, q2x, q2y, q1x, q1y)) return true;
|
|
58
|
+
|
|
59
|
+
// p2, q2 and p1 are colinear and p1 lies on segment p2q2
|
|
60
|
+
if (o3 == 0 && onSegment(p2x, p2y, p1x, p1y, q2x, q2y)) return true;
|
|
61
|
+
|
|
62
|
+
// p2, q2 and q1 are colinear and q1 lies on segment p2q2
|
|
63
|
+
if (o4 == 0 && onSegment(p2x, p2y, q1x, q1y, q2x, q2y)) return true;
|
|
64
|
+
|
|
65
|
+
return false; // Doesn't fall in any of the above cases
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// no extra storage, but brute-force O(N^2) in number of segments. See selfIntersectFast for better approach.
|
|
69
|
+
export function selfIntersect(poly: any): boolean
|
|
70
|
+
{
|
|
71
|
+
let pp = P.polyNormalize(poly);
|
|
72
|
+
if (pp == null) return false;
|
|
73
|
+
let bIntersect = false;
|
|
74
|
+
PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
|
|
75
|
+
if (bIntersect) return;
|
|
76
|
+
let iEnd = iOffset + ((nPoints-2) * 2);
|
|
77
|
+
let jEnd = iOffset + ((nPoints-1) * 2);
|
|
78
|
+
for (let i = iOffset; i < iEnd; i += 2)
|
|
79
|
+
for (let j = i + 4; j < jEnd; j += 2)
|
|
80
|
+
if (doIntersect(b[i], b[i+1], b[i+2], b[i+3], b[j], b[j+1], b[j+2], b[j+3]))
|
|
81
|
+
{
|
|
82
|
+
bIntersect = true;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return bIntersect;
|
|
87
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import * as Util from '../util/all';
|
|
2
|
+
|
|
3
|
+
import * as P from './poly';
|
|
4
|
+
import * as PP from './polypack';
|
|
5
|
+
|
|
6
|
+
// Adapted from rowanwins/shamos-hoey
|
|
7
|
+
/*
|
|
8
|
+
MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2019 Rowan Winsemius
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// External libraries
|
|
32
|
+
import TinyQueue from 'tinyqueue'
|
|
33
|
+
import SplayTree from 'splaytree'
|
|
34
|
+
|
|
35
|
+
class Event
|
|
36
|
+
{
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
otherEvent: Event;
|
|
40
|
+
isLeftEndpoint: boolean | null;
|
|
41
|
+
segment: Segment;
|
|
42
|
+
|
|
43
|
+
constructor(x: number, y: number)
|
|
44
|
+
{
|
|
45
|
+
this.x = x
|
|
46
|
+
this.y = y
|
|
47
|
+
|
|
48
|
+
this.otherEvent = null
|
|
49
|
+
this.isLeftEndpoint = null
|
|
50
|
+
this.segment = null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
isOtherEndOfSegment(eventToCheck: Event): boolean
|
|
54
|
+
{
|
|
55
|
+
return this === eventToCheck.otherEvent
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
isSamePoint(eventToCheck: Event): boolean
|
|
59
|
+
{
|
|
60
|
+
return this.x === eventToCheck.x && this.y === eventToCheck.y
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isBelow(p: Event): boolean
|
|
64
|
+
{
|
|
65
|
+
return this.isLeftEndpoint ?
|
|
66
|
+
signedArea(this, this.otherEvent, p) > 0 :
|
|
67
|
+
signedArea(this.otherEvent, p, this) > 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
isAbove(p: Event): boolean
|
|
71
|
+
{
|
|
72
|
+
return !this.isBelow(p)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class EventQueue
|
|
77
|
+
{
|
|
78
|
+
tiny: TinyQueue<Event>;
|
|
79
|
+
|
|
80
|
+
constructor()
|
|
81
|
+
{
|
|
82
|
+
this.tiny = new TinyQueue<Event>([], checkWhichEventIsLeft)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
push(event: Event): void { this.tiny.push(event) }
|
|
86
|
+
pop(): Event { return this.tiny.pop() as Event }
|
|
87
|
+
get length(): number { return this.tiny.length }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class Segment
|
|
91
|
+
{
|
|
92
|
+
leftSweepEvent: Event;
|
|
93
|
+
rightSweepEvent: Event;
|
|
94
|
+
segmentAbove: Segment;
|
|
95
|
+
segmentBelow: Segment;
|
|
96
|
+
|
|
97
|
+
constructor(event: Event)
|
|
98
|
+
{
|
|
99
|
+
this.leftSweepEvent = event
|
|
100
|
+
this.rightSweepEvent = event.otherEvent
|
|
101
|
+
this.segmentAbove = null
|
|
102
|
+
this.segmentBelow = null
|
|
103
|
+
|
|
104
|
+
event.segment = this
|
|
105
|
+
event.otherEvent.segment = this
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class SweepLine
|
|
110
|
+
{
|
|
111
|
+
tree: SplayTree<Segment>;
|
|
112
|
+
|
|
113
|
+
constructor()
|
|
114
|
+
{
|
|
115
|
+
this.tree = new SplayTree<Segment>(compareSegments)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
addSegment(event: Event): Segment
|
|
119
|
+
{
|
|
120
|
+
const seg = new Segment(event)
|
|
121
|
+
const node: any = this.tree.insert(seg)
|
|
122
|
+
const nextNode: any = this.tree.next(node)
|
|
123
|
+
const prevNode: any = this.tree.prev(node)
|
|
124
|
+
if (nextNode !== null)
|
|
125
|
+
{
|
|
126
|
+
seg.segmentAbove = nextNode.key
|
|
127
|
+
seg.segmentAbove.segmentBelow = seg
|
|
128
|
+
}
|
|
129
|
+
if (prevNode !== null)
|
|
130
|
+
{
|
|
131
|
+
seg.segmentBelow = prevNode.key
|
|
132
|
+
seg.segmentBelow.segmentAbove = seg
|
|
133
|
+
}
|
|
134
|
+
return node.key
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
findSegment(seg: Segment): Segment
|
|
138
|
+
{
|
|
139
|
+
const node: any = this.tree.find(seg)
|
|
140
|
+
if (node === null) return null
|
|
141
|
+
return node.key
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
removeSegmentFromSweepline(seg: Segment): void
|
|
145
|
+
{
|
|
146
|
+
const node: any = this.tree.find(seg)
|
|
147
|
+
if (node === null) return
|
|
148
|
+
const nextNode: any = this.tree.next(node)
|
|
149
|
+
const prevNode: any = this.tree.prev(node)
|
|
150
|
+
|
|
151
|
+
if (nextNode !== null)
|
|
152
|
+
{
|
|
153
|
+
const nextSeg = nextNode.key
|
|
154
|
+
nextSeg.segmentBelow = seg.segmentBelow
|
|
155
|
+
}
|
|
156
|
+
if (prevNode !== null)
|
|
157
|
+
{
|
|
158
|
+
const prevSeg = prevNode.key
|
|
159
|
+
prevSeg.segmentAbove = seg.segmentAbove
|
|
160
|
+
}
|
|
161
|
+
this.tree.remove(seg)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
testIntersect(seg1: Segment, seg2: Segment): boolean
|
|
165
|
+
{
|
|
166
|
+
if (seg1 === null || seg2 === null) return false
|
|
167
|
+
|
|
168
|
+
if (seg1.rightSweepEvent.isSamePoint(seg2.leftSweepEvent) ||
|
|
169
|
+
seg1.rightSweepEvent.isSamePoint(seg2.rightSweepEvent) ||
|
|
170
|
+
seg1.leftSweepEvent.isSamePoint(seg2.leftSweepEvent) ||
|
|
171
|
+
seg1.leftSweepEvent.isSamePoint(seg2.rightSweepEvent)) return false
|
|
172
|
+
|
|
173
|
+
const x1 = seg1.leftSweepEvent.x
|
|
174
|
+
const y1 = seg1.leftSweepEvent.y
|
|
175
|
+
const x2 = seg1.rightSweepEvent.x
|
|
176
|
+
const y2 = seg1.rightSweepEvent.y
|
|
177
|
+
const x3 = seg2.leftSweepEvent.x
|
|
178
|
+
const y3 = seg2.leftSweepEvent.y
|
|
179
|
+
const x4 = seg2.rightSweepEvent.x
|
|
180
|
+
const y4 = seg2.rightSweepEvent.y
|
|
181
|
+
|
|
182
|
+
const denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1))
|
|
183
|
+
const numeA = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))
|
|
184
|
+
const numeB = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))
|
|
185
|
+
|
|
186
|
+
if (denom === 0)
|
|
187
|
+
{
|
|
188
|
+
if (numeA === 0 && numeB === 0) return false
|
|
189
|
+
return false
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const uA = numeA / denom
|
|
193
|
+
const uB = numeB / denom
|
|
194
|
+
|
|
195
|
+
return (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function checkWhichEventIsLeft(e1: Event, e2: Event): number
|
|
200
|
+
{
|
|
201
|
+
if (e1.x > e2.x) return 1
|
|
202
|
+
if (e1.x < e2.x) return -1
|
|
203
|
+
|
|
204
|
+
if (e1.y !== e2.y) return e1.y > e2.y ? 1 : -1
|
|
205
|
+
return 1
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function compareSegments(seg1: Segment, seg2: Segment): number
|
|
209
|
+
{
|
|
210
|
+
if (seg1 === seg2) return 0
|
|
211
|
+
|
|
212
|
+
if (signedArea(seg1.leftSweepEvent, seg1.rightSweepEvent, seg2.leftSweepEvent) !== 0 ||
|
|
213
|
+
signedArea(seg1.leftSweepEvent, seg1.rightSweepEvent, seg2.rightSweepEvent) !== 0)
|
|
214
|
+
{
|
|
215
|
+
// If the segments share their left endpoints
|
|
216
|
+
// use the right endpoint to sort
|
|
217
|
+
if (seg1.leftSweepEvent.isSamePoint(seg2.leftSweepEvent)) return seg1.leftSweepEvent.isBelow(seg2.rightSweepEvent) ? -1 : 1
|
|
218
|
+
|
|
219
|
+
// If the segments have different left endpoints
|
|
220
|
+
// use the left endpoint to sort
|
|
221
|
+
if (seg1.leftSweepEvent.x === seg2.leftSweepEvent.x) return seg1.leftSweepEvent.y < seg2.leftSweepEvent.y ? -1 : 1
|
|
222
|
+
|
|
223
|
+
// If the line segment associated to e1 been inserted
|
|
224
|
+
// into S after the line segment associated to e2 ?
|
|
225
|
+
if (checkWhichEventIsLeft(seg1.leftSweepEvent, seg2.leftSweepEvent) === 1) return seg2.leftSweepEvent.isAbove(seg1.leftSweepEvent) ? -1 : 1
|
|
226
|
+
|
|
227
|
+
// The line segment associated to e2 has been inserted
|
|
228
|
+
// into S after the line segment associated to e1
|
|
229
|
+
return seg1.leftSweepEvent.isBelow(seg2.leftSweepEvent) ? -1 : 1
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return checkWhichEventIsLeft(seg1.leftSweepEvent, seg2.leftSweepEvent) === 1 ? 1 : -1
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function runCheck(eventQueue: EventQueue): boolean
|
|
236
|
+
{
|
|
237
|
+
const sweepLine = new SweepLine()
|
|
238
|
+
let currentSegment = null
|
|
239
|
+
while (eventQueue.length)
|
|
240
|
+
{
|
|
241
|
+
const event = eventQueue.pop()
|
|
242
|
+
|
|
243
|
+
if (event.isLeftEndpoint)
|
|
244
|
+
{
|
|
245
|
+
currentSegment = sweepLine.addSegment(event)
|
|
246
|
+
if (sweepLine.testIntersect(currentSegment, currentSegment.segmentAbove)) return true
|
|
247
|
+
if (sweepLine.testIntersect(currentSegment, currentSegment.segmentBelow)) return true
|
|
248
|
+
}
|
|
249
|
+
else
|
|
250
|
+
{
|
|
251
|
+
if (!event.segment) continue
|
|
252
|
+
if (sweepLine.testIntersect(event.segment.segmentAbove, event.segment.segmentBelow)) return true
|
|
253
|
+
sweepLine.removeSegmentFromSweepline(event.segment)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return false
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function signedArea(p0: any, p1: any, p2: any): number
|
|
260
|
+
{
|
|
261
|
+
return (p0.x - p2.x) * (p1.y - p2.y) - (p1.x - p2.x) * (p0.y - p2.y)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function selfIntersectFast(poly: any): boolean
|
|
265
|
+
{
|
|
266
|
+
let pp = P.polyNormalize(poly);
|
|
267
|
+
if (pp == null) return false;
|
|
268
|
+
|
|
269
|
+
// Fill queue
|
|
270
|
+
let eventQueue = new EventQueue();
|
|
271
|
+
PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
|
|
272
|
+
let iEnd = iOffset + (nPoints - 1) * 2; // iterating over segments so right before last point
|
|
273
|
+
for (; iOffset < iEnd; iOffset += 2)
|
|
274
|
+
{
|
|
275
|
+
const e1 = new Event(b[iOffset], b[iOffset+1]);
|
|
276
|
+
const e2 = new Event(b[iOffset+2], b[iOffset+3]);
|
|
277
|
+
|
|
278
|
+
e1.otherEvent = e2
|
|
279
|
+
e2.otherEvent = e1
|
|
280
|
+
|
|
281
|
+
if (checkWhichEventIsLeft(e1, e2) > 0)
|
|
282
|
+
{
|
|
283
|
+
e2.isLeftEndpoint = true
|
|
284
|
+
e1.isLeftEndpoint = false
|
|
285
|
+
}
|
|
286
|
+
else
|
|
287
|
+
{
|
|
288
|
+
e1.isLeftEndpoint = true
|
|
289
|
+
e2.isLeftEndpoint = false
|
|
290
|
+
}
|
|
291
|
+
eventQueue.push(e1)
|
|
292
|
+
eventQueue.push(e2)
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return runCheck(eventQueue)
|
|
297
|
+
}
|