From b5d917e9ef2408b64106724c73136d6aa58734ed Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 24 Jan 2017 13:53:56 +0300 Subject: [PATCH 01/41] Introduce snap-points strategy --- scatter-fancy.js | 107 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index 7a96a7e..6468083 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -8,6 +8,7 @@ var textCache = require('text-cache') var pool = require('typedarray-pool') var vectorizeText = require('vectorize-text') var shaders = require('./lib/shaders') +var snapPoints = require('snap-points-2d') var BOUNDARIES = {} @@ -113,7 +114,15 @@ function GLScatterFancy( this.numPoints = 0 this.numVertices = 0 this.pickOffset = 0 + + //positions data this.points = null + + //lod scales + this.scales = [] + + //data vertices offsets + this.pointOffset = [] } var proto = GLScatterFancy.prototype @@ -126,6 +135,8 @@ var proto = GLScatterFancy.prototype var PIXEL_SCALE = [0, 0] + var pixelSize + function calcScales() { var plot = this.plot var bounds = this.bounds @@ -156,6 +167,8 @@ var proto = GLScatterFancy.prototype var screenX = viewBox[2] - viewBox[0] var screenY = viewBox[3] - viewBox[1] + pixelSize = Math.min(dataX / screenX, dataY / screenY) + PIXEL_SCALE[0] = 2 * pixelRatio / screenX PIXEL_SCALE[1] = 2 * pixelRatio / screenY } @@ -215,7 +228,24 @@ var proto = GLScatterFancy.prototype shader.uniforms.translateHi = TRANSLATE_HI shader.uniforms.translateLo = TRANSLATE_LO - gl.drawArrays(gl.TRIANGLES, 0, numVertices) + var scales = this.scales + + for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) { + var lod = scales[scaleNum] + if(lod.pixelSize < pixelSize && scaleNum > 1) { + continue + } + + var intervalStart = lod.offset + var intervalEnd = lod.count + intervalStart + + var startOffset = this.pointOffset[intervalStart] + var endOffset = this.pointOffset[intervalEnd] || numVertices + + if (endOffset > startOffset) { + gl.drawArrays(gl.TRIANGLES, startOffset, (endOffset - startOffset)) + } + } if(pick) return offset + this.numPoints } @@ -251,22 +281,31 @@ proto.update = function(options) { this.points = positions + //create packed positions here + var pointCount = this.points.length / 2 + var packedId = pool.mallocInt32(pointCount) + var packedW = pool.mallocFloat32(2 * pointCount) + var packed = pool.mallocFloat64(2 * pointCount) + packed.set(this.points) + this.scales = snapPoints(packed, packedId, packedW, this.bounds) + var bounds = this.bounds = [Infinity, Infinity, -Infinity, -Infinity] var numVertices = 0 - var glyphMeshes = [] - var glyphBoundaries = [] + var glyphMeshes = Array(pointCount) + var glyphBoundaries = Array(pointCount) var glyph, border - for(i = 0; i < glyphs.length; ++i) { - glyph = textCache('sans-serif', glyphs[i]) - border = getBoundary(glyphs[i]) - glyphMeshes.push(glyph) - glyphBoundaries.push(border) + for(i = 0; i < pointCount; ++i) { + var id = packedId[i] + glyph = textCache('sans-serif', glyphs[id]) + border = getBoundary(glyphs[id]) + glyphMeshes[id] = glyph + glyphBoundaries[id] = border numVertices += (glyph.data.length + border.coords.length) >> 1 for(j = 0; j < 2; ++j) { - bounds[j] = Math.min(bounds[j], positions[2 * i + j]) - bounds[2 + j] = Math.max(bounds[2 + j], positions[2 * i + j]) + bounds[j] = Math.min(bounds[j], positions[2 * id + j]) + bounds[2 + j] = Math.max(bounds[2 + j], positions[2 * id + j]) } } @@ -282,6 +321,7 @@ proto.update = function(options) { var tx = bounds[0] var ty = bounds[1] + //v_position contains normalized positions to the available range of positions var v_position = pool.mallocFloat64(2 * numVertices) var v_posHi = pool.mallocFloat32(2 * numVertices) var v_posLo = pool.mallocFloat32(2 * numVertices) @@ -290,16 +330,21 @@ proto.update = function(options) { var v_ids = pool.mallocUint32(numVertices) var ptr = 0 - for(i = 0; i < glyphs.length; ++i) { - glyph = glyphMeshes[i] - border = glyphBoundaries[i] - var x = sx * (positions[2 * i] - tx) - var y = sy * (positions[2 * i + 1] - ty) - var s = sizes[i] - var r = colors[4 * i] * 255 - var g = colors[4 * i + 1] * 255 - var b = colors[4 * i + 2] * 255 - var a = colors[4 * i + 3] * 255 + this.pointOffset.length = pointCount + + for(i = 0; i < pointCount; ++i) { + this.pointOffset[i] = ptr + + var id = packedId[i] + glyph = glyphMeshes[id] + border = glyphBoundaries[id] + var x = sx * (positions[2 * id] - tx) + var y = sy * (positions[2 * id + 1] - ty) + var s = sizes[id] + var r = colors[4 * id] * 255 + var g = colors[4 * id + 1] * 255 + var b = colors[4 * id + 2] * 255 + var a = colors[4 * id + 3] * 255 var gx = 0.5 * (border.bounds[0] + border.bounds[2]) var gy = 0.5 * (border.bounds[1] + border.bounds[3]) @@ -313,16 +358,16 @@ proto.update = function(options) { v_color[4 * ptr + 1] = g v_color[4 * ptr + 2] = b v_color[4 * ptr + 3] = a - v_ids[ptr] = i + v_ids[ptr] = id ptr += 1 } - var w = borderWidths[i] - r = borderColors[4 * i] * 255 - g = borderColors[4 * i + 1] * 255 - b = borderColors[4 * i + 2] * 255 - a = borderColors[4 * i + 3] * 255 + var w = borderWidths[id] + r = borderColors[4 * id] * 255 + g = borderColors[4 * id + 1] * 255 + b = borderColors[4 * id + 2] * 255 + a = borderColors[4 * id + 3] * 255 for(j = 0; j < border.coords.length; j += 2) { v_position[2 * ptr] = x @@ -333,13 +378,13 @@ proto.update = function(options) { v_color[4 * ptr + 1] = g v_color[4 * ptr + 2] = b v_color[4 * ptr + 3] = a - v_ids[ptr] = i + v_ids[ptr] = id ptr += 1 } } - this.numPoints = glyphs.length + this.numPoints = pointCount this.numVertices = numVertices v_posHi.set(v_position) @@ -358,6 +403,10 @@ proto.update = function(options) { pool.free(v_offset) pool.free(v_color) pool.free(v_ids) + + pool.free(packed) + pool.free(packedId) + pool.free(packedW) } proto.dispose = function() { @@ -398,4 +447,4 @@ function createFancyScatter2D(plot, options) { plot.addObject(scatter) return scatter -} \ No newline at end of file +} From b8bf62dc80ad95c9c3cd000100baa907ad6d4a1c Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 1 Feb 2017 23:40:44 +0300 Subject: [PATCH 02/41] Init SDF rendering --- lib/shaders/frag.glsl | 19 ++++- lib/shaders/pick-vertex.glsl | 2 + lib/shaders/vertex.glsl | 19 ++++- lib/shaders/xform.glsl | 12 +-- package.json | 11 ++- scatter-fancy.js | 147 +++++++++++++++++++++++++++-------- test.js | 58 ++++++++++++++ 7 files changed, 223 insertions(+), 45 deletions(-) create mode 100644 test.js diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index f4b0088..d9ce05e 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -1,5 +1,20 @@ -precision lowp float; +precision highp float; + +uniform sampler2D chars; +uniform vec2 charsShape; + varying vec4 fragColor; +varying vec2 charCoord; +varying vec2 pointCoord; + void main() { - gl_FragColor = vec4(fragColor.rgb * fragColor.a, fragColor.a); + //FIXME: why pixelScale is double? + //FIXME: whis does not include dataBox viewport + vec2 coord = (pointCoord - gl_FragCoord.xy + 16.) / 32.; + vec2 uvCoord = ((vec2(0,0) + coord) * 64.) / charsShape; + vec4 color = texture2D(chars, uvCoord); + // float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist); + gl_FragColor = vec4(color.rgb, 1.); + + // gl_FragColor = vec4(fragColor.rgb * fragColor.a, fragColor.a); } diff --git a/lib/shaders/pick-vertex.glsl b/lib/shaders/pick-vertex.glsl index 9de1d24..d4d893f 100644 --- a/lib/shaders/pick-vertex.glsl +++ b/lib/shaders/pick-vertex.glsl @@ -25,6 +25,8 @@ void main() { fragColor = fragId / 255.0; + gl_PointSize = 1.; + gl_Position = computePosition( positionHi, positionLo, scaleHi, scaleLo, diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index ce073f9..a8a72ce 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -2,20 +2,37 @@ precision highp float; attribute vec2 positionHi, positionLo; attribute vec2 offset; +attribute vec2 char; attribute vec4 color; -uniform vec2 scaleHi, scaleLo, translateHi, translateLo, pixelScale; +//this is 64-bit form of scale and translate +uniform vec2 scaleHi, scaleLo, translateHi, translateLo; +uniform vec2 pixelScale, charsShape; +uniform vec4 viewBox; varying vec4 fragColor; +varying vec2 charCoord; +varying vec2 pointCoord; +varying float size; +varying vec2 position; #pragma glslify: computePosition = require("./xform.glsl") void main() { + // vec4 color = texture2D(colors, vec2(colorIdx, .5)); + fragColor = color; + gl_PointSize = 32.; + size = 32.; + + charCoord = char; + gl_Position = computePosition( positionHi, positionLo, scaleHi, scaleLo, translateHi, translateLo, pixelScale, offset); + + pointCoord = viewBox.xy + (viewBox.zw - viewBox.xy) * (gl_Position.xy * .5 + .5); } diff --git a/lib/shaders/xform.glsl b/lib/shaders/xform.glsl index dbac565..5dd7bdb 100644 --- a/lib/shaders/xform.glsl +++ b/lib/shaders/xform.glsl @@ -1,8 +1,10 @@ #pragma glslify: export(computePosition) vec4 computePosition(vec2 posHi, vec2 posLo, vec2 scHi, vec2 scLo, vec2 trHi, vec2 trLo, vec2 screenScale, vec2 screenOffset) { return vec4((posHi + trHi) * scHi - + (posLo + trLo) * scHi - + (posHi + trHi) * scLo - + (posLo + trLo) * scLo - + screenScale * screenOffset, 0, 1); -} \ No newline at end of file + //FIXME: this thingy does not give noticeable precision gain for me + // + (posLo + trLo) * scHi + // + (posHi + trHi) * scLo + // + (posLo + trLo) * scLo + // + screenScale * screenOffset + , 0, 1); +} diff --git a/package.json b/package.json index 395387f..c170681 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,10 @@ "plot" ], "author": "Mikola Lysenko", - "contributors": ["Étienne Tétreault-Pinard", "Robert Monfera"], + "contributors": [ + "Étienne Tétreault-Pinard", + "Robert Monfera" + ], "license": "MIT", "bugs": { "url": "https://github.com/gl-vis/gl-scatter2d-fancy/issues" @@ -27,11 +30,11 @@ }, "homepage": "https://github.com/gl-vis/gl-scatter2d-fancy#readme", "dependencies": { + "font-atlas-sdf": "^1.0.0", "gl-buffer": "^2.1.2", "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", "glslify": "^2.3.1", - "text-cache": "^4.0.0", - "typedarray-pool": "^1.1.0", - "vectorize-text": "^3.0.2" + "typedarray-pool": "^1.1.0" } } diff --git a/scatter-fancy.js b/scatter-fancy.js index 6468083..97919f8 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -9,6 +9,9 @@ var pool = require('typedarray-pool') var vectorizeText = require('vectorize-text') var shaders = require('./lib/shaders') var snapPoints = require('snap-points-2d') +var atlas = require('font-atlas-sdf') +var createTexture = require('gl-texture2d') + var BOUNDARIES = {} @@ -101,7 +104,8 @@ function GLScatterFancy( positionLoBuffer, offsetBuffer, colorBuffer, - idBuffer) { + idBuffer, + charBuffer) { this.plot = plot this.shader = shader this.pickShader = pickShader @@ -110,6 +114,7 @@ function GLScatterFancy( this.offsetBuffer = offsetBuffer this.colorBuffer = colorBuffer this.idBuffer = idBuffer + this.charBuffer = charBuffer this.bounds = [Infinity, Infinity, -Infinity, -Infinity] this.numPoints = 0 this.numVertices = 0 @@ -123,6 +128,11 @@ function GLScatterFancy( //data vertices offsets this.pointOffset = [] + + //font atlas texture + this.charCanvas = document.createElement('canvas') + this.charTexture = createTexture(this.plot.gl, this.charCanvas) + // this.colorsTexture = createTexture(this.plot.gl, [512, 512]) } var proto = GLScatterFancy.prototype @@ -169,6 +179,7 @@ var proto = GLScatterFancy.prototype pixelSize = Math.min(dataX / screenX, dataY / screenY) + //FIXME: why double? PIXEL_SCALE[0] = 2 * pixelRatio / screenX PIXEL_SCALE[1] = 2 * pixelRatio / screenY } @@ -176,7 +187,6 @@ var proto = GLScatterFancy.prototype var PICK_OFFSET = [0, 0, 0, 0] proto.drawPick = function(offset) { - var pick = offset !== undefined var plot = this.plot @@ -207,10 +217,24 @@ var proto = GLScatterFancy.prototype shader.attributes.id.pointer(gl.UNSIGNED_BYTE, false) } else { - this.colorBuffer.bind() + // shader.attributes.color = [0,1,0,1] shader.attributes.color.pointer(gl.UNSIGNED_BYTE, true) + this.charBuffer.bind() + shader.attributes.char.pointer(gl.UNSIGNED_BYTE, true) + + // let ctx = this.charCanvas.getContext('2d'); + // ctx.fillStyle = 'red'; + // ctx.fillRect(30,30,4,4); + + //TODO: get rid of recreating texture each fucking draw time + this.charTexture = createTexture(this.plot.gl, this.charCanvas) + shader.uniforms.chars = this.charTexture.bind(0) + //TODO: get rid of this resetting each redraw time + this.charTexture.setPixels(this.charCanvas) + document.body.appendChild(this.charCanvas) + shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] } this.posHiBuffer.bind() @@ -227,6 +251,9 @@ var proto = GLScatterFancy.prototype shader.uniforms.scaleLo = SCALE_LO shader.uniforms.translateHi = TRANSLATE_HI shader.uniforms.translateLo = TRANSLATE_LO + //FIXME: this may be an issue for changed viewbox, test it + //FIXME: make sure we can't do the same with PIXEL_SCALE or something + shader.uniforms.viewBox = plot.viewBox var scales = this.scales @@ -242,8 +269,10 @@ var proto = GLScatterFancy.prototype var startOffset = this.pointOffset[intervalStart] var endOffset = this.pointOffset[intervalEnd] || numVertices + //TODO: we can shave off even more by slicing by left/right limits, see gl-scatter. Points are arranged by x coordinate so just calc bounds + if (endOffset > startOffset) { - gl.drawArrays(gl.TRIANGLES, startOffset, (endOffset - startOffset)) + gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) } } @@ -278,6 +307,7 @@ proto.update = function(options) { var borderWidths = options.borderWidths || [] var borderColors = options.borderColors || [] var i, j + var gl = this.plot.gl this.points = positions @@ -294,7 +324,12 @@ proto.update = function(options) { var glyphMeshes = Array(pointCount) var glyphBoundaries = Array(pointCount) - var glyph, border + var glyph, border, glyphData + + bounds[0] = 0 + bounds[1] = 0 + bounds[2] = 1 + bounds[3] = 1 for(i = 0; i < pointCount; ++i) { var id = packedId[i] @@ -302,19 +337,20 @@ proto.update = function(options) { border = getBoundary(glyphs[id]) glyphMeshes[id] = glyph glyphBoundaries[id] = border - numVertices += (glyph.data.length + border.coords.length) >> 1 - for(j = 0; j < 2; ++j) { - bounds[j] = Math.min(bounds[j], positions[2 * id + j]) - bounds[2 + j] = Math.max(bounds[2 + j], positions[2 * id + j]) - } + // numVertices += (glyph.data.length + border.coords.length) >> 1 + numVertices += 1 + // for(j = 0; j < 2; ++j) { + // bounds[j] = Math.min(bounds[j], positions[2 * id + j]) + // bounds[2 + j] = Math.max(bounds[2 + j], positions[2 * id + j]) + // } } - if(bounds[0] === bounds[2]) { - bounds[2] += 1 - } - if(bounds[3] === bounds[1]) { - bounds[3] += 1 - } + // if(bounds[0] === bounds[2]) { + // bounds[2] += 1 + // } + // if(bounds[3] === bounds[1]) { + // bounds[3] += 1 + // } var sx = 1 / (bounds[2] - bounds[0]) var sy = 1 / (bounds[3] - bounds[1]) @@ -328,6 +364,7 @@ proto.update = function(options) { var v_offset = pool.mallocFloat32(2 * numVertices) var v_color = pool.mallocUint8(4 * numVertices) var v_ids = pool.mallocUint32(numVertices) + var v_chars = pool.mallocUint8(2 * numVertices) var ptr = 0 this.pointOffset.length = pointCount @@ -338,6 +375,8 @@ proto.update = function(options) { var id = packedId[i] glyph = glyphMeshes[id] border = glyphBoundaries[id] + // glyphData = glyph.data + glyphData = [0, 0]//, 0, 1, 1, 0] var x = sx * (positions[2 * id] - tx) var y = sy * (positions[2 * id + 1] - ty) var s = sizes[id] @@ -349,11 +388,12 @@ proto.update = function(options) { var gx = 0.5 * (border.bounds[0] + border.bounds[2]) var gy = 0.5 * (border.bounds[1] + border.bounds[3]) - for(j = 0; j < glyph.data.length; j += 2) { + for(j = 0; j < glyphData.length; j += 2) { v_position[2 * ptr] = x v_position[2 * ptr + 1] = y - v_offset[2 * ptr] = -s * (glyph.data[j] - gx) - v_offset[2 * ptr + 1] = -s * (glyph.data[j + 1] - gy) + //FIXME: replace offset with size + v_offset[2 * ptr] = 0//-s * (glyphData[j] - gx) + v_offset[2 * ptr + 1] = 0//-s * (glyphData[j + 1] - gy) v_color[4 * ptr] = r v_color[4 * ptr + 1] = g v_color[4 * ptr + 2] = b @@ -369,19 +409,19 @@ proto.update = function(options) { b = borderColors[4 * id + 2] * 255 a = borderColors[4 * id + 3] * 255 - for(j = 0; j < border.coords.length; j += 2) { - v_position[2 * ptr] = x - v_position[2 * ptr + 1] = y - v_offset[2 * ptr] = - (s * (border.coords[j] - gx) + w * border.normals[j]) - v_offset[2 * ptr + 1] = - (s * (border.coords[j + 1] - gy) + w * border.normals[j + 1]) - v_color[4 * ptr] = r - v_color[4 * ptr + 1] = g - v_color[4 * ptr + 2] = b - v_color[4 * ptr + 3] = a - v_ids[ptr] = id - - ptr += 1 - } + // for(j = 0; j < border.coords.length; j += 2) { + // v_position[2 * ptr] = x + // v_position[2 * ptr + 1] = y + // v_offset[2 * ptr] = - (s * (border.coords[j] - gx) + w * border.normals[j]) + // v_offset[2 * ptr + 1] = - (s * (border.coords[j + 1] - gy) + w * border.normals[j + 1]) + // v_color[4 * ptr] = r + // v_color[4 * ptr + 1] = g + // v_color[4 * ptr + 2] = b + // v_color[4 * ptr + 3] = a + // v_ids[ptr] = id + + // ptr += 1 + // } } this.numPoints = pointCount @@ -391,11 +431,48 @@ proto.update = function(options) { for(i = 0; i < v_position.length; i++) v_posLo[i] = v_position[i] - v_posHi[i] + //aggregate glyphs + var glyphChars = {}; + for (var i = 0, l = pointCount, k = 0; i < l; i++) { + var char = glyphs[i] + if (glyphChars[char] == null) { + glyphChars[char] = k++ + } + } + + //generate font atlas + //TODO: make step depend on chars number + var chars = Object.keys(glyphChars) + var size = 32 + var step = size*2 + var maxW = gl.getParameter(gl.MAX_TEXTURE_SIZE) + var atlasW = Math.min(maxW, step*chars.length) + var atlasH = Math.min(maxW, step*Math.floor(step*chars.length/atlasW)) + this.charCanvas = atlas({ + canvas: this.charCanvas, + family: 'sans-serif', + size: size, + shape: [atlasW, atlasH], + step: [step, step], + chars: chars + }) + + //populate char indexes + var cols = atlasW / step + for (var i = 0; i < pointCount; i++) { + var char = glyphs[i] + var charIdx = glyphChars[char] + v_chars[2*i + 1] = Math.floor(i / cols) + v_chars[2*i] = i % cols + } + + //update data this.posHiBuffer.update(v_posHi) this.posLoBuffer.update(v_posLo) this.offsetBuffer.update(v_offset) this.colorBuffer.update(v_color) this.idBuffer.update(v_ids) + this.charBuffer.update(v_chars) pool.free(v_position) pool.free(v_posHi) @@ -403,6 +480,7 @@ proto.update = function(options) { pool.free(v_offset) pool.free(v_color) pool.free(v_ids) + pool.free(v_chars) pool.free(packed) pool.free(packedId) @@ -417,6 +495,7 @@ proto.dispose = function() { this.offsetBuffer.dispose() this.colorBuffer.dispose() this.idBuffer.dispose() + this.charBuffer.dispose() this.plot.removeObject(this) } @@ -431,6 +510,7 @@ function createFancyScatter2D(plot, options) { var offsetBuffer = createBuffer(gl) var colorBuffer = createBuffer(gl) var idBuffer = createBuffer(gl) + var charBuffer = createBuffer(gl) var scatter = new GLScatterFancy( plot, @@ -440,7 +520,8 @@ function createFancyScatter2D(plot, options) { positionLoBuffer, offsetBuffer, colorBuffer, - idBuffer) + idBuffer, + charBuffer) scatter.update(options) diff --git a/test.js b/test.js new file mode 100644 index 0000000..7a9eb4b --- /dev/null +++ b/test.js @@ -0,0 +1,58 @@ +const atlasSDF = require('font-atlas-sdf') +const atlas = require('font-atlas') +const createPanel = require('settings-panel') +const assign = require('object-assign') + +let c1 = document.body.appendChild(document.createElement('canvas')) +let c2 = document.body.appendChild(document.createElement('canvas')) + +let opts = { + family: 'sans-serif', + size: 32, + chars: '●#✝+' +} + +function update (o) { + console.time('sdf') + assign(opts, o) + + let w = [Math.min(400, opts.size*16)] + let size = opts.size + + atlasSDF({ + canvas: c1, + family: opts.family + , size: opts.size + , shape: [w,w] + , step: [size*2, size*2], + chars: opts.chars + }) + console.timeEnd('sdf') + + + console.time('bm') + atlas({ + canvas: c2, + family: opts.family + , size: opts.size + , shape: [w,w] + , step: [size*2, size*2], + chars: opts.chars + }) + console.timeEnd('bm') +} + +createPanel([ +{id: 'size', type: 'range', min: 1, max: 128, value: opts.size, step: 1, change: v => { + update({size: v}) +}}, +{id: 'family', type: 'text', value: opts.family, change: v => { + update({family: v}) +}}, +{id: 'chars', type: 'text', value: opts.chars, change: v => { + update({chars: v}) +}} +// {id: 'step', type: 'range', min: 1, max: 128, value: 21, step: 1, change: v => { +// update({size: v}) +// }} +]) From 2cb4a6180165c812e19fbdd3099b9f2f45aa1a59 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 2 Feb 2017 14:11:19 +0300 Subject: [PATCH 03/41] Make variable chars --- lib/shaders/frag.glsl | 2 +- lib/shaders/xform.glsl | 8 ++++---- scatter-fancy.js | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index d9ce05e..bf01ad2 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -11,7 +11,7 @@ void main() { //FIXME: why pixelScale is double? //FIXME: whis does not include dataBox viewport vec2 coord = (pointCoord - gl_FragCoord.xy + 16.) / 32.; - vec2 uvCoord = ((vec2(0,0) + coord) * 64.) / charsShape; + vec2 uvCoord = ((charCoord + coord) * 64.) / charsShape; vec4 color = texture2D(chars, uvCoord); // float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist); gl_FragColor = vec4(color.rgb, 1.); diff --git a/lib/shaders/xform.glsl b/lib/shaders/xform.glsl index 5dd7bdb..0b9e4d7 100644 --- a/lib/shaders/xform.glsl +++ b/lib/shaders/xform.glsl @@ -2,9 +2,9 @@ vec4 computePosition(vec2 posHi, vec2 posLo, vec2 scHi, vec2 scLo, vec2 trHi, vec2 trLo, vec2 screenScale, vec2 screenOffset) { return vec4((posHi + trHi) * scHi //FIXME: this thingy does not give noticeable precision gain for me - // + (posLo + trLo) * scHi - // + (posHi + trHi) * scLo - // + (posLo + trLo) * scLo - // + screenScale * screenOffset + + (posLo + trLo) * scHi + + (posHi + trHi) * scLo + + (posLo + trLo) * scLo + + screenScale * screenOffset , 0, 1); } diff --git a/scatter-fancy.js b/scatter-fancy.js index 97919f8..aa650e1 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -222,7 +222,7 @@ var proto = GLScatterFancy.prototype shader.attributes.color.pointer(gl.UNSIGNED_BYTE, true) this.charBuffer.bind() - shader.attributes.char.pointer(gl.UNSIGNED_BYTE, true) + shader.attributes.char.pointer(gl.UNSIGNED_BYTE, false) // let ctx = this.charCanvas.getContext('2d'); // ctx.fillStyle = 'red'; @@ -462,8 +462,8 @@ proto.update = function(options) { for (var i = 0; i < pointCount; i++) { var char = glyphs[i] var charIdx = glyphChars[char] - v_chars[2*i + 1] = Math.floor(i / cols) - v_chars[2*i] = i % cols + v_chars[2*i + 1] = Math.floor(charIdx / cols) + v_chars[2*i] = charIdx % cols } //update data From b38a9703d18243d8b9060178f6b579c5cddb89e0 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 2 Feb 2017 15:07:17 +0300 Subject: [PATCH 04/41] Render data with colors --- lib/shaders/frag.glsl | 21 +++++++++++++-------- lib/shaders/vertex.glsl | 4 ++-- scatter-fancy.js | 6 ++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index bf01ad2..b1839c7 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -2,19 +2,24 @@ precision highp float; uniform sampler2D chars; uniform vec2 charsShape; +uniform float gamma; varying vec4 fragColor; -varying vec2 charCoord; +varying vec2 charOffset; varying vec2 pointCoord; +const float BUFFER = .64; +const float GAMMA = .16; + void main() { - //FIXME: why pixelScale is double? - //FIXME: whis does not include dataBox viewport - vec2 coord = (pointCoord - gl_FragCoord.xy + 16.) / 32.; - vec2 uvCoord = ((charCoord + coord) * 64.) / charsShape; - vec4 color = texture2D(chars, uvCoord); - // float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist); - gl_FragColor = vec4(color.rgb, 1.); + vec2 pointUV = (pointCoord - gl_FragCoord.xy + 16.) / 32.; + vec2 texCoord = ((charOffset + pointUV) * 64.) / charsShape; + float dist = texture2D(chars, texCoord).r; + + float alpha = smoothstep(BUFFER - GAMMA, BUFFER + GAMMA, dist); + gl_FragColor = vec4(fragColor.rgb, alpha * fragColor.a); + + // gl_FragColor = vec4(vec3(dist), 1.); // gl_FragColor = vec4(fragColor.rgb * fragColor.a, fragColor.a); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index a8a72ce..4959da7 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -11,7 +11,7 @@ uniform vec2 pixelScale, charsShape; uniform vec4 viewBox; varying vec4 fragColor; -varying vec2 charCoord; +varying vec2 charOffset; varying vec2 pointCoord; varying float size; varying vec2 position; @@ -26,7 +26,7 @@ void main() { gl_PointSize = 32.; size = 32.; - charCoord = char; + charOffset = char; gl_Position = computePosition( positionHi, positionLo, diff --git a/scatter-fancy.js b/scatter-fancy.js index aa650e1..12ee8c3 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -217,6 +217,12 @@ var proto = GLScatterFancy.prototype shader.attributes.id.pointer(gl.UNSIGNED_BYTE, false) } else { + //enable data blending + //FIXME: make sure it does not trigger each and every draw call + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + gl.enable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + this.colorBuffer.bind() // shader.attributes.color = [0,1,0,1] shader.attributes.color.pointer(gl.UNSIGNED_BYTE, true) From be900c7f754cd4ce1d4478b1832d1158f680088d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 2 Feb 2017 15:51:57 +0300 Subject: [PATCH 05/41] Get first rendering done --- .eslintrc.json | 43 ++++++++++++++++++++ lib/shaders/frag.glsl | 14 +++---- lib/shaders/pick-vertex.glsl | 3 +- lib/shaders/vertex.glsl | 14 +++---- scatter-fancy.js | 78 +++++++++++++----------------------- 5 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..c50c250 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,43 @@ +{ + "env": { + "browser": true, + "node": true, + "commonjs": true, + "es6": true + }, + "extends": "eslint:recommended", + "rules": { + "strict": 2, + "indent": 0, + "linebreak-style": 0, + "quotes": 0, + "semi": 0, + "no-cond-assign": 1, + "no-constant-condition": 1, + "no-duplicate-case": 1, + "no-empty": 1, + "no-ex-assign": 1, + "no-extra-boolean-cast": 1, + "no-extra-semi": 1, + "no-fallthrough": 1, + "no-func-assign": 1, + "no-global-assign": 1, + "no-implicit-globals": 2, + "no-inner-declarations": ["error", "functions"], + "no-irregular-whitespace": 2, + "no-loop-func": 1, + "no-multi-str": 1, + "no-mixed-spaces-and-tabs": 1, + "no-proto": 1, + "no-sequences": 1, + "no-throw-literal": 1, + "no-unmodified-loop-condition": 1, + "no-useless-call": 1, + "no-void": 1, + "no-with": 2, + "wrap-iife": 1, + "no-redeclare": 1, + "no-unused-vars": ["error", { "vars": "all", "args": "none" }], + "no-sparse-arrays": 1 + } +} diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index b1839c7..6dc469f 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -2,24 +2,24 @@ precision highp float; uniform sampler2D chars; uniform vec2 charsShape; -uniform float gamma; +uniform float charsStep; varying vec4 fragColor; varying vec2 charOffset; varying vec2 pointCoord; +varying float pointSize; -const float BUFFER = .64; -const float GAMMA = .16; +const float BUFFER = .72; +const float GAMMA = .05; void main() { - vec2 pointUV = (pointCoord - gl_FragCoord.xy + 16.) / 32.; - vec2 texCoord = ((charOffset + pointUV) * 64.) / charsShape; + vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; + pointUV.x = 1. - pointUV.x; + vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; float alpha = smoothstep(BUFFER - GAMMA, BUFFER + GAMMA, dist); gl_FragColor = vec4(fragColor.rgb, alpha * fragColor.a); // gl_FragColor = vec4(vec3(dist), 1.); - - // gl_FragColor = vec4(fragColor.rgb * fragColor.a, fragColor.a); } diff --git a/lib/shaders/pick-vertex.glsl b/lib/shaders/pick-vertex.glsl index d4d893f..0fdc14b 100644 --- a/lib/shaders/pick-vertex.glsl +++ b/lib/shaders/pick-vertex.glsl @@ -1,7 +1,6 @@ precision highp float; attribute vec2 positionHi, positionLo; -attribute vec2 offset; attribute vec4 id; uniform vec2 scaleHi, scaleLo, translateHi, translateLo, pixelScale; @@ -31,5 +30,5 @@ void main() { positionHi, positionLo, scaleHi, scaleLo, translateHi, translateLo, - pixelScale, offset); + pixelScale, vec2(0)); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 4959da7..0a6ed76 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -1,30 +1,28 @@ precision highp float; attribute vec2 positionHi, positionLo; -attribute vec2 offset; +attribute float size; attribute vec2 char; attribute vec4 color; //this is 64-bit form of scale and translate uniform vec2 scaleHi, scaleLo, translateHi, translateLo; -uniform vec2 pixelScale, charsShape; +uniform vec2 pixelScale; uniform vec4 viewBox; varying vec4 fragColor; varying vec2 charOffset; varying vec2 pointCoord; -varying float size; +varying float pointSize; varying vec2 position; #pragma glslify: computePosition = require("./xform.glsl") void main() { - // vec4 color = texture2D(colors, vec2(colorIdx, .5)); - fragColor = color; - gl_PointSize = 32.; - size = 32.; + gl_PointSize = size*2.; + pointSize = size*2.; charOffset = char; @@ -32,7 +30,7 @@ void main() { positionHi, positionLo, scaleHi, scaleLo, translateHi, translateLo, - pixelScale, offset); + pixelScale, vec2(0)); pointCoord = viewBox.xy + (viewBox.zw - viewBox.xy) * (gl_Position.xy * .5 + .5); } diff --git a/scatter-fancy.js b/scatter-fancy.js index 12ee8c3..a404a4c 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -5,8 +5,8 @@ module.exports = createFancyScatter2D var createShader = require('gl-shader') var createBuffer = require('gl-buffer') var textCache = require('text-cache') -var pool = require('typedarray-pool') var vectorizeText = require('vectorize-text') +var pool = require('typedarray-pool') var shaders = require('./lib/shaders') var snapPoints = require('snap-points-2d') var atlas = require('font-atlas-sdf') @@ -102,7 +102,7 @@ function GLScatterFancy( pickShader, positionHiBuffer, positionLoBuffer, - offsetBuffer, + sizeBuffer, colorBuffer, idBuffer, charBuffer) { @@ -111,7 +111,7 @@ function GLScatterFancy( this.pickShader = pickShader this.posHiBuffer = positionHiBuffer this.posLoBuffer = positionLoBuffer - this.offsetBuffer = offsetBuffer + this.sizeBuffer = sizeBuffer this.colorBuffer = colorBuffer this.idBuffer = idBuffer this.charBuffer = charBuffer @@ -237,10 +237,14 @@ var proto = GLScatterFancy.prototype //TODO: get rid of recreating texture each fucking draw time this.charTexture = createTexture(this.plot.gl, this.charCanvas) shader.uniforms.chars = this.charTexture.bind(0) + // document.body.appendChild(this.charCanvas) //TODO: get rid of this resetting each redraw time this.charTexture.setPixels(this.charCanvas) - document.body.appendChild(this.charCanvas) shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] + shader.uniforms.charsStep = this.charStep; + + this.sizeBuffer.bind() + shader.attributes.size.pointer() } this.posHiBuffer.bind() @@ -249,9 +253,6 @@ var proto = GLScatterFancy.prototype this.posLoBuffer.bind() shader.attributes.positionLo.pointer() - this.offsetBuffer.bind() - shader.attributes.offset.pointer() - shader.uniforms.pixelScale = PIXEL_SCALE shader.uniforms.scaleHi = SCALE_HI shader.uniforms.scaleLo = SCALE_LO @@ -275,7 +276,7 @@ var proto = GLScatterFancy.prototype var startOffset = this.pointOffset[intervalStart] var endOffset = this.pointOffset[intervalEnd] || numVertices - //TODO: we can shave off even more by slicing by left/right limits, see gl-scatter. Points are arranged by x coordinate so just calc bounds + //TODO: we can shave off even more by slicing by left/right limits, see gl-scatter2d. Points are arranged by x coordinate so just calc bounds if (endOffset > startOffset) { gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) @@ -367,7 +368,7 @@ proto.update = function(options) { var v_position = pool.mallocFloat64(2 * numVertices) var v_posHi = pool.mallocFloat32(2 * numVertices) var v_posLo = pool.mallocFloat32(2 * numVertices) - var v_offset = pool.mallocFloat32(2 * numVertices) + var v_size = pool.mallocFloat32(numVertices) var v_color = pool.mallocUint8(4 * numVertices) var v_ids = pool.mallocUint32(numVertices) var v_chars = pool.mallocUint8(2 * numVertices) @@ -379,10 +380,6 @@ proto.update = function(options) { this.pointOffset[i] = ptr var id = packedId[i] - glyph = glyphMeshes[id] - border = glyphBoundaries[id] - // glyphData = glyph.data - glyphData = [0, 0]//, 0, 1, 1, 0] var x = sx * (positions[2 * id] - tx) var y = sy * (positions[2 * id + 1] - ty) var s = sizes[id] @@ -391,43 +388,22 @@ proto.update = function(options) { var b = colors[4 * id + 2] * 255 var a = colors[4 * id + 3] * 255 - var gx = 0.5 * (border.bounds[0] + border.bounds[2]) - var gy = 0.5 * (border.bounds[1] + border.bounds[3]) - - for(j = 0; j < glyphData.length; j += 2) { - v_position[2 * ptr] = x - v_position[2 * ptr + 1] = y - //FIXME: replace offset with size - v_offset[2 * ptr] = 0//-s * (glyphData[j] - gx) - v_offset[2 * ptr + 1] = 0//-s * (glyphData[j + 1] - gy) - v_color[4 * ptr] = r - v_color[4 * ptr + 1] = g - v_color[4 * ptr + 2] = b - v_color[4 * ptr + 3] = a - v_ids[ptr] = id - - ptr += 1 - } + v_position[2 * ptr] = x + v_position[2 * ptr + 1] = y + v_size[ptr] = s + v_color[4 * ptr] = r + v_color[4 * ptr + 1] = g + v_color[4 * ptr + 2] = b + v_color[4 * ptr + 3] = a + v_ids[ptr] = id + + ptr += 1 var w = borderWidths[id] r = borderColors[4 * id] * 255 g = borderColors[4 * id + 1] * 255 b = borderColors[4 * id + 2] * 255 a = borderColors[4 * id + 3] * 255 - - // for(j = 0; j < border.coords.length; j += 2) { - // v_position[2 * ptr] = x - // v_position[2 * ptr + 1] = y - // v_offset[2 * ptr] = - (s * (border.coords[j] - gx) + w * border.normals[j]) - // v_offset[2 * ptr + 1] = - (s * (border.coords[j + 1] - gy) + w * border.normals[j + 1]) - // v_color[4 * ptr] = r - // v_color[4 * ptr + 1] = g - // v_color[4 * ptr + 2] = b - // v_color[4 * ptr + 3] = a - // v_ids[ptr] = id - - // ptr += 1 - // } } this.numPoints = pointCount @@ -449,7 +425,7 @@ proto.update = function(options) { //generate font atlas //TODO: make step depend on chars number var chars = Object.keys(glyphChars) - var size = 32 + var size = 64 var step = size*2 var maxW = gl.getParameter(gl.MAX_TEXTURE_SIZE) var atlasW = Math.min(maxW, step*chars.length) @@ -462,6 +438,8 @@ proto.update = function(options) { step: [step, step], chars: chars }) + this.charStep = step + this.charSize = size //populate char indexes var cols = atlasW / step @@ -475,7 +453,7 @@ proto.update = function(options) { //update data this.posHiBuffer.update(v_posHi) this.posLoBuffer.update(v_posLo) - this.offsetBuffer.update(v_offset) + this.sizeBuffer.update(v_size) this.colorBuffer.update(v_color) this.idBuffer.update(v_ids) this.charBuffer.update(v_chars) @@ -483,7 +461,7 @@ proto.update = function(options) { pool.free(v_position) pool.free(v_posHi) pool.free(v_posLo) - pool.free(v_offset) + pool.free(v_size) pool.free(v_color) pool.free(v_ids) pool.free(v_chars) @@ -498,7 +476,7 @@ proto.dispose = function() { this.pickShader.dispose() this.posHiBuffer.dispose() this.posLoBuffer.dispose() - this.offsetBuffer.dispose() + this.sizeBuffer.dispose() this.colorBuffer.dispose() this.idBuffer.dispose() this.charBuffer.dispose() @@ -513,7 +491,7 @@ function createFancyScatter2D(plot, options) { var positionHiBuffer = createBuffer(gl) var positionLoBuffer = createBuffer(gl) - var offsetBuffer = createBuffer(gl) + var sizeBuffer = createBuffer(gl) var colorBuffer = createBuffer(gl) var idBuffer = createBuffer(gl) var charBuffer = createBuffer(gl) @@ -524,7 +502,7 @@ function createFancyScatter2D(plot, options) { pickShader, positionHiBuffer, positionLoBuffer, - offsetBuffer, + sizeBuffer, colorBuffer, idBuffer, charBuffer) From 6e60ffd5f1934c07b65990c1b99b88146c41c9f6 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 3 Feb 2017 16:22:32 +0300 Subject: [PATCH 06/41] Clean up & normalize glyphs order --- lib/shaders/frag.glsl | 7 +- scatter-fancy.js | 200 ++++++++---------------------------------- 2 files changed, 42 insertions(+), 165 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 6dc469f..bc73a3a 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -9,8 +9,8 @@ varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; -const float BUFFER = .72; -const float GAMMA = .05; +const float BUFFER = .725; +const float GAMMA = .0275; void main() { vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; @@ -18,8 +18,9 @@ void main() { vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; + //FIXME: step scales with the point size, make sure it remains absolute float alpha = smoothstep(BUFFER - GAMMA, BUFFER + GAMMA, dist); gl_FragColor = vec4(fragColor.rgb, alpha * fragColor.a); - // gl_FragColor = vec4(vec3(dist), 1.); + // gl_FragColor = vec4(vec3(1.-dist), 1.); } diff --git a/scatter-fancy.js b/scatter-fancy.js index a404a4c..e5676f7 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -5,7 +5,6 @@ module.exports = createFancyScatter2D var createShader = require('gl-shader') var createBuffer = require('gl-buffer') var textCache = require('text-cache') -var vectorizeText = require('vectorize-text') var pool = require('typedarray-pool') var shaders = require('./lib/shaders') var snapPoints = require('snap-points-2d') @@ -13,89 +12,6 @@ var atlas = require('font-atlas-sdf') var createTexture = require('gl-texture2d') -var BOUNDARIES = {} - -function getBoundary(glyph) { - if(glyph in BOUNDARIES) { - return BOUNDARIES[glyph] - } - - var polys = vectorizeText(glyph, { - polygons: true, - font: 'sans-serif', - textAlign: 'left', - textBaseline: 'alphabetic' - }) - - var coords = [] - var normals = [] - - polys.forEach(function(loops) { - loops.forEach(function(loop) { - for(var i=0; i < loop.length; ++i) { - var a = loop[(i + loop.length - 1) % loop.length] - var b = loop[i] - var c = loop[(i + 1) % loop.length] - var d = loop[(i + 2) % loop.length] - - var dx = b[0] - a[0] - var dy = b[1] - a[1] - var dl = Math.sqrt(dx * dx + dy * dy) - dx /= dl - dy /= dl - - coords.push(a[0], a[1] + 1.4) - normals.push(dy, -dx) - coords.push(a[0], a[1] + 1.4) - normals.push(-dy, dx) - coords.push(b[0], b[1] + 1.4) - normals.push(-dy, dx) - - coords.push(b[0], b[1] + 1.4) - normals.push(-dy, dx) - coords.push(a[0], a[1] + 1.4) - normals.push(dy, -dx) - coords.push(b[0], b[1] + 1.4) - normals.push(dy, -dx) - - var ex = d[0] - c[0] - var ey = d[1] - c[1] - var el = Math.sqrt(ex * ex + ey * ey) - ex /= el - ey /= el - - coords.push(b[0], b[1] + 1.4) - normals.push(dy, -dx) - coords.push(b[0], b[1] + 1.4) - normals.push(-dy, dx) - coords.push(c[0], c[1] + 1.4) - normals.push(-ey, ex) - - coords.push(c[0], c[1] + 1.4) - normals.push(-ey, ex) - coords.push(b[0], b[1] + 1.4) - normals.push(ey, -ex) - coords.push(c[0], c[1] + 1.4) - normals.push(ey, -ex) - } - }) - }) - - var bounds = [Infinity, Infinity, -Infinity, -Infinity] - for(var i = 0; i < coords.length; i += 2) { - for(var j = 0; j < 2; ++j) { - bounds[j] = Math.min(bounds[j], coords[i + j]) - bounds[2 + j] = Math.max(bounds[2 + j], coords[i + j]) - } - } - - return BOUNDARIES[glyph] = { - coords: coords, - normals: normals, - bounds: bounds - } -} - function GLScatterFancy( plot, shader, @@ -116,8 +32,7 @@ function GLScatterFancy( this.idBuffer = idBuffer this.charBuffer = charBuffer this.bounds = [Infinity, Infinity, -Infinity, -Infinity] - this.numPoints = 0 - this.numVertices = 0 + this.pointCount = 0 this.pickOffset = 0 //positions data @@ -126,13 +41,9 @@ function GLScatterFancy( //lod scales this.scales = [] - //data vertices offsets - this.pointOffset = [] - //font atlas texture this.charCanvas = document.createElement('canvas') this.charTexture = createTexture(this.plot.gl, this.charCanvas) - // this.colorsTexture = createTexture(this.plot.gl, [512, 512]) } var proto = GLScatterFancy.prototype @@ -179,7 +90,7 @@ var proto = GLScatterFancy.prototype pixelSize = Math.min(dataX / screenX, dataY / screenY) - //FIXME: why double? + //FIXME: why twice? PIXEL_SCALE[0] = 2 * pixelRatio / screenX PIXEL_SCALE[1] = 2 * pixelRatio / screenY } @@ -190,9 +101,9 @@ var proto = GLScatterFancy.prototype var pick = offset !== undefined var plot = this.plot - var numVertices = this.numVertices + var pointCount = this.pointCount - if(!numVertices) { + if(!pointCount) { return offset } @@ -219,27 +130,18 @@ var proto = GLScatterFancy.prototype } else { //enable data blending //FIXME: make sure it does not trigger each and every draw call - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.enable(gl.BLEND); gl.disable(gl.DEPTH_TEST); this.colorBuffer.bind() - // shader.attributes.color = [0,1,0,1] + shader.attributes.color.pointer(gl.UNSIGNED_BYTE, true) this.charBuffer.bind() shader.attributes.char.pointer(gl.UNSIGNED_BYTE, false) - // let ctx = this.charCanvas.getContext('2d'); - // ctx.fillStyle = 'red'; - // ctx.fillRect(30,30,4,4); - - //TODO: get rid of recreating texture each fucking draw time - this.charTexture = createTexture(this.plot.gl, this.charCanvas) - shader.uniforms.chars = this.charTexture.bind(0) - // document.body.appendChild(this.charCanvas) - //TODO: get rid of this resetting each redraw time - this.charTexture.setPixels(this.charCanvas) + shader.uniforms.chars = this.charTexture.bind() shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] shader.uniforms.charsStep = this.charStep; @@ -273,8 +175,8 @@ var proto = GLScatterFancy.prototype var intervalStart = lod.offset var intervalEnd = lod.count + intervalStart - var startOffset = this.pointOffset[intervalStart] - var endOffset = this.pointOffset[intervalEnd] || numVertices + var startOffset = intervalStart + var endOffset = intervalEnd || pointCount //TODO: we can shave off even more by slicing by left/right limits, see gl-scatter2d. Points are arranged by x coordinate so just calc bounds @@ -283,7 +185,7 @@ var proto = GLScatterFancy.prototype } } - if(pick) return offset + this.numPoints + if(pick) return offset + this.pointCount } })() @@ -291,7 +193,7 @@ proto.draw = proto.drawPick proto.pick = function(x, y, value) { var pickOffset = this.pickOffset - var pointCount = this.numPoints + var pointCount = this.pointCount if(value < pickOffset || value >= pickOffset + pointCount) { return null } @@ -326,59 +228,30 @@ proto.update = function(options) { packed.set(this.points) this.scales = snapPoints(packed, packedId, packedW, this.bounds) + //FIXME: figure out what these bounds are about or get rid of them var bounds = this.bounds = [Infinity, Infinity, -Infinity, -Infinity] - var numVertices = 0 - - var glyphMeshes = Array(pointCount) - var glyphBoundaries = Array(pointCount) - var glyph, border, glyphData bounds[0] = 0 bounds[1] = 0 bounds[2] = 1 bounds[3] = 1 - for(i = 0; i < pointCount; ++i) { - var id = packedId[i] - glyph = textCache('sans-serif', glyphs[id]) - border = getBoundary(glyphs[id]) - glyphMeshes[id] = glyph - glyphBoundaries[id] = border - // numVertices += (glyph.data.length + border.coords.length) >> 1 - numVertices += 1 - // for(j = 0; j < 2; ++j) { - // bounds[j] = Math.min(bounds[j], positions[2 * id + j]) - // bounds[2 + j] = Math.max(bounds[2 + j], positions[2 * id + j]) - // } - } - - // if(bounds[0] === bounds[2]) { - // bounds[2] += 1 - // } - // if(bounds[3] === bounds[1]) { - // bounds[3] += 1 - // } - var sx = 1 / (bounds[2] - bounds[0]) var sy = 1 / (bounds[3] - bounds[1]) var tx = bounds[0] var ty = bounds[1] //v_position contains normalized positions to the available range of positions - var v_position = pool.mallocFloat64(2 * numVertices) - var v_posHi = pool.mallocFloat32(2 * numVertices) - var v_posLo = pool.mallocFloat32(2 * numVertices) - var v_size = pool.mallocFloat32(numVertices) - var v_color = pool.mallocUint8(4 * numVertices) - var v_ids = pool.mallocUint32(numVertices) - var v_chars = pool.mallocUint8(2 * numVertices) + var v_position = pool.mallocFloat64(2 * pointCount) + var v_posHi = pool.mallocFloat32(2 * pointCount) + var v_posLo = pool.mallocFloat32(2 * pointCount) + var v_size = pool.mallocFloat32(pointCount) + var v_color = pool.mallocUint8(4 * pointCount) + var v_ids = pool.mallocUint32(pointCount) + var v_chars = pool.mallocUint8(2 * pointCount) var ptr = 0 - this.pointOffset.length = pointCount - for(i = 0; i < pointCount; ++i) { - this.pointOffset[i] = ptr - var id = packedId[i] var x = sx * (positions[2 * id] - tx) var y = sy * (positions[2 * id + 1] - ty) @@ -388,16 +261,14 @@ proto.update = function(options) { var b = colors[4 * id + 2] * 255 var a = colors[4 * id + 3] * 255 - v_position[2 * ptr] = x - v_position[2 * ptr + 1] = y - v_size[ptr] = s - v_color[4 * ptr] = r - v_color[4 * ptr + 1] = g - v_color[4 * ptr + 2] = b - v_color[4 * ptr + 3] = a - v_ids[ptr] = id - - ptr += 1 + v_position[2 * i] = x + v_position[2 * i + 1] = y + v_size[i] = s + v_color[4 * i] = r + v_color[4 * i + 1] = g + v_color[4 * i + 2] = b + v_color[4 * i + 3] = a + v_ids[i] = id var w = borderWidths[id] r = borderColors[4 * id] * 255 @@ -406,9 +277,9 @@ proto.update = function(options) { a = borderColors[4 * id + 3] * 255 } - this.numPoints = pointCount - this.numVertices = numVertices + this.pointCount = pointCount + //collect hi-precition tails v_posHi.set(v_position) for(i = 0; i < v_position.length; i++) v_posLo[i] = v_position[i] - v_posHi[i] @@ -423,9 +294,9 @@ proto.update = function(options) { } //generate font atlas - //TODO: make step depend on chars number + //TODO: make size depend on chars number/max size of a point var chars = Object.keys(glyphChars) - var size = 64 + var size = 128 var step = size*2 var maxW = gl.getParameter(gl.MAX_TEXTURE_SIZE) var atlasW = Math.min(maxW, step*chars.length) @@ -438,13 +309,15 @@ proto.update = function(options) { step: [step, step], chars: chars }) + // document.body.appendChild(this.charCanvas) this.charStep = step this.charSize = size //populate char indexes var cols = atlasW / step for (var i = 0; i < pointCount; i++) { - var char = glyphs[i] + var idx = packedId[i] + var char = glyphs[idx] var charIdx = glyphChars[char] v_chars[2*i + 1] = Math.floor(charIdx / cols) v_chars[2*i] = charIdx % cols @@ -458,6 +331,10 @@ proto.update = function(options) { this.idBuffer.update(v_ids) this.charBuffer.update(v_chars) + //create char texture + this.charTexture.shape = [this.charCanvas.width, this.charCanvas.height] + this.charTexture.setPixels(this.charCanvas) + pool.free(v_position) pool.free(v_posHi) pool.free(v_posLo) @@ -465,7 +342,6 @@ proto.update = function(options) { pool.free(v_color) pool.free(v_ids) pool.free(v_chars) - pool.free(packed) pool.free(packedId) pool.free(packedW) From 67ecda91a27641eb93f8e15000eb36ef32ba4fe8 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 3 Feb 2017 16:40:36 +0300 Subject: [PATCH 07/41] Remove redundant test --- test.js | 58 --------------------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 test.js diff --git a/test.js b/test.js deleted file mode 100644 index 7a9eb4b..0000000 --- a/test.js +++ /dev/null @@ -1,58 +0,0 @@ -const atlasSDF = require('font-atlas-sdf') -const atlas = require('font-atlas') -const createPanel = require('settings-panel') -const assign = require('object-assign') - -let c1 = document.body.appendChild(document.createElement('canvas')) -let c2 = document.body.appendChild(document.createElement('canvas')) - -let opts = { - family: 'sans-serif', - size: 32, - chars: '●#✝+' -} - -function update (o) { - console.time('sdf') - assign(opts, o) - - let w = [Math.min(400, opts.size*16)] - let size = opts.size - - atlasSDF({ - canvas: c1, - family: opts.family - , size: opts.size - , shape: [w,w] - , step: [size*2, size*2], - chars: opts.chars - }) - console.timeEnd('sdf') - - - console.time('bm') - atlas({ - canvas: c2, - family: opts.family - , size: opts.size - , shape: [w,w] - , step: [size*2, size*2], - chars: opts.chars - }) - console.timeEnd('bm') -} - -createPanel([ -{id: 'size', type: 'range', min: 1, max: 128, value: opts.size, step: 1, change: v => { - update({size: v}) -}}, -{id: 'family', type: 'text', value: opts.family, change: v => { - update({family: v}) -}}, -{id: 'chars', type: 'text', value: opts.chars, change: v => { - update({chars: v}) -}} -// {id: 'step', type: 'range', min: 1, max: 128, value: 21, step: 1, change: v => { -// update({size: v}) -// }} -]) From 02f9ee8a2aabcd52b96d5769924fdd2a17dff553 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 3 Feb 2017 19:47:06 +0300 Subject: [PATCH 08/41] Ensure max number of chars --- lib/shaders/frag.glsl | 8 ++++---- scatter-fancy.js | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index bc73a3a..d80d446 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -9,17 +9,17 @@ varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; -const float BUFFER = .725; -const float GAMMA = .0275; - void main() { vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; pointUV.x = 1. - pointUV.x; vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; + float buffer = .725; + float gamma = .0275; + //FIXME: step scales with the point size, make sure it remains absolute - float alpha = smoothstep(BUFFER - GAMMA, BUFFER + GAMMA, dist); + float alpha = smoothstep(buffer - gamma, buffer + gamma, dist); gl_FragColor = vec4(fragColor.rgb, alpha * fragColor.a); // gl_FragColor = vec4(vec3(1.-dist), 1.); diff --git a/scatter-fancy.js b/scatter-fancy.js index e5676f7..34feb4d 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -299,8 +299,12 @@ proto.update = function(options) { var size = 128 var step = size*2 var maxW = gl.getParameter(gl.MAX_TEXTURE_SIZE) + var maxChars = (maxW / step) * (maxW / step) var atlasW = Math.min(maxW, step*chars.length) - var atlasH = Math.min(maxW, step*Math.floor(step*chars.length/atlasW)) + var atlasH = Math.min(maxW, step*Math.ceil(step*chars.length/maxW)) + if (chars.length > maxChars) { + console.warn('gl-scatter2d-fancy: number of characters is more than maximum texture size. Try reducing it.') + } this.charCanvas = atlas({ canvas: this.charCanvas, family: 'sans-serif', @@ -309,7 +313,6 @@ proto.update = function(options) { step: [step, step], chars: chars }) - // document.body.appendChild(this.charCanvas) this.charStep = step this.charSize = size From ad2327acd41bf85708588d5429bcf5404ced91ea Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 8 Feb 2017 11:22:21 +0300 Subject: [PATCH 09/41] Make edge absolute --- lib/shaders/frag.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index d80d446..d608415 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -16,7 +16,7 @@ void main() { float dist = texture2D(chars, texCoord).r; float buffer = .725; - float gamma = .0275; + float gamma = .0275 * 50. / pointSize; //FIXME: step scales with the point size, make sure it remains absolute float alpha = smoothstep(buffer - gamma, buffer + gamma, dist); From e5abb9dcaf7eea6131cb769e635df63a37e94e21 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 8 Feb 2017 21:01:12 +0300 Subject: [PATCH 10/41] Generate color palette --- lib/shaders/frag.glsl | 14 ++--- lib/shaders/vertex.glsl | 9 ++- package.json | 1 + scatter-fancy.js | 135 ++++++++++++++++++++++++---------------- 4 files changed, 97 insertions(+), 62 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index d608415..86a3755 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -4,7 +4,8 @@ uniform sampler2D chars; uniform vec2 charsShape; uniform float charsStep; -varying vec4 fragColor; +varying vec4 borderColor; +varying vec4 charColor; varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; @@ -13,14 +14,13 @@ void main() { vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; pointUV.x = 1. - pointUV.x; vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; - float dist = texture2D(chars, texCoord).r; + float dist = texture2D(chars, texCoord).r; - float buffer = .725; - float gamma = .0275 * 50. / pointSize; + float buffer = .725; + float gamma = .0275 * 50. / pointSize; - //FIXME: step scales with the point size, make sure it remains absolute float alpha = smoothstep(buffer - gamma, buffer + gamma, dist); - gl_FragColor = vec4(fragColor.rgb, alpha * fragColor.a); + gl_FragColor = vec4(charColor.rgb, alpha * charColor.a); - // gl_FragColor = vec4(vec3(1.-dist), 1.); + // gl_FragColor = vec4(vec3(1.-dist), 1.); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 0a6ed76..d7ac896 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -3,14 +3,16 @@ precision highp float; attribute vec2 positionHi, positionLo; attribute float size; attribute vec2 char; -attribute vec4 color; +attribute vec2 color; //this is 64-bit form of scale and translate uniform vec2 scaleHi, scaleLo, translateHi, translateLo; uniform vec2 pixelScale; uniform vec4 viewBox; +uniform sampler2D palette; -varying vec4 fragColor; +varying vec4 charColor; +varying vec4 borderColor; varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; @@ -19,7 +21,8 @@ varying vec2 position; #pragma glslify: computePosition = require("./xform.glsl") void main() { - fragColor = color; + charColor = texture2D(palette, vec2(color.x / 255., 0)); + borderColor = texture2D(palette, vec2(color.y / 255., 0)); gl_PointSize = size*2.; pointSize = size*2.; diff --git a/package.json b/package.json index c170681..51399d4 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "homepage": "https://github.com/gl-vis/gl-scatter2d-fancy#readme", "dependencies": { + "color-id": "^1.0.0", "font-atlas-sdf": "^1.0.0", "gl-buffer": "^2.1.2", "gl-shader": "^4.2.1", diff --git a/scatter-fancy.js b/scatter-fancy.js index 34feb4d..9c47047 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -10,7 +10,8 @@ var shaders = require('./lib/shaders') var snapPoints = require('snap-points-2d') var atlas = require('font-atlas-sdf') var createTexture = require('gl-texture2d') - +var colorId = require('color-id') +var ndarray = require('ndarray') function GLScatterFancy( plot, @@ -25,12 +26,15 @@ function GLScatterFancy( this.plot = plot this.shader = shader this.pickShader = pickShader + + //buffers this.posHiBuffer = positionHiBuffer this.posLoBuffer = positionLoBuffer this.sizeBuffer = sizeBuffer this.colorBuffer = colorBuffer this.idBuffer = idBuffer this.charBuffer = charBuffer + this.bounds = [Infinity, Infinity, -Infinity, -Infinity] this.pointCount = 0 this.pickOffset = 0 @@ -42,8 +46,12 @@ function GLScatterFancy( this.scales = [] //font atlas texture - this.charCanvas = document.createElement('canvas') - this.charTexture = createTexture(this.plot.gl, this.charCanvas) + this.charCanvas = document.createElement('canvas') + this.charTexture = createTexture(this.plot.gl, this.charCanvas) + this.charStep = 400 + + //border/char colors texture + this.paletteTexture = createTexture(this.plot.gl, [256, 1]) } var proto = GLScatterFancy.prototype @@ -135,15 +143,15 @@ var proto = GLScatterFancy.prototype gl.disable(gl.DEPTH_TEST); this.colorBuffer.bind() - - shader.attributes.color.pointer(gl.UNSIGNED_BYTE, true) + shader.attributes.color.pointer(gl.UNSIGNED_BYTE, false) this.charBuffer.bind() shader.attributes.char.pointer(gl.UNSIGNED_BYTE, false) - shader.uniforms.chars = this.charTexture.bind() + shader.uniforms.chars = this.charTexture.bind(0) shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] shader.uniforms.charsStep = this.charStep; + shader.uniforms.palette = this.paletteTexture.bind(1) this.sizeBuffer.bind() shader.attributes.size.pointer() @@ -228,6 +236,8 @@ proto.update = function(options) { packed.set(this.points) this.scales = snapPoints(packed, packedId, packedW, this.bounds) + this.pointCount = pointCount + //FIXME: figure out what these bounds are about or get rid of them var bounds = this.bounds = [Infinity, Infinity, -Infinity, -Infinity] @@ -246,44 +256,39 @@ proto.update = function(options) { var v_posHi = pool.mallocFloat32(2 * pointCount) var v_posLo = pool.mallocFloat32(2 * pointCount) var v_size = pool.mallocFloat32(pointCount) - var v_color = pool.mallocUint8(4 * pointCount) + var v_color = pool.mallocUint8(2 * pointCount) var v_ids = pool.mallocUint32(pointCount) var v_chars = pool.mallocUint8(2 * pointCount) var ptr = 0 - for(i = 0; i < pointCount; ++i) { - var id = packedId[i] - var x = sx * (positions[2 * id] - tx) - var y = sy * (positions[2 * id + 1] - ty) - var s = sizes[id] - var r = colors[4 * id] * 255 - var g = colors[4 * id + 1] * 255 - var b = colors[4 * id + 2] * 255 - var a = colors[4 * id + 3] * 255 - - v_position[2 * i] = x - v_position[2 * i + 1] = y - v_size[i] = s - v_color[4 * i] = r - v_color[4 * i + 1] = g - v_color[4 * i + 2] = b - v_color[4 * i + 3] = a - v_ids[i] = id - - var w = borderWidths[id] - r = borderColors[4 * id] * 255 - g = borderColors[4 * id + 1] * 255 - b = borderColors[4 * id + 2] * 255 - a = borderColors[4 * id + 3] * 255 + //aggregate colors + var paletteIds = {}, colorIds = [], paletteColors = [], bColorIds = [] + for (var i = 0, l = pointCount, k = 0; i < l; ++i) { + var channels = [colors[4 * i] * 255, colors[4 * i + 1] * 255, colors[4 * i + 2] * 255, colors[4 * i + 3] * 255] + var cId = colorId(channels, false) + if (paletteIds[cId] == null) { + paletteIds[cId] = k++ + paletteColors.push(channels[0]) + paletteColors.push(channels[1]) + paletteColors.push(channels[2]) + paletteColors.push(channels[3]) + } + colorIds.push(cId) + + if (borderColors && borderColors.length) { + channels = [borderColors[4 * i] * 255, borderColors[4 * i + 1] * 255, borderColors[4 * i + 2] * 255, borderColors[4 * i + 3] * 255] + cId = colorId(channels, false) + if (paletteIds[cId] == null) { + paletteIds[cId] = k++ + paletteColors.push(channels[0]) + paletteColors.push(channels[1]) + paletteColors.push(channels[2]) + paletteColors.push(channels[3]) + } + bColorIds.push(cId) + } } - this.pointCount = pointCount - - //collect hi-precition tails - v_posHi.set(v_position) - for(i = 0; i < v_position.length; i++) - v_posLo[i] = v_position[i] - v_posHi[i] - //aggregate glyphs var glyphChars = {}; for (var i = 0, l = pointCount, k = 0; i < l; i++) { @@ -296,12 +301,13 @@ proto.update = function(options) { //generate font atlas //TODO: make size depend on chars number/max size of a point var chars = Object.keys(glyphChars) - var size = 128 - var step = size*2 + var step = this.charStep + var size = Math.floor(step / 2) var maxW = gl.getParameter(gl.MAX_TEXTURE_SIZE) var maxChars = (maxW / step) * (maxW / step) var atlasW = Math.min(maxW, step*chars.length) var atlasH = Math.min(maxW, step*Math.ceil(step*chars.length/maxW)) + var cols = atlasW / step if (chars.length > maxChars) { console.warn('gl-scatter2d-fancy: number of characters is more than maximum texture size. Try reducing it.') } @@ -313,20 +319,44 @@ proto.update = function(options) { step: [step, step], chars: chars }) - this.charStep = step - this.charSize = size - //populate char indexes - var cols = atlasW / step - for (var i = 0; i < pointCount; i++) { - var idx = packedId[i] - var char = glyphs[idx] - var charIdx = glyphChars[char] - v_chars[2*i + 1] = Math.floor(charIdx / cols) - v_chars[2*i] = charIdx % cols + //collect buffers data + for(i = 0; i < pointCount; ++i) { + var id = packedId[i] + var x = sx * (positions[2 * id] - tx) + var y = sy * (positions[2 * id + 1] - ty) + var s = sizes[id] + + v_position[2 * i] = x + v_position[2 * i + 1] = y + v_size[i] = s + + v_ids[i] = id + + var w = borderWidths[id] + + //color/bufferColor indexes + var cId = colorIds[id] + var pcId = paletteIds[cId] + v_color[2 * i] = pcId + var bcId = bColorIds[id] + var pbcId = paletteIds[bcId] + v_color[2 * i + 1] = pbcId + + //char indexes + var char = glyphs[id] + var charId = glyphChars[char] + v_chars[2 * i + 1] = Math.floor(charId / cols) + v_chars[2 * i] = charId % cols } - //update data + + //collect hi-precition tails + v_posHi.set(v_position) + for(i = 0; i < v_position.length; i++) + v_posLo[i] = v_position[i] - v_posHi[i] + + //fill buffes this.posHiBuffer.update(v_posHi) this.posLoBuffer.update(v_posLo) this.sizeBuffer.update(v_size) @@ -334,9 +364,10 @@ proto.update = function(options) { this.idBuffer.update(v_ids) this.charBuffer.update(v_chars) - //create char texture + //update char/color textures this.charTexture.shape = [this.charCanvas.width, this.charCanvas.height] this.charTexture.setPixels(this.charCanvas) + this.paletteTexture.setPixels(ndarray(paletteColors.slice(0, 256*4), [256, 1, 4])) pool.free(v_position) pool.free(v_posHi) From 866038fd45ee52735283653496c48778a313fcc2 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 8 Feb 2017 21:49:30 +0300 Subject: [PATCH 11/41] Render borders --- lib/shaders/frag.glsl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 86a3755..072cec4 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -10,17 +10,25 @@ varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; +const float borderWidth = 1.; + void main() { vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; pointUV.x = 1. - pointUV.x; vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; - float buffer = .725; - float gamma = .0275 * 50. / pointSize; + // border color + float dif = borderWidth / pointSize; + float borderLevel = .75 - dif; + float charLevel = .75 + dif; + float gamma = .005 * charsStep / pointSize; + + float alpha = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); + float mixAmount = smoothstep(charLevel - gamma, charLevel + gamma, dist); - float alpha = smoothstep(buffer - gamma, buffer + gamma, dist); - gl_FragColor = vec4(charColor.rgb, alpha * charColor.a); + //mix border color and color + gl_FragColor = vec4(mix(charColor.rgb, borderColor.rgb, 1. - mixAmount), alpha * borderColor.a); // gl_FragColor = vec4(vec3(1.-dist), 1.); } From b47cc45049ed58bdd741f8c80242df43d721bb82 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 8 Feb 2017 22:24:28 +0300 Subject: [PATCH 12/41] Squash size and width into single buffer --- lib/shaders/frag.glsl | 11 +++++------ lib/shaders/vertex.glsl | 7 ++++--- scatter-fancy.js | 36 ++++++++++++++++++------------------ 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 072cec4..230f831 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -9,8 +9,7 @@ varying vec4 charColor; varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; - -const float borderWidth = 1.; +varying float borderWidth; void main() { vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; @@ -19,16 +18,16 @@ void main() { float dist = texture2D(chars, texCoord).r; // border color - float dif = borderWidth / pointSize; - float borderLevel = .75 - dif; - float charLevel = .75 + dif; + float dif = 2. * borderWidth / pointSize; + float borderLevel = .75 - dif * .8; + float charLevel = .75 + dif * .2; float gamma = .005 * charsStep / pointSize; float alpha = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); float mixAmount = smoothstep(charLevel - gamma, charLevel + gamma, dist); //mix border color and color - gl_FragColor = vec4(mix(charColor.rgb, borderColor.rgb, 1. - mixAmount), alpha * borderColor.a); + gl_FragColor = vec4(mix(charColor.rgb, borderColor.rgb, 1. - mixAmount), alpha * borderColor.a * charColor.a); // gl_FragColor = vec4(vec3(1.-dist), 1.); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index d7ac896..4e0cecc 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -1,9 +1,8 @@ precision highp float; attribute vec2 positionHi, positionLo; -attribute float size; -attribute vec2 char; -attribute vec2 color; +attribute float size, border; +attribute vec2 char, color; //this is 64-bit form of scale and translate uniform vec2 scaleHi, scaleLo, translateHi, translateLo; @@ -17,6 +16,7 @@ varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; varying vec2 position; +varying float borderWidth; #pragma glslify: computePosition = require("./xform.glsl") @@ -28,6 +28,7 @@ void main() { pointSize = size*2.; charOffset = char; + borderWidth = border; gl_Position = computePosition( positionHi, positionLo, diff --git a/scatter-fancy.js b/scatter-fancy.js index 9c47047..db34040 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -154,7 +154,8 @@ var proto = GLScatterFancy.prototype shader.uniforms.palette = this.paletteTexture.bind(1) this.sizeBuffer.bind() - shader.attributes.size.pointer() + shader.attributes.size.pointer(gl.UNSIGNED_BYTE, false, 2, 0) + shader.attributes.border.pointer(gl.UNSIGNED_BYTE, false, 2, 1) } this.posHiBuffer.bind() @@ -252,13 +253,13 @@ proto.update = function(options) { var ty = bounds[1] //v_position contains normalized positions to the available range of positions - var v_position = pool.mallocFloat64(2 * pointCount) - var v_posHi = pool.mallocFloat32(2 * pointCount) - var v_posLo = pool.mallocFloat32(2 * pointCount) - var v_size = pool.mallocFloat32(pointCount) - var v_color = pool.mallocUint8(2 * pointCount) - var v_ids = pool.mallocUint32(pointCount) - var v_chars = pool.mallocUint8(2 * pointCount) + var v_position = pool.mallocFloat64(2 * pointCount) + var v_posHi = pool.mallocFloat32(2 * pointCount) + var v_posLo = pool.mallocFloat32(2 * pointCount) + var v_sizeWidth = pool.mallocUint8(2 * pointCount) + var v_color = pool.mallocUint8(2 * pointCount) + var v_ids = pool.mallocUint32(pointCount) + var v_chars = pool.mallocUint8(2 * pointCount) var ptr = 0 //aggregate colors @@ -326,15 +327,14 @@ proto.update = function(options) { var x = sx * (positions[2 * id] - tx) var y = sy * (positions[2 * id + 1] - ty) var s = sizes[id] - - v_position[2 * i] = x - v_position[2 * i + 1] = y - v_size[i] = s - - v_ids[i] = id - var w = borderWidths[id] + v_position[2 * i] = x + v_position[2 * i + 1] = y + v_sizeWidth[2 * i] = s + v_sizeWidth[2 * i + 1] = w + v_ids[i] = id + //color/bufferColor indexes var cId = colorIds[id] var pcId = paletteIds[cId] @@ -359,7 +359,7 @@ proto.update = function(options) { //fill buffes this.posHiBuffer.update(v_posHi) this.posLoBuffer.update(v_posLo) - this.sizeBuffer.update(v_size) + this.sizeBuffer.update(v_sizeWidth) this.colorBuffer.update(v_color) this.idBuffer.update(v_ids) this.charBuffer.update(v_chars) @@ -372,7 +372,7 @@ proto.update = function(options) { pool.free(v_position) pool.free(v_posHi) pool.free(v_posLo) - pool.free(v_size) + pool.free(v_sizeWidth) pool.free(v_color) pool.free(v_ids) pool.free(v_chars) @@ -401,7 +401,7 @@ function createFancyScatter2D(plot, options) { var positionHiBuffer = createBuffer(gl) var positionLoBuffer = createBuffer(gl) - var sizeBuffer = createBuffer(gl) + var sizeBuffer = createBuffer(gl) var colorBuffer = createBuffer(gl) var idBuffer = createBuffer(gl) var charBuffer = createBuffer(gl) From 28b65e02f45f81370810eaa5a356eb8551256f44 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 8 Feb 2017 22:55:37 +0300 Subject: [PATCH 13/41] Squash position buffer --- scatter-fancy.js | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index db34040..5279e2c 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -17,8 +17,7 @@ function GLScatterFancy( plot, shader, pickShader, - positionHiBuffer, - positionLoBuffer, + positionBuffer, sizeBuffer, colorBuffer, idBuffer, @@ -28,8 +27,7 @@ function GLScatterFancy( this.pickShader = pickShader //buffers - this.posHiBuffer = positionHiBuffer - this.posLoBuffer = positionLoBuffer + this.positionBuffer = positionBuffer this.sizeBuffer = sizeBuffer this.colorBuffer = colorBuffer this.idBuffer = idBuffer @@ -158,11 +156,9 @@ var proto = GLScatterFancy.prototype shader.attributes.border.pointer(gl.UNSIGNED_BYTE, false, 2, 1) } - this.posHiBuffer.bind() - shader.attributes.positionHi.pointer() - - this.posLoBuffer.bind() - shader.attributes.positionLo.pointer() + this.positionBuffer.bind() + shader.attributes.positionHi.pointer(gl.FLOAT, false, 16, 0) + shader.attributes.positionLo.pointer(gl.FLOAT, false, 16, 8) shader.uniforms.pixelScale = PIXEL_SCALE shader.uniforms.scaleHi = SCALE_HI @@ -253,9 +249,7 @@ proto.update = function(options) { var ty = bounds[1] //v_position contains normalized positions to the available range of positions - var v_position = pool.mallocFloat64(2 * pointCount) - var v_posHi = pool.mallocFloat32(2 * pointCount) - var v_posLo = pool.mallocFloat32(2 * pointCount) + var v_position = pool.mallocFloat32(4 * pointCount) var v_sizeWidth = pool.mallocUint8(2 * pointCount) var v_color = pool.mallocUint8(2 * pointCount) var v_ids = pool.mallocUint32(pointCount) @@ -329,8 +323,12 @@ proto.update = function(options) { var s = sizes[id] var w = borderWidths[id] - v_position[2 * i] = x - v_position[2 * i + 1] = y + //write hi- and lo- position parts + v_position[4 * i] = x + v_position[4 * i + 1] = y + v_position[4 * i + 2] = x - v_position[4 * i] + v_position[4 * i + 3] = y - v_position[4 * i + 1] + v_sizeWidth[2 * i] = s v_sizeWidth[2 * i + 1] = w v_ids[i] = id @@ -351,14 +349,8 @@ proto.update = function(options) { } - //collect hi-precition tails - v_posHi.set(v_position) - for(i = 0; i < v_position.length; i++) - v_posLo[i] = v_position[i] - v_posHi[i] - //fill buffes - this.posHiBuffer.update(v_posHi) - this.posLoBuffer.update(v_posLo) + this.positionBuffer.update(v_position) this.sizeBuffer.update(v_sizeWidth) this.colorBuffer.update(v_color) this.idBuffer.update(v_ids) @@ -370,8 +362,6 @@ proto.update = function(options) { this.paletteTexture.setPixels(ndarray(paletteColors.slice(0, 256*4), [256, 1, 4])) pool.free(v_position) - pool.free(v_posHi) - pool.free(v_posLo) pool.free(v_sizeWidth) pool.free(v_color) pool.free(v_ids) @@ -384,8 +374,7 @@ proto.update = function(options) { proto.dispose = function() { this.shader.dispose() this.pickShader.dispose() - this.posHiBuffer.dispose() - this.posLoBuffer.dispose() + this.positionBuffer.dispose() this.sizeBuffer.dispose() this.colorBuffer.dispose() this.idBuffer.dispose() @@ -399,8 +388,7 @@ function createFancyScatter2D(plot, options) { var shader = createShader(gl, shaders.vertex, shaders.fragment) var pickShader = createShader(gl, shaders.pickVertex, shaders.pickFragment) - var positionHiBuffer = createBuffer(gl) - var positionLoBuffer = createBuffer(gl) + var positionBuffer = createBuffer(gl) var sizeBuffer = createBuffer(gl) var colorBuffer = createBuffer(gl) var idBuffer = createBuffer(gl) @@ -410,8 +398,7 @@ function createFancyScatter2D(plot, options) { plot, shader, pickShader, - positionHiBuffer, - positionLoBuffer, + positionBuffer, sizeBuffer, colorBuffer, idBuffer, From 360e16c9aa12766c43c22f098254dda9c0a378f6 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 00:03:53 +0300 Subject: [PATCH 14/41] Automatically detect step, some optimizations --- lib/shaders/frag.glsl | 7 +++---- lib/shaders/vertex.glsl | 3 ++- package.json | 1 + scatter-fancy.js | 17 ++++++++--------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 230f831..9b480e3 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -18,7 +18,7 @@ void main() { float dist = texture2D(chars, texCoord).r; // border color - float dif = 2. * borderWidth / pointSize; + float dif = 5. * borderWidth / pointSize; float borderLevel = .75 - dif * .8; float charLevel = .75 + dif * .2; float gamma = .005 * charsStep / pointSize; @@ -27,7 +27,6 @@ void main() { float mixAmount = smoothstep(charLevel - gamma, charLevel + gamma, dist); //mix border color and color - gl_FragColor = vec4(mix(charColor.rgb, borderColor.rgb, 1. - mixAmount), alpha * borderColor.a * charColor.a); - - // gl_FragColor = vec4(vec3(1.-dist), 1.); + vec4 color = mix(borderColor, charColor, mixAmount); + gl_FragColor = vec4(color.rgb, alpha * color.a); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 4e0cecc..0b3336e 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -1,5 +1,7 @@ precision highp float; +#pragma glslify: computePosition = require("./xform.glsl") + attribute vec2 positionHi, positionLo; attribute float size, border; attribute vec2 char, color; @@ -18,7 +20,6 @@ varying float pointSize; varying vec2 position; varying float borderWidth; -#pragma glslify: computePosition = require("./xform.glsl") void main() { charColor = texture2D(palette, vec2(color.x / 255., 0)); diff --git a/package.json b/package.json index 51399d4..20c5fb7 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "homepage": "https://github.com/gl-vis/gl-scatter2d-fancy#readme", "dependencies": { + "clamp": "^1.0.1", "color-id": "^1.0.0", "font-atlas-sdf": "^1.0.0", "gl-buffer": "^2.1.2", diff --git a/scatter-fancy.js b/scatter-fancy.js index 5279e2c..0056893 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -12,6 +12,7 @@ var atlas = require('font-atlas-sdf') var createTexture = require('gl-texture2d') var colorId = require('color-id') var ndarray = require('ndarray') +var clamp = require('clamp') function GLScatterFancy( plot, @@ -134,11 +135,8 @@ var proto = GLScatterFancy.prototype shader.attributes.id.pointer(gl.UNSIGNED_BYTE, false) } else { - //enable data blending - //FIXME: make sure it does not trigger each and every draw call gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.enable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); this.colorBuffer.bind() shader.attributes.color.pointer(gl.UNSIGNED_BYTE, false) @@ -236,12 +234,7 @@ proto.update = function(options) { this.pointCount = pointCount //FIXME: figure out what these bounds are about or get rid of them - var bounds = this.bounds = [Infinity, Infinity, -Infinity, -Infinity] - - bounds[0] = 0 - bounds[1] = 0 - bounds[2] = 1 - bounds[3] = 1 + var bounds = this.bounds = [0, 0, 1, 1] var sx = 1 / (bounds[2] - bounds[0]) var sy = 1 / (bounds[3] - bounds[1]) @@ -295,6 +288,12 @@ proto.update = function(options) { //generate font atlas //TODO: make size depend on chars number/max size of a point + var maxSize = 0; + for (var i = 0, l = sizes.length; i < l; ++i) { + if (sizes[i] > maxSize) maxSize = sizes[i]; + } + this.charStep = clamp(Math.ceil(maxSize*5), 128, 512) + var chars = Object.keys(glyphChars) var step = this.charStep var size = Math.floor(step / 2) From 9f24234eef0c3128840a78d011a61a28322386bf Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 01:01:14 +0300 Subject: [PATCH 15/41] Fix deps --- package.json | 1 + scatter-fancy.js | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 20c5fb7..dca16ad 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "gl-shader": "^4.2.1", "gl-texture2d": "^2.1.0", "glslify": "^2.3.1", + "snap-points-2d": "^3.1.0", "typedarray-pool": "^1.1.0" } } diff --git a/scatter-fancy.js b/scatter-fancy.js index 0056893..2a043da 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -4,7 +4,6 @@ module.exports = createFancyScatter2D var createShader = require('gl-shader') var createBuffer = require('gl-buffer') -var textCache = require('text-cache') var pool = require('typedarray-pool') var shaders = require('./lib/shaders') var snapPoints = require('snap-points-2d') @@ -287,7 +286,6 @@ proto.update = function(options) { } //generate font atlas - //TODO: make size depend on chars number/max size of a point var maxSize = 0; for (var i = 0, l = sizes.length; i < l; ++i) { if (sizes[i] > maxSize) maxSize = sizes[i]; From 0e1de2b24bee5effec5c527e542ef9aa22688c09 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 01:17:55 +0300 Subject: [PATCH 16/41] Fix canvas texture error --- scatter-fancy.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index 2a043da..00ebf67 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -355,7 +355,9 @@ proto.update = function(options) { //update char/color textures this.charTexture.shape = [this.charCanvas.width, this.charCanvas.height] - this.charTexture.setPixels(this.charCanvas) + if (this.charCanvas && this.charCanvas.width) { + this.charTexture.setPixels(this.charCanvas) + } this.paletteTexture.setPixels(ndarray(paletteColors.slice(0, 256*4), [256, 1, 4])) pool.free(v_position) From fe542e5654fbf6c539045424828a64aa8476cae1 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 01:20:27 +0300 Subject: [PATCH 17/41] Discard zero-dist --- lib/shaders/frag.glsl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 9b480e3..d60d556 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -17,6 +17,9 @@ void main() { vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; + if (dist == 0.) + discard; + // border color float dif = 5. * borderWidth / pointSize; float borderLevel = .75 - dif * .8; From c64eeb506e25ef42f515acb86592fb350ddf7194 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 01:36:22 +0300 Subject: [PATCH 18/41] Fix edge cases --- lib/shaders/frag.glsl | 2 +- lib/shaders/vertex.glsl | 4 ++-- scatter-fancy.js | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index d60d556..ba0124c 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -17,7 +17,7 @@ void main() { vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; - if (dist == 0.) + if (dist < 1e-3) discard; // border color diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 0b3336e..09a8c0b 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -25,8 +25,8 @@ void main() { charColor = texture2D(palette, vec2(color.x / 255., 0)); borderColor = texture2D(palette, vec2(color.y / 255., 0)); - gl_PointSize = size*2.; - pointSize = size*2.; + gl_PointSize = max(size*2., 10.); + pointSize = max(size*2., 10.); charOffset = char; borderWidth = border; diff --git a/scatter-fancy.js b/scatter-fancy.js index 00ebf67..b04bac3 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -134,8 +134,8 @@ var proto = GLScatterFancy.prototype shader.attributes.id.pointer(gl.UNSIGNED_BYTE, false) } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + gl.enable(gl.BLEND) this.colorBuffer.bind() shader.attributes.color.pointer(gl.UNSIGNED_BYTE, false) @@ -145,7 +145,7 @@ var proto = GLScatterFancy.prototype shader.uniforms.chars = this.charTexture.bind(0) shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] - shader.uniforms.charsStep = this.charStep; + shader.uniforms.charsStep = this.charStep shader.uniforms.palette = this.paletteTexture.bind(1) this.sizeBuffer.bind() @@ -170,7 +170,7 @@ var proto = GLScatterFancy.prototype for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) { var lod = scales[scaleNum] - if(lod.pixelSize < pixelSize && scaleNum > 1) { + if(lod.pixelSize < pixelSize * 6 && scaleNum > 1) { continue } @@ -277,7 +277,7 @@ proto.update = function(options) { } //aggregate glyphs - var glyphChars = {}; + var glyphChars = {} for (var i = 0, l = pointCount, k = 0; i < l; i++) { var char = glyphs[i] if (glyphChars[char] == null) { @@ -286,9 +286,9 @@ proto.update = function(options) { } //generate font atlas - var maxSize = 0; + var maxSize = 0 for (var i = 0, l = sizes.length; i < l; ++i) { - if (sizes[i] > maxSize) maxSize = sizes[i]; + if (sizes[i] > maxSize) maxSize = sizes[i] } this.charStep = clamp(Math.ceil(maxSize*5), 128, 512) From a02dcb1a2612f9920373be2099407fec3b1359a5 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 15:56:35 +0300 Subject: [PATCH 19/41] Enhance precision, fix bad character offsets --- lib/shaders/frag.glsl | 21 +++++++++++---------- lib/shaders/vertex.glsl | 4 ++-- package.json | 1 + scatter-fancy.js | 17 ++++++++++------- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index ba0124c..3b5d3e3 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -17,19 +17,20 @@ void main() { vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; - if (dist < 1e-3) - discard; + //max-distance alpha + // if (dist == 0.) + // discard; - // border color float dif = 5. * borderWidth / pointSize; - float borderLevel = .75 - dif * .8; - float charLevel = .75 + dif * .2; + float borderLevel = .735 - dif * 1.; + float charLevel = .735 + dif * 0.; float gamma = .005 * charsStep / pointSize; - float alpha = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); - float mixAmount = smoothstep(charLevel - gamma, charLevel + gamma, dist); + float borderAmt = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); + float charAmt = smoothstep(charLevel - gamma, charLevel + gamma, dist); - //mix border color and color - vec4 color = mix(borderColor, charColor, mixAmount); - gl_FragColor = vec4(color.rgb, alpha * color.a); + vec4 color = borderColor; + color.a *= borderAmt; + + gl_FragColor = mix(color, charColor, charAmt); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 09a8c0b..4c64ade 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -25,8 +25,8 @@ void main() { charColor = texture2D(palette, vec2(color.x / 255., 0)); borderColor = texture2D(palette, vec2(color.y / 255., 0)); - gl_PointSize = max(size*2., 10.); - pointSize = max(size*2., 10.); + gl_PointSize = size; + pointSize = size; charOffset = char; borderWidth = border; diff --git a/package.json b/package.json index dca16ad..a609e81 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "homepage": "https://github.com/gl-vis/gl-scatter2d-fancy#readme", "dependencies": { + "binary-search-bounds": "^2.0.3", "clamp": "^1.0.1", "color-id": "^1.0.0", "font-atlas-sdf": "^1.0.0", diff --git a/scatter-fancy.js b/scatter-fancy.js index b04bac3..4ca84f4 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -12,6 +12,7 @@ var createTexture = require('gl-texture2d') var colorId = require('color-id') var ndarray = require('ndarray') var clamp = require('clamp') +var search = require('binary-search-bounds') function GLScatterFancy( plot, @@ -135,6 +136,7 @@ var proto = GLScatterFancy.prototype } else { gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + gl.blendColor(1,1,1,1); gl.enable(gl.BLEND) this.colorBuffer.bind() @@ -149,8 +151,8 @@ var proto = GLScatterFancy.prototype shader.uniforms.palette = this.paletteTexture.bind(1) this.sizeBuffer.bind() - shader.attributes.size.pointer(gl.UNSIGNED_BYTE, false, 2, 0) - shader.attributes.border.pointer(gl.UNSIGNED_BYTE, false, 2, 1) + shader.attributes.size.pointer(gl.FLOAT, false, 8, 0) + shader.attributes.border.pointer(gl.FLOAT, false, 8, 4) } this.positionBuffer.bind() @@ -242,7 +244,7 @@ proto.update = function(options) { //v_position contains normalized positions to the available range of positions var v_position = pool.mallocFloat32(4 * pointCount) - var v_sizeWidth = pool.mallocUint8(2 * pointCount) + var v_sizeWidth = pool.mallocFloat32(2 * pointCount) var v_color = pool.mallocUint8(2 * pointCount) var v_ids = pool.mallocUint32(pointCount) var v_chars = pool.mallocUint8(2 * pointCount) @@ -294,19 +296,19 @@ proto.update = function(options) { var chars = Object.keys(glyphChars) var step = this.charStep - var size = Math.floor(step / 2) + var charSize = Math.floor(step / 2) var maxW = gl.getParameter(gl.MAX_TEXTURE_SIZE) var maxChars = (maxW / step) * (maxW / step) var atlasW = Math.min(maxW, step*chars.length) var atlasH = Math.min(maxW, step*Math.ceil(step*chars.length/maxW)) - var cols = atlasW / step + var cols = Math.floor(atlasW / step) if (chars.length > maxChars) { console.warn('gl-scatter2d-fancy: number of characters is more than maximum texture size. Try reducing it.') } this.charCanvas = atlas({ canvas: this.charCanvas, family: 'sans-serif', - size: size, + size: charSize, shape: [atlasW, atlasH], step: [step, step], chars: chars @@ -326,7 +328,8 @@ proto.update = function(options) { v_position[4 * i + 2] = x - v_position[4 * i] v_position[4 * i + 3] = y - v_position[4 * i + 1] - v_sizeWidth[2 * i] = s + //size is doubled bc character SDF is twice less than character step + v_sizeWidth[2 * i] = s*2 v_sizeWidth[2 * i + 1] = w v_ids[i] = id From 6e451f2da8d82494409f84024d70752cd26799ff Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 16:03:59 +0300 Subject: [PATCH 20/41] Add demo --- example/rendering.js | 219 +++++++++++++++++++++++++++++++++++++++++++ package.json | 9 ++ 2 files changed, 228 insertions(+) create mode 100644 example/rendering.js diff --git a/example/rendering.js b/example/rendering.js new file mode 100644 index 0000000..c1cfc3f --- /dev/null +++ b/example/rendering.js @@ -0,0 +1,219 @@ +var fit = require('canvas-fit') +var mouseWheel = require('mouse-wheel') +var mouseChange = require('mouse-change') +// var createScatter = require('gl-scatter2d-fancy') +var createScatter = require('../') +var createSelectBox = require('gl-select-box') +var createSpikes = require('gl-spikes2d') +var createPlot = require('gl-plot2d') +var createFps = require('fps-indicator') + +createFps() + +var canvas = document.createElement('canvas') +document.body.appendChild(canvas) +window.addEventListener('resize', fit(canvas, null, +window.devicePixelRatio), false) + +var gl = canvas.getContext('webgl', { + depth: false, + // alpha: true, + // premultipliedAlpha: true +}) + +//5e6 is allocation maximum +// var POINT_COUNT = 3e6 +var POINT_COUNT = 1e3 + +var aspect = gl.drawingBufferWidth / gl.drawingBufferHeight +var dataBox = [-10,-10/aspect,10,10/aspect] + +function makeTicks(lo, hi) { + var result = [] + for(var i=lo; i<=hi; ++i) { + result.push({ + x: i, + text: i + '' + }) + } + return result +} + +var options = { + gl: gl, + dataBox: dataBox, + title: '100 million points', + ticks: [ makeTicks(-20,20), makeTicks(-20,20) ], + labels: ['x', 'y'], + pixelRatio: +window.devicePixelRatio, + tickMarkWidth: [1,1,1,1], + tickMarkLength: [3,3,3,3] +} + +var plot = createPlot(options) + + + +var selectBox = createSelectBox(plot, { + innerFill: false, + outerFill: true +}) +selectBox.enabled = false + +var spikes = createSpikes(plot) + + + +var positions = new Float32Array(2 * POINT_COUNT) +for(var i=0; i<2*POINT_COUNT; ++i) { + positions[i] = Math.random() +} + +var glyphs = new Array(POINT_COUNT) +var MARKERS = [ '●', '#', '✝', '+' ] +for(var i=0; i Date: Thu, 9 Feb 2017 17:00:25 +0300 Subject: [PATCH 21/41] Separate test-cases --- example/index.js | 162 ++++++++++++++++++++++++++++++++ example/points.js | 48 ++++++++++ example/render.js | 33 +++++++ example/rendering.js | 219 ------------------------------------------- scatter-fancy.js | 2 +- 5 files changed, 244 insertions(+), 220 deletions(-) create mode 100644 example/index.js create mode 100644 example/points.js create mode 100644 example/render.js delete mode 100644 example/rendering.js diff --git a/example/index.js b/example/index.js new file mode 100644 index 0000000..aa87eac --- /dev/null +++ b/example/index.js @@ -0,0 +1,162 @@ +/** + * Create gl-scatter2d-fancy test-case + */ + +var fit = require('canvas-fit') +var mouseWheel = require('mouse-wheel') +var mouseChange = require('mouse-change') +// var createScatter = require('gl-scatter2d-fancy') +var createScatter = require('../') +var createSelectBox = require('gl-select-box') +var createSpikes = require('gl-spikes2d') +var createPlot = require('gl-plot2d') +var createFps = require('fps-indicator') + + +module.exports = setup; + + +function setup (options) { + createFps() + + var canvas = document.createElement('canvas') + document.body.appendChild(canvas) + window.addEventListener('resize', fit(canvas, null, +window.devicePixelRatio), false) + + var gl = canvas.getContext('webgl', { + depth: false, + // alpha: true, + // premultipliedAlpha: true + }) + + var aspect = gl.drawingBufferWidth / gl.drawingBufferHeight + var dataBox = [-10,-10/aspect,10,10/aspect] + + function makeTicks(lo, hi) { + var result = [] + for(var i=lo; i<=hi; ++i) { + result.push({ + x: i, + text: i + '' + }) + } + return result + } + + var plot = createPlot({ + gl: gl, + dataBox: dataBox, + title: 'gl-scatter2d-fancy', + ticks: [ makeTicks(-20,20), makeTicks(-20,20) ], + labels: ['x', 'y'], + pixelRatio: +window.devicePixelRatio, + tickMarkWidth: [1,1,1,1], + tickMarkLength: [3,3,3,3] + }) + + + + var selectBox = createSelectBox(plot, { + innerFill: false, + outerFill: true + }) + selectBox.enabled = false + + var spikes = createSpikes(plot) + + var scatter = createScatter(plot, options) + + + var lastX = 0, lastY = 0 + var boxStart = [0,0] + var boxEnd = [0,0] + var boxEnabled = false + mouseChange(function(buttons, x, y, mods) { + y = window.innerHeight - y + x *= plot.pixelRatio + y *= plot.pixelRatio + + if(buttons & 1) { + if(mods.shift) { + var dataX = (x - plot.viewBox[0]) / (plot.viewBox[2]-plot.viewBox[0]) * (dataBox[2] - dataBox[0]) + dataBox[0] + var dataY = (y - plot.viewBox[1]) / (plot.viewBox[3]-plot.viewBox[1]) * (dataBox[3] - dataBox[1]) + dataBox[1] + if(!boxEnabled) { + boxStart[0] = dataX + boxStart[1] = dataY + } + boxEnd[0] = dataX + boxEnd[1] = dataY + boxEnabled = true + spikes.update() + } else { + var dx = (lastX - x) * (dataBox[2] - dataBox[0]) / (plot.viewBox[2]-plot.viewBox[0]) + var dy = (lastY - y) * (dataBox[3] - dataBox[1]) / (plot.viewBox[3] - plot.viewBox[1]) + + dataBox[0] += dx + dataBox[1] += dy + dataBox[2] += dx + dataBox[3] += dy + + plot.setDataBox(dataBox) + spikes.update() + } + } else { + var result = plot.pick(x/plot.pixelRatio, y/plot.pixelRatio) + if(result) { + spikes.update({center: result.dataCoord}) + } else { + spikes.update() + } + } + + if(boxEnabled) { + selectBox.enabled = true + selectBox.selectBox = [ + Math.min(boxStart[0], boxEnd[0]), + Math.min(boxStart[1], boxEnd[1]), + Math.max(boxStart[0], boxEnd[0]), + Math.max(boxStart[1], boxEnd[1]) + ] + plot.setDirty() + if(!((buttons&1) && mods.shift)) { + selectBox.enabled = false + dataBox = [ + Math.min(boxStart[0], boxEnd[0]), + Math.min(boxStart[1], boxEnd[1]), + Math.max(boxStart[0], boxEnd[0]), + Math.max(boxStart[1], boxEnd[1]) + ] + plot.setDataBox(dataBox) + boxEnabled = false + } + } + + lastX = x + lastY = y + }) + + mouseWheel(function(dx, dy, dz) { + var scale = Math.exp(0.1 * dy / gl.drawingBufferHeight) + + var cx = (lastX - plot.viewBox[0]) / (plot.viewBox[2] - plot.viewBox[0]) * (dataBox[2] - dataBox[0]) + dataBox[0] + var cy = (plot.viewBox[1] - lastY) / (plot.viewBox[3] - plot.viewBox[1]) * (dataBox[3] - dataBox[1]) + dataBox[3] + + dataBox[0] = (dataBox[0] - cx) * scale + cx + dataBox[1] = (dataBox[1] - cy) * scale + cy + dataBox[2] = (dataBox[2] - cx) * scale + cx + dataBox[3] = (dataBox[3] - cy) * scale + cy + + plot.setDataBox(dataBox) + + return true + }) + + function render() { + requestAnimationFrame(render) + plot.draw() + } + + render() + + return scatter +} diff --git a/example/points.js b/example/points.js new file mode 100644 index 0000000..da476f6 --- /dev/null +++ b/example/points.js @@ -0,0 +1,48 @@ +/** + * Test multiple points + */ + +const setup = require('./') + + +//5e6 is allocation maximum +// var POINT_COUNT = 3e6 +var POINT_COUNT = 1e6 + +var positions = new Float32Array(2 * POINT_COUNT) +for(var i=0; i<2*POINT_COUNT; ++i) { + positions[i] = Math.random() * 10 - 5 +} + +var glyphs = new Array(POINT_COUNT) +var MARKERS = [ '●', '#', '✝', '+' ] +for(var i=0; i= 0; scaleNum--) { var lod = scales[scaleNum] - if(lod.pixelSize < pixelSize * 6 && scaleNum > 1) { + if(lod.pixelSize < pixelSize * 1.5 && scaleNum > 1) { continue } From 0170e58d24c04e409abd344245ef0e4b0b0189e5 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 17:05:06 +0300 Subject: [PATCH 22/41] Explain basic setup --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 643b60d..573e1db 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,23 @@ gl-scatter2d-fancy ================== This code is similar to gl-scatter2d, but supports a larger range of styling features like custom marker glyphs, per marker colors, etc. As a result, it is not as performant but provides more customization. +```js +const createPlot = require('gl-plot2d') +const createScatter = require('gl-scatter2d-fancy') + +let plot = createPlot({gl, ...}) + +let scatter = createScatter(plot, { + positions: [.5,.5, .6,.7, ...], + sizes: [2, 3, ...], + colors: [0,0,0,1, .5,.5,1,1, ...], + glyphs: ['x', 'y', ...], + borderWidths: [.5, 1, ...], + borderColors: [1,0,0,.5, 0,0,1,.5, ...] +}) + +plot.draw() +``` + # License (c) 2015 Mikola Lysenko. MIT License From 9f2c80ae2060c360153cbc38864c615b83804241 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 9 Feb 2017 18:32:34 +0300 Subject: [PATCH 23/41] Add #566 test-case --- example/566.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 example/566.js diff --git a/example/566.js b/example/566.js new file mode 100644 index 0000000..2108493 --- /dev/null +++ b/example/566.js @@ -0,0 +1,23 @@ +/** + * plotly.js #566 case + */ + +const setup = require('./') + +let N = 1e3 + +let positions = Array(N*2).fill(0).map((v, i) => (Math.random() * 10 - 5) ) +let sizes = Array(N).fill(0).map(v => Math.random() * 10 + 10) +let colors = Array(N*4).fill(0).map((v, i) => !((i + 1) % 4) ? .7 : Math.random()) +let glyphs = Array(N).fill('●') +let borderWidths = Array(N).fill(.5) +let borderColors = Array(N*4).fill(0).map((v, i) => !((i+1) % 4) ? .7 : 1) + +setup({ + positions, + sizes, + colors, + glyphs, + borderColors, + borderWidths +}) From 8435add8a1cf5be2cb31d6d25cc547efeabdf4d9 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 10:26:27 +0300 Subject: [PATCH 24/41] Account for pixelRatio --- example/566.js | 2 +- example/index.js | 4 ++-- lib/shaders/vertex.glsl | 5 +++-- scatter-fancy.js | 3 +++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/example/566.js b/example/566.js index 2108493..882f753 100644 --- a/example/566.js +++ b/example/566.js @@ -7,7 +7,7 @@ const setup = require('./') let N = 1e3 let positions = Array(N*2).fill(0).map((v, i) => (Math.random() * 10 - 5) ) -let sizes = Array(N).fill(0).map(v => Math.random() * 10 + 10) +let sizes = Array(N).fill(0).map(v => 10) let colors = Array(N*4).fill(0).map((v, i) => !((i + 1) % 4) ? .7 : Math.random()) let glyphs = Array(N).fill('●') let borderWidths = Array(N).fill(.5) diff --git a/example/index.js b/example/index.js index aa87eac..6f3eec5 100644 --- a/example/index.js +++ b/example/index.js @@ -5,7 +5,7 @@ var fit = require('canvas-fit') var mouseWheel = require('mouse-wheel') var mouseChange = require('mouse-change') -// var createScatter = require('gl-scatter2d-fancy') +// var createScatter = require('../../plotly.js/node_modules/gl-scatter2d-fancy') var createScatter = require('../') var createSelectBox = require('gl-select-box') var createSpikes = require('gl-spikes2d') @@ -49,7 +49,7 @@ function setup (options) { title: 'gl-scatter2d-fancy', ticks: [ makeTicks(-20,20), makeTicks(-20,20) ], labels: ['x', 'y'], - pixelRatio: +window.devicePixelRatio, + pixelRatio: 2, tickMarkWidth: [1,1,1,1], tickMarkLength: [3,3,3,3] }) diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 4c64ade..2b39f7a 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -9,6 +9,7 @@ attribute vec2 char, color; //this is 64-bit form of scale and translate uniform vec2 scaleHi, scaleLo, translateHi, translateLo; uniform vec2 pixelScale; +uniform float pixelRatio; uniform vec4 viewBox; uniform sampler2D palette; @@ -25,8 +26,8 @@ void main() { charColor = texture2D(palette, vec2(color.x / 255., 0)); borderColor = texture2D(palette, vec2(color.y / 255., 0)); - gl_PointSize = size; - pointSize = size; + gl_PointSize = size * pixelRatio; + pointSize = size * pixelRatio; charOffset = char; borderWidth = border; diff --git a/scatter-fancy.js b/scatter-fancy.js index aa2bff3..0e2a402 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -67,6 +67,8 @@ var proto = GLScatterFancy.prototype function calcScales() { var plot = this.plot + + //bounds are positions range bottom-left top-right var bounds = this.bounds var viewBox = plot.viewBox var dataBox = plot.dataBox @@ -160,6 +162,7 @@ var proto = GLScatterFancy.prototype shader.attributes.positionLo.pointer(gl.FLOAT, false, 16, 8) shader.uniforms.pixelScale = PIXEL_SCALE + shader.uniforms.pixelRatio = plot.pixelRatio shader.uniforms.scaleHi = SCALE_HI shader.uniforms.scaleLo = SCALE_LO shader.uniforms.translateHi = TRANSLATE_HI From 84c2b2fda56f4b0891a18cf5245c8be66de8ee0c Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 10:45:23 +0300 Subject: [PATCH 25/41] Fix alpha --- lib/shaders/frag.glsl | 4 ++-- scatter-fancy.js | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 3b5d3e3..0004f3c 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -18,8 +18,8 @@ void main() { float dist = texture2D(chars, texCoord).r; //max-distance alpha - // if (dist == 0.) - // discard; + if (dist == 0.) + discard; float dif = 5. * borderWidth / pointSize; float borderLevel = .735 - dif * 1.; diff --git a/scatter-fancy.js b/scatter-fancy.js index 0e2a402..c94f7c5 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -124,7 +124,6 @@ var proto = GLScatterFancy.prototype shader.bind() if(pick) { - this.pickOffset = offset for (var i = 0; i < 4; ++i) { @@ -138,7 +137,7 @@ var proto = GLScatterFancy.prototype } else { gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) - gl.blendColor(1,1,1,1); + gl.blendColor(0,0,0,1); gl.enable(gl.BLEND) this.colorBuffer.bind() @@ -167,15 +166,13 @@ var proto = GLScatterFancy.prototype shader.uniforms.scaleLo = SCALE_LO shader.uniforms.translateHi = TRANSLATE_HI shader.uniforms.translateLo = TRANSLATE_LO - //FIXME: this may be an issue for changed viewbox, test it - //FIXME: make sure we can't do the same with PIXEL_SCALE or something shader.uniforms.viewBox = plot.viewBox var scales = this.scales for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) { var lod = scales[scaleNum] - if(lod.pixelSize < pixelSize * 1.5 && scaleNum > 1) { + if(lod.pixelSize < pixelSize * 1.7 && scaleNum > 1) { continue } From e71259f033b9090fbd984f2245d75220220501ae Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 11:10:53 +0300 Subject: [PATCH 26/41] Filter offsets --- scatter-fancy.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index c94f7c5..cc0d057 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -43,6 +43,7 @@ function GLScatterFancy( //lod scales this.scales = [] + this.xCoords = [] //font atlas texture this.charCanvas = document.createElement('canvas') @@ -63,7 +64,8 @@ var proto = GLScatterFancy.prototype var PIXEL_SCALE = [0, 0] - var pixelSize + var pixelSize, xStart, xEnd + function calcScales() { var plot = this.plot @@ -73,6 +75,7 @@ var proto = GLScatterFancy.prototype var viewBox = plot.viewBox var dataBox = plot.dataBox var pixelRatio = plot.pixelRatio + var size = this.size var boundX = bounds[2] - bounds[0] var boundY = bounds[3] - bounds[1] @@ -102,6 +105,9 @@ var proto = GLScatterFancy.prototype //FIXME: why twice? PIXEL_SCALE[0] = 2 * pixelRatio / screenX PIXEL_SCALE[1] = 2 * pixelRatio / screenY + + xStart = dataBox[0] + xEnd = dataBox[2] } var PICK_OFFSET = [0, 0, 0, 0] @@ -179,10 +185,8 @@ var proto = GLScatterFancy.prototype var intervalStart = lod.offset var intervalEnd = lod.count + intervalStart - var startOffset = intervalStart - var endOffset = intervalEnd || pointCount - - //TODO: we can shave off even more by slicing by left/right limits, see gl-scatter2d. Points are arranged by x coordinate so just calc bounds + var startOffset = search.ge(this.xCoords, xStart, intervalStart, intervalEnd - 1) + var endOffset = search.lt(this.xCoords, xEnd, startOffset, intervalEnd - 1) + 1 if (endOffset > startOffset) { gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) @@ -328,6 +332,8 @@ proto.update = function(options) { v_position[4 * i + 2] = x - v_position[4 * i] v_position[4 * i + 3] = y - v_position[4 * i + 1] + this.xCoords[i] = x + //size is doubled bc character SDF is twice less than character step v_sizeWidth[2 * i] = s*2 v_sizeWidth[2 * i + 1] = w From b87846fe8a72d7d8c0560a304f8f0e2db92abe2d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 11:39:38 +0300 Subject: [PATCH 27/41] Fix colors blending --- lib/shaders/frag.glsl | 6 +++--- scatter-fancy.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 0004f3c..9fa981d 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -18,12 +18,12 @@ void main() { float dist = texture2D(chars, texCoord).r; //max-distance alpha - if (dist == 0.) + if (dist < 1e-2) discard; float dif = 5. * borderWidth / pointSize; - float borderLevel = .735 - dif * 1.; - float charLevel = .735 + dif * 0.; + float borderLevel = .735 - dif * .8; + float charLevel = .735 + dif * .2; float gamma = .005 * charsStep / pointSize; float borderAmt = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); diff --git a/scatter-fancy.js b/scatter-fancy.js index cc0d057..ba484c8 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -142,7 +142,7 @@ var proto = GLScatterFancy.prototype shader.attributes.id.pointer(gl.UNSIGNED_BYTE, false) } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.blendColor(0,0,0,1); gl.enable(gl.BLEND) From ddfc58815b6823340d0ed974bc9010c313827cf6 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 11:45:46 +0300 Subject: [PATCH 28/41] Lint --- scatter-fancy.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index ba484c8..f13861a 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -75,7 +75,6 @@ var proto = GLScatterFancy.prototype var viewBox = plot.viewBox var dataBox = plot.dataBox var pixelRatio = plot.pixelRatio - var size = this.size var boundX = bounds[2] - bounds[0] var boundY = bounds[3] - bounds[1] @@ -223,7 +222,6 @@ proto.update = function(options) { var sizes = options.sizes || [] var borderWidths = options.borderWidths || [] var borderColors = options.borderColors || [] - var i, j var gl = this.plot.gl this.points = positions @@ -252,7 +250,6 @@ proto.update = function(options) { var v_color = pool.mallocUint8(2 * pointCount) var v_ids = pool.mallocUint32(pointCount) var v_chars = pool.mallocUint8(2 * pointCount) - var ptr = 0 //aggregate colors var paletteIds = {}, colorIds = [], paletteColors = [], bColorIds = [] @@ -319,7 +316,7 @@ proto.update = function(options) { }) //collect buffers data - for(i = 0; i < pointCount; ++i) { + for(var i = 0; i < pointCount; ++i) { var id = packedId[i] var x = sx * (positions[2 * id] - tx) var y = sy * (positions[2 * id + 1] - ty) From a970cdbb85d62ca16ba19f204cea0f3b0ca3a720 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 11:50:04 +0300 Subject: [PATCH 29/41] Refine point size --- lib/shaders/frag.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 9fa981d..c3b5aa1 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -22,8 +22,8 @@ void main() { discard; float dif = 5. * borderWidth / pointSize; - float borderLevel = .735 - dif * .8; - float charLevel = .735 + dif * .2; + float borderLevel = .74 - dif * .8; + float charLevel = .74 + dif * .2; float gamma = .005 * charsStep / pointSize; float borderAmt = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); From 83f32ac9296205ec6a9354cdf811a4291b1def5a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 11:54:21 +0300 Subject: [PATCH 30/41] Clean up shaders --- lib/shaders/pick-vertex.glsl | 5 ++--- lib/shaders/vertex.glsl | 8 ++------ lib/shaders/xform.glsl | 5 ++--- scatter-fancy.js | 1 - 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/lib/shaders/pick-vertex.glsl b/lib/shaders/pick-vertex.glsl index 0fdc14b..515e10f 100644 --- a/lib/shaders/pick-vertex.glsl +++ b/lib/shaders/pick-vertex.glsl @@ -3,7 +3,7 @@ precision highp float; attribute vec2 positionHi, positionLo; attribute vec4 id; -uniform vec2 scaleHi, scaleLo, translateHi, translateLo, pixelScale; +uniform vec2 scaleHi, scaleLo, translateHi, translateLo; uniform vec4 pickOffset; varying vec4 fragColor; @@ -29,6 +29,5 @@ void main() { gl_Position = computePosition( positionHi, positionLo, scaleHi, scaleLo, - translateHi, translateLo, - pixelScale, vec2(0)); + translateHi, translateLo); } diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 2b39f7a..632d482 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -8,17 +8,14 @@ attribute vec2 char, color; //this is 64-bit form of scale and translate uniform vec2 scaleHi, scaleLo, translateHi, translateLo; -uniform vec2 pixelScale; uniform float pixelRatio; uniform vec4 viewBox; uniform sampler2D palette; -varying vec4 charColor; -varying vec4 borderColor; +varying vec4 charColor, borderColor; varying vec2 charOffset; varying vec2 pointCoord; varying float pointSize; -varying vec2 position; varying float borderWidth; @@ -35,8 +32,7 @@ void main() { gl_Position = computePosition( positionHi, positionLo, scaleHi, scaleLo, - translateHi, translateLo, - pixelScale, vec2(0)); + translateHi, translateLo); pointCoord = viewBox.xy + (viewBox.zw - viewBox.xy) * (gl_Position.xy * .5 + .5); } diff --git a/lib/shaders/xform.glsl b/lib/shaders/xform.glsl index 0b9e4d7..ac4d311 100644 --- a/lib/shaders/xform.glsl +++ b/lib/shaders/xform.glsl @@ -1,10 +1,9 @@ #pragma glslify: export(computePosition) -vec4 computePosition(vec2 posHi, vec2 posLo, vec2 scHi, vec2 scLo, vec2 trHi, vec2 trLo, vec2 screenScale, vec2 screenOffset) { +vec4 computePosition(vec2 posHi, vec2 posLo, vec2 scHi, vec2 scLo, vec2 trHi, vec2 trLo) { return vec4((posHi + trHi) * scHi - //FIXME: this thingy does not give noticeable precision gain for me + //FIXME: this thingy does not give noticeable precision gain, need test + (posLo + trLo) * scHi + (posHi + trHi) * scLo + (posLo + trLo) * scLo - + screenScale * screenOffset , 0, 1); } diff --git a/scatter-fancy.js b/scatter-fancy.js index f13861a..5431ccd 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -165,7 +165,6 @@ var proto = GLScatterFancy.prototype shader.attributes.positionHi.pointer(gl.FLOAT, false, 16, 0) shader.attributes.positionLo.pointer(gl.FLOAT, false, 16, 8) - shader.uniforms.pixelScale = PIXEL_SCALE shader.uniforms.pixelRatio = plot.pixelRatio shader.uniforms.scaleHi = SCALE_HI shader.uniforms.scaleLo = SCALE_LO From e30dde45fa9dced2678491303c3e2ce80bb65877 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 10 Feb 2017 11:56:00 +0300 Subject: [PATCH 31/41] Normalize test --- example/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/index.js b/example/index.js index 6f3eec5..02f72a6 100644 --- a/example/index.js +++ b/example/index.js @@ -49,7 +49,7 @@ function setup (options) { title: 'gl-scatter2d-fancy', ticks: [ makeTicks(-20,20), makeTicks(-20,20) ], labels: ['x', 'y'], - pixelRatio: 2, + pixelRatio: 1, tickMarkWidth: [1,1,1,1], tickMarkLength: [3,3,3,3] }) From 797842153467d06ae8f3f7606d84e9515682101d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 11 Feb 2017 16:46:18 +0300 Subject: [PATCH 32/41] Make border width depend on pixelRatio, make centered --- example/index.js | 2 +- lib/shaders/frag.glsl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/index.js b/example/index.js index 02f72a6..6f3eec5 100644 --- a/example/index.js +++ b/example/index.js @@ -49,7 +49,7 @@ function setup (options) { title: 'gl-scatter2d-fancy', ticks: [ makeTicks(-20,20), makeTicks(-20,20) ], labels: ['x', 'y'], - pixelRatio: 1, + pixelRatio: 2, tickMarkWidth: [1,1,1,1], tickMarkLength: [3,3,3,3] }) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index c3b5aa1..1034c88 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -2,7 +2,7 @@ precision highp float; uniform sampler2D chars; uniform vec2 charsShape; -uniform float charsStep; +uniform float charsStep, pixelRatio; varying vec4 borderColor; varying vec4 charColor; @@ -21,9 +21,9 @@ void main() { if (dist < 1e-2) discard; - float dif = 5. * borderWidth / pointSize; - float borderLevel = .74 - dif * .8; - float charLevel = .74 + dif * .2; + float dif = 5. * pixelRatio * borderWidth / pointSize; + float borderLevel = .74 - dif * .5; + float charLevel = .74 + dif * .5; float gamma = .005 * charsStep / pointSize; float borderAmt = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); From d7a29fc69f9b5378cbc423c46e84ab4b6d33a1f3 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 11 Feb 2017 16:54:32 +0300 Subject: [PATCH 33/41] Fix pick size --- example/index.js | 2 +- lib/shaders/frag.glsl | 4 ++-- lib/shaders/pick-vertex.glsl | 4 +++- scatter-fancy.js | 8 ++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/example/index.js b/example/index.js index 6f3eec5..02f72a6 100644 --- a/example/index.js +++ b/example/index.js @@ -49,7 +49,7 @@ function setup (options) { title: 'gl-scatter2d-fancy', ticks: [ makeTicks(-20,20), makeTicks(-20,20) ], labels: ['x', 'y'], - pixelRatio: 2, + pixelRatio: 1, tickMarkWidth: [1,1,1,1], tickMarkLength: [3,3,3,3] }) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 1034c88..5682089 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -22,8 +22,8 @@ void main() { discard; float dif = 5. * pixelRatio * borderWidth / pointSize; - float borderLevel = .74 - dif * .5; - float charLevel = .74 + dif * .5; + float borderLevel = .74 - dif * .7; + float charLevel = .74 + dif * .3; float gamma = .005 * charsStep / pointSize; float borderAmt = smoothstep(borderLevel - gamma, borderLevel + gamma, dist); diff --git a/lib/shaders/pick-vertex.glsl b/lib/shaders/pick-vertex.glsl index 515e10f..a5d36e8 100644 --- a/lib/shaders/pick-vertex.glsl +++ b/lib/shaders/pick-vertex.glsl @@ -2,9 +2,11 @@ precision highp float; attribute vec2 positionHi, positionLo; attribute vec4 id; +attribute float size; uniform vec2 scaleHi, scaleLo, translateHi, translateLo; uniform vec4 pickOffset; +uniform float pixelRatio; varying vec4 fragColor; @@ -24,7 +26,7 @@ void main() { fragColor = fragId / 255.0; - gl_PointSize = 1.; + gl_PointSize = size * .3 * pixelRatio; gl_Position = computePosition( positionHi, positionLo, diff --git a/scatter-fancy.js b/scatter-fancy.js index 5431ccd..360fae3 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -155,12 +155,12 @@ var proto = GLScatterFancy.prototype shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] shader.uniforms.charsStep = this.charStep shader.uniforms.palette = this.paletteTexture.bind(1) - - this.sizeBuffer.bind() - shader.attributes.size.pointer(gl.FLOAT, false, 8, 0) - shader.attributes.border.pointer(gl.FLOAT, false, 8, 4) } + this.sizeBuffer.bind() + shader.attributes.size.pointer(gl.FLOAT, false, 8, 0) + !pick && shader.attributes.border.pointer(gl.FLOAT, false, 8, 4) + this.positionBuffer.bind() shader.attributes.positionHi.pointer(gl.FLOAT, false, 16, 0) shader.attributes.positionLo.pointer(gl.FLOAT, false, 16, 8) From 76f6853605c11bc9af0d0b8b4ea636fa2f3fa65a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 11 Feb 2017 21:58:52 +0300 Subject: [PATCH 34/41] Fix bounds --- lib/shaders/pick-vertex.glsl | 2 +- scatter-fancy.js | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/shaders/pick-vertex.glsl b/lib/shaders/pick-vertex.glsl index a5d36e8..0439000 100644 --- a/lib/shaders/pick-vertex.glsl +++ b/lib/shaders/pick-vertex.glsl @@ -26,7 +26,7 @@ void main() { fragColor = fragId / 255.0; - gl_PointSize = size * .3 * pixelRatio; + gl_PointSize = size * .25 * pixelRatio; gl_Position = computePosition( positionHi, positionLo, diff --git a/scatter-fancy.js b/scatter-fancy.js index 360fae3..c86c453 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -159,7 +159,7 @@ var proto = GLScatterFancy.prototype this.sizeBuffer.bind() shader.attributes.size.pointer(gl.FLOAT, false, 8, 0) - !pick && shader.attributes.border.pointer(gl.FLOAT, false, 8, 4) + if (!pick) shader.attributes.border.pointer(gl.FLOAT, false, 8, 4) this.positionBuffer.bind() shader.attributes.positionHi.pointer(gl.FLOAT, false, 16, 0) @@ -191,7 +191,11 @@ var proto = GLScatterFancy.prototype } } - if(pick) return offset + this.pointCount + if (pick) return offset + this.pointCount + else { + // gl.blendFunc(gl.ONE, gl.ZERO); + gl.disable(gl.BLEND) + } } })() @@ -236,7 +240,13 @@ proto.update = function(options) { this.pointCount = pointCount //FIXME: figure out what these bounds are about or get rid of them - var bounds = this.bounds = [0, 0, 1, 1] + var bounds = this.bounds = [Infinity, Infinity, -Infinity, -Infinity] + for (var i = 0; i < pointCount; i++) { + bounds[0] = Math.min(bounds[0], positions[2 * i]) + bounds[1] = Math.min(bounds[1], positions[2 * i + 1]) + bounds[2] = Math.max(bounds[2], positions[2 * i]) + bounds[3] = Math.max(bounds[3], positions[2 * i + 1]) + } var sx = 1 / (bounds[2] - bounds[0]) var sy = 1 / (bounds[3] - bounds[1]) @@ -292,7 +302,7 @@ proto.update = function(options) { for (var i = 0, l = sizes.length; i < l; ++i) { if (sizes[i] > maxSize) maxSize = sizes[i] } - this.charStep = clamp(Math.ceil(maxSize*5), 128, 512) + this.charStep = clamp(Math.ceil(maxSize*6), 128, 512) var chars = Object.keys(glyphChars) var step = this.charStep From a8f34909b1995b957d8ae762d43b4ed2ab9eca91 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 11 Feb 2017 22:23:34 +0300 Subject: [PATCH 35/41] Ignore zero-level lod check --- scatter-fancy.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index c86c453..9b968e1 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -176,7 +176,7 @@ var proto = GLScatterFancy.prototype for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) { var lod = scales[scaleNum] - if(lod.pixelSize < pixelSize * 1.7 && scaleNum > 1) { + if(lod.pixelSize && (lod.pixelSize < pixelSize * 1.25) && scaleNum > 1) { continue } @@ -187,13 +187,13 @@ var proto = GLScatterFancy.prototype var endOffset = search.lt(this.xCoords, xEnd, startOffset, intervalEnd - 1) + 1 if (endOffset > startOffset) { - gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) + // gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) + gl.drawArrays(gl.POINTS, intervalStart, (intervalEnd - intervalStart)) } } if (pick) return offset + this.pointCount else { - // gl.blendFunc(gl.ONE, gl.ZERO); gl.disable(gl.BLEND) } } From 2cb412da43929df2f7d57c095743f4a3c7cddf4b Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 11 Feb 2017 22:32:36 +0300 Subject: [PATCH 36/41] Fix range --- scatter-fancy.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index 9b968e1..2f6d1e4 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -187,8 +187,7 @@ var proto = GLScatterFancy.prototype var endOffset = search.lt(this.xCoords, xEnd, startOffset, intervalEnd - 1) + 1 if (endOffset > startOffset) { - // gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) - gl.drawArrays(gl.POINTS, intervalStart, (intervalEnd - intervalStart)) + gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) } } From ba9ea4a1bc09d4f5dcdab0ac4688fd8e18242b20 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sun, 12 Feb 2017 20:50:09 +0300 Subject: [PATCH 37/41] Add character offset --- lib/shaders/frag.glsl | 7 ++++--- lib/shaders/vertex.glsl | 4 ++-- scatter-fancy.js | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 5682089..208642b 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -2,11 +2,11 @@ precision highp float; uniform sampler2D chars; uniform vec2 charsShape; -uniform float charsStep, pixelRatio; +uniform float charsStep, pixelRatio, charOffset; varying vec4 borderColor; varying vec4 charColor; -varying vec2 charOffset; +varying vec2 charId; varying vec2 pointCoord; varying float pointSize; varying float borderWidth; @@ -14,7 +14,8 @@ varying float borderWidth; void main() { vec2 pointUV = (pointCoord - gl_FragCoord.xy + pointSize * .5) / pointSize; pointUV.x = 1. - pointUV.x; - vec2 texCoord = ((charOffset + pointUV) * charsStep) / charsShape; + pointUV.y += charOffset; + vec2 texCoord = ((charId + pointUV) * charsStep) / charsShape; float dist = texture2D(chars, texCoord).r; //max-distance alpha diff --git a/lib/shaders/vertex.glsl b/lib/shaders/vertex.glsl index 632d482..9e03f95 100644 --- a/lib/shaders/vertex.glsl +++ b/lib/shaders/vertex.glsl @@ -13,7 +13,7 @@ uniform vec4 viewBox; uniform sampler2D palette; varying vec4 charColor, borderColor; -varying vec2 charOffset; +varying vec2 charId; varying vec2 pointCoord; varying float pointSize; varying float borderWidth; @@ -26,7 +26,7 @@ void main() { gl_PointSize = size * pixelRatio; pointSize = size * pixelRatio; - charOffset = char; + charId = char; borderWidth = border; gl_Position = computePosition( diff --git a/scatter-fancy.js b/scatter-fancy.js index 2f6d1e4..0f65274 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -50,6 +50,9 @@ function GLScatterFancy( this.charTexture = createTexture(this.plot.gl, this.charCanvas) this.charStep = 400 + //due to font alignmens some glyphs are off a bit + this.charOffset = .03 + //border/char colors texture this.paletteTexture = createTexture(this.plot.gl, [256, 1]) } @@ -155,6 +158,7 @@ var proto = GLScatterFancy.prototype shader.uniforms.charsShape = [this.charCanvas.width, this.charCanvas.height] shader.uniforms.charsStep = this.charStep shader.uniforms.palette = this.paletteTexture.bind(1) + shader.uniforms.charOffset = this.charOffset } this.sizeBuffer.bind() From eca38f29f4324586c9cfe445cc498f115889f3ae Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sun, 12 Feb 2017 20:57:08 +0300 Subject: [PATCH 38/41] Get rid of bounds --- scatter-fancy.js | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index 0f65274..ee6658d 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -34,7 +34,6 @@ function GLScatterFancy( this.idBuffer = idBuffer this.charBuffer = charBuffer - this.bounds = [Infinity, Infinity, -Infinity, -Infinity] this.pointCount = 0 this.pickOffset = 0 @@ -73,21 +72,17 @@ var proto = GLScatterFancy.prototype function calcScales() { var plot = this.plot - //bounds are positions range bottom-left top-right - var bounds = this.bounds var viewBox = plot.viewBox var dataBox = plot.dataBox var pixelRatio = plot.pixelRatio - var boundX = bounds[2] - bounds[0] - var boundY = bounds[3] - bounds[1] var dataX = dataBox[2] - dataBox[0] var dataY = dataBox[3] - dataBox[1] - var scaleX = 2 * boundX / dataX - var scaleY = 2 * boundY / dataY - var translateX = (bounds[0] - dataBox[0] - 0.5 * dataX) / boundX - var translateY = (bounds[1] - dataBox[1] - 0.5 * dataY) / boundY + var scaleX = 2 / dataX + var scaleY = 2 / dataY + var translateX = (- dataBox[0] - 0.5 * dataX) + var translateY = (- dataBox[1] - 0.5 * dataY) SCALE_HI[0] = scaleX SCALE_LO[0] = scaleX - SCALE_HI[0] @@ -238,24 +233,10 @@ proto.update = function(options) { var packedW = pool.mallocFloat32(2 * pointCount) var packed = pool.mallocFloat64(2 * pointCount) packed.set(this.points) - this.scales = snapPoints(packed, packedId, packedW, this.bounds) + this.scales = snapPoints(packed, packedId, packedW) this.pointCount = pointCount - //FIXME: figure out what these bounds are about or get rid of them - var bounds = this.bounds = [Infinity, Infinity, -Infinity, -Infinity] - for (var i = 0; i < pointCount; i++) { - bounds[0] = Math.min(bounds[0], positions[2 * i]) - bounds[1] = Math.min(bounds[1], positions[2 * i + 1]) - bounds[2] = Math.max(bounds[2], positions[2 * i]) - bounds[3] = Math.max(bounds[3], positions[2 * i + 1]) - } - - var sx = 1 / (bounds[2] - bounds[0]) - var sy = 1 / (bounds[3] - bounds[1]) - var tx = bounds[0] - var ty = bounds[1] - //v_position contains normalized positions to the available range of positions var v_position = pool.mallocFloat32(4 * pointCount) var v_sizeWidth = pool.mallocFloat32(2 * pointCount) @@ -330,8 +311,8 @@ proto.update = function(options) { //collect buffers data for(var i = 0; i < pointCount; ++i) { var id = packedId[i] - var x = sx * (positions[2 * id] - tx) - var y = sy * (positions[2 * id + 1] - ty) + var x = positions[2 * id] + var y = positions[2 * id + 1] var s = sizes[id] var w = borderWidths[id] From 874a4f918eadf22714aaced59ea01852d308cf7d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sun, 12 Feb 2017 21:01:23 +0300 Subject: [PATCH 39/41] Make offset optional --- scatter-fancy.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scatter-fancy.js b/scatter-fancy.js index ee6658d..cb36949 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -225,6 +225,8 @@ proto.update = function(options) { var borderColors = options.borderColors || [] var gl = this.plot.gl + if (options.charOffset != null) this.charOffset = options.charOffset + this.points = positions //create packed positions here From 3f3b85e4bd1479db5d26718fdb7cdb7e20dcdefe Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 16 Feb 2017 11:31:13 +0300 Subject: [PATCH 40/41] Detect snapping --- lib/shaders/frag.glsl | 2 +- scatter-fancy.js | 46 ++++++++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/shaders/frag.glsl b/lib/shaders/frag.glsl index 208642b..476b47c 100644 --- a/lib/shaders/frag.glsl +++ b/lib/shaders/frag.glsl @@ -22,7 +22,7 @@ void main() { if (dist < 1e-2) discard; - float dif = 5. * pixelRatio * borderWidth / pointSize; + float dif = 6. * pixelRatio * borderWidth / pointSize; float borderLevel = .74 - dif * .7; float charLevel = .74 + dif * .3; float gamma = .005 * charsStep / pointSize; diff --git a/scatter-fancy.js b/scatter-fancy.js index cb36949..a5181ca 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -52,6 +52,9 @@ function GLScatterFancy( //due to font alignmens some glyphs are off a bit this.charOffset = .03 + //snapping loses points sorting, so disable snapping on small number of points + this.snapThreshold = 1e4 + //border/char colors texture this.paletteTexture = createTexture(this.plot.gl, [256, 1]) } @@ -112,8 +115,8 @@ var proto = GLScatterFancy.prototype proto.drawPick = function(offset) { var pick = offset !== undefined var plot = this.plot - var pointCount = this.pointCount + var snap = pointCount > this.snapThreshold if(!pointCount) { return offset @@ -173,24 +176,29 @@ var proto = GLScatterFancy.prototype var scales = this.scales - for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) { - var lod = scales[scaleNum] - if(lod.pixelSize && (lod.pixelSize < pixelSize * 1.25) && scaleNum > 1) { - continue - } + if (snap) { + for (var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) { + var lod = scales[scaleNum] + if(lod.pixelSize && (lod.pixelSize < pixelSize * 1.25) && scaleNum > 1) { + continue + } - var intervalStart = lod.offset - var intervalEnd = lod.count + intervalStart + var intervalStart = lod.offset + var intervalEnd = lod.count + intervalStart - var startOffset = search.ge(this.xCoords, xStart, intervalStart, intervalEnd - 1) - var endOffset = search.lt(this.xCoords, xEnd, startOffset, intervalEnd - 1) + 1 + var startOffset = search.ge(this.xCoords, xStart, intervalStart, intervalEnd - 1) + var endOffset = search.lt(this.xCoords, xEnd, startOffset, intervalEnd - 1) + 1 - if (endOffset > startOffset) { - gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) - } + if (endOffset > startOffset) { + gl.drawArrays(gl.POINTS, startOffset, (endOffset - startOffset)) + } + } + } + else { + gl.drawArrays(gl.POINTS, 0, pointCount) } - if (pick) return offset + this.pointCount + if (pick) return offset + pointCount else { gl.disable(gl.BLEND) } @@ -235,10 +243,15 @@ proto.update = function(options) { var packedW = pool.mallocFloat32(2 * pointCount) var packed = pool.mallocFloat64(2 * pointCount) packed.set(this.points) - this.scales = snapPoints(packed, packedId, packedW) this.pointCount = pointCount + var snap = pointCount > this.snapThreshold + + if (snap) { + this.scales = snapPoints(packed, packedId, packedW) + } + //v_position contains normalized positions to the available range of positions var v_position = pool.mallocFloat32(4 * pointCount) var v_sizeWidth = pool.mallocFloat32(2 * pointCount) @@ -312,7 +325,7 @@ proto.update = function(options) { //collect buffers data for(var i = 0; i < pointCount; ++i) { - var id = packedId[i] + var id = snap ? packedId[i] : i var x = positions[2 * id] var y = positions[2 * id + 1] var s = sizes[id] @@ -346,6 +359,7 @@ proto.update = function(options) { v_chars[2 * i] = charId % cols } + // if (!v_color.length) return //fill buffes this.positionBuffer.update(v_position) From 498ed1759935d197520ace69f0317f0c6ce64117 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 16 Feb 2017 11:52:34 +0300 Subject: [PATCH 41/41] Add atlas optimization --- scatter-fancy.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scatter-fancy.js b/scatter-fancy.js index a5181ca..adff192 100644 --- a/scatter-fancy.js +++ b/scatter-fancy.js @@ -314,14 +314,19 @@ proto.update = function(options) { if (chars.length > maxChars) { console.warn('gl-scatter2d-fancy: number of characters is more than maximum texture size. Try reducing it.') } - this.charCanvas = atlas({ - canvas: this.charCanvas, - family: 'sans-serif', - size: charSize, - shape: [atlasW, atlasH], - step: [step, step], - chars: chars - }) + + //do not overupdate atlas + if (!this.chars || (this.chars+'' !== chars+'')) { + this.charCanvas = atlas({ + canvas: this.charCanvas, + family: 'sans-serif', + size: charSize, + shape: [atlasW, atlasH], + step: [step, step], + chars: chars + }) + this.chars = chars + } //collect buffers data for(var i = 0; i < pointCount; ++i) {