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.
201 lines
4.4 KiB
201 lines
4.4 KiB
4 years ago
|
|
||
|
var π = Math.PI
|
||
|
var _120 = radians(120)
|
||
|
|
||
|
module.exports = normalize
|
||
|
|
||
|
/**
|
||
|
* describe `path` in terms of cubic bézier
|
||
|
* curves and move commands
|
||
|
*
|
||
|
* @param {Array} path
|
||
|
* @return {Array}
|
||
|
*/
|
||
|
|
||
|
function normalize(path){
|
||
|
// init state
|
||
|
var prev
|
||
|
var result = []
|
||
|
var bezierX = 0
|
||
|
var bezierY = 0
|
||
|
var startX = 0
|
||
|
var startY = 0
|
||
|
var quadX = null
|
||
|
var quadY = null
|
||
|
var x = 0
|
||
|
var y = 0
|
||
|
|
||
|
for (var i = 0, len = path.length; i < len; i++) {
|
||
|
var seg = path[i]
|
||
|
var command = seg[0]
|
||
|
switch (command) {
|
||
|
case 'M':
|
||
|
startX = seg[1]
|
||
|
startY = seg[2]
|
||
|
break
|
||
|
case 'A':
|
||
|
seg = arc(x, y,seg[1],seg[2],radians(seg[3]),seg[4],seg[5],seg[6],seg[7])
|
||
|
// split multi part
|
||
|
seg.unshift('C')
|
||
|
if (seg.length > 7) {
|
||
|
result.push(seg.splice(0, 7))
|
||
|
seg.unshift('C')
|
||
|
}
|
||
|
break
|
||
|
case 'S':
|
||
|
// default control point
|
||
|
var cx = x
|
||
|
var cy = y
|
||
|
if (prev == 'C' || prev == 'S') {
|
||
|
cx += cx - bezierX // reflect the previous command's control
|
||
|
cy += cy - bezierY // point relative to the current point
|
||
|
}
|
||
|
seg = ['C', cx, cy, seg[1], seg[2], seg[3], seg[4]]
|
||
|
break
|
||
|
case 'T':
|
||
|
if (prev == 'Q' || prev == 'T') {
|
||
|
quadX = x * 2 - quadX // as with 'S' reflect previous control point
|
||
|
quadY = y * 2 - quadY
|
||
|
} else {
|
||
|
quadX = x
|
||
|
quadY = y
|
||
|
}
|
||
|
seg = quadratic(x, y, quadX, quadY, seg[1], seg[2])
|
||
|
break
|
||
|
case 'Q':
|
||
|
quadX = seg[1]
|
||
|
quadY = seg[2]
|
||
|
seg = quadratic(x, y, seg[1], seg[2], seg[3], seg[4])
|
||
|
break
|
||
|
case 'L':
|
||
|
seg = line(x, y, seg[1], seg[2])
|
||
|
break
|
||
|
case 'H':
|
||
|
seg = line(x, y, seg[1], y)
|
||
|
break
|
||
|
case 'V':
|
||
|
seg = line(x, y, x, seg[1])
|
||
|
break
|
||
|
case 'Z':
|
||
|
seg = line(x, y, startX, startY)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
// update state
|
||
|
prev = command
|
||
|
x = seg[seg.length - 2]
|
||
|
y = seg[seg.length - 1]
|
||
|
if (seg.length > 4) {
|
||
|
bezierX = seg[seg.length - 4]
|
||
|
bezierY = seg[seg.length - 3]
|
||
|
} else {
|
||
|
bezierX = x
|
||
|
bezierY = y
|
||
|
}
|
||
|
result.push(seg)
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
function line(x1, y1, x2, y2){
|
||
|
return ['C', x1, y1, x2, y2, x2, y2]
|
||
|
}
|
||
|
|
||
|
function quadratic(x1, y1, cx, cy, x2, y2){
|
||
|
return [
|
||
|
'C',
|
||
|
x1/3 + (2/3) * cx,
|
||
|
y1/3 + (2/3) * cy,
|
||
|
x2/3 + (2/3) * cx,
|
||
|
y2/3 + (2/3) * cy,
|
||
|
x2,
|
||
|
y2
|
||
|
]
|
||
|
}
|
||
|
|
||
|
// This function is ripped from
|
||
|
// github.com/DmitryBaranovskiy/raphael/blob/4d97d4/raphael.js#L2216-L2304
|
||
|
// which references w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
||
|
// TODO: make it human readable
|
||
|
|
||
|
function arc(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
|
||
|
if (!recursive) {
|
||
|
var xy = rotate(x1, y1, -angle)
|
||
|
x1 = xy.x
|
||
|
y1 = xy.y
|
||
|
xy = rotate(x2, y2, -angle)
|
||
|
x2 = xy.x
|
||
|
y2 = xy.y
|
||
|
var x = (x1 - x2) / 2
|
||
|
var y = (y1 - y2) / 2
|
||
|
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry)
|
||
|
if (h > 1) {
|
||
|
h = Math.sqrt(h)
|
||
|
rx = h * rx
|
||
|
ry = h * ry
|
||
|
}
|
||
|
var rx2 = rx * rx
|
||
|
var ry2 = ry * ry
|
||
|
var k = (large_arc_flag == sweep_flag ? -1 : 1)
|
||
|
* Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)))
|
||
|
if (k == Infinity) k = 1 // neutralize
|
||
|
var cx = k * rx * y / ry + (x1 + x2) / 2
|
||
|
var cy = k * -ry * x / rx + (y1 + y2) / 2
|
||
|
var f1 = Math.asin(((y1 - cy) / ry).toFixed(9))
|
||
|
var f2 = Math.asin(((y2 - cy) / ry).toFixed(9))
|
||
|
|
||
|
f1 = x1 < cx ? π - f1 : f1
|
||
|
f2 = x2 < cx ? π - f2 : f2
|
||
|
if (f1 < 0) f1 = π * 2 + f1
|
||
|
if (f2 < 0) f2 = π * 2 + f2
|
||
|
if (sweep_flag && f1 > f2) f1 = f1 - π * 2
|
||
|
if (!sweep_flag && f2 > f1) f2 = f2 - π * 2
|
||
|
} else {
|
||
|
f1 = recursive[0]
|
||
|
f2 = recursive[1]
|
||
|
cx = recursive[2]
|
||
|
cy = recursive[3]
|
||
|
}
|
||
|
// greater than 120 degrees requires multiple segments
|
||
|
if (Math.abs(f2 - f1) > _120) {
|
||
|
var f2old = f2
|
||
|
var x2old = x2
|
||
|
var y2old = y2
|
||
|
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1)
|
||
|
x2 = cx + rx * Math.cos(f2)
|
||
|
y2 = cy + ry * Math.sin(f2)
|
||
|
var res = arc(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy])
|
||
|
}
|
||
|
var t = Math.tan((f2 - f1) / 4)
|
||
|
var hx = 4 / 3 * rx * t
|
||
|
var hy = 4 / 3 * ry * t
|
||
|
var curve = [
|
||
|
2 * x1 - (x1 + hx * Math.sin(f1)),
|
||
|
2 * y1 - (y1 - hy * Math.cos(f1)),
|
||
|
x2 + hx * Math.sin(f2),
|
||
|
y2 - hy * Math.cos(f2),
|
||
|
x2,
|
||
|
y2
|
||
|
]
|
||
|
if (recursive) return curve
|
||
|
if (res) curve = curve.concat(res)
|
||
|
for (var i = 0; i < curve.length;) {
|
||
|
var rot = rotate(curve[i], curve[i+1], angle)
|
||
|
curve[i++] = rot.x
|
||
|
curve[i++] = rot.y
|
||
|
}
|
||
|
return curve
|
||
|
}
|
||
|
|
||
|
function rotate(x, y, rad){
|
||
|
return {
|
||
|
x: x * Math.cos(rad) - y * Math.sin(rad),
|
||
|
y: x * Math.sin(rad) + y * Math.cos(rad)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function radians(degress){
|
||
|
return degress * (π / 180)
|
||
|
}
|