From 6ba7f81bc3db54cd7b010825de586c8f3a5969ef Mon Sep 17 00:00:00 2001 From: dy Date: Mon, 24 Sep 2018 18:12:36 +0300 Subject: [PATCH 1/5] Introduce tests --- test.js => example.js | 0 index.js | 2 +- package.json | 1 + test/35a.png | Bin 0 -> 643 bytes test/35b.png | Bin 0 -> 672 bytes test/index.js | 276 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 278 insertions(+), 1 deletion(-) rename test.js => example.js (100%) create mode 100644 test/35a.png create mode 100644 test/35b.png create mode 100644 test/index.js diff --git a/test.js b/example.js similarity index 100% rename from test.js rename to example.js diff --git a/index.js b/index.js index 1cb719c..b4c7b56 100644 --- a/index.js +++ b/index.js @@ -38,6 +38,7 @@ function Line2D (regl, options) { // persistent variables this.gl = regl._gl this.regl = regl + this.canvas = this.gl.canvas // list of options for lines this.passes = [] @@ -323,7 +324,6 @@ Line2D.prototype.render = function (...args) { this.draw() } - Line2D.prototype.draw = function (...args) { // render multiple polylines via regl batch (args.length ? args : this.passes).forEach((s, i) => { diff --git a/package.json b/package.json index 1f6d497..ce499d8 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "enable-mobile": "^1.0.7", "fps-indicator": "^1.1.0", "gauss-random": "^1.0.1", + "gl-util": "^3.0.9", "mobilify": "^1.0.1", "nanoraf": "^3.0.1", "next-pow-2": "^1.0.0", diff --git a/test/35a.png b/test/35a.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2d0d266b1220ae7529051cea66ad196ccd4cec GIT binary patch literal 643 zcmeAS@N?(olHy`uVBq!ia0vp^DImGaSW-5dwch6-ys7Y*N0zD zvd`P2>@c@6$g6(Rk6U(YA{4H^x;}XWN0P=KpZ5nMPA#4C$(Yx0hTr5rkBWP4zhyY` zVqwG~1{WS?0p&zZ6;!C{(rz9u!6OSMu!^iU(r^)IcA6Kk=JWyq zM;@+9nY%SVpI(}L`R|&yZTZJT?I-=3&@AQl%1OlVQqzj0B`kr*JlGfYP2@Vl>?S3k zFQMsRli0%ku}x6wh_YLRzU_05N$URBaSxCjWO~#{)M^nH+uKk6o z-~Q&02PQBuK9)Z$!_pJZ6D(kG&+v@Hfrw)UjEs+s-O2?Ft{LuXYWOI-SeirPw!yB3 zhK;g|Ejc898_a5I*eJhPl0)LS!K{XcLYc)@91_nBRy8#g$}N`SkSH}+)zA>g|JVe` zH1un12$X+pz#)-qz}MJtkuTXC$TTbiGUbwuIV5Tg%z#Y($J#)qVVL8A7yTavS$L#- zTm=l?7~FF>u;FM0BjaJYWIiLko|I41d8M`=OY7P%|Hdaj=h%Z)pRElT7JUu)!_=|v W&Ki@FX-j~Ki^0>?&t;ucLK6UT7WJ3_ literal 0 HcmV?d00001 diff --git a/test/35b.png b/test/35b.png new file mode 100644 index 0000000000000000000000000000000000000000..756b5ff454aebbb69b359b158a8f5a47b40c184c GIT binary patch literal 672 zcmeAS@N?(olHy`uVBq!ia0vp^DImxCxhy}K#IIzzS`=$_f$ncV+WUqBwMS*VTL7^9+i?gk!*$_*#O5)nC42-{ZZl#F&nmyB)9QkcgI;>u})4;TH@{#{}Dq zI3&^~x)c(oBxp1?1a>V13X1j|Q%Hz8n!&+zOt!~OA>q!^44|lR&nbljo1-f@nA~`K zyc80y99qG^K{vKI@nXV~!Br?P#m&G-wMV3F-c=>E;^TIFP8jOt2oXPp8f5pi0xb`?t w@Jy3zjc(g?{C!dLiA{U%H(u<1n15G(zs=k|+%6HTfGLl`)78&qol`;+0K3%?bpQYW literal 0 HcmV?d00001 diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..7ecbeb9 --- /dev/null +++ b/test/index.js @@ -0,0 +1,276 @@ +'use strict' + +const createLine = require('../') +const random = require('gauss-random') +const rgba = require('color-rgba') +const createScatter = require('regl-scatter2d') +const t = require('tape') +const normalize = require('array-normalize') +const extend = require('object-assign') +const arc = require('arc-to') +const curve = require('adaptive-bezier-curve') +const flatten = require('flatten-vertex-data') +const arrFrom = require('array-from') +const arrFill = require('array-fill') +const ctx = require('gl-util/context')({width: 100, height: 100}) +const regl = require('regl')({ + gl: ctx, + attributes: { preserveDrawingBuffer: true }, + extensions: ['ANGLE_instanced_arrays', 'OES_element_index_uint'] +}) +const imageEqual = require('../../image-equal') + + +t.only('#35: updating null pass', t => { + let line2d = createLine(regl) + + line2d.update([ + { thickness: 4, points: [0,0, 1,1, 1,0], close: true, color: 'red' }, + undefined, + { thickness: 4, points: [0,1, 1,1, 0,0], close: true, color: 'blue' } + ]) + line2d.draw(0) + line2d.draw(1) + + imageEqual('./test/35a.png', line2d) + .catch(t.fail) + .then(() => { + line2d.update([ + { thickness: 4, points: [0,0, 1,1, 1,0], close: true, color: 'red' }, + null, + { thickness: 4, points: [0,1, 1,1, 0,0], close: true, color: 'blue' } + ]) + line2d.draw(0) + line2d.draw(1) + + return imageEqual('./test/35b.png', line2d) + }) + .catch(t.fail) + .then(() => { + line2d.update([ + { thickness: 4, points: [0,0, 1,1, 1,0], close: true, color: 'red' }, + null, + { thickness: 4, points: [0,1, 1,1, 0,0], close: true, color: 'blue' } + ]) + line2d.draw(0) + line2d.draw(2) + + return imageEqual('./test/35a.png', line2d) + }) + .then(t.end, t.fail) +}) + +t('aligned line', t => { + batch.push(extend({}, options, { + positions: [ 0,0, 0.5,0, 1,0 ], + type: 'round' + })) + + t.end() +}) + +t('multiple points', t => { + let N = 1e4 + let positions = Array(2 * N) + for(var i=0; i<2*N; i+=2) { + // positions[i] = (i/N)*10.0-10.0 + positions[i] = random() * 2 + positions[i+1] = random() * 2 + } + + scale(positions, .15, .15) + translate(positions, -5, -3) + + batch.push(extend({}, options, { + color: 'red', + join: 'rect', + positions, thickness: 3, range, dash: [3, 3] + })) + + t.end() +}) + +t('closed circuit', t => { + // [0, 0.4, 0, 0.4, 0, 1, 1, 0, 1, 0, 1, 0] + let positions = [0,0, 0,3, 3,-2, -3,-3, -6,0, -6,-2, .5,-2, 0.5,1]//, 0,0] + + scale(positions, .25, .25) + translate(positions, -1.5, -3) + + batch.push(extend({}, options, { + color: 'green', + close: false, + join: 'miter', + positions: positions, overlay: false, thickness: 30, + dash: [8, 2] + })) + + t.end() +}) + +t('basic edge cases', t => { + let positions = [-3,4, -3,3, -3,0, -1,0, -.7,-.5, 0,1, -.5,-.5, .5,1, 0,0, .5,.5, 1,0.5, 2,2, 5,-3, -1,-1.5, -2.5,-2, -5,-3, -4,1, -5,1, -4.5,1, -4.5,2] + + scale(positions, .25, .25) + translate(positions, 1.5, -3) + + batch.push(extend({}, options, {overlay: true, positions: positions, thickness: 10, dash: [15, 5]})) + + t.end() +}) + +t('near-opposite directions / miter clipping', t => { + let positions = [0,1, 0.25,4, .25,-4, .5,-1, .65,0] + + // normalize(positions, 2) + // translate(positions, 2, 1) + scale(positions, 1.75, 1) + scale(positions, .5, .5) + translate(positions, 5.5, -1.82) + + batch.push(extend({}, options, {overlay: true, positions: positions, thickness: 30, dash: [9, 1]})) + + t.end() +}) + +t('plotly linear approx', t => { + let positions = [0.07511045655375555,0.06510416666666519,0.1281296023564065,-0.013020833333332593,0.18114874815905743,-0.2734375,0.22533136966126657,-1.0546875,0.25184094256259204,-1.9140625,0.2739322533136965,-2.916666666666665,0.2960235640648011,-4.0625,0.3136966126656848,-5.234375,0.33136966126656847,-6.2890625,0.3490427098674521,-7.3828125,0.36671575846833576,-8.4765625,0.38438880706921935,-9.53125,0.406480117820324,-10.468750000000004,0.4462444771723122,-11.640625,0.4872711971386493,-12.533482142857144,0.5434462444771723,-12.135416666666668,0.5964653902798233,-11.679687500000004,0.6494845360824743,-11.223958333333332,0.7025036818851251,-10.638020833333343,0.7555228276877761,-10.10416666666667,0.8085419734904271,-9.622395833333334,0.8615611192930782,-9.140625,0.914580265095729,-8.6328125,0.96759941089838,-8.151041666666664,1.020618556701031,-7.565104166666665,1.0736377025036818,-7.018229166666665,1.1266568483063328,-6.471354166666668,1.1796759941089838,-5.976562499999998,1.2326951399116348,-5.442708333333335,1.285714285714286,-4.908854166666666,1.3387334315169366,-4.3619791666666625,1.3917525773195876,-3.867187499999999,1.4447717231222386,-3.372395833333334,1.4977908689248896,-2.8385416666666643,1.5508100147275405,-2.304687499999999,1.6038291605301915,-1.7708333333333326,1.6568483063328423,-1.263020833333337,1.7098674521354933,-0.7291666666666663,1.7628865979381445,-0.1822916666666663,1.7614138438880704,1.2500000000000022,1.8159057437407953,0.3385416666666696,1.8689248895434465,0.8203125000000044,1.9219440353460973,1.3541666666666696,1.974963181148748,1.8359375000000022,2.0279823269513995,2.304687500000002,2.0810014727540502,2.8515624999999956,2.1340206185567006,3.320312499999998,2.1870397643593518,3.8541666666666674,2.240058910162003,4.335937499999996,2.2930780559646537,4.81770833333333,2.346097201767305,5.351562500000002,2.3991163475699557,5.820312499999998,2.452135493372607,6.289062499999993,2.5051546391752577,6.770833333333335,2.5581737849779085,7.3177083333333375,2.6111929307805597,7.91666666666667,2.6642120765832105,8.3203125,2.7172312223858617,8.671875000000002,2.770250368188513,9.088541666666664,2.823269513991163,9.531249999999998,2.8762886597938144,10.130208333333336,2.929307805596465,10.71614583333333,2.9823269513991164,11.22395833333334,3.035346097201767,11.54947916666667,3.0883652430044184,12.05729166666666,3.141384388807069,12.1875,3.1855670103092786,11.5234375,3.212076583210604,10.2734375,3.2297496318114876,9.179687500000007,3.247422680412371,8.046874999999991,3.2650957290132547,6.562500000000002,3.282768777614138,5.078124999999996,3.300441826215022,3.8671874999999933,3.3181148748159055,2.7734375000000044,3.3446244477172313,1.5039062499999978,3.38880706921944,0.7031249999999978,3.441826215022091,0.4687500000000022,3.4948453608247423,0.46875000000000444,3.547864506627393,0.4687499999999978,3.6008836524300443,0.5729166666666718,3.653902798232695,0.9114583333333259,3.7069219440353467,1.3020833333333393,3.759941089837997,1.4453125000000022,3.804123711340206,0.9570312500000022,3.8350515463917527,-0.052083333333334814,3.857142857142857,-1.250000000000001,3.874815905743741,-2.578124999999999,3.8924889543446244,-3.7500000000000036,3.9101620029455084,-5.039062499999999,3.9366715758468334,-6.523437500000004,3.949926362297496,-7.265624999999993,3.9808541973490428,-6.249999999999998,3.9985272459499264,-5.234375,4.01620029455081,-4.023437499999996,4.033873343151693,-2.6562500000000067,4.051546391752577,0.07812499999999778,4.047128129602356,-1.4062500000000044,4.060382916053019,1.9921875000000067,4.055964653902798,1.1718749999999978,4.070692194403534,4.114583333333329,4.06480117820324,2.890625,4.078055964653903,5.703125000000004,4.088365243004418,7.96875,4.082474226804123,6.796875000000009,4.095729013254786,9.492187500000002,4.1060382916053015,10.57291666666666,4.139911634756995,11.575520833333343,4.142562592047128,12.953124999999996,4.166421207658321,9.6875,4.175257731958763,8.281250000000004,4.182621502209131,6.640625,4.191458026509574,4.947916666666671,4.210603829160529,3.7499999999999956,4.228276877761414,2.5390625000000044,4.250368188512518,1.3802083333333304,4.2901325478645065,0.390625,4.343151693667157,0.09114583333332815,4.396170839469808,0.07812499999999334,4.44918998527246,0.07812500000000444,4.5022091310751104,0.07812499999999556,4.555228276877761,0.07812500000000888,4.608247422680412,0.07812499999999556,4.661266568483064,0.07812500000000888,4.714285714285714,0.14322916666667407,4.767304860088365,0.15625,4.820324005891017,0.10416666666666297,4.873343151693667,0.07812499999999556,4.926362297496317,0.07812499999999778,4.97938144329897,0.10416666666667185,5.03240058910162,0.15625000000000222,5.085419734904271,0.07812500000000888,5.1384388807069215,0.07812499999999334,5.191458026509573,0.07812500000000444,5.244477172312224,0.07812500000000444,5.297496318114875,0.07812499999999556,5.350515463917525,0.1432291666666652,5.403534609720176,0.1302083333333326,5.456553755522828,0.07812499999999556,5.509572901325479,0.07812499999999778,5.562592047128129,0.07812500000000222,5.615611192930779,0.07812499999999334,5.668630338733432,0.07812499999999334,5.721649484536082,0.07812500000000222,5.774668630338733,0.07812500000000444,5.827687776141384,0.10416666666667185,5.876288659793816,0.15625000000000666]//.slice(198,-86) + + scale(positions, 1, .12) + // scale(positions, 2, .25) + translate(positions, -6, 0) + + batch.push(extend({}, options, {join: 'round', positions: positions, thickness: 3, dash: null})) + + t.end() +}) + +t('closed path', t => { + let positions + + positions = [0,0, 1,0, 1,1, 0,1, 0,0] + translate(positions, 4, 2) + batch.push(extend({}, options, {overlay: true, fill: 'green', close: true, positions: positions, thickness: 10, dash: null})) + + positions = [0,0, 1,0, .5,1] + translate(positions, 5, 2) + batch.push(extend({}, options, {overlay: true, fill: 'blue', close: true, positions: positions, thickness: 10, dash: null})) + + + positions = circle(3.5, 2.5, .5) + batch.push(extend({}, options, {overlay: true, fill: 'red', close: true, positions: positions, thickness: 10, dash: null})) + + t.end() +}) + +t('time case', t => { + batch.push({ + type: 'rect', + positions: [25741380000,1293840000000,25741380001,1293926400000,25741380002,1294012800000,25741380003,1294099200000,25741380004,1294185600000,1477434180000,1294272000000,1477434180001,1294358400000,1477434180002,1294444800000,1477434180003,1294531200000,1477434180004,1294617600000], + range: [1477434179998,1293725711392.4,1477434180004,1295595888607.6], + width: 20, + color: 'red' + }) + + t.end() +}) + +t('update(1,2) after update(1,2,3) removes 3rd pass') + +t.skip('disconnected', t => { + batch.push({ + type: 'round', + positions: [1, -2, 2, -2, 3, NaN, 4, 2, 5, 2], + width: 20, + color: 'red' + }) +}) + +t('fill', t => { + batch.push({ + fill: '#F9F38C', + strokeWidth: 6, + stroke: '#D07735', + close: false, + positions: translate(scale([0,40, 40,40, 40,80, 80,80, 80,120, 120,120, 120,160],.015,.015), 2, -1.5), + range + }) + + t.end() +}) + +t('colorscale', t => { + let positions = translate(scale(flatten(curve([4, 4], [7, 10], [12, 2], [20, 4], 5)), .25, .25), -3, -1) + let colors = arrFrom({ length: positions.length / 2 }).map(x => palette[Math.floor(Math.random() * palette.length)]) + + translate(positions, 0, 5) + scale(positions, .5, .5) + + batch.push({ + positions: positions, + color: colors, + range, + thickness: 60 + }) + + t.end() +}) + +t.skip('changing color', t => { + let thickness = 100 + let positions = [0,0, 1,1] + let colors = 'green' + + line2d.update({colors, positions, join: 'rect', thickness: 30}) + line2d.draw() + + setTimeout(() => { + regl.clear({color: true, depth: true}) + line2d.update({colors: 'black'}) + line2d.draw() + }) + + t.end() +}) + +t('round join', t => { + let thickness = 100 + let positions = [-1,-.9, -1,-1, -1,1, -.9,-.9, -.8,.8, -.7,-.6, -.5,.4, -.2,-.2, .4,0, .8,0] + let colors = ['black', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'gray', 'blue', 'black'] + + scale(positions, 1, 1) + translate(positions, -4, 2.5) + + batch.push(extend({}, options, {color: colors, opacity: 1, join: 'round', overlay: true, positions: positions, miterlimit: 1, thickness: 30, dash: null})) + + t.end() +}) + +t.skip('rect line', t => { + t.end() +}) + +t.skip('painting', t => { + pan = false + t.end() +}) + +t.skip('fix horizontal segments', t => { + let positions = { + x: [ 0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,3,3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8,3.9,4,4.1,4.2,4.3,4.4,4.5,4.6,4.7,4.8,4.9,5,5.1,5.2,5.3,5.4,5.5,5.6,5.7,5.8,5.9,6,6.1,6.2,6.3,6.4,6.5,6.6,6.7,6.8,6.9,7,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8,8.1,8.2,8.3,8.4,8.5,8.6,8.7,8.8,8.9,9,9.1,9.2,9.3,9.4,9.5,9.6,9.7,9.8,9.9,10], + y: [ 20.392,20.388,20.386,20.374,20.384,20.384,20.384,20.38,20.384,20.384,20.384,20.372,20.388,20.384,20.386,20.376,20.38,20.384,20.38,20.386,20.382,20.378,20.372,20.378,20.386,20.384,20.386,20.394,20.388,20.38,20.384,20.384,20.374,20.36,20.378,20.384,20.378,20.378,20.384,20.38,20.384,20.386,20.378,20.384,20.386,20.384,20.384,20.384,20.386,20.384,20.38,20.374,20.384,20.384,20.384,20.384,20.384,20.384,20.378,20.384,20.384,20.38,20.372,20.384,20.374,20.38,20.384,20.378,20.394,20.384,20.384,20.384,20.376,20.38,20.378,20.378,20.384,20.372,20.384,20.378,20.384,20.384,20.378,20.378,20.384,20.38,20.376,20.38,20.38,20.384,20.384,20.378,20.38,20.384,20.38,20.394,20.384,20.384,20.378,20.372] + } + + line2d.update({color: 'gray', positions, join: 'round', thickness: 10}) + line2d.draw() + + batch.push(extend({}, options, { + positions, + type: 'round', + thickness: 10, + color: 'gray' + })) + + t.end() +}) + From 00d50b0767c92db9c746f2b717ba602510b0548d Mon Sep 17 00:00:00 2001 From: dy Date: Fri, 5 Oct 2018 21:07:55 +0300 Subject: [PATCH 2/5] Add dash pattern sectioning --- index.html | 17165 -------------------- index.js | 25 +- readme.md | 1 + fill-frag.glsl => shader/fill-frag.glsl | 0 fill-vert.glsl => shader/fill-vert.glsl | 0 miter-frag.glsl => shader/miter-frag.glsl | 6 +- miter-vert.glsl => shader/miter-vert.glsl | 0 rect-frag.glsl => shader/rect-frag.glsl | 8 +- rect-vert.glsl => shader/rect-vert.glsl | 0 shader/section-snap.glsl | 18 + test/index.js | 26 +- 11 files changed, 70 insertions(+), 17179 deletions(-) delete mode 100644 index.html rename fill-frag.glsl => shader/fill-frag.glsl (100%) rename fill-vert.glsl => shader/fill-vert.glsl (100%) rename miter-frag.glsl => shader/miter-frag.glsl (93%) rename miter-vert.glsl => shader/miter-vert.glsl (100%) rename rect-frag.glsl => shader/rect-frag.glsl (64%) rename rect-vert.glsl => shader/rect-vert.glsl (100%) create mode 100644 shader/section-snap.glsl diff --git a/index.html b/index.html deleted file mode 100644 index cfe0701..0000000 --- a/index.html +++ /dev/null @@ -1,17165 +0,0 @@ - - - -regl-line2d - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/index.js b/index.js index b4c7b56..f5d77f8 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const rgba = require('color-normalize') const getBounds = require('array-bounds') const extend = require('object-assign') -const glslify = require('glslify') +const glsl = require('glslify') const pick = require('pick-by-alias') const flatten = require('flatten-vertex-data') const triangulate = require('earcut') @@ -89,6 +89,7 @@ Line2D.createShaders = function (regl) { opacity: regl.prop('opacity'), pixelRatio: regl.context('pixelRatio'), id: regl.prop('id'), + sections: regl.prop('dashSections'), dashSize: regl.prop('dashLength'), viewport: (c, p) => [p.viewport.x, p.viewport.y, c.viewportWidth, c.viewportHeight], depth: regl.prop('depth') @@ -124,8 +125,8 @@ Line2D.createShaders = function (regl) { // simplified rectangular line shader let drawRectLine = regl(extend({ - vert: glslify('./rect-vert.glsl'), - frag: glslify('./rect-frag.glsl'), + vert: glsl('./shader/rect-vert.glsl'), + frag: glsl('./shader/rect-frag.glsl'), attributes: { // if point is at the end of segment @@ -188,8 +189,8 @@ Line2D.createShaders = function (regl) { face: 'back' }, - vert: glslify('./miter-vert.glsl'), - frag: glslify('./miter-frag.glsl'), + vert: glsl('./shader/miter-vert.glsl'), + frag: glsl('./shader/miter-frag.glsl'), attributes: { // is line end @@ -257,8 +258,8 @@ Line2D.createShaders = function (regl) { elements: (ctx, prop) => prop.triangles, offset: 0, - vert: glslify('./fill-vert.glsl'), - frag: glslify('./fill-frag.glsl'), + vert: glsl('./shader/fill-vert.glsl'), + frag: glsl('./shader/fill-frag.glsl'), uniforms: { scale: regl.prop('scale'), @@ -312,7 +313,8 @@ Line2D.defaults = { viewport: null, range: null, close: false, - fill: null + fill: null, + dashSections: 8 } @@ -391,7 +393,7 @@ Line2D.prototype.update = function (options) { thickness: 'thickness lineWidth lineWidths line-width linewidth width stroke-width strokewidth strokeWidth', join: 'lineJoin linejoin join type mode', miterLimit: 'miterlimit miterLimit', - dashes: 'dash dashes dasharray dash-array dashArray', + dashes: 'dash dashes dasharray dash-array dashArray dashPattern pattern', color: 'color colour stroke colors colours stroke-color strokeColor', fill: 'fill fill-color fillColor', opacity: 'alpha opacity', @@ -399,7 +401,8 @@ Line2D.prototype.update = function (options) { close: 'closed close closed-path closePath', range: 'range dataBox', viewport: 'viewport viewBox', - hole: 'holes hole hollow' + hole: 'holes hole hollow', + dashSections: 'sections dashSections patternSections' }) // init state @@ -444,6 +447,7 @@ Line2D.prototype.update = function (options) { o = extend({}, Line2D.defaults, o) } if (o.thickness != null) state.thickness = parseFloat(o.thickness) + if (o.dashSections != null) state.dashSections = parseInt(o.dashSections) if (o.opacity != null) state.opacity = parseFloat(o.opacity) if (o.miterLimit != null) state.miterLimit = parseFloat(o.miterLimit) if (o.overlay != null) { @@ -645,6 +649,7 @@ Line2D.prototype.update = function (options) { }, 0, 0) } + if (!state.color && !o.color) o.color = Line2D.defaults.color if (o.color) { let count = state.count let colors = o.color diff --git a/readme.md b/readme.md index 9185198..fbf4192 100644 --- a/readme.md +++ b/readme.md @@ -42,6 +42,7 @@ Option | Default | Description `dashes`, `dash`, `dasharray` | `null` | Array with dash lengths in px, altering color/space pairs, ie. `[2,10, 5,10, ...]`. `null` corresponds to solid line. `join`, `type` | `bevel` | Join style: `'rect'`, `'round'`, `'bevel'`. Applied to caps too. `miterLimit` | `1` | Max ratio of the join length to the thickness. +`dashSections` | `8` | Number of sections for dash pattern, full 360⁰ circle divided by this number. `close`, `closed`, `closePath` | `false` | Connect last point with the first point with a segment. `overlay` | `false` | Enable overlay of line segments. `range`, `dataBox` | `null` | Visible data range. diff --git a/fill-frag.glsl b/shader/fill-frag.glsl similarity index 100% rename from fill-frag.glsl rename to shader/fill-frag.glsl diff --git a/fill-vert.glsl b/shader/fill-vert.glsl similarity index 100% rename from fill-vert.glsl rename to shader/fill-vert.glsl diff --git a/miter-frag.glsl b/shader/miter-frag.glsl similarity index 93% rename from miter-frag.glsl rename to shader/miter-frag.glsl index 2417a40..8de5076 100644 --- a/miter-frag.glsl +++ b/shader/miter-frag.glsl @@ -1,7 +1,9 @@ +#pragma glslify: snapToSection = require('./section-snap.glsl') + precision highp float; uniform sampler2D dashPattern; -uniform float dashSize, pixelRatio, thickness, opacity, id, miterMode; +uniform float dashSize, pixelRatio, thickness, opacity, id, miterMode, sections; varying vec4 fragColor; varying vec2 tangent; @@ -71,6 +73,8 @@ void main() { } } + vec2 tangent = snapToSection(tangent, sections); + float t = fract(dot(tangent, gl_FragCoord.xy) / dashSize) * .5 + .25; float dash = texture2D(dashPattern, vec2(t, .5)).r; diff --git a/miter-vert.glsl b/shader/miter-vert.glsl similarity index 100% rename from miter-vert.glsl rename to shader/miter-vert.glsl diff --git a/rect-frag.glsl b/shader/rect-frag.glsl similarity index 64% rename from rect-frag.glsl rename to shader/rect-frag.glsl index 87e64f2..560a536 100644 --- a/rect-frag.glsl +++ b/shader/rect-frag.glsl @@ -1,16 +1,22 @@ +#pragma glslify: snapToSection = require('./section-snap.glsl') + precision highp float; uniform sampler2D dashPattern; -uniform float dashSize, pixelRatio, thickness, opacity, id; +uniform float dashSize, pixelRatio, thickness, opacity, id, sections; varying vec4 fragColor; varying vec2 tangent; + void main() { float alpha = 1.; + vec2 tangent = snapToSection(tangent, sections); + float t = fract(dot(tangent, gl_FragCoord.xy) / dashSize) * .5 + .25; + float dash = texture2D(dashPattern, vec2(t, .5)).r; gl_FragColor = fragColor; diff --git a/rect-vert.glsl b/shader/rect-vert.glsl similarity index 100% rename from rect-vert.glsl rename to shader/rect-vert.glsl diff --git a/shader/section-snap.glsl b/shader/section-snap.glsl new file mode 100644 index 0000000..e50e374 --- /dev/null +++ b/shader/section-snap.glsl @@ -0,0 +1,18 @@ +precision highp float; + +// turn, or 2 * pi +const float TAU = 6.283185307179586; + +// snap tangent to angle section +vec2 snap(vec2 tangent, float sections) { + float angle = atan(tangent.y, tangent.x); + float step = TAU / sections; + angle = floor(angle / step) * step; + + if (angle > TAU) angle -= TAU; + if (angle < 0.) angle += TAU; + + return vec2(cos(angle), sin(angle)); +} + +#pragma glslify: export(snap) diff --git a/test/index.js b/test/index.js index 7ecbeb9..2a50bd1 100644 --- a/test/index.js +++ b/test/index.js @@ -12,7 +12,7 @@ const curve = require('adaptive-bezier-curve') const flatten = require('flatten-vertex-data') const arrFrom = require('array-from') const arrFill = require('array-fill') -const ctx = require('gl-util/context')({width: 100, height: 100}) +const ctx = require('gl-util/context')() const regl = require('regl')({ gl: ctx, attributes: { preserveDrawingBuffer: true }, @@ -21,7 +21,7 @@ const regl = require('regl')({ const imageEqual = require('../../image-equal') -t.only('#35: updating null pass', t => { +t.skip('#35: updating null pass', t => { let line2d = createLine(regl) line2d.update([ @@ -60,6 +60,28 @@ t.only('#35: updating null pass', t => { .then(t.end, t.fail) }) +t.only('sin dash pattern', t => { + let line = createLine(regl) + + var i, steps = 100 + var data = [] + for (i = 0; i < steps; i++) { + data.push(i, Math.sin(i * 10 / steps)) + } + + line.update({ + type: 'join', + overlay: true, + data: data, + thickness: 20, + dash: [20,20], + range: [0,-1,steps,1] + }) + line.draw() + + t.end() +}) + t('aligned line', t => { batch.push(extend({}, options, { positions: [ 0,0, 0.5,0, 1,0 ], From 8bfb0177cd6ad9f71e939cf323b0d491b6e693ab Mon Sep 17 00:00:00 2001 From: dy Date: Fri, 5 Oct 2018 22:29:46 +0300 Subject: [PATCH 3/5] Add circle to test --- shader/section-snap.glsl | 5 +++++ test/index.js | 27 ++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/shader/section-snap.glsl b/shader/section-snap.glsl index e50e374..710b7aa 100644 --- a/shader/section-snap.glsl +++ b/shader/section-snap.glsl @@ -7,11 +7,16 @@ const float TAU = 6.283185307179586; vec2 snap(vec2 tangent, float sections) { float angle = atan(tangent.y, tangent.x); float step = TAU / sections; + angle = floor(angle / step) * step; + // shift section by half + angle += TAU * .5 / sections; + if (angle > TAU) angle -= TAU; if (angle < 0.) angle += TAU; + return vec2(cos(angle), sin(angle)); } diff --git a/test/index.js b/test/index.js index 2a50bd1..da71485 100644 --- a/test/index.js +++ b/test/index.js @@ -69,14 +69,22 @@ t.only('sin dash pattern', t => { data.push(i, Math.sin(i * 10 / steps)) } - line.update({ + line.update([{ type: 'join', overlay: true, data: data, - thickness: 20, - dash: [20,20], - range: [0,-1,steps,1] - }) + thickness: 4, + dash: [8,8], + range: [0,-2,steps * 2,2] + }, { + type: 'join', + overlay: true, + data: circle(0, 0, 24), + thickness: 80, + dash: [8,8], + close: true, + range: [-200,-80,60,80] + }]) line.draw() t.end() @@ -296,3 +304,12 @@ t.skip('fix horizontal segments', t => { t.end() }) + + + + +function circle(x, y, radius) { + var c = arc(x, y, radius, 0, Math.PI*2, false) + c.pop() + return c +} From 7198a764cda98d55b9ffae9aae12a9b65448af70 Mon Sep 17 00:00:00 2001 From: dy Date: Fri, 5 Oct 2018 23:10:27 +0300 Subject: [PATCH 4/5] Fix symmetrical patterns --- shader/section-snap.glsl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/shader/section-snap.glsl b/shader/section-snap.glsl index 710b7aa..09c4806 100644 --- a/shader/section-snap.glsl +++ b/shader/section-snap.glsl @@ -2,20 +2,22 @@ precision highp float; // turn, or 2 * pi const float TAU = 6.283185307179586; +const float PI = 3.141592653589793; + +float round (float v) { + return floor(v + .5); +} // snap tangent to angle section vec2 snap(vec2 tangent, float sections) { float angle = atan(tangent.y, tangent.x); - float step = TAU / sections; - angle = floor(angle / step) * step; - - // shift section by half - angle += TAU * .5 / sections; + float step = TAU / sections; - if (angle > TAU) angle -= TAU; - if (angle < 0.) angle += TAU; + angle = round(angle / step) * step; + // exploit symmetrical pattern alignment + angle = mod(angle, PI); return vec2(cos(angle), sin(angle)); } From e30b00a37907a531c868bb03cf3bf2be2270bbf2 Mon Sep 17 00:00:00 2001 From: dy Date: Sat, 6 Oct 2018 01:35:11 +0300 Subject: [PATCH 5/5] Fix color init --- index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index f5d77f8..ca0056f 100644 --- a/index.js +++ b/index.js @@ -427,6 +427,7 @@ Line2D.prototype.update = function (options) { min: 'linear' }), + colorCount: 0, colorBuffer: regl.buffer({ usage: 'dynamic', type: 'uint8', @@ -649,13 +650,11 @@ Line2D.prototype.update = function (options) { }, 0, 0) } - if (!state.color && !o.color) o.color = Line2D.defaults.color + if (!state.colorCount && state.count && !o.color) o.color = Line2D.defaults.color if (o.color) { let count = state.count let colors = o.color - if (!colors) colors = 'transparent' - let colorData = new Uint8Array(count * 4 + 4) // convert colors to typed arrays @@ -678,6 +677,8 @@ Line2D.prototype.update = function (options) { type: 'uint8', data: colorData }) + + state.colorCount = count } })