@footgun/cobalt 0.3.2 → 0.4.0
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/CHANGELOG.md +4 -0
- package/bundle.js +15 -21
- package/esbuild.js +1 -3
- package/examples/07-sdl/main.js +0 -1
- package/examples/07-sdl/package.json +2 -2
- package/examples/09-sdl-polar-meters/package.json +3 -3
- package/package.json +9 -5
- package/packages/box-intersect/index.js +138 -0
- package/packages/box-intersect/lib/brute.js +144 -0
- package/packages/box-intersect/lib/intersect.js +494 -0
- package/packages/box-intersect/lib/median.js +142 -0
- package/packages/box-intersect/lib/partition.js +20 -0
- package/packages/box-intersect/lib/sort.js +236 -0
- package/packages/box-intersect/lib/sweep.js +434 -0
- package/packages/box-intersect/package.json +24 -0
- package/packages/clean-pslg/clean-pslg.js +381 -0
- package/packages/clean-pslg/lib/rat-seg-intersect.js +42 -0
- package/packages/clean-pslg/package.json +29 -0
- package/packages/poly-to-pslg/package.json +15 -0
- package/packages/poly-to-pslg/poly-to-pslg.js +51 -0
- package/packages/typedarray-pool/package.json +17 -0
- package/packages/typedarray-pool/pool.js +225 -0
- package/src/primitives/public-api.js +1 -2
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = boxIntersectIter
|
|
4
|
+
|
|
5
|
+
var pool = require('typedarray-pool')
|
|
6
|
+
var bits = require('bit-twiddle')
|
|
7
|
+
var bruteForce = require('./brute')
|
|
8
|
+
var bruteForcePartial = bruteForce.partial
|
|
9
|
+
var bruteForceFull = bruteForce.full
|
|
10
|
+
var sweep = require('./sweep')
|
|
11
|
+
var findMedian = require('./median')
|
|
12
|
+
var genPartition = require('./partition')
|
|
13
|
+
|
|
14
|
+
//Twiddle parameters
|
|
15
|
+
var BRUTE_FORCE_CUTOFF = 128 //Cut off for brute force search
|
|
16
|
+
var SCAN_CUTOFF = (1<<22) //Cut off for two way scan
|
|
17
|
+
var SCAN_COMPLETE_CUTOFF = (1<<22)
|
|
18
|
+
|
|
19
|
+
//Partition functions
|
|
20
|
+
var partitionInteriorContainsInterval = genPartition(
|
|
21
|
+
'!(lo>=p0)&&!(p1>=hi)',
|
|
22
|
+
['p0', 'p1'])
|
|
23
|
+
|
|
24
|
+
var partitionStartEqual = genPartition(
|
|
25
|
+
'lo===p0',
|
|
26
|
+
['p0'])
|
|
27
|
+
|
|
28
|
+
var partitionStartLessThan = genPartition(
|
|
29
|
+
'lo<p0',
|
|
30
|
+
['p0'])
|
|
31
|
+
|
|
32
|
+
var partitionEndLessThanEqual = genPartition(
|
|
33
|
+
'hi<=p0',
|
|
34
|
+
['p0'])
|
|
35
|
+
|
|
36
|
+
var partitionContainsPoint = genPartition(
|
|
37
|
+
'lo<=p0&&p0<=hi',
|
|
38
|
+
['p0'])
|
|
39
|
+
|
|
40
|
+
var partitionContainsPointProper = genPartition(
|
|
41
|
+
'lo<p0&&p0<=hi',
|
|
42
|
+
['p0'])
|
|
43
|
+
|
|
44
|
+
//Frame size for iterative loop
|
|
45
|
+
var IFRAME_SIZE = 6
|
|
46
|
+
var DFRAME_SIZE = 2
|
|
47
|
+
|
|
48
|
+
//Data for box statck
|
|
49
|
+
var INIT_CAPACITY = 1024
|
|
50
|
+
var BOX_ISTACK = pool.mallocInt32(INIT_CAPACITY)
|
|
51
|
+
var BOX_DSTACK = pool.mallocDouble(INIT_CAPACITY)
|
|
52
|
+
|
|
53
|
+
//Initialize iterative loop queue
|
|
54
|
+
function iterInit(d, count) {
|
|
55
|
+
var levels = (8 * bits.log2(count+1) * (d+1))|0
|
|
56
|
+
var maxInts = bits.nextPow2(IFRAME_SIZE*levels)
|
|
57
|
+
if(BOX_ISTACK.length < maxInts) {
|
|
58
|
+
pool.free(BOX_ISTACK)
|
|
59
|
+
BOX_ISTACK = pool.mallocInt32(maxInts)
|
|
60
|
+
}
|
|
61
|
+
var maxDoubles = bits.nextPow2(DFRAME_SIZE*levels)
|
|
62
|
+
if(BOX_DSTACK.length < maxDoubles) {
|
|
63
|
+
pool.free(BOX_DSTACK)
|
|
64
|
+
BOX_DSTACK = pool.mallocDouble(maxDoubles)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//Append item to queue
|
|
69
|
+
function iterPush(ptr,
|
|
70
|
+
axis,
|
|
71
|
+
redStart, redEnd,
|
|
72
|
+
blueStart, blueEnd,
|
|
73
|
+
state,
|
|
74
|
+
lo, hi) {
|
|
75
|
+
|
|
76
|
+
var iptr = IFRAME_SIZE * ptr
|
|
77
|
+
BOX_ISTACK[iptr] = axis
|
|
78
|
+
BOX_ISTACK[iptr+1] = redStart
|
|
79
|
+
BOX_ISTACK[iptr+2] = redEnd
|
|
80
|
+
BOX_ISTACK[iptr+3] = blueStart
|
|
81
|
+
BOX_ISTACK[iptr+4] = blueEnd
|
|
82
|
+
BOX_ISTACK[iptr+5] = state
|
|
83
|
+
|
|
84
|
+
var dptr = DFRAME_SIZE * ptr
|
|
85
|
+
BOX_DSTACK[dptr] = lo
|
|
86
|
+
BOX_DSTACK[dptr+1] = hi
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//Special case: Intersect single point with list of intervals
|
|
90
|
+
function onePointPartial(
|
|
91
|
+
d, axis, visit, flip,
|
|
92
|
+
redStart, redEnd, red, redIndex,
|
|
93
|
+
blueOffset, blue, blueId) {
|
|
94
|
+
|
|
95
|
+
var elemSize = 2 * d
|
|
96
|
+
var bluePtr = blueOffset * elemSize
|
|
97
|
+
var blueX = blue[bluePtr + axis]
|
|
98
|
+
|
|
99
|
+
red_loop:
|
|
100
|
+
for(var i=redStart, redPtr=redStart*elemSize; i<redEnd; ++i, redPtr+=elemSize) {
|
|
101
|
+
var r0 = red[redPtr+axis]
|
|
102
|
+
var r1 = red[redPtr+axis+d]
|
|
103
|
+
if(blueX < r0 || r1 < blueX) {
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
106
|
+
if(flip && blueX === r0) {
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
var redId = redIndex[i]
|
|
110
|
+
for(var j=axis+1; j<d; ++j) {
|
|
111
|
+
var r0 = red[redPtr+j]
|
|
112
|
+
var r1 = red[redPtr+j+d]
|
|
113
|
+
var b0 = blue[bluePtr+j]
|
|
114
|
+
var b1 = blue[bluePtr+j+d]
|
|
115
|
+
if(r1 < b0 || b1 < r0) {
|
|
116
|
+
continue red_loop
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
var retval
|
|
120
|
+
if(flip) {
|
|
121
|
+
retval = visit(blueId, redId)
|
|
122
|
+
} else {
|
|
123
|
+
retval = visit(redId, blueId)
|
|
124
|
+
}
|
|
125
|
+
if(retval !== void 0) {
|
|
126
|
+
return retval
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
//Special case: Intersect one point with list of intervals
|
|
132
|
+
function onePointFull(
|
|
133
|
+
d, axis, visit,
|
|
134
|
+
redStart, redEnd, red, redIndex,
|
|
135
|
+
blueOffset, blue, blueId) {
|
|
136
|
+
|
|
137
|
+
var elemSize = 2 * d
|
|
138
|
+
var bluePtr = blueOffset * elemSize
|
|
139
|
+
var blueX = blue[bluePtr + axis]
|
|
140
|
+
|
|
141
|
+
red_loop:
|
|
142
|
+
for(var i=redStart, redPtr=redStart*elemSize; i<redEnd; ++i, redPtr+=elemSize) {
|
|
143
|
+
var redId = redIndex[i]
|
|
144
|
+
if(redId === blueId) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
var r0 = red[redPtr+axis]
|
|
148
|
+
var r1 = red[redPtr+axis+d]
|
|
149
|
+
if(blueX < r0 || r1 < blueX) {
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
for(var j=axis+1; j<d; ++j) {
|
|
153
|
+
var r0 = red[redPtr+j]
|
|
154
|
+
var r1 = red[redPtr+j+d]
|
|
155
|
+
var b0 = blue[bluePtr+j]
|
|
156
|
+
var b1 = blue[bluePtr+j+d]
|
|
157
|
+
if(r1 < b0 || b1 < r0) {
|
|
158
|
+
continue red_loop
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
var retval = visit(redId, blueId)
|
|
162
|
+
if(retval !== void 0) {
|
|
163
|
+
return retval
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//The main box intersection routine
|
|
169
|
+
function boxIntersectIter(
|
|
170
|
+
d, visit, initFull,
|
|
171
|
+
xSize, xBoxes, xIndex,
|
|
172
|
+
ySize, yBoxes, yIndex) {
|
|
173
|
+
|
|
174
|
+
//Reserve memory for stack
|
|
175
|
+
iterInit(d, xSize + ySize)
|
|
176
|
+
|
|
177
|
+
var top = 0
|
|
178
|
+
var elemSize = 2 * d
|
|
179
|
+
var retval
|
|
180
|
+
|
|
181
|
+
iterPush(top++,
|
|
182
|
+
0,
|
|
183
|
+
0, xSize,
|
|
184
|
+
0, ySize,
|
|
185
|
+
initFull ? 16 : 0,
|
|
186
|
+
-Infinity, Infinity)
|
|
187
|
+
if(!initFull) {
|
|
188
|
+
iterPush(top++,
|
|
189
|
+
0,
|
|
190
|
+
0, ySize,
|
|
191
|
+
0, xSize,
|
|
192
|
+
1,
|
|
193
|
+
-Infinity, Infinity)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
while(top > 0) {
|
|
197
|
+
top -= 1
|
|
198
|
+
|
|
199
|
+
var iptr = top * IFRAME_SIZE
|
|
200
|
+
var axis = BOX_ISTACK[iptr]
|
|
201
|
+
var redStart = BOX_ISTACK[iptr+1]
|
|
202
|
+
var redEnd = BOX_ISTACK[iptr+2]
|
|
203
|
+
var blueStart = BOX_ISTACK[iptr+3]
|
|
204
|
+
var blueEnd = BOX_ISTACK[iptr+4]
|
|
205
|
+
var state = BOX_ISTACK[iptr+5]
|
|
206
|
+
|
|
207
|
+
var dptr = top * DFRAME_SIZE
|
|
208
|
+
var lo = BOX_DSTACK[dptr]
|
|
209
|
+
var hi = BOX_DSTACK[dptr+1]
|
|
210
|
+
|
|
211
|
+
//Unpack state info
|
|
212
|
+
var flip = (state & 1)
|
|
213
|
+
var full = !!(state & 16)
|
|
214
|
+
|
|
215
|
+
//Unpack indices
|
|
216
|
+
var red = xBoxes
|
|
217
|
+
var redIndex = xIndex
|
|
218
|
+
var blue = yBoxes
|
|
219
|
+
var blueIndex = yIndex
|
|
220
|
+
if(flip) {
|
|
221
|
+
red = yBoxes
|
|
222
|
+
redIndex = yIndex
|
|
223
|
+
blue = xBoxes
|
|
224
|
+
blueIndex = xIndex
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if(state & 2) {
|
|
228
|
+
redEnd = partitionStartLessThan(
|
|
229
|
+
d, axis,
|
|
230
|
+
redStart, redEnd, red, redIndex,
|
|
231
|
+
hi)
|
|
232
|
+
if(redStart >= redEnd) {
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if(state & 4) {
|
|
237
|
+
redStart = partitionEndLessThanEqual(
|
|
238
|
+
d, axis,
|
|
239
|
+
redStart, redEnd, red, redIndex,
|
|
240
|
+
lo)
|
|
241
|
+
if(redStart >= redEnd) {
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
var redCount = redEnd - redStart
|
|
247
|
+
var blueCount = blueEnd - blueStart
|
|
248
|
+
|
|
249
|
+
if(full) {
|
|
250
|
+
if(d * redCount * (redCount + blueCount) < SCAN_COMPLETE_CUTOFF) {
|
|
251
|
+
retval = sweep.scanComplete(
|
|
252
|
+
d, axis, visit,
|
|
253
|
+
redStart, redEnd, red, redIndex,
|
|
254
|
+
blueStart, blueEnd, blue, blueIndex)
|
|
255
|
+
if(retval !== void 0) {
|
|
256
|
+
return retval
|
|
257
|
+
}
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
if(d * Math.min(redCount, blueCount) < BRUTE_FORCE_CUTOFF) {
|
|
262
|
+
//If input small, then use brute force
|
|
263
|
+
retval = bruteForcePartial(
|
|
264
|
+
d, axis, visit, flip,
|
|
265
|
+
redStart, redEnd, red, redIndex,
|
|
266
|
+
blueStart, blueEnd, blue, blueIndex)
|
|
267
|
+
if(retval !== void 0) {
|
|
268
|
+
return retval
|
|
269
|
+
}
|
|
270
|
+
continue
|
|
271
|
+
} else if(d * redCount * blueCount < SCAN_CUTOFF) {
|
|
272
|
+
//If input medium sized, then use sweep and prune
|
|
273
|
+
retval = sweep.scanBipartite(
|
|
274
|
+
d, axis, visit, flip,
|
|
275
|
+
redStart, redEnd, red, redIndex,
|
|
276
|
+
blueStart, blueEnd, blue, blueIndex)
|
|
277
|
+
if(retval !== void 0) {
|
|
278
|
+
return retval
|
|
279
|
+
}
|
|
280
|
+
continue
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
//First, find all red intervals whose interior contains (lo,hi)
|
|
285
|
+
var red0 = partitionInteriorContainsInterval(
|
|
286
|
+
d, axis,
|
|
287
|
+
redStart, redEnd, red, redIndex,
|
|
288
|
+
lo, hi)
|
|
289
|
+
|
|
290
|
+
//Lower dimensional case
|
|
291
|
+
if(redStart < red0) {
|
|
292
|
+
|
|
293
|
+
if(d * (red0 - redStart) < BRUTE_FORCE_CUTOFF) {
|
|
294
|
+
//Special case for small inputs: use brute force
|
|
295
|
+
retval = bruteForceFull(
|
|
296
|
+
d, axis+1, visit,
|
|
297
|
+
redStart, red0, red, redIndex,
|
|
298
|
+
blueStart, blueEnd, blue, blueIndex)
|
|
299
|
+
if(retval !== void 0) {
|
|
300
|
+
return retval
|
|
301
|
+
}
|
|
302
|
+
} else if(axis === d-2) {
|
|
303
|
+
if(flip) {
|
|
304
|
+
retval = sweep.sweepBipartite(
|
|
305
|
+
d, visit,
|
|
306
|
+
blueStart, blueEnd, blue, blueIndex,
|
|
307
|
+
redStart, red0, red, redIndex)
|
|
308
|
+
} else {
|
|
309
|
+
retval = sweep.sweepBipartite(
|
|
310
|
+
d, visit,
|
|
311
|
+
redStart, red0, red, redIndex,
|
|
312
|
+
blueStart, blueEnd, blue, blueIndex)
|
|
313
|
+
}
|
|
314
|
+
if(retval !== void 0) {
|
|
315
|
+
return retval
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
iterPush(top++,
|
|
319
|
+
axis+1,
|
|
320
|
+
redStart, red0,
|
|
321
|
+
blueStart, blueEnd,
|
|
322
|
+
flip,
|
|
323
|
+
-Infinity, Infinity)
|
|
324
|
+
iterPush(top++,
|
|
325
|
+
axis+1,
|
|
326
|
+
blueStart, blueEnd,
|
|
327
|
+
redStart, red0,
|
|
328
|
+
flip^1,
|
|
329
|
+
-Infinity, Infinity)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
//Divide and conquer phase
|
|
334
|
+
if(red0 < redEnd) {
|
|
335
|
+
|
|
336
|
+
//Cut blue into 3 parts:
|
|
337
|
+
//
|
|
338
|
+
// Points < mid point
|
|
339
|
+
// Points = mid point
|
|
340
|
+
// Points > mid point
|
|
341
|
+
//
|
|
342
|
+
var blue0 = findMedian(
|
|
343
|
+
d, axis,
|
|
344
|
+
blueStart, blueEnd, blue, blueIndex)
|
|
345
|
+
var mid = blue[elemSize * blue0 + axis]
|
|
346
|
+
var blue1 = partitionStartEqual(
|
|
347
|
+
d, axis,
|
|
348
|
+
blue0, blueEnd, blue, blueIndex,
|
|
349
|
+
mid)
|
|
350
|
+
|
|
351
|
+
//Right case
|
|
352
|
+
if(blue1 < blueEnd) {
|
|
353
|
+
iterPush(top++,
|
|
354
|
+
axis,
|
|
355
|
+
red0, redEnd,
|
|
356
|
+
blue1, blueEnd,
|
|
357
|
+
(flip|4) + (full ? 16 : 0),
|
|
358
|
+
mid, hi)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
//Left case
|
|
362
|
+
if(blueStart < blue0) {
|
|
363
|
+
iterPush(top++,
|
|
364
|
+
axis,
|
|
365
|
+
red0, redEnd,
|
|
366
|
+
blueStart, blue0,
|
|
367
|
+
(flip|2) + (full ? 16 : 0),
|
|
368
|
+
lo, mid)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
//Center case (the hard part)
|
|
372
|
+
if(blue0 + 1 === blue1) {
|
|
373
|
+
//Optimization: Range with exactly 1 point, use a brute force scan
|
|
374
|
+
if(full) {
|
|
375
|
+
retval = onePointFull(
|
|
376
|
+
d, axis, visit,
|
|
377
|
+
red0, redEnd, red, redIndex,
|
|
378
|
+
blue0, blue, blueIndex[blue0])
|
|
379
|
+
} else {
|
|
380
|
+
retval = onePointPartial(
|
|
381
|
+
d, axis, visit, flip,
|
|
382
|
+
red0, redEnd, red, redIndex,
|
|
383
|
+
blue0, blue, blueIndex[blue0])
|
|
384
|
+
}
|
|
385
|
+
if(retval !== void 0) {
|
|
386
|
+
return retval
|
|
387
|
+
}
|
|
388
|
+
} else if(blue0 < blue1) {
|
|
389
|
+
var red1
|
|
390
|
+
if(full) {
|
|
391
|
+
//If full intersection, need to handle special case
|
|
392
|
+
red1 = partitionContainsPoint(
|
|
393
|
+
d, axis,
|
|
394
|
+
red0, redEnd, red, redIndex,
|
|
395
|
+
mid)
|
|
396
|
+
if(red0 < red1) {
|
|
397
|
+
var redX = partitionStartEqual(
|
|
398
|
+
d, axis,
|
|
399
|
+
red0, red1, red, redIndex,
|
|
400
|
+
mid)
|
|
401
|
+
if(axis === d-2) {
|
|
402
|
+
//Degenerate sweep intersection:
|
|
403
|
+
// [red0, redX] with [blue0, blue1]
|
|
404
|
+
if(red0 < redX) {
|
|
405
|
+
retval = sweep.sweepComplete(
|
|
406
|
+
d, visit,
|
|
407
|
+
red0, redX, red, redIndex,
|
|
408
|
+
blue0, blue1, blue, blueIndex)
|
|
409
|
+
if(retval !== void 0) {
|
|
410
|
+
return retval
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//Normal sweep intersection:
|
|
415
|
+
// [redX, red1] with [blue0, blue1]
|
|
416
|
+
if(redX < red1) {
|
|
417
|
+
retval = sweep.sweepBipartite(
|
|
418
|
+
d, visit,
|
|
419
|
+
redX, red1, red, redIndex,
|
|
420
|
+
blue0, blue1, blue, blueIndex)
|
|
421
|
+
if(retval !== void 0) {
|
|
422
|
+
return retval
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
if(red0 < redX) {
|
|
427
|
+
iterPush(top++,
|
|
428
|
+
axis+1,
|
|
429
|
+
red0, redX,
|
|
430
|
+
blue0, blue1,
|
|
431
|
+
16,
|
|
432
|
+
-Infinity, Infinity)
|
|
433
|
+
}
|
|
434
|
+
if(redX < red1) {
|
|
435
|
+
iterPush(top++,
|
|
436
|
+
axis+1,
|
|
437
|
+
redX, red1,
|
|
438
|
+
blue0, blue1,
|
|
439
|
+
0,
|
|
440
|
+
-Infinity, Infinity)
|
|
441
|
+
iterPush(top++,
|
|
442
|
+
axis+1,
|
|
443
|
+
blue0, blue1,
|
|
444
|
+
redX, red1,
|
|
445
|
+
1,
|
|
446
|
+
-Infinity, Infinity)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
if(flip) {
|
|
452
|
+
red1 = partitionContainsPointProper(
|
|
453
|
+
d, axis,
|
|
454
|
+
red0, redEnd, red, redIndex,
|
|
455
|
+
mid)
|
|
456
|
+
} else {
|
|
457
|
+
red1 = partitionContainsPoint(
|
|
458
|
+
d, axis,
|
|
459
|
+
red0, redEnd, red, redIndex,
|
|
460
|
+
mid)
|
|
461
|
+
}
|
|
462
|
+
if(red0 < red1) {
|
|
463
|
+
if(axis === d-2) {
|
|
464
|
+
if(flip) {
|
|
465
|
+
retval = sweep.sweepBipartite(
|
|
466
|
+
d, visit,
|
|
467
|
+
blue0, blue1, blue, blueIndex,
|
|
468
|
+
red0, red1, red, redIndex)
|
|
469
|
+
} else {
|
|
470
|
+
retval = sweep.sweepBipartite(
|
|
471
|
+
d, visit,
|
|
472
|
+
red0, red1, red, redIndex,
|
|
473
|
+
blue0, blue1, blue, blueIndex)
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
iterPush(top++,
|
|
477
|
+
axis+1,
|
|
478
|
+
red0, red1,
|
|
479
|
+
blue0, blue1,
|
|
480
|
+
flip,
|
|
481
|
+
-Infinity, Infinity)
|
|
482
|
+
iterPush(top++,
|
|
483
|
+
axis+1,
|
|
484
|
+
blue0, blue1,
|
|
485
|
+
red0, red1,
|
|
486
|
+
flip^1,
|
|
487
|
+
-Infinity, Infinity)
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = findMedian
|
|
4
|
+
|
|
5
|
+
var genPartition = require('./partition')
|
|
6
|
+
|
|
7
|
+
var partitionStartLessThan = genPartition('lo<p0', ['p0'])
|
|
8
|
+
|
|
9
|
+
var PARTITION_THRESHOLD = 8 //Cut off for using insertion sort in findMedian
|
|
10
|
+
|
|
11
|
+
//Base case for median finding: Use insertion sort
|
|
12
|
+
function insertionSort(d, axis, start, end, boxes, ids) {
|
|
13
|
+
var elemSize = 2 * d
|
|
14
|
+
var boxPtr = elemSize * (start+1) + axis
|
|
15
|
+
for(var i=start+1; i<end; ++i, boxPtr+=elemSize) {
|
|
16
|
+
var x = boxes[boxPtr]
|
|
17
|
+
for(var j=i, ptr=elemSize*(i-1);
|
|
18
|
+
j>start && boxes[ptr+axis] > x;
|
|
19
|
+
--j, ptr-=elemSize) {
|
|
20
|
+
//Swap
|
|
21
|
+
var aPtr = ptr
|
|
22
|
+
var bPtr = ptr+elemSize
|
|
23
|
+
for(var k=0; k<elemSize; ++k, ++aPtr, ++bPtr) {
|
|
24
|
+
var y = boxes[aPtr]
|
|
25
|
+
boxes[aPtr] = boxes[bPtr]
|
|
26
|
+
boxes[bPtr] = y
|
|
27
|
+
}
|
|
28
|
+
var tmp = ids[j]
|
|
29
|
+
ids[j] = ids[j-1]
|
|
30
|
+
ids[j-1] = tmp
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//Find median using quick select algorithm
|
|
36
|
+
// takes O(n) time with high probability
|
|
37
|
+
function findMedian(d, axis, start, end, boxes, ids) {
|
|
38
|
+
if(end <= start+1) {
|
|
39
|
+
return start
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
var lo = start
|
|
43
|
+
var hi = end
|
|
44
|
+
var mid = ((end + start) >>> 1)
|
|
45
|
+
var elemSize = 2*d
|
|
46
|
+
var pivot = mid
|
|
47
|
+
var value = boxes[elemSize*mid+axis]
|
|
48
|
+
|
|
49
|
+
while(lo < hi) {
|
|
50
|
+
if(hi - lo < PARTITION_THRESHOLD) {
|
|
51
|
+
insertionSort(d, axis, lo, hi, boxes, ids)
|
|
52
|
+
value = boxes[elemSize*mid+axis]
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//Select pivot using median-of-3
|
|
57
|
+
var count = hi - lo
|
|
58
|
+
var pivot0 = (Math.random()*count+lo)|0
|
|
59
|
+
var value0 = boxes[elemSize*pivot0 + axis]
|
|
60
|
+
var pivot1 = (Math.random()*count+lo)|0
|
|
61
|
+
var value1 = boxes[elemSize*pivot1 + axis]
|
|
62
|
+
var pivot2 = (Math.random()*count+lo)|0
|
|
63
|
+
var value2 = boxes[elemSize*pivot2 + axis]
|
|
64
|
+
if(value0 <= value1) {
|
|
65
|
+
if(value2 >= value1) {
|
|
66
|
+
pivot = pivot1
|
|
67
|
+
value = value1
|
|
68
|
+
} else if(value0 >= value2) {
|
|
69
|
+
pivot = pivot0
|
|
70
|
+
value = value0
|
|
71
|
+
} else {
|
|
72
|
+
pivot = pivot2
|
|
73
|
+
value = value2
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
if(value1 >= value2) {
|
|
77
|
+
pivot = pivot1
|
|
78
|
+
value = value1
|
|
79
|
+
} else if(value2 >= value0) {
|
|
80
|
+
pivot = pivot0
|
|
81
|
+
value = value0
|
|
82
|
+
} else {
|
|
83
|
+
pivot = pivot2
|
|
84
|
+
value = value2
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//Swap pivot to end of array
|
|
89
|
+
var aPtr = elemSize * (hi-1)
|
|
90
|
+
var bPtr = elemSize * pivot
|
|
91
|
+
for(var i=0; i<elemSize; ++i, ++aPtr, ++bPtr) {
|
|
92
|
+
var x = boxes[aPtr]
|
|
93
|
+
boxes[aPtr] = boxes[bPtr]
|
|
94
|
+
boxes[bPtr] = x
|
|
95
|
+
}
|
|
96
|
+
var y = ids[hi-1]
|
|
97
|
+
ids[hi-1] = ids[pivot]
|
|
98
|
+
ids[pivot] = y
|
|
99
|
+
|
|
100
|
+
//Partition using pivot
|
|
101
|
+
pivot = partitionStartLessThan(
|
|
102
|
+
d, axis,
|
|
103
|
+
lo, hi-1, boxes, ids,
|
|
104
|
+
value)
|
|
105
|
+
|
|
106
|
+
//Swap pivot back
|
|
107
|
+
var aPtr = elemSize * (hi-1)
|
|
108
|
+
var bPtr = elemSize * pivot
|
|
109
|
+
for(var i=0; i<elemSize; ++i, ++aPtr, ++bPtr) {
|
|
110
|
+
var x = boxes[aPtr]
|
|
111
|
+
boxes[aPtr] = boxes[bPtr]
|
|
112
|
+
boxes[bPtr] = x
|
|
113
|
+
}
|
|
114
|
+
var y = ids[hi-1]
|
|
115
|
+
ids[hi-1] = ids[pivot]
|
|
116
|
+
ids[pivot] = y
|
|
117
|
+
|
|
118
|
+
//Swap pivot to last pivot
|
|
119
|
+
if(mid < pivot) {
|
|
120
|
+
hi = pivot-1
|
|
121
|
+
while(lo < hi &&
|
|
122
|
+
boxes[elemSize*(hi-1)+axis] === value) {
|
|
123
|
+
hi -= 1
|
|
124
|
+
}
|
|
125
|
+
hi += 1
|
|
126
|
+
} else if(pivot < mid) {
|
|
127
|
+
lo = pivot + 1
|
|
128
|
+
while(lo < hi &&
|
|
129
|
+
boxes[elemSize*lo+axis] === value) {
|
|
130
|
+
lo += 1
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
break
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//Make sure pivot is at start
|
|
138
|
+
return partitionStartLessThan(
|
|
139
|
+
d, axis,
|
|
140
|
+
start, mid, boxes, ids,
|
|
141
|
+
boxes[elemSize*mid+axis])
|
|
142
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = genPartition
|
|
4
|
+
|
|
5
|
+
var code = 'for(var j=2*a,k=j*c,l=k,m=c,n=b,o=a+b,p=c;d>p;++p,k+=j){var _;if($)if(m===p)m+=1,l+=j;else{for(var s=0;j>s;++s){var t=e[k+s];e[k+s]=e[l],e[l++]=t}var u=f[p];f[p]=f[m],f[m++]=u}}return m'
|
|
6
|
+
|
|
7
|
+
function genPartition(predicate, args) {
|
|
8
|
+
var fargs ='abcdef'.split('').concat(args)
|
|
9
|
+
var reads = []
|
|
10
|
+
if(predicate.indexOf('lo') >= 0) {
|
|
11
|
+
reads.push('lo=e[k+n]')
|
|
12
|
+
}
|
|
13
|
+
if(predicate.indexOf('hi') >= 0) {
|
|
14
|
+
reads.push('hi=e[k+o]')
|
|
15
|
+
}
|
|
16
|
+
fargs.push(
|
|
17
|
+
code.replace('_', reads.join())
|
|
18
|
+
.replace('$', predicate))
|
|
19
|
+
return Function.apply(void 0, fargs)
|
|
20
|
+
}
|