Created
October 15, 2019 16:50
-
-
Save vinnitu/4c9bba6d8651d8593b1ec22729887a4e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(g,f){typeof exports==='object'&&typeof module!=='undefined'?f(exports,require('d3-selection'),require('@d3fc/d3fc-chart'),require('@d3fc/d3fc-rebind'),require('d3-scale'),require('d3-color'),require('d3-shape'),require('d3-array'),require('@d3fc/d3fc-series')):typeof define==='function'&&define.amd?define(['exports','d3-selection','@d3fc/d3fc-chart','@d3fc/d3fc-rebind','d3-scale','d3-color','d3-shape','d3-array','@d3fc/d3fc-series'],f):(g=g||self,f(g.fcWebgl={},g.d3,g.fc,g.fc,g.d3,g.d3,g.d3,g.d3,g.fc));}(this,function(exports, d3Selection, d3fcChart, d3fcRebind, d3Scale, d3Color, d3Shape, d3Array, d3fcSeries){'use strict';var cartesian = (function (xScale, yScale) { | |
var base = d3fcChart.chartCartesian(xScale, yScale); | |
var chart = function chart(selection) { | |
var result = base(selection); | |
selection.select('d3fc-canvas.plot-area').on('draw', function (d, i, nodes) { | |
var canvas = d3Selection.select(nodes[i]).select('canvas').node(); | |
var series = base.canvasPlotArea(); | |
series.context(canvas.getContext('webgl')).xScale(xScale).yScale(yScale); | |
series(d); | |
}); | |
return result; | |
}; | |
d3fcRebind.rebindAll(chart, base); | |
return chart; | |
});// Initialize a shader program, so WebGL knows how to draw our data | |
var initShaders = (function (gl, vsSource, fsSource) { | |
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); | |
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); // Create the shader program | |
var shaderProgram = gl.createProgram(); | |
gl.attachShader(shaderProgram, vertexShader); | |
gl.attachShader(shaderProgram, fragmentShader); | |
gl.linkProgram(shaderProgram); // If creating the shader program failed, alert | |
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { | |
console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); | |
return null; | |
} | |
return shaderProgram; | |
}); // creates a shader of the given type, uploads the source and compiles it. | |
function loadShader(gl, type, source) { | |
var shader = gl.createShader(type); // Send the source to the shader object | |
gl.shaderSource(shader, source); // Compile the shader program | |
gl.compileShader(shader); // See if it compiled successfully | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); | |
gl.deleteShader(shader); | |
return null; | |
} | |
return shader; | |
}var buffer = (function (gl) { | |
var glBuffer = gl.createBuffer(); | |
var buffer = function buffer(array) { | |
// Select this buffer as the one to apply buffer operations to. | |
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffer); // Only create a copy if it's not already a Float32Array | |
var srcArray = array.constructor === Float32Array ? array : new Float32Array(array); | |
gl.bufferData(gl.ARRAY_BUFFER, srcArray, gl.STATIC_DRAW); | |
return glBuffer; | |
}; | |
buffer.addr = function () { | |
return glBuffer; | |
}; | |
return buffer; | |
});var baseShader = (function (gl, vsSource, fsSource) { | |
var numComponents = 2; | |
var positionBuffer = buffer(gl); | |
var lastColor = [-1, -1, -1, -1]; | |
var shaderProgram = initShaders(gl, vsSource, fsSource); | |
var vertexLocation = gl.getAttribLocation(shaderProgram, 'aVertexPosition'); | |
var offsetLocation = gl.getUniformLocation(shaderProgram, 'uOffset'); | |
var scaleLocation = gl.getUniformLocation(shaderProgram, 'uScale'); | |
var seriesColorLocation = gl.getUniformLocation(shaderProgram, 'uSeriesColor'); | |
var draw = function draw(positions, color) { | |
var mode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : gl.TRIANGLES; | |
var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; | |
var count = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1; | |
var fColor = color || [0.0, 0.0, 0.0, 0.0]; | |
if (fColor.some(function (c, i) { | |
return c !== lastColor[i]; | |
})) { | |
setColor(fColor); | |
} | |
positionBuffer(positions); | |
var vertexCount = count !== -1 ? count : positions.length / numComponents - offset; | |
gl.drawArrays(mode, offset, vertexCount); | |
}; | |
draw.activate = function () { | |
setupProgram(); | |
lastColor = [-1, -1, -1, -1]; | |
}; | |
draw.setModelView = function (_ref) { | |
var offset = _ref.offset, | |
scale = _ref.scale; | |
gl.uniform2fv(offsetLocation, offset); | |
gl.uniform2fv(scaleLocation, scale); | |
}; | |
draw.shaderProgram = function () { | |
return shaderProgram; | |
}; | |
draw.numComponents = function () { | |
if (!arguments.length) { | |
return numComponents; | |
} | |
numComponents = arguments.length <= 0 ? undefined : arguments[0]; | |
return draw; | |
}; | |
function setupProgram() { | |
// Tell WebGL to use our program when drawing | |
gl.useProgram(shaderProgram); // Tell WebGL how to pull out the positions from the position | |
// buffer into the vertexPosition attribute. | |
{ | |
var type = gl.FLOAT; // the data in the buffer is 32bit floats | |
var normalize = false; // don't normalize | |
var stride = 0; // how many bytes to get from one set of values to the next | |
// 0 = use type and numComponents above | |
var offset = 0; // how many bytes inside the buffer to start from | |
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer.addr()); | |
gl.vertexAttribPointer(vertexLocation, numComponents, type, normalize, stride, offset); | |
gl.enableVertexAttribArray(vertexLocation); | |
} | |
} | |
function setColor(color) { | |
gl.uniform4fv(seriesColorLocation, color); | |
} | |
return draw; | |
});// Vertex shader program | |
var vsSource = "\n attribute vec4 aVertexPosition;\n\n uniform vec2 uOffset;\n uniform vec2 uScale;\n\n void main() {\n vec2 vertex = vec2(aVertexPosition[0], aVertexPosition[1]);\n vec2 clipSpace = 2.0 * (vertex - uOffset) / uScale - 1.0;\n gl_Position = vec4(clipSpace, 0.0, 1.0);\n }\n"; | |
var fsSource = "\n precision mediump float;\n uniform vec4 uSeriesColor;\n\n void main() {\n gl_FragColor = uSeriesColor;\n }\n"; // Available modes: | |
// gl.TRIANGLES | |
// gl.TRIANGLE_STRIP | |
// gl.TRIANGLE_FAN | |
// gl.LINES | |
// gl.LINES_STRIP | |
var raw = (function (gl) { | |
var base = baseShader(gl, vsSource, fsSource); | |
var draw = function draw(positions, color) { | |
var mode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : gl.TRIANGLES; | |
var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; | |
var count = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1; | |
base(positions, color, mode, offset, count); | |
}; | |
d3fcRebind.rebindAll(draw, base); | |
return draw; | |
});var pointsBase = (function (gl, vsSource, fsSource) { | |
var base = baseShader(gl, vsSource, fsSource).numComponents(3); | |
var lastWidth = -1; | |
var lastStrokeColor = [-1, -1, -1, -1]; | |
var edgeColorLocation = gl.getUniformLocation(base.shaderProgram(), 'uEdgeColor'); | |
var lineWidthLocation = gl.getUniformLocation(base.shaderProgram(), 'uLineWidth'); | |
var draw = function draw(positions, color) { | |
var lineWidth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | |
var strokeColor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; | |
var fColor = color || [0.0, 0.0, 0.0, 0.0]; | |
var sColor = strokeColor || fColor; | |
if (lineWidth !== lastWidth || sColor.some(function (c, i) { | |
return c !== lastStrokeColor[i]; | |
})) { | |
setColor(lineWidth, sColor); | |
lastWidth = lineWidth; | |
lastStrokeColor = sColor; | |
} | |
base(positions, color, gl.POINTS); | |
}; | |
draw.activate = function () { | |
base.activate(); | |
lastStrokeColor = [-1, -1, -1, -1]; | |
}; | |
function setColor(lineWidth, strokeColor) { | |
gl.uniform4fv(edgeColorLocation, strokeColor); | |
gl.uniform1f(lineWidthLocation, lineWidth); | |
} | |
d3fcRebind.rebindAll(draw, base, d3fcRebind.exclude('activate')); | |
return draw; | |
});// Vertex shader program | |
var vsSource$1 = "\nprecision lowp float;\nattribute vec4 aVertexPosition;\n\nuniform vec2 uOffset;\nuniform vec2 uScale;\nuniform float uLineWidth;\n\nvarying float vSize;\n\nvoid main() {\n vec2 vertex = vec2(aVertexPosition[0], aVertexPosition[1]);\n vec2 clipSpace = 2.0 * (vertex - uOffset) / uScale - 1.0;\n\n vSize = sqrt(aVertexPosition[2]) + uLineWidth / 2.0;\n gl_PointSize = vSize + 1.0;\n gl_Position = vec4(clipSpace, 0.0, 1.0);\n}"; | |
var fsSource$1 = "\nprecision lowp float;\n\nuniform float uLineWidth;\nuniform vec4 uEdgeColor;\nuniform vec4 uSeriesColor;\n\nvarying float vSize;\n\nvoid main() {\n float dist = length(2.0 * gl_PointCoord - 1.0) * vSize;\n float inner = vSize - 2.0 * uLineWidth - 1.0;\n\n if (dist > vSize + 1.0) {\n discard;\n } else if (uEdgeColor[3] < 0.1) {\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n } else if (dist < inner) {\n gl_FragColor = uSeriesColor;\n } else {\n float rAlias = clamp((dist - vSize) / 2.0 + 0.5, 0.0, 1.0);\n vec4 transparent = vec4(0.0);\n vec4 edgeColor = rAlias * transparent + (1.0 - rAlias) * uEdgeColor;\n\n float rEdge = clamp(dist - inner, 0.0, 1.0);\n gl_FragColor = rEdge * edgeColor + (1.0 - rEdge) * uSeriesColor;\n }\n}"; | |
var circles = (function (gl) { | |
var base = pointsBase(gl, vsSource$1, fsSource$1); | |
var draw = function draw(positions, color) { | |
var lineWidth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | |
var strokeColor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; | |
base(positions, color, lineWidth, strokeColor); | |
}; | |
d3fcRebind.rebindAll(draw, base); | |
return draw; | |
});// Vertex shader program | |
var vsSource$2 = "\nprecision lowp float;\nattribute vec4 aVertexPosition;\n\nuniform vec2 uOffset;\nuniform vec2 uScale;\nuniform float uLineWidth;\n\nvarying float vSize;\n\nvoid main() {\n vec2 vertex = vec2(aVertexPosition[0], aVertexPosition[1]);\n vec2 clipSpace = 2.0 * (vertex - uOffset) / uScale - 1.0;\n\n vSize = sqrt(aVertexPosition[2]) * 2.0 + uLineWidth / 2.0;\n gl_PointSize = vSize + 1.0;\n gl_Position = vec4(clipSpace, 0.0, 1.0);\n}"; | |
var fsSource$2 = "\nprecision lowp float;\n\nuniform float uLineWidth;\nuniform vec4 uEdgeColor;\nuniform vec4 uSeriesColor;\nuniform sampler2D uSampler;\n\nvarying float vSize;\n\nbool edge(vec2 coord) {\n float w = uLineWidth / vSize;\n vec4 tex1 = texture2D(uSampler, coord + vec2(0, w), -0.5);\n vec4 tex2 = texture2D(uSampler, coord + vec2(0, -w), -0.5);\n vec4 tex3 = texture2D(uSampler, coord + vec2(w, 0), -0.5);\n vec4 tex4 = texture2D(uSampler, coord + vec2(-w, 0), -0.5);\n\n return (tex1[3] + tex2[3] + tex3[3] + tex4[3]) < 3.8;\n}\n\nvoid main() {\n vec4 edgeTex = texture2D(uSampler, gl_PointCoord, -0.5);\n\n if (uEdgeColor[3] < 0.1) {\n gl_FragColor = edgeTex;\n } else if (uLineWidth < 0.1) {\n gl_FragColor = uSeriesColor * edgeTex[3];\n } else {\n if (edge(gl_PointCoord)) {\n gl_FragColor = uEdgeColor * edgeTex[3];\n } else if (uSeriesColor[3] < 0.1) {\n gl_FragColor = edgeTex;\n } else {\n gl_FragColor = uSeriesColor;\n }\n }\n}"; | |
var pointTextures = (function (gl) { | |
var base = pointsBase(gl, vsSource$2, fsSource$2); | |
var texture = gl.createTexture(); | |
var samplerLocation = gl.getUniformLocation(base.shaderProgram(), 'uSampler'); | |
var draw = function draw(positions, image, color) { | |
var lineWidth = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; | |
var strokeColor = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; | |
setupTexture(image); | |
base(positions, color, lineWidth, strokeColor); | |
}; | |
function setupTexture(image) { | |
// Tell WebGL we want to affect texture unit 0 | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); // WebGL1 has different requirements for power of 2 images | |
// vs non power of 2 images so check if the image is a | |
// power of 2 in both dimensions. | |
if (isPowerOf2(image.width) && isPowerOf2(image.height)) { | |
// Yes, it's a power of 2. Generate mips. | |
gl.generateMipmap(gl.TEXTURE_2D); | |
} else { | |
// No, it's not a power of 2. Turn off mips and set | |
// wrapping to clamp to edge | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); | |
} // Tell the shader we bound the texture to texture unit 0 | |
gl.uniform1i(samplerLocation, 0); | |
} | |
d3fcRebind.rebindAll(draw, base); | |
return draw; | |
}); | |
function isPowerOf2(value) { | |
return (value & value - 1) === 0; | |
}var drawFunctions = { | |
raw: raw, | |
circles: circles, | |
pointTextures: pointTextures | |
}; | |
var PRIVATE = '__d3fcAPI'; | |
var helper = (function (gl) { | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
if (gl[PRIVATE]) return gl[PRIVATE]; | |
gl.enable(gl.BLEND); | |
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); | |
var drawModules = {}; | |
var modelView = null; // Helper API functions | |
var api = {}; | |
var activated; | |
Object.keys(drawFunctions).forEach(function (key) { | |
api[key] = function () { | |
if (!drawModules[key]) { | |
// Lazy-load the shaders when used | |
drawModules[key] = drawFunctions[key](gl); | |
} // Activate the shader if not already activate | |
if (activated !== key) drawModules[key].activate(); | |
activated = key; | |
drawModules[key].setModelView(modelView); | |
return drawModules[key].apply(drawModules, arguments); | |
}; | |
}); | |
api.applyScales = function (xScale, yScale) { | |
var x = convertScale(xScale, gl.canvas.width, false); | |
var y = convertScale(yScale, gl.canvas.height, true); | |
modelView = { | |
offset: [x.offset, y.offset], | |
scale: [x.scaleFactor, y.scaleFactor] | |
}; | |
return { | |
pixel: { | |
x: x.pixelSize, | |
y: y.pixelSize | |
}, | |
xScale: x.scale, | |
yScale: y.scale | |
}; | |
}; | |
var isLinear = function isLinear(scale) { | |
if (scale.domain && scale.range && scale.clamp && !scale.exponent && !scale.base) { | |
return !scale.clamp(); | |
} | |
return false; | |
}; | |
var convertScale = function convertScale(scale, screenSize) { | |
var invert = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | |
var range = scale.range(); | |
var domain = scale.domain(); | |
var invertConst = invert ? -1 : 1; // screen: (0 -> screenSize), scale: (range[0] -> range[1]) | |
if (isLinear(scale)) { | |
var asDate = domain[0] instanceof Date; | |
var numDomain = asDate ? domain.map(function (d) { | |
return d.valueOf(); | |
}) : domain; | |
var scaleFn = asDate ? function (d) { | |
return d.valueOf(); | |
} : function (d) { | |
return d; | |
}; // Calculate the screen-space domain for the projection | |
var domainSize = (numDomain[1] - numDomain[0]) * screenSize / (range[1] - range[0]); // numDomain[0] = screenDomainStart + range[0] * domainSize / screenSize; | |
var screenDomainStart = numDomain[0] - domainSize * range[0] / screenSize; | |
var screenDomain = [screenDomainStart, screenDomainStart + domainSize]; | |
return { | |
pixelSize: Math.abs((screenDomain[1] - screenDomain[0]) / screenSize), | |
offset: screenDomain[invert ? 1 : 0], | |
scaleFactor: invertConst * (screenDomain[1] - screenDomain[0]), | |
scale: scaleFn | |
}; | |
} else { | |
var screenRange = range.map(function (r) { | |
return 2 * r / screenSize - 1; | |
}); | |
var factor = (screenRange[1] - screenRange[0]) / (range[1] - range[0]); | |
var _scaleFn = function _scaleFn(d) { | |
return (scale(d) - range[0]) * factor + screenRange[0]; | |
}; | |
return { | |
pixelSize: Math.abs(2 / screenSize), | |
offset: invert ? 1 : -1, | |
scaleFactor: invertConst * 2, | |
scale: _scaleFn | |
}; | |
} | |
}; | |
gl[PRIVATE] = api; | |
return api; | |
});var functor = (function (d) { | |
return typeof d === 'function' ? d : function () { | |
return d; | |
}; | |
});// Checks that passes properties are 'defined', meaning that calling them with (d, i) returns non null values | |
function defined() { | |
var outerArguments = arguments; | |
return function (d, i) { | |
for (var c = 0, j = outerArguments.length; c < j; c++) { | |
if (outerArguments[c](d, i) == null) { | |
return false; | |
} | |
} | |
return true; | |
}; | |
}// determines the offset required along the cross scale based | |
// on the series alignment | |
var alignOffset = (function (align, width) { | |
switch (align) { | |
case 'left': | |
return width / 2; | |
case 'right': | |
return -width / 2; | |
default: | |
return 0; | |
} | |
});var createBase = (function (initialValues) { | |
var env = Object.assign({}, initialValues); | |
var base = function base() {}; | |
Object.keys(env).forEach(function (key) { | |
base[key] = function () { | |
if (!arguments.length) { | |
return env[key]; | |
} | |
env[key] = arguments.length <= 0 ? undefined : arguments[0]; | |
return base; | |
}; | |
}); | |
return base; | |
});var xyBase = (function () { | |
var baseValue = function baseValue() { | |
return 0; | |
}; | |
var crossValue = function crossValue(d) { | |
return d.x; | |
}; | |
var mainValue = function mainValue(d) { | |
return d.y; | |
}; | |
var align = 'center'; | |
var bandwidth = function bandwidth() { | |
return 5; | |
}; | |
var orient = 'vertical'; | |
var base = createBase({ | |
decorate: function decorate() {}, | |
defined: function defined$1(d, i) { | |
return defined(baseValue, crossValue, mainValue)(d, i); | |
}, | |
xScale: d3Scale.scaleIdentity(), | |
yScale: d3Scale.scaleIdentity() | |
}); | |
base.values = function (d, i) { | |
var width = bandwidth(d, i); | |
var offset = alignOffset(align, width); | |
var xScale = base.xScale(); | |
var yScale = base.yScale(); | |
if (orient === 'vertical') { | |
var y = yScale(mainValue(d, i), i); | |
var y0 = yScale(baseValue(d, i), i); | |
var x = xScale(crossValue(d, i), i) + offset; | |
return { | |
d: d, | |
x: x, | |
y: y, | |
y0: y0, | |
width: width, | |
height: y - y0, | |
origin: [x, y], | |
baseOrigin: [x, y0], | |
transposedX: x, | |
transposedY: y | |
}; | |
} else { | |
var _y = xScale(mainValue(d, i), i); | |
var _y2 = xScale(baseValue(d, i), i); | |
var _x = yScale(crossValue(d, i), i) + offset; | |
return { | |
d: d, | |
x: _x, | |
y: _y, | |
y0: _y2, | |
width: width, | |
height: _y - _y2, | |
origin: [_y, _x], | |
baseOrigin: [_y2, _x], | |
transposedX: _y, | |
transposedY: _x | |
}; | |
} | |
}; | |
base.baseValue = function () { | |
if (!arguments.length) { | |
return baseValue; | |
} | |
baseValue = functor(arguments.length <= 0 ? undefined : arguments[0]); | |
return base; | |
}; | |
base.crossValue = function () { | |
if (!arguments.length) { | |
return crossValue; | |
} | |
crossValue = functor(arguments.length <= 0 ? undefined : arguments[0]); | |
return base; | |
}; | |
base.mainValue = function () { | |
if (!arguments.length) { | |
return mainValue; | |
} | |
mainValue = functor(arguments.length <= 0 ? undefined : arguments[0]); | |
return base; | |
}; | |
base.bandwidth = function () { | |
if (!arguments.length) { | |
return bandwidth; | |
} | |
bandwidth = functor(arguments.length <= 0 ? undefined : arguments[0]); | |
return base; | |
}; | |
base.align = function () { | |
if (!arguments.length) { | |
return align; | |
} | |
align = arguments.length <= 0 ? undefined : arguments[0]; | |
return base; | |
}; | |
base.orient = function () { | |
if (!arguments.length) { | |
return orient; | |
} | |
orient = arguments.length <= 0 ? undefined : arguments[0]; | |
return base; | |
}; | |
return base; | |
});var glBase = (function () { | |
var base = xyBase(); | |
var context = null; | |
var cacheEnabled = false; | |
var glAPI = null; | |
var cached = null; | |
var glBase = function glBase(data, helperAPI) { | |
glAPI = helperAPI || helper(context); | |
}; | |
glBase.context = function () { | |
if (!arguments.length) { | |
return context; | |
} | |
context = arguments.length <= 0 ? undefined : arguments[0]; | |
return glBase; | |
}; | |
glBase.cacheEnabled = function () { | |
if (!arguments.length) { | |
return cacheEnabled; | |
} | |
cacheEnabled = arguments.length <= 0 ? undefined : arguments[0]; | |
cached = null; | |
return glBase; | |
}; | |
glBase.cached = function () { | |
if (!arguments.length) { | |
return cached; | |
} | |
cached = cacheEnabled ? arguments.length <= 0 ? undefined : arguments[0] : null; | |
return glBase; | |
}; | |
glBase.glAPI = function () { | |
return glAPI; | |
}; | |
d3fcRebind.rebindAll(glBase, base); | |
return glBase; | |
});var red = '#c60'; | |
var green = '#6c0'; | |
var black = '#000'; | |
var gray = '#ddd'; | |
var darkGray = '#999'; | |
var colors = { | |
red: red, | |
green: green, | |
black: black, | |
gray: gray, | |
darkGray: darkGray | |
};var toGl = function toGl(v) { | |
return v / 255; | |
}; | |
var glColor = (function (value) { | |
if (!value) return null; | |
var c = d3Color.color(value); | |
return [toGl(c.r), toGl(c.g), toGl(c.b), Math.sqrt(c.opacity)]; | |
});var bar = (function () { | |
var base = glBase(); | |
var bar = function bar(data, helperAPI) { | |
base(data, helperAPI); | |
var context = base.context(); | |
var glAPI = base.glAPI(); | |
var scales = glAPI.applyScales(base.xScale(), base.yScale()); | |
context.fillStyle = colors.darkGray; | |
context.strokeStyle = 'transparent'; | |
base.decorate()(context, data, 0); | |
var fillColor = glColor(context.fillStyle); | |
var withLines = context.strokeStyle !== 'transparent'; | |
var filteredData = data.filter(base.defined()); | |
var projected = getProjectedData(filteredData, withLines, scales); | |
glAPI.raw(projected.triangles, fillColor, context.TRIANGLES); | |
if (projected.lines) { | |
var strokeColor = withLines ? glColor(context.strokeStyle) : null; | |
glAPI.raw(projected.lines, strokeColor, context.LINES); | |
} | |
}; | |
var getProjectedData = function getProjectedData(data, withLines, scales) { | |
var pixel = scales.pixel; | |
var cachedProjected = base.cached(); | |
if (cachedProjected && cachedProjected.pixel.x === pixel.x && (!withLines || cachedProjected.lines)) { | |
return cachedProjected; | |
} | |
var crossFn = base.crossValue(); | |
var mainFn = base.mainValue(); | |
var baseFn = base.baseValue(); | |
var vertical = base.orient() === 'vertical'; // 2 triangles per bar, with 3 x/y vertices per triangle | |
var triangles = new Float32Array(data.length * 12); | |
var triangleIndex = 0; | |
var insertTriangle = function insertTriangle(x1, y1, x2, y2, x3, y3) { | |
triangles[triangleIndex++] = x1; | |
triangles[triangleIndex++] = y1; | |
triangles[triangleIndex++] = x2; | |
triangles[triangleIndex++] = y2; | |
triangles[triangleIndex++] = x3; | |
triangles[triangleIndex++] = y3; | |
}; // 3 lines per bar, with 2 x/y vertices per line | |
var lines = withLines ? new Float32Array(data.length * 12) : null; | |
var lineIndex = 0; | |
var insertLine = function insertLine(x1, y1, x2, y2) { | |
lines[lineIndex++] = x1; | |
lines[lineIndex++] = y1; | |
lines[lineIndex++] = x2; | |
lines[lineIndex++] = y2; | |
}; | |
var insertBar = function insertBar(x1, y1, x2, y2, x3, y3, x4, y4) { | |
insertTriangle(x1, y1, x2, y2, x3, y3); | |
insertTriangle(x3, y3, x4, y4, x1, y1); | |
if (withLines) { | |
insertLine(x1, y1, x2, y2); | |
insertLine(x2, y2, x3, y3); | |
insertLine(x3, y3, x4, y4); | |
} | |
}; | |
data.forEach(function (d, i) { | |
var width = bar.bandwidth()(d, i); | |
var offset = alignOffset(bar.align(), width) - width / 2; | |
if (vertical) { | |
var y = scales.yScale(mainFn(d, i), i); | |
var y0 = scales.yScale(baseFn(d, i), i); | |
var xl = scales.xScale(crossFn(d, i), i) + offset * pixel.x; | |
var xr = xl + width * pixel.x; | |
insertBar(xl, y0, xl, y, xr, y, xr, y0); | |
} else { | |
var x = scales.xScale(mainFn(d, i), i); | |
var x0 = scales.xScale(baseFn(d, i), i); | |
var yu = scales.yScale(crossFn(d, i), i) + offset * pixel.y; | |
var yd = yu + width * pixel.y; | |
insertBar(x0, yu, x, yu, x, yd, x0, yd); | |
} | |
}); | |
var projectedData = { | |
triangles: triangles, | |
lines: lines, | |
pixel: pixel | |
}; | |
base.cached(projectedData); | |
return projectedData; | |
}; | |
d3fcRebind.rebindAll(bar, base, d3fcRebind.exclude('glAPI', 'cached')); | |
return bar; | |
});var line = (function () { | |
var base = glBase(); | |
var line = function line(data, helperAPI) { | |
base(data, helperAPI); | |
var context = base.context(); | |
var glAPI = base.glAPI(); | |
var scales = glAPI.applyScales(base.xScale(), base.yScale()); | |
context.strokeStyle = colors.black; | |
context.lineWidth = 1; | |
base.decorate()(context, data, 0); // Get triangle strip representing the projected data | |
var lineWidth = parseInt(context.lineWidth); | |
var strokeColor = glColor(context.strokeStyle); | |
var projected = data.constructor === Float32Array ? rawFloat32Data(data) : getProjectedData(data, scales); | |
if (lineWidth < 1.1) { | |
// Draw straight to WebGL as line strips | |
projected.batches.forEach(function (batch) { | |
glAPI.raw(projected.points, strokeColor, context.LINE_STRIP, batch.offset, batch.count); | |
}); | |
} else { | |
// Convert to a triangle strip | |
projected.batches.forEach(function (batch) { | |
var projectedTriangles = getProjectedTriangles(projected.points, batch.offset, batch.count, scales, lineWidth); | |
glAPI.raw(projectedTriangles, strokeColor, context.TRIANGLE_STRIP); | |
}); | |
} | |
}; | |
var rawFloat32Data = function rawFloat32Data(data) { | |
return { | |
points: data, | |
batches: [{ | |
offset: 0, | |
count: data.length / 2 | |
}] | |
}; | |
}; | |
var getBatches = function getBatches(data) { | |
if (data.constructor === Float32Array) { | |
return [{ | |
offset: 0, | |
count: data.length / 2 | |
}]; | |
} // Check the `defined` function for missing entries, and | |
// break the line into segments for drawing | |
var batches = []; | |
var offset = 0; | |
var pushBatch = function pushBatch(index) { | |
if (index > offset) { | |
batches.push({ | |
offset: offset, | |
count: index - offset | |
}); | |
} | |
offset = index + 1; | |
}; | |
data.forEach(function (d, i) { | |
if (!line.defined()(d, i)) { | |
pushBatch(i); | |
} | |
}); | |
pushBatch(data.length); | |
return batches; | |
}; | |
var getProjectedData = function getProjectedData(data, scales) { | |
var cachedProjected = base.cached(); | |
if (cachedProjected) { | |
return cachedProjected; | |
} | |
var crossFn = base.crossValue(); | |
var mainFn = base.mainValue(); | |
var vertical = base.orient() === 'vertical'; | |
var points = new Float32Array(data.length * 2); | |
var index = 0; | |
if (vertical) { | |
data.forEach(function (d, i) { | |
points[index++] = scales.xScale(crossFn(d, i), i); | |
points[index++] = scales.yScale(mainFn(d, i), i); | |
}); | |
} else { | |
data.forEach(function (d, i) { | |
points[index++] = scales.xScale(mainFn(d, i), i); | |
points[index++] = scales.yScale(crossFn(d, i), i); | |
}); | |
} | |
var batches = getBatches(data); | |
var projected = { | |
points: points, | |
batches: batches | |
}; | |
base.cached(projected); | |
return projected; | |
}; | |
var getProjectedTriangles = function getProjectedTriangles(points, offset, count, scales, lineWidth) { | |
// Two vertices for each data point | |
var pixel = scales.pixel; | |
var result = new Float32Array(count * 4); // Split points based on normals | |
var target = 0; | |
var factor = 0.5 * lineWidth; | |
var start = offset * 2; | |
var end = start + count * 2; | |
for (var index = start; index < end; index += 2) { | |
var normal = getNormal(points, start, end, index, pixel); | |
var normalPixels = [normal[0] * pixel.x * factor, normal[1] * pixel.y * factor]; // Apply to the pair of points | |
result[target++] = points[index] + normalPixels[0]; | |
result[target++] = points[index + 1] + normalPixels[1]; | |
result[target++] = points[index] - normalPixels[0]; | |
result[target++] = points[index + 1] - normalPixels[1]; | |
} | |
return result; | |
}; | |
var normaliseVector = function normaliseVector(vector) { | |
var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]); | |
return [vector[0] / length, vector[1] / length]; | |
}; | |
var lineNormal = function lineNormal(p1, p2) { | |
return normaliseVector([p2[1] - p1[1], -(p2[0] - p1[0])]); | |
}; | |
var getNormal = function getNormal(points, start, end, index, pixel) { | |
var lastPoint = index > start && [points[index - 2] / pixel.x, points[index - 1] / pixel.y]; | |
var thisPoint = [points[index] / pixel.x, points[index + 1] / pixel.y]; | |
var nextPoint = index < end - 2 && [points[index + 2] / pixel.x, points[index + 3] / pixel.y]; | |
if (!lastPoint) { | |
// Beginning of line | |
return lineNormal(thisPoint, nextPoint); | |
} else if (!nextPoint) { | |
// End of line | |
return lineNormal(lastPoint, thisPoint); | |
} // Calculate the miter join | |
var l1 = normaliseVector([thisPoint[0] - lastPoint[0], thisPoint[1] - lastPoint[1]]); | |
var l2 = normaliseVector([nextPoint[0] - thisPoint[0], nextPoint[1] - thisPoint[1]]); | |
var tangent = normaliseVector([l1[0] + l2[0], l1[1] + l2[1]]); | |
var miter = [-tangent[1], tangent[0]]; // Get length using dot product of the miter with one of the normals | |
var normal1 = lineNormal(lastPoint, thisPoint); | |
var length = 1 / (miter[0] * normal1[0] + miter[1] * normal1[1]); | |
return [miter[0] * length, miter[1] * length]; | |
}; | |
d3fcRebind.rebindAll(line, base, d3fcRebind.exclude('glAPI', 'cached', 'baseValue', 'bandwidth', 'align')); | |
return line; | |
});var area = (function () { | |
var base = glBase(); | |
var area = function area(data, helperAPI) { | |
base(data, helperAPI); | |
var context = base.context(); | |
var glAPI = base.glAPI(); | |
var scales = glAPI.applyScales(base.xScale(), base.yScale()); | |
context.fillStyle = colors.gray; | |
context.strokeStyle = 'transparent'; | |
base.decorate()(context, data, 0); | |
var fillColor = glColor(context.fillStyle); | |
var withLines = context.strokeStyle !== 'transparent'; | |
var projected = getProjectedData(data, withLines, scales); | |
projected.batches.area.forEach(function (batch) { | |
glAPI.raw(projected.triangles, fillColor, context.TRIANGLE_STRIP, batch.offset, batch.count); | |
}); | |
if (projected.lines) { | |
var strokeColor = withLines ? glColor(context.strokeStyle) : null; | |
projected.batches.line.forEach(function (batch) { | |
glAPI.raw(projected.lines, strokeColor, context.LINE_STRIP, batch.offset, batch.count); | |
}); | |
} | |
}; | |
var getProjectedData = function getProjectedData(data, withLines, scales) { | |
var cachedProjected = base.cached(); | |
if (cachedProjected && (!withLines || cachedProjected.lines)) { | |
return cachedProjected; | |
} | |
var crossFn = base.crossValue(); | |
var mainFn = base.mainValue(); | |
var baseFn = base.baseValue(); | |
var vertical = base.orient() === 'vertical'; // 2 triangles per data point, but as a triangle-strip, | |
// so only 2 vertices per data point | |
var dataPoints = new Float32Array(data.length * 4); | |
var index = 0; | |
var lines = withLines ? new Float32Array(data.length * 2) : null; | |
var lineIndex = 0; | |
var crossoverCount = 0; | |
var areaBatches = { | |
offset: 0, | |
batches: [] | |
}; | |
var lineBatches = { | |
offset: 0, | |
batches: [] | |
}; // "Batches" keep track of where we have to not-render points because | |
// the are not "defined" | |
var pushBatch = function pushBatch(batchSet, index) { | |
if (index > batchSet.offset) { | |
batchSet.batches.push({ | |
offset: batchSet.offset, | |
count: index - batchSet.offset | |
}); | |
} | |
batchSet.offset = index; | |
}; | |
var pushBatches = function pushBatches() { | |
pushBatch(areaBatches, index / 2 + crossoverCount * 2); | |
pushBatch(lineBatches, lineIndex / 2); | |
}; | |
var lastPositive; | |
data.forEach(function (d, i) { | |
if (area.defined()(d, i)) { | |
var p; | |
if (vertical) { | |
p = { | |
x: scales.xScale(crossFn(d, i), i), | |
y: scales.yScale(mainFn(d, i), i), | |
y0: scales.yScale(baseFn(d, i), i) | |
}; | |
p.x0 = p.x; | |
p.positive = p.y - p.y0 && p.y - p.y0 > 0; | |
} else { | |
p = { | |
x: scales.xScale(mainFn(d, i), i), | |
y: scales.yScale(crossFn(d, i), i), | |
x0: scales.xScale(baseFn(d, i), i) | |
}; | |
p.y0 = p.y; | |
p.positive = p.x - p.x0 !== 0 && p.x - p.x0 > 0; | |
} | |
dataPoints[index++] = p.x; | |
dataPoints[index++] = p.y; | |
dataPoints[index++] = p.x0; | |
dataPoints[index++] = p.y0; | |
if (withLines) { | |
lines[lineIndex++] = p.x; | |
lines[lineIndex++] = p.y; | |
} | |
if (lastPositive !== undefined && p.positive !== undefined && p.positive !== lastPositive) { | |
// If we swapped from positive to negative (or vice versa), we need to | |
// add a cross-over points (unless one of them was not "defined") | |
crossoverCount++; | |
} | |
lastPositive = p.positive; | |
} else { | |
pushBatches(); | |
lastPositive = undefined; | |
} | |
}); | |
pushBatches(); // If we have any cross-over points to add, insert them now | |
var triangles = crossoverCount > 0 ? insertCrossovers(data, dataPoints, crossoverCount) : dataPoints; | |
var batches = { | |
area: areaBatches.batches, | |
line: lineBatches.batches | |
}; | |
var projectedData = { | |
triangles: triangles, | |
lines: lines, | |
batches: batches | |
}; | |
base.cached(projectedData); | |
return projectedData; | |
}; | |
var insertCrossovers = function insertCrossovers(data, dataPoints, crossoverCount) { | |
// We need to insert two extra vertices for each crossover | |
var triangles = new Float32Array(dataPoints.length + crossoverCount * 4); | |
var index = 0; | |
var triangleIndex = 0; | |
var vertical = base.orient() === 'vertical'; | |
var last = null; | |
data.forEach(function (d, i) { | |
if (area.defined()(d, i)) { | |
var x = dataPoints[index++]; | |
var y = dataPoints[index++]; | |
var x0 = dataPoints[index++]; | |
var y0 = dataPoints[index++]; | |
var positive = vertical ? y - y0 > 0 : x - x0 > 0; | |
if (last && positive !== last.positive) { | |
// Insert the extra one at the crossover | |
var r; | |
if (vertical) { | |
r = Math.abs(last.y - last.y0) / (Math.abs(y - y0) + Math.abs(last.y - last.y0)); | |
} else { | |
r = Math.abs(last.x - last.x0) / (Math.abs(x - x0) + Math.abs(last.x - last.x0)); | |
} | |
var midx = last.x + r * (x - last.x); | |
var midy = last.y + r * (y - last.y); // Add the same point twice to skip rendering an unwanted triangle | |
triangles[triangleIndex++] = midx; | |
triangles[triangleIndex++] = midy; | |
triangles[triangleIndex++] = midx; | |
triangles[triangleIndex++] = midy; | |
} | |
last = { | |
positive: positive, | |
x: x, | |
y: y, | |
x0: x0, | |
y0: y0 | |
}; | |
triangles[triangleIndex++] = x; | |
triangles[triangleIndex++] = y; | |
triangles[triangleIndex++] = x0; | |
triangles[triangleIndex++] = y0; | |
} else { | |
last = null; | |
} | |
}); | |
return triangles; | |
}; | |
d3fcRebind.rebindAll(area, base, d3fcRebind.exclude('glAPI', 'cached', 'bandwidth', 'align')); | |
return area; | |
});var point = (function () { | |
var base = glBase(); | |
var size = 70; | |
var type = d3Shape.symbolCircle; | |
var imagePromise = null; | |
var point = function point(data, helperAPI) { | |
base(data, helperAPI); | |
var context = base.context(); | |
var glAPI = base.glAPI(); | |
var scales = glAPI.applyScales(base.xScale(), base.yScale()); | |
context.strokeStyle = type ? colors.black : undefined; | |
context.fillStyle = type ? colors.gray : undefined; | |
context.lineWidth = 1; | |
base.decorate()(context, data, 0); | |
var projectedData = data.constructor === Float32Array ? data : getProjectedData(data, scales); | |
var fillColor = glColor(context.fillStyle); | |
var lineWidth = context.strokeStyle !== 'transparent' ? parseInt(context.lineWidth) : 0; | |
var strokeColor = lineWidth > 0 ? glColor(context.strokeStyle) : null; | |
if (type === d3Shape.symbolCircle) { | |
glAPI.circles(projectedData, fillColor, lineWidth, strokeColor); | |
} else { | |
imagePromise.then(function (image) { | |
glAPI.pointTextures(projectedData, image, fillColor, lineWidth, strokeColor); | |
}); | |
} | |
}; | |
var getProjectedData = function getProjectedData(data, scales) { | |
var cachedProjected = base.cached(); | |
if (cachedProjected) { | |
return cachedProjected; | |
} | |
var filteredData = data.filter(base.defined()); | |
var crossFn = base.crossValue(); | |
var mainFn = base.mainValue(); | |
var sizeFn = typeof size === 'function' ? size : function () { | |
return size; | |
}; | |
var vertical = base.orient() === 'vertical'; | |
var result = new Float32Array(data.length * 3); | |
var index = 0; | |
if (vertical) { | |
filteredData.forEach(function (d, i) { | |
result[index++] = scales.xScale(crossFn(d, i), i); | |
result[index++] = scales.yScale(mainFn(d, i), i); | |
result[index++] = sizeFn(d); | |
}); | |
} else { | |
filteredData.forEach(function (d, i) { | |
result[index++] = scales.xScale(mainFn(d, i), i); | |
result[index++] = scales.yScale(crossFn(d, i), i); | |
result[index++] = sizeFn(d); | |
}); | |
} | |
base.cached(result); | |
return result; | |
}; | |
point.size = function () { | |
if (!arguments.length) { | |
return size; | |
} | |
size = arguments.length <= 0 ? undefined : arguments[0]; | |
return point; | |
}; | |
point.type = function () { | |
if (!arguments.length) { | |
return type; | |
} | |
type = arguments.length <= 0 ? undefined : arguments[0]; | |
if (type !== d3Shape.symbolCircle) { | |
imagePromise = getSymbolImage(type); | |
} else { | |
imagePromise = null; | |
} | |
return point; | |
}; | |
point.image = function (img) { | |
type = null; | |
imagePromise = new Promise(function (resolve) { | |
if (img.complete) { | |
resolve(img); | |
} else { | |
img.onload = function () { | |
resolve(img); | |
}; | |
} | |
}); | |
return point; | |
}; | |
d3fcRebind.rebindAll(point, base, d3fcRebind.exclude('glAPI', 'cached', 'baseValue', 'bandwidth', 'align')); | |
return point; | |
}); | |
var textureSize = 256; | |
var getSymbolImage = function getSymbolImage(type) { | |
return new Promise(function (resolve) { | |
var canvas = document.createElement('canvas'); | |
canvas.width = textureSize; | |
canvas.height = textureSize; | |
var context = canvas.getContext('2d'); | |
context.fillStyle = '#000'; | |
var halfSize = textureSize / 2; | |
context.translate(halfSize, halfSize); | |
context.beginPath(); | |
d3Shape.symbol().type(type).size(halfSize * halfSize).context(context)(); | |
context.closePath(); | |
context.fill(); | |
var image = new window.Image(); | |
image.src = canvas.toDataURL(); | |
image.onload = function () { | |
resolve(image); | |
}; | |
}); | |
};var groupedBase = (function (series) { | |
var bandwidth = function bandwidth() { | |
return 50; | |
}; | |
var align = 'center'; // the offset scale is used to offset each of the series within a group | |
var offsetScale = d3Scale.scaleBand(); | |
var grouped = createBase({ | |
decorate: function decorate() {}, | |
xScale: d3Scale.scaleLinear(), | |
yScale: d3Scale.scaleLinear() | |
}); // the bandwidth for the grouped series can be a function of datum / index. As a result | |
// the offset scale required to cluster the 'sub' series is also dependent on datum / index. | |
// This function computes the offset scale for a specific datum / index of the grouped series | |
grouped.offsetScaleForDatum = function (data, d, i) { | |
var width = bandwidth(d, i); | |
var offset = alignOffset(align, width); | |
var halfWidth = width / 2; | |
return offsetScale.domain(d3Array.range(0, data.length)).range([-halfWidth + offset, halfWidth + offset]); | |
}; | |
grouped.bandwidth = function () { | |
if (!arguments.length) { | |
return bandwidth; | |
} | |
bandwidth = functor(arguments.length <= 0 ? undefined : arguments[0]); | |
return grouped; | |
}; | |
grouped.align = function () { | |
if (!arguments.length) { | |
return align; | |
} | |
align = arguments.length <= 0 ? undefined : arguments[0]; | |
return grouped; | |
}; | |
d3fcRebind.rebindAll(grouped, offsetScale, d3fcRebind.includeMap({ | |
'paddingInner': 'paddingOuter' | |
})); | |
return grouped; | |
});function grouped (series) { | |
var base = groupedBase(series); | |
var grouped = function grouped(data) { | |
data.forEach(function (seriesData, index) { | |
// create a composite scale that applies the required offset | |
var isVertical = series.orient() !== 'horizontal'; | |
var baseScale = isVertical ? base.xScale() : base.yScale(); | |
var compositeScale = function compositeScale(d, i) { | |
var offset = base.offsetScaleForDatum(data, d, i); | |
return baseScale(d) + offset(index) + offset.bandwidth() / 2; | |
}; | |
d3fcRebind.rebindAll(compositeScale, baseScale, d3fcRebind.exclude('clamp', 'copy')); | |
if (isVertical) { | |
series.xScale(compositeScale); | |
series.yScale(base.yScale()); | |
} else { | |
series.yScale(compositeScale); | |
series.xScale(base.xScale()); | |
} // if the sub-series has a bandwidth, set this from the offset scale | |
if (series.bandwidth) { | |
series.bandwidth(function (d, i) { | |
return base.offsetScaleForDatum(data, d, i).bandwidth(); | |
}); | |
} // adapt the decorate function to give each series the correct index | |
series.decorate(function (c, d) { | |
return base.decorate()(c, d, index); | |
}); | |
series(seriesData); | |
}); | |
}; | |
d3fcRebind.rebindAll(grouped, series, d3fcRebind.exclude('decorate', 'xScale', 'yScale')); | |
d3fcRebind.rebindAll(grouped, base, d3fcRebind.exclude('offsetScaleForDatum')); | |
return grouped; | |
}Object.defineProperty(exports,'seriesWebglMulti',{enumerable:true,get:function(){return d3fcSeries.seriesCanvasMulti;}});Object.defineProperty(exports,'seriesWebglRepeat',{enumerable:true,get:function(){return d3fcSeries.seriesCanvasRepeat;}});exports.cartesian=cartesian;exports.seriesWebglArea=area;exports.seriesWebglBar=bar;exports.seriesWebglGrouped=grouped;exports.seriesWebglLine=line;exports.seriesWebglPoint=point;Object.defineProperty(exports,'__esModule',{value:true});}));//# sourceMappingURL=d3fc-webgl.js.map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment