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/simplify-planar-graph/simplify.js

271 lines
5.2 KiB

"use strict"
module.exports = simplifyPolygon
var orient = require("robust-orientation")
var sc = require("simplicial-complex")
function errorWeight(base, a, b) {
var area = Math.abs(orient(base, a, b))
var perim = Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1]-b[1], 2))
return area / perim
}
function simplifyPolygon(cells, positions, minArea) {
var n = positions.length
var nc = cells.length
var inv = new Array(n)
var outv = new Array(n)
var weights = new Array(n)
var dead = new Array(n)
//Initialize tables
for(var i=0; i<n; ++i) {
inv[i] = outv[i] = -1
weights[i] = Infinity
dead[i] = false
}
//Compute neighbors
for(var i=0; i<nc; ++i) {
var c = cells[i]
if(c.length !== 2) {
throw new Error("Input must be a graph")
}
var s = c[1]
var t = c[0]
if(outv[t] !== -1) {
outv[t] = -2
} else {
outv[t] = s
}
if(inv[s] !== -1) {
inv[s] = -2
} else {
inv[s] = t
}
}
//Updates the weight for vertex i
function computeWeight(i) {
if(dead[i]) {
return Infinity
}
//TODO: Check that the line segment doesn't cross once simplified
var s = inv[i]
var t = outv[i]
if((s<0) || (t<0)) {
return Infinity
} else {
return errorWeight(positions[i], positions[s], positions[t])
}
}
//Swaps two nodes on the heap (i,j) are the index of the nodes
function heapSwap(i,j) {
var a = heap[i]
var b = heap[j]
heap[i] = b
heap[j] = a
index[a] = j
index[b] = i
}
//Returns the weight of node i on the heap
function heapWeight(i) {
return weights[heap[i]]
}
function heapParent(i) {
if(i & 1) {
return (i - 1) >> 1
}
return (i >> 1) - 1
}
//Bubble element i down the heap
function heapDown(i) {
var w = heapWeight(i)
while(true) {
var tw = w
var left = 2*i + 1
var right = 2*(i + 1)
var next = i
if(left < heapCount) {
var lw = heapWeight(left)
if(lw < tw) {
next = left
tw = lw
}
}
if(right < heapCount) {
var rw = heapWeight(right)
if(rw < tw) {
next = right
}
}
if(next === i) {
return i
}
heapSwap(i, next)
i = next
}
}
//Bubbles element i up the heap
function heapUp(i) {
var w = heapWeight(i)
while(i > 0) {
var parent = heapParent(i)
if(parent >= 0) {
var pw = heapWeight(parent)
if(w < pw) {
heapSwap(i, parent)
i = parent
continue
}
}
return i
}
}
//Pop minimum element
function heapPop() {
if(heapCount > 0) {
var head = heap[0]
heapSwap(0, heapCount-1)
heapCount -= 1
heapDown(0)
return head
}
return -1
}
//Update heap item i
function heapUpdate(i, w) {
var a = heap[i]
if(weights[a] === w) {
return i
}
weights[a] = -Infinity
heapUp(i)
heapPop()
weights[a] = w
heapCount += 1
return heapUp(heapCount-1)
}
//Kills a vertex (assume vertex already removed from heap)
function kill(i) {
if(dead[i]) {
return
}
//Kill vertex
dead[i] = true
//Fixup topology
var s = inv[i]
var t = outv[i]
if(inv[t] >= 0) {
inv[t] = s
}
if(outv[s] >= 0) {
outv[s] = t
}
//Update weights on s and t
if(index[s] >= 0) {
heapUpdate(index[s], computeWeight(s))
}
if(index[t] >= 0) {
heapUpdate(index[t], computeWeight(t))
}
}
//Initialize weights and heap
var heap = []
var index = new Array(n)
for(var i=0; i<n; ++i) {
var w = weights[i] = computeWeight(i)
if(w < Infinity) {
index[i] = heap.length
heap.push(i)
} else {
index[i] = -1
}
}
var heapCount = heap.length
for(var i=heapCount>>1; i>=0; --i) {
heapDown(i)
}
//Kill vertices
while(true) {
var hmin = heapPop()
if((hmin < 0) || (weights[hmin] > minArea)) {
break
}
kill(hmin)
}
//Build collapsed vertex table
var npositions = []
for(var i=0; i<n; ++i) {
if(!dead[i]) {
index[i] = npositions.length
npositions.push(positions[i].slice())
}
}
var nv = npositions.length
function tortoiseHare(seq, start) {
if(seq[start] < 0) {
return start
}
var t = start
var h = start
do {
//Walk two steps with h
var nh = seq[h]
if(!dead[h] || nh < 0 || nh === h) {
break
}
h = nh
nh = seq[h]
if(!dead[h] || nh < 0 || nh === h) {
break
}
h = nh
//Walk one step with t
t = seq[t]
} while(t !== h)
//Compress cycles
for(var v=start; v!==h; v = seq[v]) {
seq[v] = h
}
return h
}
var ncells = []
cells.forEach(function(c) {
var tin = tortoiseHare(inv, c[0])
var tout = tortoiseHare(outv, c[1])
if(tin >= 0 && tout >= 0 && tin !== tout) {
var cin = index[tin]
var cout = index[tout]
if(cin !== cout) {
ncells.push([ cin, cout ])
}
}
})
//Normalize result
sc.unique(sc.normalize(ncells))
//Return final list of cells
return {
positions: npositions,
edges: ncells
}
}