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/regl-scatter2d/bundle.js

968 lines
32 KiB

'use strict';
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
var rgba = require('color-normalize');
var getBounds = require('array-bounds');
var colorId = require('color-id');
var cluster = require('point-cluster');
var extend = require('object-assign');
var glslify = require('glslify');
var pick = require('pick-by-alias');
var updateDiff = require('update-diff');
var flatten = require('flatten-vertex-data');
var ie = require('is-iexplorer');
var f32 = require('to-float32');
var parseRect = require('parse-rect');
var scatter = Scatter;
function Scatter(regl, options) {
var _this = this;
if (!(this instanceof Scatter)) return new Scatter(regl, options);
if (typeof regl === 'function') {
if (!options) options = {};
options.regl = regl;
} else {
options = regl;
regl = null;
}
if (options && options.length) options.positions = options;
regl = options.regl; // persistent variables
var gl = regl._gl,
paletteTexture,
palette = [],
paletteIds = {},
// state
groups = [],
// textures for marker keys
markerTextures = [null],
markerCache = [null];
var maxColors = 255,
maxSize = 100; // direct color buffer mode
// IE does not support palette anyways
this.tooManyColors = ie; // texture with color palette
paletteTexture = regl.texture({
data: new Uint8Array(maxColors * 4),
width: maxColors,
height: 1,
type: 'uint8',
format: 'rgba',
wrapS: 'clamp',
wrapT: 'clamp',
mag: 'nearest',
min: 'nearest'
});
extend(this, {
regl: regl,
gl: gl,
groups: groups,
markerCache: markerCache,
markerTextures: markerTextures,
palette: palette,
paletteIds: paletteIds,
paletteTexture: paletteTexture,
maxColors: maxColors,
maxSize: maxSize,
canvas: gl.canvas
});
this.update(options); // common shader options
var shaderOptions = {
uniforms: {
pixelRatio: regl.context('pixelRatio'),
palette: paletteTexture,
paletteSize: function paletteSize(ctx, prop) {
return [_this.tooManyColors ? 0 : maxColors, paletteTexture.height];
},
scale: regl.prop('scale'),
scaleFract: regl.prop('scaleFract'),
translate: regl.prop('translate'),
translateFract: regl.prop('translateFract'),
opacity: regl.prop('opacity'),
marker: regl.prop('markerTexture')
},
attributes: {
// FIXME: optimize these parts
x: function x(ctx, prop) {
return prop.xAttr || {
buffer: prop.positionBuffer,
stride: 8,
offset: 0
};
},
y: function y(ctx, prop) {
return prop.yAttr || {
buffer: prop.positionBuffer,
stride: 8,
offset: 4
};
},
xFract: function xFract(ctx, prop) {
return prop.xAttr ? {
constant: [0, 0]
} : {
buffer: prop.positionFractBuffer,
stride: 8,
offset: 0
};
},
yFract: function yFract(ctx, prop) {
return prop.yAttr ? {
constant: [0, 0]
} : {
buffer: prop.positionFractBuffer,
stride: 8,
offset: 4
};
},
size: function size(ctx, prop) {
return prop.size.length ? {
buffer: prop.sizeBuffer,
stride: 2,
offset: 0
} : {
constant: [Math.round(prop.size * 255 / _this.maxSize)]
};
},
borderSize: function borderSize(ctx, prop) {
return prop.borderSize.length ? {
buffer: prop.sizeBuffer,
stride: 2,
offset: 1
} : {
constant: [Math.round(prop.borderSize * 255 / _this.maxSize)]
};
},
colorId: function colorId(ctx, prop) {
return prop.color.length ? {
buffer: prop.colorBuffer,
stride: _this.tooManyColors ? 8 : 4,
offset: 0
} : {
constant: _this.tooManyColors ? palette.slice(prop.color * 4, prop.color * 4 + 4) : [prop.color]
};
},
borderColorId: function borderColorId(ctx, prop) {
return prop.borderColor.length ? {
buffer: prop.colorBuffer,
stride: _this.tooManyColors ? 8 : 4,
offset: _this.tooManyColors ? 4 : 2
} : {
constant: _this.tooManyColors ? palette.slice(prop.borderColor * 4, prop.borderColor * 4 + 4) : [prop.borderColor]
};
},
isActive: function isActive(ctx, prop) {
return prop.activation === true ? {
constant: [1]
} : prop.activation ? prop.activation : {
constant: [0]
};
}
},
blend: {
enable: true,
color: [0, 0, 0, 1],
// photoshop blending
func: {
srcRGB: 'src alpha',
dstRGB: 'one minus src alpha',
srcAlpha: 'one minus dst alpha',
dstAlpha: 'one'
}
},
scissor: {
enable: true,
box: regl.prop('viewport')
},
viewport: regl.prop('viewport'),
stencil: {
enable: false
},
depth: {
enable: false
},
elements: regl.prop('elements'),
count: regl.prop('count'),
offset: regl.prop('offset'),
primitive: 'points' // draw sdf-marker
};
var markerOptions = extend({}, shaderOptions);
markerOptions.frag = glslify(["precision highp float;\n#define GLSLIFY 1\n\nvarying vec4 fragColor, fragBorderColor;\nvarying float fragWidth, fragBorderColorLevel, fragColorLevel;\n\nuniform sampler2D marker;\nuniform float pixelRatio, opacity;\n\nfloat smoothStep(float x, float y) {\n return 1.0 / (1.0 + exp(50.0*(x - y)));\n}\n\nvoid main() {\n float dist = texture2D(marker, gl_PointCoord).r, delta = fragWidth;\n\n // max-distance alpha\n if (dist < 0.003) discard;\n\n // null-border case\n if (fragBorderColorLevel == fragColorLevel || fragBorderColor.a == 0.) {\n float colorAmt = smoothstep(.5 - delta, .5 + delta, dist);\n gl_FragColor = vec4(fragColor.rgb, colorAmt * fragColor.a * opacity);\n }\n else {\n float borderColorAmt = smoothstep(fragBorderColorLevel - delta, fragBorderColorLevel + delta, dist);\n float colorAmt = smoothstep(fragColorLevel - delta, fragColorLevel + delta, dist);\n\n vec4 color = fragBorderColor;\n color.a *= borderColorAmt;\n color = mix(color, fragColor, colorAmt);\n color.a *= opacity;\n\n gl_FragColor = color;\n }\n\n}\n"]);
markerOptions.vert = glslify(["precision highp float;\n#define GLSLIFY 1\n\nattribute float x, y, xFract, yFract;\nattribute float size, borderSize;\nattribute vec4 colorId, borderColorId;\nattribute float isActive;\n\nuniform vec2 scale, scaleFract, translate, translateFract, paletteSize;\nuniform float pixelRatio;\nuniform sampler2D palette;\n\nconst float maxSize = 100.;\nconst float borderLevel = .5;\n\nvarying vec4 fragColor, fragBorderColor;\nvarying float fragPointSize, fragBorderRadius, fragWidth, fragBorderColorLevel, fragColorLevel;\n\nbool isDirect = (paletteSize.x < 1.);\n\nvec4 getColor(vec4 id) {\n return isDirect ? id / 255. : texture2D(palette,\n vec2(\n (id.x + .5) / paletteSize.x,\n (id.y + .5) / paletteSize.y\n )\n );\n}\n\nvoid main() {\n if (isActive == 0.) return;\n\n vec2 position = vec2(x, y);\n vec2 positionFract = vec2(xFract, yFract);\n\n vec4 color = getColor(colorId);\n vec4 borderColor = getColor(borderColorId);\n\n float size = size * maxSize / 255.;\n float borderSize = borderSize * maxSize / 255.;\n\n gl_PointSize = 2. * size * pixelRatio;\n fragPointSize = size * pixelRatio;\n\n vec2 pos = (position + translate) * scale\n + (positionFract + translateFract) * scale\n + (position + translate) * scaleFract\n + (positionFract + translateFract) * scaleFract;\n\n gl_Position = vec4(pos * 2. - 1., 0, 1);\n\n fragColor = color;\n fragBorderColor = borderColor;\n fragWidth = 1. / gl_PointSize;\n\n fragBorderColorLevel = clamp(borderLevel - borderLevel * borderSize / size, 0., 1.);\n fragColorLevel = clamp(borderLevel + (1. - borderLevel) * borderSize / size, 0., 1.);\n}"]);
this.drawMarker = regl(markerOptions); // draw circle
var circleOptions = extend({}, shaderOptions);
circleOptions.frag = glslify(["precision highp float;\n#define GLSLIFY 1\n\nvarying vec4 fragColor, fragBorderColor;\n\nuniform float opacity;\nvarying float fragBorderRadius, fragWidth;\n\nfloat smoothStep(float edge0, float edge1, float x) {\n\tfloat t;\n\tt = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n\treturn t * t * (3.0 - 2.0 * t);\n}\n\nvoid main() {\n\tfloat radius, alpha = 1.0, delta = fragWidth;\n\n\tradius = length(2.0 * gl_PointCoord.xy - 1.0);\n\n\tif (radius > 1.0 + delta) {\n\t\tdiscard;\n\t}\n\n\talpha -= smoothstep(1.0 - delta, 1.0 + delta, radius);\n\n\tfloat borderRadius = fragBorderRadius;\n\tfloat ratio = smoothstep(borderRadius - delta, borderRadius + delta, radius);\n\tvec4 color = mix(fragColor, fragBorderColor, ratio);\n\tcolor.a *= alpha * opacity;\n\tgl_FragColor = color;\n}\n"]);
circleOptions.vert = glslify(["precision highp float;\n#define GLSLIFY 1\n\nattribute float x, y, xFract, yFract;\nattribute float size, borderSize;\nattribute vec4 colorId, borderColorId;\nattribute float isActive;\n\nuniform vec2 scale, scaleFract, translate, translateFract;\nuniform float pixelRatio;\nuniform sampler2D palette;\nuniform vec2 paletteSize;\n\nconst float maxSize = 100.;\n\nvarying vec4 fragColor, fragBorderColor;\nvarying float fragBorderRadius, fragWidth;\n\nbool isDirect = (paletteSize.x < 1.);\n\nvec4 getColor(vec4 id) {\n return isDirect ? id / 255. : texture2D(palette,\n vec2(\n (id.x + .5) / paletteSize.x,\n (id.y + .5) / paletteSize.y\n )\n );\n}\n\nvoid main() {\n // ignore inactive points\n if (isActive == 0.) return;\n\n vec2 position = vec2(x, y);\n vec2 positionFract = vec2(xFract, yFract);\n\n vec4 color = getColor(colorId);\n vec4 borderColor = getColor(borderColorId);\n\n float size = size * maxSize / 255.;\n float borderSize = borderSize * maxSize / 255.;\n\n gl_PointSize = (size + borderSize) * pixelRatio;\n\n vec2 pos = (position + translate) * scale\n + (positionFract + translateFract) * scale\n + (position + translate) * scaleFract\n + (positionFract + translateFract) * scaleFract;\n\n gl_Position = vec4(pos * 2. - 1., 0, 1);\n\n fragBorderRadius = 1. - 2. * borderSize / (size + borderSize);\n fragColor = color;\n fragBorderColor = borderColor.a == 0. || borderSize == 0. ? vec4(color.rgb, 0.) : borderColor;\n fragWidth = 1. / gl_PointSize;\n}\n"]); // polyfill IE
if (ie) {
circleOptions.frag = circleOptions.frag.replace('smoothstep', 'smoothStep');
markerOptions.frag = markerOptions.frag.replace('smoothstep', 'smoothStep');
}
this.drawCircle = regl(circleOptions);
} // single pass defaults
Scatter.defaults = {
color: 'black',
borderColor: 'transparent',
borderSize: 0,
size: 12,
opacity: 1,
marker: undefined,
viewport: null,
range: null,
pixelSize: null,
count: 0,
offset: 0,
bounds: null,
positions: [],
snap: 1e4 // update & redraw
};
Scatter.prototype.render = function () {
if (arguments.length) {
this.update.apply(this, arguments);
}
this.draw();
return this;
}; // draw all groups or only indicated ones
Scatter.prototype.draw = function () {
var _this2 = this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var groups = this.groups; // if directly array passed - treat as passes
if (args.length === 1 && Array.isArray(args[0]) && (args[0][0] === null || Array.isArray(args[0][0]))) {
args = args[0];
} // FIXME: remove once https://github.com/regl-project/regl/issues/474 resolved
this.regl._refresh();
if (args.length) {
for (var i = 0; i < args.length; i++) {
this.drawItem(i, args[i]);
}
} // draw all passes
else {
groups.forEach(function (group, i) {
_this2.drawItem(i);
});
}
return this;
}; // draw specific scatter group
Scatter.prototype.drawItem = function (id, els) {
var groups = this.groups;
var group = groups[id]; // debug viewport
// let { viewport } = group
// gl.enable(gl.SCISSOR_TEST);
// gl.scissor(viewport.x, viewport.y, viewport.width, viewport.height);
// gl.clearColor(0, 0, 0, .5);
// gl.clear(gl.COLOR_BUFFER_BIT);
if (typeof els === 'number') {
id = els;
group = groups[els];
els = null;
}
if (!(group && group.count && group.opacity)) return; // draw circles
if (group.activation[0]) {
// TODO: optimize this performance by making groups and regl.this props
this.drawCircle(this.getMarkerDrawOptions(0, group, els));
} // draw all other available markers
var batch = [];
for (var i = 1; i < group.activation.length; i++) {
if (!group.activation[i] || group.activation[i] !== true && !group.activation[i].data.length) continue;
batch.push.apply(batch, _toConsumableArray(this.getMarkerDrawOptions(i, group, els)));
}
if (batch.length) {
this.drawMarker(batch);
}
}; // get options for the marker ids
Scatter.prototype.getMarkerDrawOptions = function (markerId, group, elements) {
var range = group.range,
tree = group.tree,
viewport = group.viewport,
activation = group.activation,
selectionBuffer = group.selectionBuffer,
count = group.count;
var regl = this.regl; // direct points
if (!tree) {
// if elements array - draw unclustered points
if (elements) {
return [extend({}, group, {
markerTexture: this.markerTextures[markerId],
activation: activation[markerId],
count: elements.length,
elements: elements,
offset: 0
})];
}
return [extend({}, group, {
markerTexture: this.markerTextures[markerId],
activation: activation[markerId],
offset: 0
})];
} // clustered points
var batch = [];
var lod = tree.range(range, {
lod: true,
px: [(range[2] - range[0]) / viewport.width, (range[3] - range[1]) / viewport.height]
}); // enable elements by using selection buffer
if (elements) {
var markerActivation = activation[markerId];
var mask = markerActivation.data;
var data = new Uint8Array(count);
for (var i = 0; i < elements.length; i++) {
var id = elements[i];
data[id] = mask ? mask[id] : 1;
}
selectionBuffer.subdata(data);
}
for (var l = lod.length; l--;) {
var _lod$l = _slicedToArray(lod[l], 2),
from = _lod$l[0],
to = _lod$l[1];
batch.push(extend({}, group, {
markerTexture: this.markerTextures[markerId],
activation: elements ? selectionBuffer : activation[markerId],
offset: from,
count: to - from
}));
}
return batch;
}; // update groups options
Scatter.prototype.update = function () {
var _this3 = this;
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
if (!args.length) return; // passes are as single array
if (args.length === 1 && Array.isArray(args[0])) args = args[0];
var groups = this.groups,
gl = this.gl,
regl = this.regl,
maxSize = this.maxSize,
maxColors = this.maxColors,
palette = this.palette;
this.groups = groups = args.map(function (options, i) {
var group = groups[i];
if (options === undefined) return group;
if (options === null) options = {
positions: null
};else if (typeof options === 'function') options = {
ondraw: options
};else if (typeof options[0] === 'number') options = {
positions: options // copy options to avoid mutation & handle aliases
};
options = pick(options, {
positions: 'positions data points',
snap: 'snap cluster lod tree',
size: 'sizes size radius',
borderSize: 'borderSizes borderSize border-size bordersize borderWidth borderWidths border-width borderwidth stroke-width strokeWidth strokewidth outline',
color: 'colors color fill fill-color fillColor',
borderColor: 'borderColors borderColor stroke stroke-color strokeColor',
marker: 'markers marker shape',
range: 'range dataBox databox',
viewport: 'viewport viewPort viewBox viewbox',
opacity: 'opacity alpha transparency',
bounds: 'bound bounds boundaries limits',
tooManyColors: 'tooManyColors palette paletteMode optimizePalette enablePalette'
});
if (options.positions === null) options.positions = [];
if (options.tooManyColors != null) _this3.tooManyColors = options.tooManyColors;
if (!group) {
groups[i] = group = {
id: i,
scale: null,
translate: null,
scaleFract: null,
translateFract: null,
// buffers for active markers
activation: [],
// buffer for filtered markers
selectionBuffer: regl.buffer({
data: new Uint8Array(0),
usage: 'stream',
type: 'uint8'
}),
// buffers with data: it is faster to switch them per-pass
// than provide one congregate buffer
sizeBuffer: regl.buffer({
data: new Uint8Array(0),
usage: 'dynamic',
type: 'uint8'
}),
colorBuffer: regl.buffer({
data: new Uint8Array(0),
usage: 'dynamic',
type: 'uint8'
}),
positionBuffer: regl.buffer({
data: new Uint8Array(0),
usage: 'dynamic',
type: 'float'
}),
positionFractBuffer: regl.buffer({
data: new Uint8Array(0),
usage: 'dynamic',
type: 'float'
})
};
options = extend({}, Scatter.defaults, options);
} // force update triggers
if (options.positions && !('marker' in options)) {
options.marker = group.marker;
delete group.marker;
} // updating markers cause recalculating snapping
if (options.marker && !('positions' in options)) {
options.positions = group.positions;
delete group.positions;
} // global count of points
var hasSize = 0,
hasColor = 0;
updateDiff(group, options, [{
snap: true,
size: function size(s, group) {
if (s == null) s = Scatter.defaults.size;
hasSize += s && s.length ? 1 : 0;
return s;
},
borderSize: function borderSize(s, group) {
if (s == null) s = Scatter.defaults.borderSize;
hasSize += s && s.length ? 1 : 0;
return s;
},
opacity: parseFloat,
// add colors to palette, save references
color: function color(c, group) {
if (c == null) c = Scatter.defaults.color;
c = _this3.updateColor(c);
hasColor++;
return c;
},
borderColor: function borderColor(c, group) {
if (c == null) c = Scatter.defaults.borderColor;
c = _this3.updateColor(c);
hasColor++;
return c;
},
bounds: function bounds(_bounds, group, options) {
if (!('range' in options)) options.range = null;
return _bounds;
},
positions: function positions(_positions, group, options) {
var snap = group.snap;
var positionBuffer = group.positionBuffer,
positionFractBuffer = group.positionFractBuffer,
selectionBuffer = group.selectionBuffer; // separate buffers for x/y coordinates
if (_positions.x || _positions.y) {
if (_positions.x.length) {
group.xAttr = {
buffer: regl.buffer(_positions.x),
offset: 0,
stride: 4,
count: _positions.x.length
};
} else {
group.xAttr = {
buffer: _positions.x.buffer,
offset: _positions.x.offset * 4 || 0,
stride: (_positions.x.stride || 1) * 4,
count: _positions.x.count
};
}
if (_positions.y.length) {
group.yAttr = {
buffer: regl.buffer(_positions.y),
offset: 0,
stride: 4,
count: _positions.y.length
};
} else {
group.yAttr = {
buffer: _positions.y.buffer,
offset: _positions.y.offset * 4 || 0,
stride: (_positions.y.stride || 1) * 4,
count: _positions.y.count
};
}
group.count = Math.max(group.xAttr.count, group.yAttr.count);
return _positions;
}
_positions = flatten(_positions, 'float64');
var count = group.count = Math.floor(_positions.length / 2);
var bounds = group.bounds = count ? getBounds(_positions, 2) : null; // if range is not provided updated - recalc it
if (!options.range && !group.range) {
delete group.range;
options.range = bounds;
} // reset marker
if (!options.marker && !group.marker) {
delete group.marker;
options.marker = null;
} // build cluster tree if required
if (snap && (snap === true || count > snap)) {
group.tree = cluster(_positions, {
bounds: bounds
});
} // existing tree instance
else if (snap && snap.length) {
group.tree = snap;
}
if (group.tree) {
var opts = {
primitive: 'points',
usage: 'static',
data: group.tree,
type: 'uint32'
};
if (group.elements) group.elements(opts);else group.elements = regl.elements(opts);
} // update position buffers
positionBuffer({
data: f32.float(_positions),
usage: 'dynamic'
});
positionFractBuffer({
data: f32.fract(_positions),
usage: 'dynamic'
}); // expand selectionBuffer
selectionBuffer({
data: new Uint8Array(count),
type: 'uint8',
usage: 'stream'
});
return _positions;
}
}, {
// create marker ids corresponding to known marker textures
marker: function marker(markers, group, options) {
var activation = group.activation; // reset marker elements
activation.forEach(function (buffer) {
return buffer && buffer.destroy && buffer.destroy();
});
activation.length = 0; // single sdf marker
if (!markers || typeof markers[0] === 'number') {
var id = _this3.addMarker(markers);
activation[id] = true;
} // per-point markers use mask buffers to enable markers in vert shader
else {
var markerMasks = [];
for (var _i = 0, l = Math.min(markers.length, group.count); _i < l; _i++) {
var _id = _this3.addMarker(markers[_i]);
if (!markerMasks[_id]) markerMasks[_id] = new Uint8Array(group.count); // enable marker by default
markerMasks[_id][_i] = 1;
}
for (var _id2 = 0; _id2 < markerMasks.length; _id2++) {
if (!markerMasks[_id2]) continue;
var opts = {
data: markerMasks[_id2],
type: 'uint8',
usage: 'static'
};
if (!activation[_id2]) {
activation[_id2] = regl.buffer(opts);
} else {
activation[_id2](opts);
}
activation[_id2].data = markerMasks[_id2];
}
}
return markers;
},
range: function range(_range, group, options) {
var bounds = group.bounds; // FIXME: why do we need this?
if (!bounds) return;
if (!_range) _range = bounds;
group.scale = [1 / (_range[2] - _range[0]), 1 / (_range[3] - _range[1])];
group.translate = [-_range[0], -_range[1]];
group.scaleFract = f32.fract(group.scale);
group.translateFract = f32.fract(group.translate);
return _range;
},
viewport: function viewport(vp) {
var rect = parseRect(vp || [gl.drawingBufferWidth, gl.drawingBufferHeight]); // normalize viewport to the canvas coordinates
// rect.y = gl.drawingBufferHeight - rect.height - rect.y
return rect;
}
}]); // update size buffer, if needed
if (hasSize) {
var _group = group,
count = _group.count,
size = _group.size,
borderSize = _group.borderSize,
sizeBuffer = _group.sizeBuffer;
var sizes = new Uint8Array(count * 2);
if (size.length || borderSize.length) {
for (var _i2 = 0; _i2 < count; _i2++) {
// we downscale size to allow for fractions
sizes[_i2 * 2] = Math.round((size[_i2] == null ? size : size[_i2]) * 255 / maxSize);
sizes[_i2 * 2 + 1] = Math.round((borderSize[_i2] == null ? borderSize : borderSize[_i2]) * 255 / maxSize);
}
}
sizeBuffer({
data: sizes,
usage: 'dynamic'
});
} // update color buffer if needed
if (hasColor) {
var _group2 = group,
_count = _group2.count,
color = _group2.color,
borderColor = _group2.borderColor,
colorBuffer = _group2.colorBuffer;
var colors; // if too many colors - put colors to buffer directly
if (_this3.tooManyColors) {
if (color.length || borderColor.length) {
colors = new Uint8Array(_count * 8);
for (var _i3 = 0; _i3 < _count; _i3++) {
var _colorId = color[_i3];
colors[_i3 * 8] = palette[_colorId * 4];
colors[_i3 * 8 + 1] = palette[_colorId * 4 + 1];
colors[_i3 * 8 + 2] = palette[_colorId * 4 + 2];
colors[_i3 * 8 + 3] = palette[_colorId * 4 + 3];
var borderColorId = borderColor[_i3];
colors[_i3 * 8 + 4] = palette[borderColorId * 4];
colors[_i3 * 8 + 5] = palette[borderColorId * 4 + 1];
colors[_i3 * 8 + 6] = palette[borderColorId * 4 + 2];
colors[_i3 * 8 + 7] = palette[borderColorId * 4 + 3];
}
}
} // if limited amount of colors - keep palette color picking
// that saves significant memory
else {
if (color.length || borderColor.length) {
// we need slight data increase by 2 due to vec4 borderId in shader
colors = new Uint8Array(_count * 4 + 2);
for (var _i4 = 0; _i4 < _count; _i4++) {
// put color coords in palette texture
if (color[_i4] != null) {
colors[_i4 * 4] = color[_i4] % maxColors;
colors[_i4 * 4 + 1] = Math.floor(color[_i4] / maxColors);
}
if (borderColor[_i4] != null) {
colors[_i4 * 4 + 2] = borderColor[_i4] % maxColors;
colors[_i4 * 4 + 3] = Math.floor(borderColor[_i4] / maxColors);
}
}
}
}
colorBuffer({
data: colors || new Uint8Array(0),
type: 'uint8',
usage: 'dynamic'
});
}
return group;
});
}; // get (and create) marker texture id
Scatter.prototype.addMarker = function (sdf) {
var markerTextures = this.markerTextures,
regl = this.regl,
markerCache = this.markerCache;
var pos = sdf == null ? 0 : markerCache.indexOf(sdf);
if (pos >= 0) return pos; // convert sdf to 0..255 range
var distArr;
if (sdf instanceof Uint8Array || sdf instanceof Uint8ClampedArray) {
distArr = sdf;
} else {
distArr = new Uint8Array(sdf.length);
for (var i = 0, l = sdf.length; i < l; i++) {
distArr[i] = sdf[i] * 255;
}
}
var radius = Math.floor(Math.sqrt(distArr.length));
pos = markerTextures.length;
markerCache.push(sdf);
markerTextures.push(regl.texture({
channels: 1,
data: distArr,
radius: radius,
mag: 'linear',
min: 'linear'
}));
return pos;
}; // register color to palette, return it's index or list of indexes
Scatter.prototype.updateColor = function (colors) {
var paletteIds = this.paletteIds,
palette = this.palette,
maxColors = this.maxColors;
if (!Array.isArray(colors)) {
colors = [colors];
}
var idx = []; // if color groups - flatten them
if (typeof colors[0] === 'number') {
var grouped = [];
if (Array.isArray(colors)) {
for (var i = 0; i < colors.length; i += 4) {
grouped.push(colors.slice(i, i + 4));
}
} else {
for (var _i5 = 0; _i5 < colors.length; _i5 += 4) {
grouped.push(colors.subarray(_i5, _i5 + 4));
}
}
colors = grouped;
}
for (var _i6 = 0; _i6 < colors.length; _i6++) {
var color = colors[_i6];
color = rgba(color, 'uint8');
var id = colorId(color, false); // if new color - save it
if (paletteIds[id] == null) {
var pos = palette.length;
paletteIds[id] = Math.floor(pos / 4);
palette[pos] = color[0];
palette[pos + 1] = color[1];
palette[pos + 2] = color[2];
palette[pos + 3] = color[3];
}
idx[_i6] = paletteIds[id];
} // detect if too many colors in palette
if (!this.tooManyColors && palette.length > maxColors * 4) this.tooManyColors = true; // limit max color
this.updatePalette(palette); // keep static index for single-color property
return idx.length === 1 ? idx[0] : idx;
};
Scatter.prototype.updatePalette = function (palette) {
if (this.tooManyColors) return;
var maxColors = this.maxColors,
paletteTexture = this.paletteTexture;
var requiredHeight = Math.ceil(palette.length * .25 / maxColors); // pad data
if (requiredHeight > 1) {
palette = palette.slice();
for (var i = palette.length * .25 % maxColors; i < requiredHeight * maxColors; i++) {
palette.push(0, 0, 0, 0);
}
} // ensure height
if (paletteTexture.height < requiredHeight) {
paletteTexture.resize(maxColors, requiredHeight);
} // update full data
paletteTexture.subimage({
width: Math.min(palette.length * .25, maxColors),
height: requiredHeight,
data: palette
}, 0, 0);
}; // remove unused stuff
Scatter.prototype.destroy = function () {
this.groups.forEach(function (group) {
group.sizeBuffer.destroy();
group.positionBuffer.destroy();
group.positionFractBuffer.destroy();
group.colorBuffer.destroy();
group.activation.forEach(function (b) {
return b && b.destroy && b.destroy();
});
group.selectionBuffer.destroy();
if (group.elements) group.elements.destroy();
});
this.groups.length = 0;
this.paletteTexture.destroy();
this.markerTextures.forEach(function (txt) {
return txt && txt.destroy && txt.destroy();
});
return this;
};
var extend$1 = require('object-assign');
var reglScatter2d = function reglScatter2d(regl, options) {
var scatter$$1 = new scatter(regl, options);
var render = scatter$$1.render.bind(scatter$$1); // expose API
extend$1(render, {
render: render,
update: scatter$$1.update.bind(scatter$$1),
draw: scatter$$1.draw.bind(scatter$$1),
destroy: scatter$$1.destroy.bind(scatter$$1),
regl: scatter$$1.regl,
gl: scatter$$1.gl,
canvas: scatter$$1.gl.canvas,
groups: scatter$$1.groups,
markers: scatter$$1.markerCache,
palette: scatter$$1.palette
});
return render;
};
module.exports = reglScatter2d;