StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
StackGenVis/frontend/node_modules/box-intersect/lib/sweep.js

434 lines
11 KiB

4 years ago
'use strict'
module.exports = {
init: sqInit,
sweepBipartite: sweepBipartite,
sweepComplete: sweepComplete,
scanBipartite: scanBipartite,
scanComplete: scanComplete
}
var pool = require('typedarray-pool')
var bits = require('bit-twiddle')
var isort = require('./sort')
//Flag for blue
var BLUE_FLAG = (1<<28)
//1D sweep event queue stuff (use pool to save space)
var INIT_CAPACITY = 1024
var RED_SWEEP_QUEUE = pool.mallocInt32(INIT_CAPACITY)
var RED_SWEEP_INDEX = pool.mallocInt32(INIT_CAPACITY)
var BLUE_SWEEP_QUEUE = pool.mallocInt32(INIT_CAPACITY)
var BLUE_SWEEP_INDEX = pool.mallocInt32(INIT_CAPACITY)
var COMMON_SWEEP_QUEUE = pool.mallocInt32(INIT_CAPACITY)
var COMMON_SWEEP_INDEX = pool.mallocInt32(INIT_CAPACITY)
var SWEEP_EVENTS = pool.mallocDouble(INIT_CAPACITY * 8)
//Reserves memory for the 1D sweep data structures
function sqInit(count) {
var rcount = bits.nextPow2(count)
if(RED_SWEEP_QUEUE.length < rcount) {
pool.free(RED_SWEEP_QUEUE)
RED_SWEEP_QUEUE = pool.mallocInt32(rcount)
}
if(RED_SWEEP_INDEX.length < rcount) {
pool.free(RED_SWEEP_INDEX)
RED_SWEEP_INDEX = pool.mallocInt32(rcount)
}
if(BLUE_SWEEP_QUEUE.length < rcount) {
pool.free(BLUE_SWEEP_QUEUE)
BLUE_SWEEP_QUEUE = pool.mallocInt32(rcount)
}
if(BLUE_SWEEP_INDEX.length < rcount) {
pool.free(BLUE_SWEEP_INDEX)
BLUE_SWEEP_INDEX = pool.mallocInt32(rcount)
}
if(COMMON_SWEEP_QUEUE.length < rcount) {
pool.free(COMMON_SWEEP_QUEUE)
COMMON_SWEEP_QUEUE = pool.mallocInt32(rcount)
}
if(COMMON_SWEEP_INDEX.length < rcount) {
pool.free(COMMON_SWEEP_INDEX)
COMMON_SWEEP_INDEX = pool.mallocInt32(rcount)
}
var eventLength = 8 * rcount
if(SWEEP_EVENTS.length < eventLength) {
pool.free(SWEEP_EVENTS)
SWEEP_EVENTS = pool.mallocDouble(eventLength)
}
}
//Remove an item from the active queue in O(1)
function sqPop(queue, index, count, item) {
var idx = index[item]
var top = queue[count-1]
queue[idx] = top
index[top] = idx
}
//Insert an item into the active queue in O(1)
function sqPush(queue, index, count, item) {
queue[count] = item
index[item] = count
}
//Recursion base case: use 1D sweep algorithm
function sweepBipartite(
d, visit,
redStart, redEnd, red, redIndex,
blueStart, blueEnd, blue, blueIndex) {
//store events as pairs [coordinate, idx]
//
// red create: -(idx+1)
// red destroy: idx
// blue create: -(idx+BLUE_FLAG)
// blue destroy: idx+BLUE_FLAG
//
var ptr = 0
var elemSize = 2*d
var istart = d-1
var iend = elemSize-1
for(var i=redStart; i<redEnd; ++i) {
var idx = redIndex[i]
var redOffset = elemSize*i
SWEEP_EVENTS[ptr++] = red[redOffset+istart]
SWEEP_EVENTS[ptr++] = -(idx+1)
SWEEP_EVENTS[ptr++] = red[redOffset+iend]
SWEEP_EVENTS[ptr++] = idx
}
for(var i=blueStart; i<blueEnd; ++i) {
var idx = blueIndex[i]+BLUE_FLAG
var blueOffset = elemSize*i
SWEEP_EVENTS[ptr++] = blue[blueOffset+istart]
SWEEP_EVENTS[ptr++] = -idx
SWEEP_EVENTS[ptr++] = blue[blueOffset+iend]
SWEEP_EVENTS[ptr++] = idx
}
//process events from left->right
var n = ptr >>> 1
isort(SWEEP_EVENTS, n)
var redActive = 0
var blueActive = 0
for(var i=0; i<n; ++i) {
var e = SWEEP_EVENTS[2*i+1]|0
if(e >= BLUE_FLAG) {
//blue destroy event
e = (e-BLUE_FLAG)|0
sqPop(BLUE_SWEEP_QUEUE, BLUE_SWEEP_INDEX, blueActive--, e)
} else if(e >= 0) {
//red destroy event
sqPop(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive--, e)
} else if(e <= -BLUE_FLAG) {
//blue create event
e = (-e-BLUE_FLAG)|0
for(var j=0; j<redActive; ++j) {
var retval = visit(RED_SWEEP_QUEUE[j], e)
if(retval !== void 0) {
return retval
}
}
sqPush(BLUE_SWEEP_QUEUE, BLUE_SWEEP_INDEX, blueActive++, e)
} else {
//red create event
e = (-e-1)|0
for(var j=0; j<blueActive; ++j) {
var retval = visit(e, BLUE_SWEEP_QUEUE[j])
if(retval !== void 0) {
return retval
}
}
sqPush(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive++, e)
}
}
}
//Complete sweep
function sweepComplete(d, visit,
redStart, redEnd, red, redIndex,
blueStart, blueEnd, blue, blueIndex) {
var ptr = 0
var elemSize = 2*d
var istart = d-1
var iend = elemSize-1
for(var i=redStart; i<redEnd; ++i) {
var idx = (redIndex[i]+1)<<1
var redOffset = elemSize*i
SWEEP_EVENTS[ptr++] = red[redOffset+istart]
SWEEP_EVENTS[ptr++] = -idx
SWEEP_EVENTS[ptr++] = red[redOffset+iend]
SWEEP_EVENTS[ptr++] = idx
}
for(var i=blueStart; i<blueEnd; ++i) {
var idx = (blueIndex[i]+1)<<1
var blueOffset = elemSize*i
SWEEP_EVENTS[ptr++] = blue[blueOffset+istart]
SWEEP_EVENTS[ptr++] = (-idx)|1
SWEEP_EVENTS[ptr++] = blue[blueOffset+iend]
SWEEP_EVENTS[ptr++] = idx|1
}
//process events from left->right
var n = ptr >>> 1
isort(SWEEP_EVENTS, n)
var redActive = 0
var blueActive = 0
var commonActive = 0
for(var i=0; i<n; ++i) {
var e = SWEEP_EVENTS[2*i+1]|0
var color = e&1
if(i < n-1 && (e>>1) === (SWEEP_EVENTS[2*i+3]>>1)) {
color = 2
i += 1
}
if(e < 0) {
//Create event
var id = -(e>>1) - 1
//Intersect with common
for(var j=0; j<commonActive; ++j) {
var retval = visit(COMMON_SWEEP_QUEUE[j], id)
if(retval !== void 0) {
return retval
}
}
if(color !== 0) {
//Intersect with red
for(var j=0; j<redActive; ++j) {
var retval = visit(RED_SWEEP_QUEUE[j], id)
if(retval !== void 0) {
return retval
}
}
}
if(color !== 1) {
//Intersect with blue
for(var j=0; j<blueActive; ++j) {
var retval = visit(BLUE_SWEEP_QUEUE[j], id)
if(retval !== void 0) {
return retval
}
}
}
if(color === 0) {
//Red
sqPush(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive++, id)
} else if(color === 1) {
//Blue
sqPush(BLUE_SWEEP_QUEUE, BLUE_SWEEP_INDEX, blueActive++, id)
} else if(color === 2) {
//Both
sqPush(COMMON_SWEEP_QUEUE, COMMON_SWEEP_INDEX, commonActive++, id)
}
} else {
//Destroy event
var id = (e>>1) - 1
if(color === 0) {
//Red
sqPop(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive--, id)
} else if(color === 1) {
//Blue
sqPop(BLUE_SWEEP_QUEUE, BLUE_SWEEP_INDEX, blueActive--, id)
} else if(color === 2) {
//Both
sqPop(COMMON_SWEEP_QUEUE, COMMON_SWEEP_INDEX, commonActive--, id)
}
}
}
}
//Sweep and prune/scanline algorithm:
// Scan along axis, detect intersections
// Brute force all boxes along axis
function scanBipartite(
d, axis, visit, flip,
redStart, redEnd, red, redIndex,
blueStart, blueEnd, blue, blueIndex) {
var ptr = 0
var elemSize = 2*d
var istart = axis
var iend = axis+d
var redShift = 1
var blueShift = 1
if(flip) {
blueShift = BLUE_FLAG
} else {
redShift = BLUE_FLAG
}
for(var i=redStart; i<redEnd; ++i) {
var idx = i + redShift
var redOffset = elemSize*i
SWEEP_EVENTS[ptr++] = red[redOffset+istart]
SWEEP_EVENTS[ptr++] = -idx
SWEEP_EVENTS[ptr++] = red[redOffset+iend]
SWEEP_EVENTS[ptr++] = idx
}
for(var i=blueStart; i<blueEnd; ++i) {
var idx = i + blueShift
var blueOffset = elemSize*i
SWEEP_EVENTS[ptr++] = blue[blueOffset+istart]
SWEEP_EVENTS[ptr++] = -idx
}
//process events from left->right
var n = ptr >>> 1
isort(SWEEP_EVENTS, n)
var redActive = 0
for(var i=0; i<n; ++i) {
var e = SWEEP_EVENTS[2*i+1]|0
if(e < 0) {
var idx = -e
var isRed = false
if(idx >= BLUE_FLAG) {
isRed = !flip
idx -= BLUE_FLAG
} else {
isRed = !!flip
idx -= 1
}
if(isRed) {
sqPush(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive++, idx)
} else {
var blueId = blueIndex[idx]
var bluePtr = elemSize * idx
var b0 = blue[bluePtr+axis+1]
var b1 = blue[bluePtr+axis+1+d]
red_loop:
for(var j=0; j<redActive; ++j) {
var oidx = RED_SWEEP_QUEUE[j]
var redPtr = elemSize * oidx
if(b1 < red[redPtr+axis+1] ||
red[redPtr+axis+1+d] < b0) {
continue
}
for(var k=axis+2; k<d; ++k) {
if(blue[bluePtr + k + d] < red[redPtr + k] ||
red[redPtr + k + d] < blue[bluePtr + k]) {
continue red_loop
}
}
var redId = redIndex[oidx]
var retval
if(flip) {
retval = visit(blueId, redId)
} else {
retval = visit(redId, blueId)
}
if(retval !== void 0) {
return retval
}
}
}
} else {
sqPop(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive--, e - redShift)
}
}
}
function scanComplete(
d, axis, visit,
redStart, redEnd, red, redIndex,
blueStart, blueEnd, blue, blueIndex) {
var ptr = 0
var elemSize = 2*d
var istart = axis
var iend = axis+d
for(var i=redStart; i<redEnd; ++i) {
var idx = i + BLUE_FLAG
var redOffset = elemSize*i
SWEEP_EVENTS[ptr++] = red[redOffset+istart]
SWEEP_EVENTS[ptr++] = -idx
SWEEP_EVENTS[ptr++] = red[redOffset+iend]
SWEEP_EVENTS[ptr++] = idx
}
for(var i=blueStart; i<blueEnd; ++i) {
var idx = i + 1
var blueOffset = elemSize*i
SWEEP_EVENTS[ptr++] = blue[blueOffset+istart]
SWEEP_EVENTS[ptr++] = -idx
}
//process events from left->right
var n = ptr >>> 1
isort(SWEEP_EVENTS, n)
var redActive = 0
for(var i=0; i<n; ++i) {
var e = SWEEP_EVENTS[2*i+1]|0
if(e < 0) {
var idx = -e
if(idx >= BLUE_FLAG) {
RED_SWEEP_QUEUE[redActive++] = idx - BLUE_FLAG
} else {
idx -= 1
var blueId = blueIndex[idx]
var bluePtr = elemSize * idx
var b0 = blue[bluePtr+axis+1]
var b1 = blue[bluePtr+axis+1+d]
red_loop:
for(var j=0; j<redActive; ++j) {
var oidx = RED_SWEEP_QUEUE[j]
var redId = redIndex[oidx]
if(redId === blueId) {
break
}
var redPtr = elemSize * oidx
if(b1 < red[redPtr+axis+1] ||
red[redPtr+axis+1+d] < b0) {
continue
}
for(var k=axis+2; k<d; ++k) {
if(blue[bluePtr + k + d] < red[redPtr + k] ||
red[redPtr + k + d] < blue[bluePtr + k]) {
continue red_loop
}
}
var retval = visit(redId, blueId)
if(retval !== void 0) {
return retval
}
}
}
} else {
var idx = e - BLUE_FLAG
for(var j=redActive-1; j>=0; --j) {
if(RED_SWEEP_QUEUE[j] === idx) {
for(var k=j+1; k<redActive; ++k) {
RED_SWEEP_QUEUE[k-1] = RED_SWEEP_QUEUE[k]
}
break
}
}
--redActive
}
}
}