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/gl-contour2d/contour.js

421 lines
10 KiB

'use strict'
module.exports = createContour2D
var iota = require('iota-array')
var createShader = require('gl-shader')
var createBuffer = require('gl-buffer')
var ndarray = require('ndarray')
var surfaceNets = require('surface-nets')
var cdt2d = require('cdt2d')
var cleanPSLG = require('clean-pslg')
var bsearch = require('binary-search-bounds')
var shaders = require('./lib/shaders')
function GLContour2D (
plot,
shader,
fillShader,
positionBuffer,
colorBuffer,
idBuffer,
fillPositionBuffer,
fillColorBuffer) {
this.plot = plot
this.shader = shader
this.fillShader = fillShader
this.positionBuffer = positionBuffer
this.colorBuffer = colorBuffer
this.idBuffer = idBuffer
this.fillPositionBuffer = fillPositionBuffer
this.fillColorBuffer = fillColorBuffer
this.fillVerts = 0
this.shape = [0, 0]
this.bounds = [Infinity, Infinity, -Infinity, -Infinity]
this.numVertices = 0
this.lineWidth = 1
}
var proto = GLContour2D.prototype
var WEIGHTS = [
1, 0,
0, 0,
0, 1,
1, 0,
1, 1,
0, 1
]
proto.draw = (function () {
var MATRIX = [
1, 0, 0,
0, 1, 0,
0, 0, 1
]
var SCREEN_SHAPE = [0, 0]
return function () {
var plot = this.plot
var shader = this.shader
var fillShader = this.fillShader
var bounds = this.bounds
var numVertices = this.numVertices
var fillVerts = this.fillVerts
var uniforms, attributes
var gl = plot.gl
var viewBox = plot.viewBox
var dataBox = plot.dataBox
var boundX = bounds[2] - bounds[0]
var boundY = bounds[3] - bounds[1]
var dataX = dataBox[2] - dataBox[0]
var dataY = dataBox[3] - dataBox[1]
MATRIX[0] = 2.0 * boundX / dataX
MATRIX[4] = 2.0 * boundY / dataY
MATRIX[6] = 2.0 * (bounds[0] - dataBox[0]) / dataX - 1.0
MATRIX[7] = 2.0 * (bounds[1] - dataBox[1]) / dataY - 1.0
SCREEN_SHAPE[0] = viewBox[2] - viewBox[0]
SCREEN_SHAPE[1] = viewBox[3] - viewBox[1]
if (fillVerts > 0) {
fillShader.bind()
uniforms = fillShader.uniforms
uniforms.viewTransform = MATRIX
uniforms.screenShape = SCREEN_SHAPE
attributes = shader.attributes
this.fillPositionBuffer.bind()
attributes.position.pointer()
this.fillColorBuffer.bind()
attributes.color.pointer(gl.UNSIGNED_BYTE, true)
gl.drawArrays(gl.TRIANGLES, 0, fillVerts)
}
if (numVertices > 0) {
shader.bind()
var lineWidth = this.lineWidth * plot.pixelRatio
uniforms = shader.uniforms
uniforms.viewTransform = MATRIX
uniforms.screenShape = SCREEN_SHAPE
uniforms.lineWidth = lineWidth
uniforms.pointSize = 1000
attributes = shader.attributes
// Draw lines
this.positionBuffer.bind()
attributes.position.pointer(gl.FLOAT, false, 16, 0)
attributes.tangent.pointer(gl.FLOAT, false, 16, 8)
this.colorBuffer.bind()
attributes.color.pointer(gl.UNSIGNED_BYTE, true)
gl.drawArrays(gl.TRIANGLES, 0, numVertices)
// Draw end caps
uniforms.lineWidth = 0
uniforms.pointSize = lineWidth
this.positionBuffer.bind()
attributes.position.pointer(gl.FLOAT, false, 16 * 3, 0)
attributes.tangent.pointer(gl.FLOAT, false, 16 * 3, 8)
this.colorBuffer.bind()
attributes.color.pointer(gl.UNSIGNED_BYTE, true, 4 * 3, 0)
gl.drawArrays(gl.POINTS, 0, numVertices / 3)
}
}
})()
proto.drawPick = (function () {
return function (pickOffset) {
return pickOffset
}
})()
proto.pick = function (x, y, value) {
return null
}
function interpolate (array, point) {
var idx = Math.floor(point)
if (idx < 0) {
return array[0]
} else if (idx >= array.length - 1) {
return array[array.length - 1]
}
var t = point - idx
return (1.0 - t) * array[idx] + t * array[idx + 1]
}
proto.update = function (options) {
options = options || {}
var shape = options.shape || [0, 0]
var x = options.x || iota(shape[0])
var y = options.y || iota(shape[1])
var z = options.z || new Float32Array(shape[0] * shape[1])
var levels = options.levels || []
var levelColors = options.levelColors || []
var bounds = this.bounds
var lox = bounds[0] = x[0]
var loy = bounds[1] = y[0]
var hix = bounds[2] = x[x.length - 1]
var hiy = bounds[3] = y[y.length - 1]
if (lox === hix) {
bounds[2] += 1
hix += 1
}
if (loy === hiy) {
bounds[3] += 1
hiy += 1
}
var xs = 1.0 / (hix - lox)
var ys = 1.0 / (hiy - loy)
this.lineWidth = options.lineWidth || 1
var zarray = ndarray(z, shape)
var positions = []
var colors = []
var ids = []
var fillCells = []
var fillPositions = [
[0, 0],
[shape[0] - 1, 0],
[0, shape[1] - 1],
[shape[0] - 1, shape[1] - 1]
]
function intersect (level, x, a, b) {
var d = (b - a)
if (Math.abs(d) < 1e-6) {
return x
}
return Math.floor(x) + Math.max(0.001, Math.min(0.999, (level - a) / d))
}
for (var i = 0; i < levels.length; ++i) {
var level = levels[i]
if (i > 0 && level === levels[i - 1]) {
continue
}
var contour = surfaceNets(zarray, level)
var c_r = (255 * levelColors[4 * i]) | 0
var c_g = (255 * levelColors[4 * i + 1]) | 0
var c_b = (255 * levelColors[4 * i + 2]) | 0
var c_a = (255 * levelColors[4 * i + 3]) | 0
var c_cells = contour.cells
var c_positions = contour.positions
// Fix boundaries
var in_degree = Array(c_positions.length)
for (var j = 0; j < in_degree.length; ++j) {
in_degree[j] = 0
}
for (j = 0; j < c_cells.length; ++j) {
var edge = c_cells[j]
in_degree[edge[0]] += 1
in_degree[edge[1]] += 1
}
for (j = 0; j < in_degree.length; ++j) {
var deg = in_degree[j]
if (deg === 0) {
continue
}
var pp = c_positions[j]
in_degree[j] = fillPositions.length
fillPositions.push(pp)
if (deg > 1) {
continue
}
var ppx = pp[0]
var ppy = pp[1]
var z00 = zarray.get(Math.floor(ppx), Math.floor(ppy))
var z01 = zarray.get(Math.floor(ppx), Math.ceil(ppy))
var z10 = zarray.get(Math.ceil(ppx), Math.floor(ppy))
var z11 = zarray.get(Math.ceil(ppx), Math.ceil(ppy))
var intercept
if (Math.floor(pp[0]) === 0 &&
((z00 <= level) !== (z01 < level))) {
intercept = [0, intersect(level, pp[1], z00, z01)]
} else if (Math.ceil(pp[0]) === shape[0] - 1 &&
((z10 <= level) !== (z11 < level))) {
intercept = [shape[0] - 1, intersect(level, pp[1], z10, z11)]
} else if (Math.floor(pp[1]) === 0 &&
((z00 <= level) !== (z10 < level))) {
intercept = [intersect(level, pp[0], z00, z10), 0]
} else if (Math.ceil(pp[1]) === shape[1] - 1 &&
((z01 <= level) !== (z11 < level))) {
intercept = [intersect(level, pp[0], z01, z11), shape[1] - 1]
}
if (intercept) {
c_cells.push([j, c_positions.length])
in_degree.push(fillPositions.length)
c_positions.push(intercept)
}
}
for (j = 0; j < c_cells.length; ++j) {
var e = c_cells[j]
var a = c_positions[e[0]]
var b = c_positions[e[1]]
fillCells.push([in_degree[e[0]], in_degree[e[1]]])
var pointId = Math.round(a[0]) + shape[0] * Math.round(a[1])
var ax = interpolate(x, a[0])
var ay = interpolate(y, a[1])
var bx = interpolate(x, b[0])
var by = interpolate(y, b[1])
ax = xs * (ax - lox)
ay = ys * (ay - loy)
bx = xs * (bx - lox)
by = ys * (by - loy)
var dx = ax - bx
var dy = ay - by
for (var k = 0; k < WEIGHTS.length; k += 2) {
var wx = WEIGHTS[k]
var wix = 1.0 - wx
var wy = 2.0 * WEIGHTS[k + 1] - 1.0
positions.push(
wix * ax + wx * bx, wix * ay + wx * by,
wy * dx, wy * dy)
colors.push(c_r, c_g, c_b, c_a)
ids.push(pointId)
}
}
}
this.positionBuffer.update(new Float32Array(positions))
this.colorBuffer.update(new Uint8Array(colors))
this.idBuffer.update(new Uint32Array(ids))
this.numVertices = ids.length
var fillColors = options.fillColors
var fillCellColors = []
var fillCellPositions = []
var fillVerts = 0
if (fillColors) {
cleanPSLG(fillPositions, fillCells)
var fillMesh = cdt2d(fillPositions, fillCells, {
delaunay: false
})
for (i = 0; i < fillMesh.length; ++i) {
var cell = fillMesh[i]
var cx = 0
var cy = 0
for (j = 0; j < 3; ++j) {
var p = fillPositions[cell[j]]
var px = interpolate(x, p[0])
var py = interpolate(y, p[1])
cx += p[0]
cy += p[1]
fillCellPositions.push(
xs * (px - lox),
ys * (py - loy))
}
// Compute centroid of triangle
cx /= 3
cy /= 3
// Sample height field at triangle centroid
var cxi = Math.floor(cx)
var cyi = Math.floor(cy)
var cxf = cx - cxi
var cyf = cy - cyi
var c00 = zarray.get(cxi, cyi)
var c01 = zarray.get(cxi, cyi + 1)
var c10 = zarray.get(cxi + 1, cyi)
var c11 = zarray.get(cxi + 1, cyi + 1)
var zlevel =
(1 - cyf) * ((1 - cxf) * c00 + cxf * c10) +
cyf * ((1 - cxf) * c01 + cxf * c11)
// Color triangle using centroid data
var l = bsearch.le(levels, zlevel) + 1
var cr = (255 * fillColors[4 * l + 0]) | 0
var cg = (255 * fillColors[4 * l + 1]) | 0
var cb = (255 * fillColors[4 * l + 2]) | 0
var ca = (255 * fillColors[4 * l + 3]) | 0
fillCellColors.push(
cr, cg, cb, ca,
cr, cg, cb, ca,
cr, cg, cb, ca)
fillVerts += 3
}
this.fillPositionBuffer.update(new Float32Array(fillCellPositions))
this.fillColorBuffer.update(new Uint8Array(fillCellColors))
this.fillVerts = fillVerts
}
}
proto.dispose = function () {
this.plot.removeObject(this)
}
function createContour2D (plot, options) {
var gl = plot.gl
var shader = createShader(gl, shaders.vertex, shaders.fragment)
var fillShader = createShader(gl, shaders.fillVertex, shaders.fragment)
var positionBuffer = createBuffer(gl)
var colorBuffer = createBuffer(gl)
var idBuffer = createBuffer(gl)
var fillPositionBuffer = createBuffer(gl)
var fillColorBuffer = createBuffer(gl)
var contours = new GLContour2D(
plot,
shader,
fillShader,
positionBuffer,
colorBuffer,
idBuffer,
fillPositionBuffer,
fillColorBuffer)
contours.update(options)
plot.addObject(contours)
return contours
}