├── .circleci └── config.yml ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist ├── README.md ├── regl-gpu-lines.compat.js ├── regl-gpu-lines.compat.min.js ├── regl-gpu-lines.js └── regl-gpu-lines.min.js ├── docs ├── 3d.html ├── API.md ├── basic.html ├── batching.html ├── border.html ├── closed-loop.html ├── dash.html ├── debug-view.html ├── debug.html ├── debug.png ├── depth.html ├── hexgrid.html ├── instanced.html ├── knot.html ├── lorenz.gif ├── multiple.html ├── number-texture.html ├── postproject.html ├── strided.html ├── tests.html ├── vao.html └── variable-width.html ├── examples ├── 3d.js ├── basic.js ├── batching.js ├── border.js ├── closed-loop.js ├── dash.js ├── debug-view.js ├── debug.js ├── depth.js ├── hexgrid.js ├── instanced.js ├── knot.js ├── multiple.js ├── postproject.js ├── strided.js ├── vao.js └── variable-width.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── scripts ├── _example.html ├── browser-fixture-renderer.js ├── build-example.js ├── build-test-page.js ├── compress.js ├── dummy.js ├── get-fixtures.js ├── serve-example.js └── serve-render-tests.js ├── src ├── constants │ ├── attr-usage.js │ ├── dtypes.json │ ├── dtypesizes.js │ ├── glsltypes.js │ └── orientation.json ├── create-attr-spec.js ├── draw-segment.js ├── index.js ├── parse-pragmas.js ├── sanitize-buffer.js └── sanitize-in-list.js └── test ├── fixtures ├── miter │ ├── basic │ │ ├── expected.png │ │ └── fixture.json │ ├── dash │ │ ├── extrapolate │ │ │ ├── expected.png │ │ │ └── fixture.json │ │ └── no-extrapolate │ │ │ ├── expected.png │ │ │ └── fixture.json │ ├── degenerate │ │ ├── expected.png │ │ └── fixture.json │ ├── depth │ │ ├── expected.png │ │ └── fixture.json │ ├── insert-caps │ │ ├── nan │ │ │ ├── expected.png │ │ │ └── fixture.json │ │ ├── none │ │ │ ├── expected.png │ │ │ └── fixture.json │ │ ├── round │ │ │ ├── expected.png │ │ │ └── fixture.json │ │ └── square │ │ │ ├── expected.png │ │ │ └── fixture.json │ ├── manual-caps │ │ ├── none │ │ │ ├── expected.png │ │ │ └── fixture.json │ │ ├── round │ │ │ ├── expected.png │ │ │ └── fixture.json │ │ └── square │ │ │ ├── expected.png │ │ │ └── fixture.json │ └── sdf │ │ ├── expected.png │ │ └── fixture.json └── round │ ├── basic │ ├── expected.png │ └── fixture.json │ ├── dash │ ├── extrapolate │ │ ├── expected.png │ │ └── fixture.json │ └── no-extrapolate │ │ ├── expected.png │ │ └── fixture.json │ ├── degenerate │ ├── expected.png │ └── fixture.json │ ├── depth │ ├── expected.png │ └── fixture.json │ ├── insert-caps │ ├── none │ │ ├── expected.png │ │ └── fixture.json │ ├── round │ │ ├── expected.png │ │ └── fixture.json │ └── square │ │ ├── expected.png │ │ └── fixture.json │ ├── manual-caps │ ├── none │ │ ├── expected.png │ │ └── fixture.json │ ├── round │ │ ├── expected.png │ │ └── fixture.json │ └── square │ │ ├── expected.png │ │ └── fixture.json │ ├── postproject │ ├── expected.png │ └── fixture.json │ └── sdf │ ├── expected.png │ └── fixture.json ├── render.js └── util ├── create-context.js └── render-fixture.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | defaults: &defaults 4 | docker: 5 | - image: cimg/python:2.7-node 6 | working_directory: ~/regl-gpu-lines 7 | 8 | 9 | jobs: 10 | prepare: 11 | <<: *defaults 12 | steps: 13 | - checkout 14 | - run: python --version 15 | - run: npm install 16 | - persist_to_workspace: 17 | root: ~/ 18 | paths: 19 | - regl-gpu-lines 20 | test: 21 | <<: *defaults 22 | steps: 23 | - attach_workspace: 24 | at: ~/ 25 | - run: npm run test 26 | lint: 27 | <<: *defaults 28 | steps: 29 | - attach_workspace: 30 | at: ~/ 31 | - run: npm run lint 32 | test-render: 33 | <<: *defaults 34 | steps: 35 | - attach_workspace: 36 | at: ~/ 37 | - run: npm run test-render 38 | 39 | workflows: 40 | version: 2 41 | default: 42 | jobs: 43 | - prepare 44 | - lint: 45 | requires: 46 | - prepare 47 | - test-render: 48 | requires: 49 | - prepare 50 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 2018, 5 | "sourceType": "script" 6 | }, 7 | "env": { 8 | "node": true, 9 | "es6": true 10 | }, 11 | "rules": { 12 | "semi": "error" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | test/fixtures/**/actual.png 4 | test/fixtures/**/diff.png 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .circleci 2 | docs 3 | test/fixtures/**/*.png 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.4.0 2 | 3 | ### Features/Bugfixes 4 | 5 | - `normalized` and `type` keys were not respected for attributes, which prevented usage with more granular memory layout. 6 | 7 | ## 2.3.0 8 | 9 | ### Bugfixes 10 | 11 | - Throw an error if missing the `ANGLE_instanced_arrays` extension instead of just rendering incorrectly. 12 | 13 | ### Features 14 | 15 | - Includes sort of hacky "instanced" rendering (undocumented). Requires duplicating instanced geometry N times in order to draw N line segments, then sort of draws the first segment of all instances, then the second, etc. So it's not ideal, but it's better than issuing N draw calls. See `examples/instanced.js`. 16 | 17 | ## 2.2.1 18 | 19 | ### Bugfixes 20 | 21 | - Try hot-fixing an issue with VAO setup. 22 | 23 | ## 2.2.0 24 | 25 | ### Features 26 | 27 | - Adds support for Vertex Array Objects (VAOs). To use, move `vertexAttributes` and `endpointAttributes` out of per-frame draw call properties to the `drawLines.vao({vertexAttributes, endpointAttributes})` constructor, then pass the resulting object to the draw call as the `vao` property. 28 | 29 | ## 2.1.0 30 | 31 | ### Features 32 | 33 | - Adds `postproject` pragma to apply an additional transformation to lines after screen-projection. 34 | - Adds `viewportSize` render-time option in case you wish to project lines to some other shape than the viewport size. May be used in conjunction with `postproject`, for example to render to the unit square and then view that square from some other angle. 35 | 36 | ## 2.0.0 37 | 38 | ### Features 39 | 40 | - Moves `insertCaps` from a compile-time to a runtime configuration option. 41 | - Lazily instantiates the four potential draw command variations (endpoints vs. interior segments, and insert caps true vs. false). 42 | - Adds `reorder` as a compile-time option. When true (default: true), it internally reorders draw calls for arrays of line props (`drawLines([{...}, {...}, ...])`) to avoid repeatedly changing the shader program. 43 | 44 | ## 1.1.0 45 | 46 | ### Features 47 | 48 | - Reenable NaN to signal line breaks. It seems to work now. [Confirm here](https://rreusser.github.io/regl-gpu-lines/docs/tests.html#miter/insert-caps/nan). 49 | 50 | ## 1.0.0 51 | 52 | This release has no new features. Flipping the switch since, for the first time since starting, I don't have major new features or blocking bugs which would prevent me from calling this a piece of usable software. :tada: 53 | 54 | ## 0.0.23 55 | 56 | ### Features 57 | 58 | - Turning an integer index into a position was somewhat badly done. This release completely renumbers all of the indices. Instead of modifying geometry and flipping it in order to get winding order correct, it now shifts vertex indices by one while preserving geometry. At the cost of one extra wasted vertex, this has the effect of flipping winding order when needed in order to make it consistent--but without modifying geometry. The resulting code is easier to follow, shorter, cleaner, and shows better results. 59 | 60 | ### Bugfixes 61 | 62 | - As a result of renumbering, winding order is now consistent. 63 | - Collapsed triangle vertices are now repeated at the first and last points only, rather than scattered throughout the instances. 64 | 65 | ## 0.0.22 66 | 67 | ### Features 68 | 69 | - A new live-reloading test page with `npm run serve-render-tests`, accessible online using published module at https://rreusser.github.io/regl-gpu-lines/docs/tests.html 70 | 71 | ### Bugfixes 72 | 73 | - Fixes badly broken behavior on devices which don't successfully check for NaN in the shader 74 | 75 | ## 0.0.21 76 | 77 | ### Features 78 | 79 | - Clean up one of the worst parts of the shader code and get end cap insertion working with all combinations of joins and caps. :tada: 80 | 81 | ## 0.0.20 82 | 83 | ### Features 84 | 85 | - Add optional `extrapolate` keyword, as in `#pragma lines: extrapolate varying float name` to distinguish between varyings which are extrapolated outside the bounds of their respective segment endpoint values, and varyings which are clamped to the range of the segment. This can be used to dash caps and joins or to ensure colors are not extrapolated. 86 | 87 | ## 0.0.19 88 | 89 | ### Features 90 | 91 | - Add `insertCaps` option to be explicit about when caps are automatically inserted 92 | 93 | ### Bugfixes 94 | 95 | - Switch to preferring `w = 0` instead of `NaN` since `NaN` detection is a bit unreliable in GLSL. 96 | 97 | ## 0.0.18 98 | 99 | ### Features 100 | 101 | - Now inserts caps when it encounters NaN. With some remaining API cleanup since miters and bevels don't have enough points per instance to build a proper round. 102 | 103 | ### Bugfixes 104 | 105 | - Resolve hairpin and collinear cases!! :tada: 106 | 107 | ## 0.0.17 108 | 109 | ### Bugfixes 110 | 111 | - Fix debug instance ID 112 | 113 | ## 0.0.16 114 | 115 | ### Features 116 | 117 | - Almost a complete rewrite. Consolidated everything into a single shader with two switches (round vs. miter, cap vs. interior). 118 | - Line dashes work with both round and miter 119 | - SDF borders now work with round and miter 120 | - Bundle size down to 11kB minified, 4.5kB gzipped 121 | 122 | ### Limitations 123 | 124 | - Degenerate lines which turn 180 degrees are a regression. They sometimes but not always work. A tiny floating point offset will fix things. 125 | 126 | ## 0.0.15 127 | 128 | ### Features 129 | 130 | - Completely reworked rounded join geometry to split joins down the middle. This makes dashes usable, currently only with rounded joins. 131 | - Improved handling of z-coordinate 132 | - Improved handling of some corner cases, including self-intersecting lines and short segments 133 | - Added tests 134 | 135 | ## 0.0.14 136 | 137 | ### Features 138 | 139 | - Convert the index attribute into a unit-grid-aligned coordinate for wireframes. This is equivalent but much easier than exploding the triangle strip geometry into individual triangles. 140 | 141 | ### Bugfixes 142 | 143 | - Republish after botched 0.0.13 publish 144 | 145 | ## 0.0.12 146 | 147 | ### Bufixes 148 | 149 | - Fix an issue in which varying parameters were not triggering inclusion of their respective attributes. 150 | - Fix custom attribute divisor not set correctly 151 | 152 | ### Features 153 | 154 | - Debugged interleaved attributes. They work great! :tada: 155 | 156 | ## 0.0.11 157 | 158 | ### Features 159 | 160 | - Rename `isstart` to `capOrientation` and change from a boolean to a float. `isstart` seemed unpleasantly asymmetric. `CAP_START` and `CAP_END` are now exported as constants on the `reglLines` function. In the future this may be used as a bit mask to additionally allow signaling two-vertex lines. 161 | 162 | ### Bugfixes 163 | 164 | - Throw errors when attempting to forward `count`, `elements`, `attributes` or `instances` to regl. 165 | 166 | ## 0.0.10 167 | 168 | ### Bugfixes 169 | 170 | - Fix inner miters to clip to the lesser of the two adjacent segment lengths 171 | 172 | ## 0.0.9 173 | 174 | ### Bugfixes 175 | 176 | - Remove the minimum-length constraint (was 1/100 pixel) since it sometimes results in missing caps. 177 | - Improved documentation! 178 | 179 | ## 0.0.8 180 | 181 | ### Bugfixes 182 | 183 | - Fix an issue with using nan or w=0 to split lines into multiple segments 184 | 185 | ## 0.0.7 186 | 187 | Beginning a changelog as the module is starting to stabilize. :tada: 188 | 189 | ### Features 190 | 191 | - It's proving extremely common to implement a regl wrapper with customization that falls outside the scope of this module. This release adds the ability to merge that config with the arguments to this module. It now uses the `vert`, `frag`, and `debug` options and forward all other configuration to a regl wrapper, invoked on each draw. 192 | 193 | 194 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Ricky Reusser 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | This directory contains two sets of UMD bundles. They are identical except that the `compat` bundles are compiled with [`@babel/preset-env`](https://babeljs.io/docs/en/babel-preset-env) for people requiring additional compatibility. 2 | -------------------------------------------------------------------------------- /docs/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 81 | 96 |
97 |
98 | Code 99 |

100 | const regl = createREGL({
101 |   extensions: ['ANGLE_instanced_arrays']
102 | });
103 | 
104 | // Instantiate a command for drawing lines
105 | const drawLines = reglLines(regl, {
106 |   vert: `
107 |     precision highp float;
108 | 
109 |     // Use a vec2 attribute to construt the vec4 vertex position
110 |     #pragma lines: attribute vec2 xy;
111 |     #pragma lines: position = getPosition(xy);
112 |     vec4 getPosition(vec2 xy) {
113 |       return vec4(xy, 0, 1);
114 |     }
115 | 
116 |     // Return the line width from a uniorm
117 |     #pragma lines: width = getWidth();
118 |     uniform float width;
119 |     float getWidth() {
120 |       return width;
121 |     }`,
122 |   frag: `
123 |     precision lowp float;
124 |     void main () {
125 |       gl_FragColor = vec4(1);
126 |     }`,
127 | 
128 |   // Multiply the width by the pixel ratio for consistent width
129 |   uniforms: {
130 |     width: (ctx, props) => ctx.pixelRatio * props.width
131 |   },
132 | });
133 | 
134 | // Construct an array of xy pairs
135 | const n = 11;
136 | const xy = [...Array(n).keys()]
137 |   .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8)
138 |   .map(t => [t, 0.5 * Math.sin(8.0 * t)]);
139 | 
140 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
141 | // them on every draw call.
142 | const lineData = {
143 |   width: 30,
144 |   join: 'round',
145 |   cap: 'round',
146 |   vertexCount: xy.length,
147 |   vertexAttributes: { xy: regl.buffer(xy) },
148 |   endpointCount: 2,
149 |   endpointAttributes: { xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]) }
150 | };
151 | 
152 | function draw () {
153 |   regl.poll();
154 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
155 |   drawLines(lineData);
156 | }
157 | 
158 | draw();
159 | window.addEventListener('resize', draw);
160 | 
161 | 
162 |
163 |
164 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/border.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 122 | 137 |
138 |
139 | Code 140 |

141 | const regl = createREGL({
142 |   extensions: ['ANGLE_instanced_arrays']
143 | });
144 | 
145 | // Instantiate a command for drawing lines
146 | const drawLines = reglLines(regl, {
147 |   vert: `
148 |     precision highp float;
149 | 
150 |     #pragma lines: attribute vec2 xy;
151 | 
152 |     // Assign a function for computing the vec4 position
153 |     #pragma lines: position = getPosition(xy);
154 | 
155 |     // Assign a function for computing the float width
156 |     #pragma lines: width = getWidth(xy);
157 | 
158 |     uniform float width;
159 |     uniform vec2 translate, scale;
160 | 
161 |     // Implement the functions above
162 |     vec4 getPosition(vec2 xy) { return vec4(xy * scale + translate, 0, 1); }
163 |     float getWidth(vec2 xy) { return width; }
164 |     float getX(vec2 xy) { return xy.x; }`,
165 |   frag: `
166 |     precision highp float;
167 |     varying vec3 lineCoord;
168 |     uniform float width, borderWidth;
169 |     void main () {
170 |       // Convert the line coord into an SDF
171 |       float sdf = length(lineCoord.xy) * width;
172 | 
173 |       vec3 borderColor = 0.5 + 0.5 * vec3(lineCoord.xy, 0);
174 | 
175 |       // Apply a border with 1px transition
176 |       gl_FragColor = vec4(
177 |         mix(vec3(0), borderColor, smoothstep(width - borderWidth - 1.0, width - borderWidth + 1.0, sdf)),
178 |         1);
179 |     }`,
180 | 
181 |   // Additional regl command properties are valid
182 |   uniforms: {
183 |     width: (ctx, props) => ctx.pixelRatio * props.width,
184 |     borderWidth: (ctx, props) => ctx.pixelRatio * props.borderWidth,
185 |     translate: regl.prop('translate'),
186 |     scale: regl.prop('scale')
187 |   },
188 |   depth: {enable: false}
189 | });
190 | 
191 | // Construct an array of xy pairs
192 | const n = 11;
193 | const xy = [...Array(n).keys()]
194 |   .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8)
195 |   .map(t => [t, 0.5 * Math.sin(54.0 * t)]);
196 | 
197 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
198 | // them on every draw call.
199 | const lineData = {
200 |   vertexCount: xy.length,
201 |   vertexAttributes: {
202 |     xy: regl.buffer(xy)
203 |   },
204 |   endpointCount: 2,
205 |   endpointAttributes: {
206 |     xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()])
207 |   },
208 | 
209 |   width: 35,
210 |   borderWidth: 10,
211 |   miterLimit: 3.0,
212 |   scale: [1, 1]
213 | };
214 | 
215 | window.addEventListener('mousemove', e => {
216 |   lineData.scale = [
217 |     e.offsetX / window.innerWidth * 2 - 1,
218 |     -e.offsetY / window.innerHeight * 2 + 1
219 |   ];
220 |   draw();
221 | });
222 | 
223 | 
224 | function draw () {
225 |   regl.poll();
226 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
227 |   drawLines([{
228 |     ...lineData,
229 |     translate: [0, -0.4],
230 |     cap: 'round',
231 |     join: 'round'
232 |   }, {
233 |     ...lineData,
234 |     translate: [0, 0.4],
235 |     cap: 'round',
236 |     join: 'miter'
237 |   }]);
238 | }
239 | 
240 | draw();
241 | window.addEventListener('resize', draw);
242 | 
243 | 
244 |
245 |
246 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /docs/closed-loop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 92 | 107 |
108 |
109 | Code 110 |

111 | const regl = createREGL({extensions: ['ANGLE_instanced_arrays']});
112 | 
113 | const drawLines = reglLines(regl, {
114 |   vert: `
115 |     precision highp float;
116 | 
117 |     #pragma lines: attribute vec2 xy;
118 |     #pragma lines: position = getPosition(xy);
119 |     #pragma lines: width = getWidth(xy);
120 | 
121 |     uniform vec2 aspect;
122 | 
123 |     vec4 getPosition(vec2 xy) { return vec4(xy * aspect, 0, 1); }
124 |     float getWidth(vec2 xy) { return 50.0; }
125 |     `,
126 |   frag: `
127 |     precision lowp float;
128 |     void main () {
129 |       gl_FragColor = vec4(vec3(1), 0.5);
130 |     }`,
131 |   uniforms: {
132 |     aspect: ctx => [1, ctx.framebufferWidth / ctx.framebufferHeight]
133 |   },
134 |   blend: {
135 |     enable: true,
136 |     func: {
137 |       srcRGB: 'src alpha',
138 |       srcAlpha: 1,
139 |       dstRGB: 'one minus src alpha',
140 |       dstAlpha: 1
141 |     }
142 |   },
143 |   depth: {
144 |     enable: false
145 |   }
146 | });
147 | 
148 | // A seven-sided star
149 | const n = 7;
150 | 
151 | // Except we repeat the first three vertices again at the end. This
152 | // results in a closed shape with correct joins.
153 | const xy = [...Array(n + 3).keys()]
154 |   .map(i => i / n)
155 |   .map(t => {
156 |     const theta = t * Math.PI * 2 * 2;
157 |     const r = 0.7;
158 |     return [
159 |       r * Math.cos(theta),
160 |       r * Math.sin(theta)
161 |     ];
162 |   });
163 | 
164 | // Exclude endpointAttributes since there are no end caps to draw in
165 | // this case.
166 | const lineData = {
167 |   join: 'round',
168 |   vertexCount: xy.length,
169 |   vertexAttributes: {
170 |     xy: regl.buffer(xy)
171 |   },
172 | };
173 | 
174 | function draw () {
175 |   regl.poll();
176 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
177 |   drawLines(lineData);
178 | }
179 | 
180 | draw();
181 | window.addEventListener('resize', draw);
182 | 
183 | 
184 |
185 |
186 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/docs/debug.png -------------------------------------------------------------------------------- /docs/depth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 105 | 120 |
121 |
122 | Code 123 |

124 | const regl = createREGL({
125 |   extensions: ['ANGLE_instanced_arrays'],
126 |   attributes: {antialias: false}
127 | });
128 | 
129 | // Instantiate a command for drawing lines
130 | const drawLines = reglLines(regl, {
131 |   vert: `
132 |     precision highp float;
133 | 
134 |     uniform float width, time, phase;
135 | 
136 |     #pragma lines: attribute float x;
137 |     #pragma lines: position = getPosition(x);
138 |     vec4 getPosition(float x) {
139 |       float theta = 3.141 * (6.0 * x + time) - phase;
140 |       return vec4(
141 |         0.5 * vec3(
142 |           cos(theta),
143 |           (x * 2.0 - 1.0) * 1.5,
144 |           sin(theta)
145 |         ),
146 |         1
147 |       );
148 |     }
149 | 
150 |     // Return the line width from a uniorm
151 |     #pragma lines: width = getWidth();
152 |     float getWidth() {
153 |       return width;
154 |     }`,
155 |   frag: `
156 |     precision highp float;
157 |     uniform float width, borderWidth;
158 |     uniform vec3 color;
159 |     varying vec3 lineCoord;
160 |     void main () {
161 |       // Convert the line coord into an SDF
162 |       float sdf = length(lineCoord.xy) * width;
163 | 
164 |       // Apply a border with 1px transition
165 |       gl_FragColor = vec4(
166 |         mix(color, vec3(1), smoothstep(width - borderWidth - 0.5, width - borderWidth + 0.5, sdf)),
167 |         1);
168 |     }`,
169 | 
170 |   // Multiply the width by the pixel ratio for consistent width
171 |   uniforms: {
172 |     width: (ctx, props) => ctx.pixelRatio * props.width,
173 |     borderWidth: (ctx, props) => ctx.pixelRatio * props.borderWidth,
174 |     time: regl.context('time'),
175 |     phase: regl.prop('phase'),
176 |     color: regl.prop('color')
177 |   },
178 | 
179 |   depth: {enable: true}
180 | });
181 | 
182 | const n = 101;
183 | const x = [...Array(n).keys()].map(i => i / (n - 1));
184 | 
185 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
186 | // them on every draw call.
187 | const lineData = {
188 |   width: 30,
189 |   join: 'round',
190 |   cap: 'round',
191 |   joinResolution: 1,
192 |   vertexCount: x.length,
193 |   vertexAttributes: { x: regl.buffer(x) },
194 |   endpointCount: 2,
195 |   endpointAttributes: { x: regl.buffer([x.slice(0, 3), x.slice(-3).reverse()]) },
196 |   borderWidth: 5
197 | };
198 | 
199 | regl.frame(() => {
200 |   regl.poll();
201 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
202 |   drawLines([
203 |     {...lineData, phase: 0, color: [0.5, 1, 0]},
204 |     {...lineData, phase: Math.PI / 2, color: [0, 0.5, 1]},
205 |     {...lineData, phase: Math.PI, color: [1, 0, 0.5]}
206 |   ]);
207 | });
208 | 
209 | 
210 |
211 |
212 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /docs/instanced.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 93 | 108 |
109 |
110 | Code 111 |

112 | const regl = createREGL({
113 |   extensions: ['ANGLE_instanced_arrays']
114 | });
115 | 
116 | // Instantiate a command for drawing lines
117 | const drawLines = reglLines(regl, {
118 |   vert: `
119 |     precision highp float;
120 | 
121 |     // Use a vec2 attribute to construt the vec4 vertex position
122 |     #pragma lines: instance attribute float shift;
123 |     #pragma lines: attribute vec2 xy;
124 |     #pragma lines: position = getPosition(xy, shift);
125 |     vec4 getPosition(vec2 xy, float shift) {
126 |       return vec4(xy + vec2(0, shift), 0, 1);
127 |     }
128 | 
129 |     // Return the line width from a uniform
130 |     #pragma lines: width = getWidth();
131 |     uniform float width;
132 |     float getWidth() {
133 |       return width;
134 |     }`,
135 |   frag: `
136 |     precision lowp float;
137 |     void main () {
138 |       gl_FragColor = vec4(1);
139 |     }`,
140 | 
141 |   // Multiply the width by the pixel ratio for consistent width
142 |   uniforms: {
143 |     width: (ctx, props) => ctx.pixelRatio * props.width
144 |   },
145 | });
146 | 
147 | // Construct an array of xy pairs
148 | const n = 11;
149 | const xy = [...Array(n).keys()]
150 |   .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8)
151 |   .map(t => [t, 0.1 * Math.sin(8.0 * t)]);
152 | 
153 | const shift = regl.buffer(
154 |   [...Array(n).keys()].map(() => [-0.5, -0.25, 0, 0.25, 0.5])
155 | );
156 | 
157 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
158 | // them on every draw call.
159 | const lineData = {
160 |   width: 10,
161 |   join: 'round',
162 |   cap: 'round',
163 |   vertexCount: xy.length,
164 |   vertexAttributes: {
165 |     xy: regl.buffer(xy),
166 |     shift
167 |   },
168 |   endpointCount: 2,
169 |   endpointAttributes: {
170 |     xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]),
171 |     shift,
172 |   },
173 |   instances: 5,
174 | };
175 | 
176 | function draw () {
177 |   regl.poll();
178 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
179 |   drawLines(lineData);
180 | }
181 | 
182 | draw();
183 | window.addEventListener('resize', draw);
184 | 
185 | 
186 |
187 |
188 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /docs/knot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 110 | 125 |
126 |
127 | Code 128 |

129 | const regl = createREGL({
130 |   extensions: ['ANGLE_instanced_arrays']
131 | });
132 | 
133 | // Instantiate a command for drawing lines
134 | const drawLines = reglLines(regl, {
135 |   vert: `
136 |     precision highp float;
137 | 
138 |     uniform float width, phase;
139 |     uniform vec2 aspect;
140 | 
141 |     #pragma lines: attribute float theta;
142 |     #pragma lines: position = torusKnot(theta);
143 |     vec4 torusKnot(float theta) {
144 |       const float p = 3.0;
145 |       const float q = 5.0;
146 |       float r = cos(q * theta) + 2.0;
147 |       float phi = p * (theta - phase);
148 |       return vec4(
149 |         vec3(
150 |           aspect * r * vec2(cos(phi), sin(phi)),
151 |           -sin(q * theta)
152 |         ) * 0.25,
153 |         1
154 |       );
155 |     }
156 | 
157 |     // Return the line width from a uniform
158 |     #pragma lines: width = getWidth();
159 |     float getWidth() { return width; }`,
160 |   frag: `
161 |     precision highp float;
162 |     uniform float width, borderWidth, pixelRatio;
163 |     uniform vec3 color;
164 |     varying vec3 lineCoord;
165 |     void main () {
166 |       // Convert the line coord into an SDF
167 |       float sdf = length(lineCoord.xy) * width;
168 | 
169 |       // Apply a border with 1px transition
170 |       gl_FragColor = vec4(
171 |         mix(color, vec3(1),
172 |           smoothstep(
173 |             width - borderWidth - 0.5 / pixelRatio,
174 |             width - borderWidth + 0.5 / pixelRatio,
175 |             sdf
176 |           )
177 |         ), 1);
178 |     }`,
179 | 
180 |   // Multiply the width by the pixel ratio for consistent width
181 |   uniforms: {
182 |     pixelRatio: regl.context('pixelRatio'),
183 |     aspect: ctx => ctx.viewportWidth > ctx.viewportHeight
184 |       ? [ctx.viewportHeight / ctx.viewportWidth, 1]
185 |       : [1, ctx.viewportWidth / ctx.viewportHeight],
186 |     width: (ctx, props) => Math.min(ctx.viewportWidth, ctx.viewportHeight) / 30,
187 |     borderWidth: (ctx, props) => Math.min(ctx.viewportWidth, ctx.viewportHeight) / (30 * 4),
188 |     phase: regl.prop('phase'),
189 |     color: regl.prop('color')
190 |   },
191 | 
192 |   depth: {enable: true}
193 | });
194 | 
195 | const n = 501;
196 | 
197 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
198 | // them on every draw call.
199 | const lineData = {
200 |   join: 'round',
201 |   vertexCount: n + 3,
202 |   vertexAttributes: {
203 |     theta: regl.buffer([...Array(n + 3).keys()].map(i => i / n * Math.PI * 2))
204 |   },
205 | };
206 | 
207 | function draw() {
208 |   regl.poll();
209 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
210 |   drawLines([
211 |     {...lineData, phase: 0, color: [1, 0, 0.5]},
212 |     {...lineData, phase: Math.PI, color: [0, 0.5, 1]},
213 |   ]);
214 | }
215 | 
216 | draw();
217 | window.addEventListener('resize', draw);
218 | 
219 | 
220 |
221 |
222 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /docs/lorenz.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/docs/lorenz.gif -------------------------------------------------------------------------------- /docs/multiple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 95 | 110 |
111 |
112 | Code 113 |

114 | const regl = createREGL({extensions: ['ANGLE_instanced_arrays']});
115 | 
116 | const drawLines = reglLines(regl, {
117 |   vert: `
118 |     precision highp float;
119 | 
120 |     #pragma lines: attribute vec2 xy;
121 |     #pragma lines: position = getPosition(xy);
122 |     #pragma lines: width = getWidth();
123 |     #pragma lines: varying vec2 pos = getXY(xy);
124 | 
125 |     vec4 getPosition(vec2 xy) {
126 |       return vec4(xy, 0, 1);
127 |     }
128 |     float getWidth() { return 40.0; }
129 |     vec2 getXY(vec2 xy) { return xy; }`,
130 |   frag: `
131 |     precision lowp float;
132 |     varying vec2 pos;
133 |     void main () {
134 |       // Convert the x-coordinate into a color
135 |       gl_FragColor = vec4(0.6 + 0.4 * cos(8.0 * (pos.x - vec3(0, 1, 2) * 3.141 / 3.0)), 0.7);
136 |     }`,
137 |   // Turn off depth and turn on blending to make it very clear if we accidentally
138 |   // draw end caps twice
139 |   depth: { enable: false },
140 |   cull: {enable: true, face: 'back'},
141 |   blend: {
142 |     enable: true,
143 |     func: { srcRGB: "src alpha", srcAlpha: 1, dstRGB: "one minus src alpha", dstAlpha: 1 }
144 |   },
145 | });
146 | 
147 | const n = 31;
148 | const lineCount = 10;
149 | 
150 | function xy (line, i) {
151 |   let t = (i / (n - 1) * 2 - 1) * 0.9;
152 |   const y = ((line + 0.5) / lineCount * 2 - 1) * 0.9;
153 |   return [t, y + 1 / lineCount * Math.sin((t - line * 0.1) * 8.0)];
154 | }
155 | 
156 | // Start with a break in order to signal a cap
157 | const positions = [[NaN, NaN]];
158 | 
159 | for (let line = 0; line < lineCount; line++) {
160 |   for (let i = 0; i < n; i++) {
161 |     positions.push(xy(line, i));
162 |   }
163 |   // Signal a cap after each line
164 |   positions.push([NaN, NaN]);
165 | }
166 | 
167 | // After this, render as normal!
168 | const lineData = {
169 |   // Trigger the command to automatically insert caps at any break, signaled by a position with (w = 0)
170 |   insertCaps: true,
171 | 
172 |   join: 'round',
173 |   cap: 'round',
174 |   vertexCount: positions.length,
175 |   vertexAttributes: {
176 |     xy: regl.buffer(positions),
177 |   },
178 | };
179 | 
180 | function draw () {
181 |   regl.poll();
182 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
183 |   drawLines(lineData);
184 | }
185 | 
186 | draw();
187 | window.addEventListener('resize', draw);
188 | 
189 | 
190 |
191 |
192 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /docs/number-texture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 44 | 59 |
60 |
61 | Code 62 |

63 | module.exports = createNumberCanvas;
64 | 
65 | function createNumberCanvas (size) {
66 |   const canvas = document.createElement('canvas');
67 |   const img = document.createElement('img');
68 |   const ctx = canvas.getContext('2d');
69 |   canvas.width = 10 * size;
70 |   canvas.height = size;
71 |   ctx.font = `${size}px monospace`;
72 |   ctx.fillStyle = '#000';
73 |   ctx.fillRect(0, 0, size * 10, size);
74 |   ctx.fillStyle = '#fff';
75 |   ctx.textAlign = 'center';
76 |   ctx.textBaseline = 'middle';
77 |   for (let i = 0; i < 10; i++) {
78 |     ctx.fillText(i, (i + 0.5) * size, size * 0.5);
79 |   }
80 | 
81 |   const returnValue = new Promise(resolve => {
82 |     img.onload = () => resolve(img);
83 |   });
84 |   img.src = canvas.toDataURL();
85 |   return returnValue;
86 | }
87 | 
88 | 
89 |
90 |
91 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /docs/postproject.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 112 | 127 |
128 |
129 | Code 130 |

131 | const regl = createREGL({
132 |   extensions: ['ANGLE_instanced_arrays']
133 | });
134 | const mat4 = glMatrix.mat4;
135 | 
136 | // This example illustrates the postproject property. You may specify a function to be applied
137 | // *after* screne-space projection. Together with providing viewportSize, this allows you to
138 | // draw lines in whatever viewport you desire, then to project them to the screen. This useful,
139 | // for example, when drawing lines on a flat surface. It changes nothing if the lines are not
140 | // on a flat surface, though the joins may not look particularly good in the out-of-plane
141 | // dimension.
142 | const drawLines = reglLines(regl, {
143 |   vert: `
144 |     precision highp float;
145 | 
146 |     // Use a vec2 attribute to construt the vec4 vertex position
147 |     #pragma lines: attribute vec2 position;
148 |     #pragma lines: position = getPosition(position);
149 |     vec4 getPosition(vec2 position) {
150 |       return vec4(position, 0, 1);
151 |     }
152 | 
153 |     // Return the line width from a uniorm
154 |     #pragma lines: width = getWidth();
155 |     uniform float width;
156 |     float getWidth() {
157 |       return width;
158 |     }
159 | 
160 |     // Specify a projection function
161 |     #pragma lines: postproject = postprojectPosition;
162 |     uniform mat4 projectionView;
163 |     vec4 postprojectPosition(vec4 position) {
164 |       return projectionView * position;
165 |     }
166 |   `,
167 |   frag: `
168 |     precision lowp float;
169 |     void main () {
170 |       gl_FragColor = vec4(1);
171 |     }`,
172 | 
173 |   // Multiply the width by the pixel ratio for consistent width
174 |   uniforms: {
175 |     width: (ctx, props) => ctx.pixelRatio * props.width,
176 |     projectionView: (ctx, props) => {
177 |       //const aspect = ctx.viewportWidth / ctx.viewportHeight;
178 |       const aspect = 2;
179 |       const projection = mat4.perspective(mat4.create(), Math.PI / 4, aspect, 0.01, 10.0);
180 |       const theta = ctx.time * 0.5;
181 |       const r = 1.8;
182 |       const eye = [
183 |         r * Math.cos(theta),
184 |         r * Math.sin(theta),
185 |         0.5
186 |       ];
187 |       const center = [0, 0, 0];
188 |       const up = [0, 0, 1];
189 |       const view = mat4.lookAt(mat4.create(), eye, center, up);
190 |       return mat4.multiply(projection, projection, view);
191 |     }
192 |   },
193 | });
194 | 
195 | // Construct an array of xy pairs
196 | const n = 501;
197 | const position = [...Array(n + 3).keys()]
198 |   .map(i => i / n * Math.PI * 2)
199 |   .map(t => {
200 |     const r = 0.6 + 0.4 * Math.cos(t * 7.0);
201 |     return [r * Math.cos(t), r * Math.sin(t)]
202 |   });
203 | 
204 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
205 | // them on every draw call.
206 | const lineData = {
207 |   width: 0.03,
208 |   join: 'round',
209 |   cap: 'round',
210 |   vertexCount: position.length,
211 |   vertexAttributes: { position: regl.buffer(position) },
212 | 
213 |   // Screen-project in the 1 x 1 unit square. Since this size is divided out before
214 |   // post-projection, this only affects interpretation of "widthV.
215 |   viewportSize: [1, 1]
216 | };
217 | 
218 | regl.frame(() => {
219 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
220 |   drawLines(lineData);
221 | });
222 | 
223 | 
224 |
225 |
226 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /docs/variable-width.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 94 | 109 |
110 |
111 | Code 112 |

113 | const regl = createREGL({
114 |   extensions: ['ANGLE_instanced_arrays']
115 | });
116 | 
117 | // Instantiate a command for drawing lines
118 | const drawLines = reglLines(regl, {
119 |   vert: `
120 |     precision highp float;
121 | 
122 |     #pragma lines: attribute vec2 xy;
123 | 
124 |     // Assign a function for computing the vec4 position
125 |     #pragma lines: position = getPosition(xy);
126 | 
127 |     // Assign a function for computing the float width
128 |     #pragma lines: width = getWidth(xy);
129 | 
130 |     // Compute a varying value from the input attribute
131 |     #pragma lines: varying float x = getX(xy);
132 | 
133 |     uniform float width;
134 | 
135 |     // Implement the functions above
136 |     vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }
137 |     float getWidth(vec2 xy) { return width * (0.5 + 0.4 * cos(16.0 * xy.x)); }
138 |     float getX(vec2 xy) { return xy.x; }`,
139 |   frag: `
140 |     precision lowp float;
141 |     varying float x;
142 |     void main () {
143 |       gl_FragColor = vec4(0.5 + cos(8.0 * (x - vec3(0, 1, 2) * 3.141 / 3.0)), 1);
144 |     }`,
145 | 
146 |   // Additional regl command properties are valid
147 |   uniforms: {
148 |     width: regl.prop('width')
149 |   },
150 | 
151 |   depth: {enable: false}
152 | });
153 | 
154 | // Construct an array of xy pairs
155 | const n = 101;
156 | const xy = [...Array(n).keys()]
157 |   .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8)
158 |   .map(t => [t, 0.5 * Math.sin(8.0 * t)]);
159 | 
160 | // Set up the data to be drawn. Note that we preallocate buffers and don't create
161 | // them on every draw call.
162 | const lineData = {
163 |   join: 'round',
164 |   cap: 'round',
165 |   vertexCount: xy.length,
166 |   vertexAttributes: {
167 |     xy: regl.buffer(xy)
168 |   },
169 |   endpointCount: 2,
170 |   endpointAttributes: {
171 |     xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()])
172 |   },
173 | 
174 |   // Picked up by regl.prop('width')
175 |   width: 50
176 | };
177 | 
178 | function draw () {
179 |   regl.poll();
180 |   regl.clear({color: [0.2, 0.2, 0.2, 1]});
181 |   drawLines(lineData);
182 | }
183 | 
184 | draw();
185 | window.addEventListener('resize', draw);
186 | 
187 | 
188 |
189 |
190 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /examples/3d.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({extensions: [ 2 | 'ANGLE_instanced_arrays', 3 | 'OES_standard_derivatives', 4 | ]}); 5 | const createCamera = require('regl-camera'); 6 | 7 | const state = window.fuckit = wrapGUI(State({ 8 | count: State.Slider(8, {min: 1, max: 50, step: 1}), 9 | })); 10 | 11 | const createREGLProxy = function (regl, argWrapper) { 12 | const proxy = args => regl({...args, ...argWrapper(args)}); 13 | Object.assign(proxy, regl); 14 | return proxy; 15 | } 16 | 17 | const reglProxy = createREGLProxy(regl, function (args) { 18 | if (!args.vert) return {}; 19 | return { 20 | vert: args.vert.slice(0, args.vert.length - 1) + ` 21 | gl_Position = projection * view * gl_Position.zyxw; 22 | }` 23 | } 24 | }); 25 | 26 | const drawLines = reglLines(reglProxy, { 27 | debug: true, 28 | vert: ` 29 | precision highp float; 30 | uniform float pixelRatio, width; 31 | uniform vec2 aspect; 32 | uniform mat4 projection, view; 33 | 34 | #pragma lines: attribute vec3 xyz; 35 | #pragma lines: attribute float t; 36 | #pragma lines: varying float t = getT(t); 37 | #pragma lines: position = getPosition(xyz); 38 | #pragma lines: width = getWidth(); 39 | 40 | float getT(float t) { return t; } 41 | vec4 getPosition(vec3 xyz) { return vec4(xyz.xy * aspect, xyz.z, 1); } 42 | float getWidth() { return width * pixelRatio; } 43 | `, 44 | frag: ` 45 | #extension GL_OES_standard_derivatives : enable 46 | precision highp float; 47 | uniform float pixelRatio; 48 | varying float instanceID, t; 49 | varying vec2 triStripCoord; 50 | 51 | // Unit grid lines 52 | float grid (vec3 parameter, float width, float feather) { 53 | float w1 = width - feather * 0.5; 54 | vec3 d = fwidth(parameter); 55 | vec3 looped = 0.5 - abs(mod(parameter, 1.0) - 0.5); 56 | vec3 a3 = smoothstep(d * w1, d * (w1 + feather), looped); 57 | return min(min(a3.x, a3.y), a3.z); 58 | } 59 | 60 | void main () { 61 | if (instanceID < 0.0) { 62 | // End caps are red 63 | gl_FragColor.rgb = vec3(0.8, 0.1, 0.4); 64 | } else { 65 | // Remaining segments alternate blues 66 | gl_FragColor.rgb = mod(instanceID, 2.0) == 0.0 ? vec3(0.4, 0.7, 1.0) : vec3(0.2, 0.3, 0.7); 67 | } 68 | 69 | gl_FragColor.rgb *= fract(t * 32.0) > 0.5 ? 1.0 : 0.7; 70 | 71 | // Draw unit grid lines and a diagonal line using the vertex ID turned into a vec2 varying. 72 | // 73 | // 0 2 4 6 8 74 | // + --- + --- + --- + --- + 75 | // | / | / | / | / | 76 | // | / | / | / | / | 77 | // + --- + --- + --- + --- + 78 | // 1 3 5 7 9 79 | // 80 | float wire = grid(vec3(triStripCoord, triStripCoord.x + triStripCoord.y), 0.5 * pixelRatio, 1.0); 81 | gl_FragColor.rgb = mix(vec3(1), gl_FragColor.rgb, wire); 82 | 83 | //gl_FragColor.rgb *= 0.8; 84 | gl_FragColor.a = 1.0; 85 | }`, 86 | uniforms: { 87 | pixelRatio: regl.context('pixelRatio'), 88 | aspect: ctx => [1, ctx.framebufferWidth / ctx.framebufferHeight], 89 | width: regl.prop('width') 90 | }, 91 | blend: { 92 | enable: false, 93 | func: { 94 | srcRGB: 'src alpha', 95 | srcAlpha: 1, 96 | dstRGB: 'one minus src alpha', 97 | dstAlpha: 1 98 | } 99 | }, 100 | depth: { 101 | enable: true 102 | } 103 | }); 104 | 105 | // Construct an array of xy pairs 106 | const path = [ 107 | [-1, 0.001], 108 | [-1, 0.0], 109 | //[-0.5, -0.5], 110 | //[-0.25, 0.5], 111 | //[0.0, 0.0], 112 | //[0.01, 0.0], 113 | //[0.02, 0.0], 114 | //[0.03, 0.0], 115 | //[0.04, 0.0], 116 | //[0.05, 0.0], 117 | [0.0, 0.0], 118 | [0.0, 0.5], 119 | [0.0, 0.], 120 | //[0.07, 0.0], 121 | //[0.25, 0.0], 122 | //[0.5, -0.5], 123 | [0.75, 0.], 124 | [1, 0.0] 125 | ]; 126 | const n = 9;//path.length; 127 | const t = [...Array(n).keys()] 128 | .map(i => (i / (n - 1) * 2.0 - 1.0)) 129 | const xyz = t.map((t, i) => 130 | //path[i].concat([t]) 131 | [0.8 * Math.cos(2 * t * 3.14 - 0.5), 0.8 * Math.sin(t * 3.14), t] 132 | ); 133 | 134 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 135 | // them on every draw call. 136 | const lineData = { 137 | width: 100, 138 | join: 'round', 139 | cap: 'round', 140 | joinResolution: 2, 141 | vertexCount: xyz.length, 142 | vertexAttributes: { 143 | xyz: regl.buffer(xyz), 144 | t: regl.buffer(t), 145 | }, 146 | endpointCount: 2, 147 | endpointAttributes: { 148 | xyz: regl.buffer([xyz.slice(0, 3), xyz.slice(-3).reverse()]), 149 | t: regl.buffer([t.slice(0, 3), t.slice(-3).reverse()]), 150 | } 151 | }; 152 | 153 | const camera = createCamera(regl, { 154 | noScroll: true, 155 | theta: 2 * Math.PI / 2 - 0.5, 156 | phi: 0.5, 157 | distance: 40, 158 | near: 0.1, 159 | far: 100.0, 160 | fovy: 0.07, 161 | damping: 0, 162 | }); 163 | 164 | let dirty = true; 165 | regl.frame(({tick}) => { 166 | camera(/*{dtheta: Math.PI / 100 * Math.sin(tick / 100)},*/ state => { 167 | if (!state.dirty && !dirty) return; 168 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 169 | drawLines(lineData); 170 | dirty = false; 171 | }); 172 | }); 173 | state.$onChange(() => dirty = true); 174 | -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | 5 | // Instantiate a command for drawing lines 6 | const drawLines = reglLines(regl, { 7 | vert: ` 8 | precision highp float; 9 | 10 | // Use a vec2 attribute to construt the vec4 vertex position 11 | #pragma lines: attribute vec2 xy; 12 | #pragma lines: position = getPosition(xy); 13 | vec4 getPosition(vec2 xy) { 14 | return vec4(xy, 0, 1); 15 | } 16 | 17 | // Return the line width from a uniorm 18 | #pragma lines: width = getWidth(); 19 | uniform float width; 20 | float getWidth() { 21 | return width; 22 | }`, 23 | frag: ` 24 | precision lowp float; 25 | void main () { 26 | gl_FragColor = vec4(1); 27 | }`, 28 | 29 | // Multiply the width by the pixel ratio for consistent width 30 | uniforms: { 31 | width: (ctx, props) => ctx.pixelRatio * props.width 32 | }, 33 | }); 34 | 35 | // Construct an array of xy pairs 36 | const n = 11; 37 | const xy = [...Array(n).keys()] 38 | .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8) 39 | .map(t => [t, 0.5 * Math.sin(8.0 * t)]); 40 | 41 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 42 | // them on every draw call. 43 | const lineData = { 44 | width: 30, 45 | join: 'round', 46 | cap: 'round', 47 | vertexCount: xy.length, 48 | vertexAttributes: { xy: regl.buffer(xy) }, 49 | endpointCount: 2, 50 | endpointAttributes: { xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]) } 51 | }; 52 | 53 | function draw () { 54 | regl.poll(); 55 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 56 | drawLines(lineData); 57 | } 58 | 59 | draw(); 60 | window.addEventListener('resize', draw); 61 | -------------------------------------------------------------------------------- /examples/batching.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({extensions: ['ANGLE_instanced_arrays']}); 2 | 3 | // This example illustrates use of batching to reduce the number of shader program changes from 4 | // thirty to just four--though the number of draw calls remains unchanged. This applies when each line 5 | // has its own buffers and requires a separate draw call. Since some of the options (endpoints vs. 6 | // interior segments and whether ot insert caps) need to be known at command *compile* time, it's 7 | // possible to draw lines in a manner which switches the draw program many times. 8 | // 9 | // When using regl, you may invoke commands independently, e.g. 10 | // 11 | // drawLines({...}) 12 | // drawLines({...}) 13 | // drawLines({...}) 14 | // 15 | // or batched as an array, e.g. 16 | // 17 | // drawLines([{...}, {...}, ...]) 18 | // 19 | // The latter form allows regl to bypass some state change checks. 20 | // 21 | // This library performs the additional step of *reordering* draw commands to group together draw calls 22 | // using the same shader program. It only applies when called with array of props. This option is 23 | // *enabled* by default. It may be disabled by specifying `reorder = false`, avoided by independently 24 | // invoking draw calls, or its potential side effects mitigated by using depth to enforce ordering of 25 | // opaque objects. 26 | const drawLines = reglLines(regl, { 27 | vert: ` 28 | precision highp float; 29 | 30 | #pragma lines: attribute vec2 xy; 31 | #pragma lines: position = getPosition(xy); 32 | #pragma lines: width = getWidth(); 33 | uniform float pixelRatio; 34 | 35 | vec4 getPosition(vec2 xy) { 36 | return vec4(xy, 0, 1); 37 | } 38 | float getWidth() { return 20.0 * pixelRatio; } 39 | vec2 getXY(vec2 xy) { return xy; }`, 40 | frag: ` 41 | precision lowp float; 42 | uniform vec3 color; 43 | void main () { 44 | gl_FragColor = vec4(color, 0.7); 45 | }`, 46 | 47 | // Extra config passed to the draw command 48 | depth: { enable: false }, 49 | cull: {enable: true, face: 'back'}, 50 | blend: { 51 | enable: true, 52 | func: { srcRGB: "src alpha", srcAlpha: 1, dstRGB: "one minus src alpha", dstAlpha: 1 } 53 | }, 54 | uniforms: { 55 | pixelRatio: regl.context('pixelRatio'), 56 | color: regl.prop('color') 57 | } 58 | }); 59 | 60 | const n = 20; 61 | const lineCount = 15; 62 | 63 | function xy (line, i) { 64 | let t = (i / (n - 1) * 2 - 1) * 0.9; 65 | const y = ((line + 0.5) / lineCount * 2 - 1) * 0.9; 66 | return [t, y + 1.5 / lineCount * Math.sin((t - line * 0.2) * 30.0)]; 67 | } 68 | 69 | // Construct independent sets of props, to be passed as an array and rendered in one pass. 70 | // When called in this fashion, all lines using the same draw command will be batched together. 71 | // The key compile-time variables affecting this are: 72 | // - insertCaps: insert caps to fill NaN gaps 73 | // - isEndpoints: true when rendering dedicated, separately-provided endpoint instances 74 | // This may affect layering if depth is disabled. To get around this, you may simply invoke 75 | // rendering independently for each line. 76 | const lineList = []; 77 | for (let line = 0; line < lineCount; line++) { 78 | const positions = []; 79 | for (let i = 0; i < n; i++) { 80 | positions.push(i === Math.floor(n / 2) ? [NaN, NaN] : xy(line, i)); 81 | } 82 | lineList.push({ 83 | color: [0, 1, 2].map(i => 0.5 + Math.cos((i / 3 + (line / lineCount)) * Math.PI * 2)), 84 | cap: line % 2 === 0 ? 'round' : 'square', 85 | join: line % 4 === 0 ? 'miter' : 'round', 86 | insertCaps: line % 3 === 0, 87 | vertexCount: positions.length, 88 | endpointCount: 2, 89 | vertexAttributes: { 90 | xy: regl.buffer(positions) 91 | }, 92 | endpointAttributes: { 93 | xy: regl.buffer([positions.slice(0, 3), positions.slice(-3).reverse()]) 94 | } 95 | }); 96 | } 97 | 98 | function draw () { 99 | regl.poll(); 100 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 101 | drawLines(lineList); 102 | } 103 | 104 | draw(); 105 | window.addEventListener('resize', draw); 106 | -------------------------------------------------------------------------------- /examples/border.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | 5 | // Instantiate a command for drawing lines 6 | const drawLines = reglLines(regl, { 7 | vert: ` 8 | precision highp float; 9 | 10 | #pragma lines: attribute vec2 xy; 11 | 12 | // Assign a function for computing the vec4 position 13 | #pragma lines: position = getPosition(xy); 14 | 15 | // Assign a function for computing the float width 16 | #pragma lines: width = getWidth(xy); 17 | 18 | uniform float width; 19 | uniform vec2 translate, scale; 20 | 21 | // Implement the functions above 22 | vec4 getPosition(vec2 xy) { return vec4(xy * scale + translate, 0, 1); } 23 | float getWidth(vec2 xy) { return width; } 24 | float getX(vec2 xy) { return xy.x; }`, 25 | frag: ` 26 | precision highp float; 27 | varying vec3 lineCoord; 28 | uniform float width, borderWidth; 29 | void main () { 30 | // Convert the line coord into an SDF 31 | float sdf = length(lineCoord.xy) * width; 32 | 33 | vec3 borderColor = 0.5 + 0.5 * vec3(lineCoord.xy, 0); 34 | 35 | // Apply a border with 1px transition 36 | gl_FragColor = vec4( 37 | mix(vec3(0), borderColor, smoothstep(width - borderWidth - 1.0, width - borderWidth + 1.0, sdf)), 38 | 1); 39 | }`, 40 | 41 | // Additional regl command properties are valid 42 | uniforms: { 43 | width: (ctx, props) => ctx.pixelRatio * props.width, 44 | borderWidth: (ctx, props) => ctx.pixelRatio * props.borderWidth, 45 | translate: regl.prop('translate'), 46 | scale: regl.prop('scale') 47 | }, 48 | depth: {enable: false} 49 | }); 50 | 51 | // Construct an array of xy pairs 52 | const n = 11; 53 | const xy = [...Array(n).keys()] 54 | .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8) 55 | .map(t => [t, 0.5 * Math.sin(54.0 * t)]); 56 | 57 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 58 | // them on every draw call. 59 | const lineData = { 60 | vertexCount: xy.length, 61 | vertexAttributes: { 62 | xy: regl.buffer(xy) 63 | }, 64 | endpointCount: 2, 65 | endpointAttributes: { 66 | xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]) 67 | }, 68 | 69 | width: 35, 70 | borderWidth: 10, 71 | miterLimit: 3.0, 72 | scale: [1, 1] 73 | }; 74 | 75 | window.addEventListener('mousemove', e => { 76 | lineData.scale = [ 77 | e.offsetX / window.innerWidth * 2 - 1, 78 | -e.offsetY / window.innerHeight * 2 + 1 79 | ]; 80 | draw(); 81 | }); 82 | 83 | 84 | function draw () { 85 | regl.poll(); 86 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 87 | drawLines([{ 88 | ...lineData, 89 | translate: [0, -0.4], 90 | cap: 'round', 91 | join: 'round' 92 | }, { 93 | ...lineData, 94 | translate: [0, 0.4], 95 | cap: 'round', 96 | join: 'miter' 97 | }]); 98 | } 99 | 100 | draw(); 101 | window.addEventListener('resize', draw); 102 | -------------------------------------------------------------------------------- /examples/closed-loop.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({extensions: ['ANGLE_instanced_arrays']}); 2 | 3 | const drawLines = reglLines(regl, { 4 | vert: ` 5 | precision highp float; 6 | 7 | #pragma lines: attribute vec2 xy; 8 | #pragma lines: position = getPosition(xy); 9 | #pragma lines: width = getWidth(xy); 10 | 11 | uniform vec2 aspect; 12 | 13 | vec4 getPosition(vec2 xy) { return vec4(xy * aspect, 0, 1); } 14 | float getWidth(vec2 xy) { return 50.0; } 15 | `, 16 | frag: ` 17 | precision lowp float; 18 | void main () { 19 | gl_FragColor = vec4(vec3(1), 0.5); 20 | }`, 21 | uniforms: { 22 | aspect: ctx => [1, ctx.framebufferWidth / ctx.framebufferHeight] 23 | }, 24 | blend: { 25 | enable: true, 26 | func: { 27 | srcRGB: 'src alpha', 28 | srcAlpha: 1, 29 | dstRGB: 'one minus src alpha', 30 | dstAlpha: 1 31 | } 32 | }, 33 | depth: { 34 | enable: false 35 | } 36 | }); 37 | 38 | // A seven-sided star 39 | const n = 7; 40 | 41 | // Except we repeat the first three vertices again at the end. This 42 | // results in a closed shape with correct joins. 43 | const xy = [...Array(n + 3).keys()] 44 | .map(i => i / n) 45 | .map(t => { 46 | const theta = t * Math.PI * 2 * 2; 47 | const r = 0.7; 48 | return [ 49 | r * Math.cos(theta), 50 | r * Math.sin(theta) 51 | ]; 52 | }); 53 | 54 | // Exclude endpointAttributes since there are no end caps to draw in 55 | // this case. 56 | const lineData = { 57 | join: 'round', 58 | vertexCount: xy.length, 59 | vertexAttributes: { 60 | xy: regl.buffer(xy) 61 | }, 62 | }; 63 | 64 | function draw () { 65 | regl.poll(); 66 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 67 | drawLines(lineData); 68 | } 69 | 70 | draw(); 71 | window.addEventListener('resize', draw); 72 | -------------------------------------------------------------------------------- /examples/dash.js: -------------------------------------------------------------------------------- 1 | function vec2distance (a, b) { 2 | return Math.hypot(a[0] - b[0], a[1] - b[1]); 3 | } 4 | 5 | const pixelRatio = window.devicePixelRatio; 6 | 7 | const regl = createREGL({ 8 | pixelRatio, 9 | extensions: ['ANGLE_instanced_arrays'] 10 | }); 11 | 12 | // Instantiate a command for drawing lines 13 | const drawLines = reglLines(regl, { 14 | vert: ` 15 | precision highp float; 16 | 17 | // Use a vec2 attribute to construt the vec4 vertex position 18 | #pragma lines: attribute vec2 xy; 19 | #pragma lines: position = getPosition(xy); 20 | vec4 getPosition(vec2 xy) { 21 | return vec4(xy, 0, 1); 22 | } 23 | 24 | // Pass the distance without modification as a varying 25 | #pragma lines: attribute float dist; 26 | #pragma lines: varying float dist = getDist(dist); 27 | float getDist(float dist) { 28 | return dist; 29 | } 30 | 31 | // Return the line width from a uniorm 32 | #pragma lines: width = getWidth(); 33 | uniform float width; 34 | float getWidth() { 35 | return width; 36 | }`, 37 | frag: ` 38 | precision lowp float; 39 | varying float dist; 40 | uniform float dashLength; 41 | 42 | float linearstep (float a, float b, float x) { 43 | return clamp((x - a) / (b - a), 0.0, 1.0); 44 | } 45 | 46 | void main () { 47 | float dashvar = fract(dist / dashLength) * dashLength; 48 | gl_FragColor = vec4(vec3( 49 | linearstep(0.0, 1.0, dashvar) 50 | * linearstep(dashLength * 0.5 + 1.0, dashLength * 0.5, dashvar) 51 | ), 1); 52 | }`, 53 | // Multiply the width by the pixel ratio for consistent width 54 | uniforms: { 55 | width: (ctx, props) => ctx.pixelRatio * props.width, 56 | dashLength: (ctx, props) => ctx.pixelRatio * props.width * props.dashLength * 2.0, 57 | }, 58 | depth: { enable: true }, 59 | cull: { enable: false } 60 | }); 61 | 62 | // Construct an array of xy pairs 63 | const n = 11; 64 | const path = [...Array(n).keys()] 65 | .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8) 66 | .map(t => [t, 0.5 * Math.sin(8.0 * t)]); 67 | 68 | function project(point) { 69 | return [ 70 | (0.5 + 0.5 * point[0]) * regl._gl.canvas.width, 71 | (0.5 + 0.5 * point[1]) * regl._gl.canvas.height 72 | ]; 73 | } 74 | 75 | function computeCumulativeDistance (dist, points, project) { 76 | let prevPoint = project(points[0]); 77 | for (let i = 1; i < points.length; i++) { 78 | const point = project(points[i]); 79 | const d = dist[i - 1] + vec2distance(point, prevPoint); 80 | dist[i] = d; 81 | prevPoint = point; 82 | } 83 | return dist; 84 | } 85 | 86 | const dist = Array(path.length).fill(0); 87 | const distBuffer = regl.buffer(dist); 88 | const endpointDistBuffer = regl.buffer(new Float32Array(6)); 89 | const pathBuffer = regl.buffer(path); 90 | const endpointBuffer = regl.buffer(new Float32Array(6)); 91 | 92 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 93 | // them on every draw call. 94 | const lineData = { 95 | width: 40, 96 | dashLength: 4, 97 | join: 'round', 98 | cap: 'round', 99 | vertexCount: path.length, 100 | vertexAttributes: { 101 | xy: regl.buffer(path), 102 | dist: distBuffer 103 | }, 104 | endpointCount: 2, 105 | endpointAttributes: { 106 | xy: regl.buffer([path.slice(0, 3), path.slice(-3).reverse()]), 107 | dist: endpointDistBuffer 108 | } 109 | }; 110 | 111 | function updateBuffers () { 112 | lineData.vertexAttributes.dist.subdata(dist); 113 | lineData.endpointAttributes.dist.subdata([dist.slice(0, 3), dist.slice(-3).reverse()]); 114 | lineData.vertexAttributes.xy.subdata(path); 115 | lineData.endpointAttributes.xy.subdata([path.slice(0, 3), path.slice(-3).reverse()]); 116 | } 117 | 118 | function draw () { 119 | updateBuffers(); 120 | 121 | regl.poll(); 122 | regl.clear({color: [0.2, 0.2, 0.2, 1], depth: 1}); 123 | drawLines(lineData); 124 | } 125 | 126 | window.addEventListener('mousemove', function (event) { 127 | const lastPoint = path[0]; 128 | const newPoint = [ 129 | event.offsetX / window.innerWidth * 2 - 1, 130 | -event.offsetY / window.innerHeight * 2 + 1 131 | ]; 132 | const newDist = Math.hypot( 133 | window.innerWidth * (lastPoint[0] - newPoint[0]), 134 | window.innerHeight * (lastPoint[1] - newPoint[1]) 135 | ); 136 | if (newDist < Math.max(2, lineData.width * 0.5)) return; 137 | 138 | path.unshift(newPoint); 139 | dist.unshift(dist[0] - newDist); 140 | 141 | path.pop(); 142 | dist.pop(); 143 | 144 | draw(); 145 | }); 146 | 147 | computeCumulativeDistance(dist, path, project); 148 | draw(); 149 | 150 | window.addEventListener('resize', function () { 151 | computeCumulativeDistance(dist, path, project); 152 | draw(); 153 | }); 154 | -------------------------------------------------------------------------------- /examples/debug-view.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: [ 3 | 'ANGLE_instanced_arrays', 4 | 'OES_standard_derivatives', 5 | ], 6 | }); 7 | 8 | const n = 21; 9 | const x = [...Array(n).keys()].map(t => (t / (n - 1) * 2 - 1) * 0.8); 10 | const xy = x.map(x => [x, 0.5 * Math.cos(x * 8)]) 11 | 12 | const lineData = window.linedata = { 13 | join: 'miter', 14 | joinResolution: 4, 15 | cap: 'round', 16 | capResolution: 8, 17 | vertexCount: xy.length, 18 | vertexAttributes: { 19 | point: regl.buffer(xy), 20 | }, 21 | endpointCount: 2, 22 | endpointAttributes: { 23 | point: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]), 24 | } 25 | }; 26 | 27 | const drawLines = reglLines(regl, { 28 | debug: true, 29 | vert: ` 30 | precision highp float; 31 | uniform mediump float pixelRatio; 32 | 33 | #pragma lines: attribute vec2 point; 34 | #pragma lines: position = project(point); 35 | #pragma lines: width = getWidth(); 36 | 37 | vec4 project (vec2 p) { return vec4(p, 0, 1); } 38 | float getWidth () { return 50.0 * pixelRatio; }`, 39 | frag: ` 40 | #extension GL_OES_standard_derivatives : enable 41 | precision mediump float; 42 | uniform float pixelRatio; 43 | varying float instanceID; 44 | varying vec2 triStripCoord; 45 | 46 | // Unit grid lines 47 | float grid (vec3 parameter, float width, float feather) { 48 | float w1 = width - feather * 0.5; 49 | vec3 d = fwidth(parameter); 50 | vec3 looped = 0.5 - abs(mod(parameter, 1.0) - 0.5); 51 | vec3 a3 = smoothstep(d * w1, d * (w1 + feather), looped); 52 | return min(min(a3.x, a3.y), a3.z); 53 | } 54 | 55 | void main () { 56 | float iInstanceID = floor(instanceID + 0.5); 57 | if (iInstanceID < 0.0) { 58 | // End caps are red 59 | gl_FragColor.rgb = vec3(0.8, 0.1, 0.4); 60 | } else { 61 | // Remaining segments alternate blues 62 | gl_FragColor.rgb = mod(iInstanceID, 2.0) == 0.0 ? vec3(0.4, 0.7, 1.0) : vec3(0.2, 0.3, 0.7); 63 | } 64 | 65 | // Draw unit grid lines and a diagonal line using the vertex ID turned into a vec2 varying. 66 | // 67 | // 0 2 4 6 8 68 | // + --- + --- + --- + --- + 69 | // | / | / | / | / | 70 | // | / | / | / | / | 71 | // + --- + --- + --- + --- + 72 | // 1 3 5 7 9 73 | // 74 | float wire = grid(vec3(triStripCoord, triStripCoord.x + triStripCoord.y), 0.5 * pixelRatio, 1.0); 75 | gl_FragColor.rgb = mix(vec3(1), gl_FragColor.rgb, wire); 76 | 77 | gl_FragColor.a = 0.6; 78 | }`, 79 | uniforms: { 80 | pixelRatio: regl.context('pixelRatio'), 81 | lineWidth: (ctx, props) => ctx.pixelRatio * props.lineWidth, 82 | debug: regl.prop('debug'), 83 | }, 84 | // Draw with depth disabled and some alpha so we can see overlap 85 | blend: { 86 | enable: true, 87 | func: { 88 | srcRGB: 'src alpha', 89 | srcAlpha: 1, 90 | dstRGB: 'one minus src alpha', 91 | dstAlpha: 1 92 | } 93 | }, 94 | depth: { 95 | enable: false 96 | } 97 | }); 98 | 99 | 100 | function draw () { 101 | regl.poll(); 102 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 103 | drawLines(lineData); 104 | } 105 | 106 | draw(); 107 | window.addEventListener('resize', draw); 108 | -------------------------------------------------------------------------------- /examples/depth.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'], 3 | attributes: {antialias: false} 4 | }); 5 | 6 | // Instantiate a command for drawing lines 7 | const drawLines = reglLines(regl, { 8 | vert: ` 9 | precision highp float; 10 | 11 | uniform float width, time, phase; 12 | 13 | #pragma lines: attribute float x; 14 | #pragma lines: position = getPosition(x); 15 | vec4 getPosition(float x) { 16 | float theta = 3.141 * (6.0 * x + time) - phase; 17 | return vec4( 18 | 0.5 * vec3( 19 | cos(theta), 20 | (x * 2.0 - 1.0) * 1.5, 21 | sin(theta) 22 | ), 23 | 1 24 | ); 25 | } 26 | 27 | // Return the line width from a uniorm 28 | #pragma lines: width = getWidth(); 29 | float getWidth() { 30 | return width; 31 | }`, 32 | frag: ` 33 | precision highp float; 34 | uniform float width, borderWidth; 35 | uniform vec3 color; 36 | varying vec3 lineCoord; 37 | void main () { 38 | // Convert the line coord into an SDF 39 | float sdf = length(lineCoord.xy) * width; 40 | 41 | // Apply a border with 1px transition 42 | gl_FragColor = vec4( 43 | mix(color, vec3(1), smoothstep(width - borderWidth - 0.5, width - borderWidth + 0.5, sdf)), 44 | 1); 45 | }`, 46 | 47 | // Multiply the width by the pixel ratio for consistent width 48 | uniforms: { 49 | width: (ctx, props) => ctx.pixelRatio * props.width, 50 | borderWidth: (ctx, props) => ctx.pixelRatio * props.borderWidth, 51 | time: regl.context('time'), 52 | phase: regl.prop('phase'), 53 | color: regl.prop('color') 54 | }, 55 | 56 | depth: {enable: true} 57 | }); 58 | 59 | const n = 101; 60 | const x = [...Array(n).keys()].map(i => i / (n - 1)); 61 | 62 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 63 | // them on every draw call. 64 | const lineData = { 65 | width: 30, 66 | join: 'round', 67 | cap: 'round', 68 | joinResolution: 1, 69 | vertexCount: x.length, 70 | vertexAttributes: { x: regl.buffer(x) }, 71 | endpointCount: 2, 72 | endpointAttributes: { x: regl.buffer([x.slice(0, 3), x.slice(-3).reverse()]) }, 73 | borderWidth: 5 74 | }; 75 | 76 | regl.frame(() => { 77 | regl.poll(); 78 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 79 | drawLines([ 80 | {...lineData, phase: 0, color: [0.5, 1, 0]}, 81 | {...lineData, phase: Math.PI / 2, color: [0, 0.5, 1]}, 82 | {...lineData, phase: Math.PI, color: [1, 0, 0.5]} 83 | ]); 84 | }); 85 | -------------------------------------------------------------------------------- /examples/hexgrid.js: -------------------------------------------------------------------------------- 1 | function mat2create() { return [1, 0, 0, 1]; } 2 | function mat2rotate(out, a, rad) { 3 | var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; 4 | var s = Math.sin(rad); 5 | var c = Math.cos(rad); 6 | out[0] = a0 * c + a2 * s; 7 | out[1] = a1 * c + a3 * s; 8 | out[2] = a0 * -s + a2 * c; 9 | out[3] = a1 * -s + a3 * c; 10 | return out; 11 | } 12 | 13 | const extent = 2; 14 | 15 | const regl = createREGL({ 16 | extensions: ['ANGLE_instanced_arrays'], 17 | attributes: {antialias: false}, 18 | }); 19 | 20 | // Instantiate a command for drawing lines 21 | const drawLines = reglLines(regl, { 22 | vert: ` 23 | precision highp float; 24 | 25 | #pragma lines: attribute vec3 xy; 26 | #pragma lines: position = getPosition(xy); 27 | #pragma lines: varying vec2 position = getXY(xy); 28 | uniform mat2 transform; 29 | uniform vec2 aspect; 30 | uniform float scale; 31 | vec4 getPosition(vec3 xy) { 32 | return vec4((transform * (xy.xy * scale)) * aspect, 0, 1); 33 | } 34 | vec2 getXY(vec3 xy) { return xy.xy; } 35 | 36 | #pragma lines: varying float t = getT(xy); 37 | float getT(vec3 xy) { return xy.z; } 38 | 39 | // Return the line width from a uniorm 40 | #pragma lines: width = getWidth(); 41 | uniform lowp float width; 42 | float getWidth() { 43 | return width; 44 | }`, 45 | frag: ` 46 | precision lowp float; 47 | varying float t; 48 | varying vec3 lineCoord; 49 | varying vec2 position; 50 | uniform vec3 color; 51 | uniform vec2 range; 52 | uniform lowp float width; 53 | uniform float pixelRatio; 54 | float linearstep(float a, float b, float x) { return clamp((x - a) / (b - a), 0.0, 1.0); } 55 | void main () { 56 | float sdf = width * abs(lineCoord.y); 57 | float alpha = linearstep(width, width - 1.5, sdf); 58 | float c = fract(t); 59 | if (c > 1.0 / 3.0 || alpha < 0.2) discard; 60 | alpha *= smoothstep(range.x, range.y, length(position)) * 0.8; 61 | gl_FragColor = vec4(color, alpha); 62 | }`, 63 | 64 | // Multiply the width by the pixel ratio for consistent width 65 | uniforms: { 66 | range: [34 * extent, 29 * extent], 67 | scale: 0.03 / extent, 68 | color: regl.prop('color'), 69 | transform: regl.prop('transform'), 70 | pixelRatio: regl.context('pixelRatio'), 71 | width: (ctx, props) => { 72 | return Math.min(ctx.viewportWidth, ctx.viewportHeight) / 600; 73 | }, 74 | aspect: ctx => ctx.viewportWidth > ctx.viewportHeight 75 | ? [ctx.viewportHeight / ctx.viewportWidth, 1] 76 | : [1, ctx.viewportWidth / ctx.viewportHeight], 77 | }, 78 | 79 | blend: { 80 | enable: true, 81 | func: { 82 | srcRGB: 'src alpha', 83 | srcAlpha: 1, 84 | dstRGB: 1, 85 | dstAlpha: 1 86 | } 87 | }, 88 | 89 | depth: {enable: true} 90 | }); 91 | 92 | let xy = []; 93 | 94 | const yShift = Math.sin(Math.PI / 3); 95 | const unit = [[0, 0], [1 - Math.cos(Math.PI / 3), Math.sin(Math.PI / 3)]] 96 | let c = 0; 97 | const t = []; 98 | const rows = 20 * extent; 99 | const cols = 11 * extent; 100 | 101 | for (let k = -cols; k <=cols; k++) { 102 | for (let j of [-1, 1]) { 103 | for (let i = -rows; i <= rows; i++) { 104 | const pt = unit.map(([x, y]) => [ 105 | x + j * 0.75 + k * 3 - 0.25, 106 | (y + i * yShift * 2) * j - yShift * 0.5 107 | - j * yShift * 0.5, 108 | -1, 109 | ]); 110 | 111 | if (Math.hypot(pt[0][0], pt[0][1]) > 36.0 * extent) continue; 112 | xy.push(pt); 113 | } 114 | } 115 | } 116 | 117 | for (let j = rows; j >= -rows; j--) { 118 | const x1 = cols * 3 + 1.25; 119 | const e = 0.5; 120 | const shift = 1.5; 121 | const dx = -0.25; 122 | const dy = -yShift * 0.25; 123 | const ext = 2 * cols + 1; 124 | xy.push([ 125 | [x1 + dx, j + dy, ext], 126 | [-x1 - e + dx, j + dy, 0], 127 | [-x1 - e + dx + shift, j - 0.5 + dy, 0], 128 | [x1 + dx + shift, j - 0.5 + dy, ext] 129 | ].map(([x, y, z]) => [ 130 | x, 131 | (y + 0.25) * yShift * 2, 132 | z 133 | ])); 134 | } 135 | 136 | // Center it about zero 137 | xy = xy.flat(); 138 | xy.reverse(); 139 | 140 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 141 | // them on every draw call. 142 | const lineData = { 143 | roundResolution: 1, 144 | join: 'bevel', 145 | vertexCount: xy.length, 146 | vertexAttributes: { 147 | xy: regl.buffer(xy), 148 | } 149 | }; 150 | 151 | 152 | 153 | const colors = [ 154 | [1, 0.5, 0], 155 | [0, 0.5, 1], 156 | [0.5, 1, 0], 157 | ]; 158 | regl.frame(({time}) => { 159 | //const time = 1.0 * Math.PI / 180 / 0.1; 160 | regl.poll(); 161 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 162 | [0, 0.1].forEach((rate, i) => { 163 | regl.clear({depth: 1}); 164 | drawLines({ 165 | ...lineData, 166 | transform: mat2rotate([], mat2create(), time * rate), 167 | color: colors[i % colors.length] 168 | }) 169 | }); 170 | }); 171 | -------------------------------------------------------------------------------- /examples/instanced.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | 5 | // Instantiate a command for drawing lines 6 | const drawLines = reglLines(regl, { 7 | vert: ` 8 | precision highp float; 9 | 10 | // Use a vec2 attribute to construt the vec4 vertex position 11 | #pragma lines: instance attribute float shift; 12 | #pragma lines: attribute vec2 xy; 13 | #pragma lines: position = getPosition(xy, shift); 14 | vec4 getPosition(vec2 xy, float shift) { 15 | return vec4(xy + vec2(0, shift), 0, 1); 16 | } 17 | 18 | // Return the line width from a uniform 19 | #pragma lines: width = getWidth(); 20 | uniform float width; 21 | float getWidth() { 22 | return width; 23 | }`, 24 | frag: ` 25 | precision lowp float; 26 | void main () { 27 | gl_FragColor = vec4(1); 28 | }`, 29 | 30 | // Multiply the width by the pixel ratio for consistent width 31 | uniforms: { 32 | width: (ctx, props) => ctx.pixelRatio * props.width 33 | }, 34 | }); 35 | 36 | // Construct an array of xy pairs 37 | const n = 11; 38 | const xy = [...Array(n).keys()] 39 | .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8) 40 | .map(t => [t, 0.1 * Math.sin(8.0 * t)]); 41 | 42 | const shift = regl.buffer( 43 | [...Array(n).keys()].map(() => [-0.5, -0.25, 0, 0.25, 0.5]) 44 | ); 45 | 46 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 47 | // them on every draw call. 48 | const lineData = { 49 | width: 10, 50 | join: 'round', 51 | cap: 'round', 52 | vertexCount: xy.length, 53 | vertexAttributes: { 54 | xy: regl.buffer(xy), 55 | shift 56 | }, 57 | endpointCount: 2, 58 | endpointAttributes: { 59 | xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]), 60 | shift, 61 | }, 62 | instances: 5, 63 | }; 64 | 65 | function draw () { 66 | regl.poll(); 67 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 68 | drawLines(lineData); 69 | } 70 | 71 | draw(); 72 | window.addEventListener('resize', draw); 73 | -------------------------------------------------------------------------------- /examples/knot.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | 5 | // Instantiate a command for drawing lines 6 | const drawLines = reglLines(regl, { 7 | vert: ` 8 | precision highp float; 9 | 10 | uniform float width, phase; 11 | uniform vec2 aspect; 12 | 13 | #pragma lines: attribute float theta; 14 | #pragma lines: position = torusKnot(theta); 15 | vec4 torusKnot(float theta) { 16 | const float p = 3.0; 17 | const float q = 5.0; 18 | float r = cos(q * theta) + 2.0; 19 | float phi = p * (theta - phase); 20 | return vec4( 21 | vec3( 22 | aspect * r * vec2(cos(phi), sin(phi)), 23 | -sin(q * theta) 24 | ) * 0.25, 25 | 1 26 | ); 27 | } 28 | 29 | // Return the line width from a uniform 30 | #pragma lines: width = getWidth(); 31 | float getWidth() { return width; }`, 32 | frag: ` 33 | precision highp float; 34 | uniform float width, borderWidth, pixelRatio; 35 | uniform vec3 color; 36 | varying vec3 lineCoord; 37 | void main () { 38 | // Convert the line coord into an SDF 39 | float sdf = length(lineCoord.xy) * width; 40 | 41 | // Apply a border with 1px transition 42 | gl_FragColor = vec4( 43 | mix(color, vec3(1), 44 | smoothstep( 45 | width - borderWidth - 0.5 / pixelRatio, 46 | width - borderWidth + 0.5 / pixelRatio, 47 | sdf 48 | ) 49 | ), 1); 50 | }`, 51 | 52 | // Multiply the width by the pixel ratio for consistent width 53 | uniforms: { 54 | pixelRatio: regl.context('pixelRatio'), 55 | aspect: ctx => ctx.viewportWidth > ctx.viewportHeight 56 | ? [ctx.viewportHeight / ctx.viewportWidth, 1] 57 | : [1, ctx.viewportWidth / ctx.viewportHeight], 58 | width: (ctx, props) => Math.min(ctx.viewportWidth, ctx.viewportHeight) / 30, 59 | borderWidth: (ctx, props) => Math.min(ctx.viewportWidth, ctx.viewportHeight) / (30 * 4), 60 | phase: regl.prop('phase'), 61 | color: regl.prop('color') 62 | }, 63 | 64 | depth: {enable: true} 65 | }); 66 | 67 | const n = 501; 68 | 69 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 70 | // them on every draw call. 71 | const lineData = { 72 | join: 'round', 73 | vertexCount: n + 3, 74 | vertexAttributes: { 75 | theta: regl.buffer([...Array(n + 3).keys()].map(i => i / n * Math.PI * 2)) 76 | }, 77 | }; 78 | 79 | function draw() { 80 | regl.poll(); 81 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 82 | drawLines([ 83 | {...lineData, phase: 0, color: [1, 0, 0.5]}, 84 | {...lineData, phase: Math.PI, color: [0, 0.5, 1]}, 85 | ]); 86 | } 87 | 88 | draw(); 89 | window.addEventListener('resize', draw); 90 | -------------------------------------------------------------------------------- /examples/multiple.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({extensions: ['ANGLE_instanced_arrays']}); 2 | 3 | const drawLines = reglLines(regl, { 4 | vert: ` 5 | precision highp float; 6 | 7 | #pragma lines: attribute vec2 xy; 8 | #pragma lines: position = getPosition(xy); 9 | #pragma lines: width = getWidth(); 10 | #pragma lines: varying vec2 pos = getXY(xy); 11 | 12 | vec4 getPosition(vec2 xy) { 13 | return vec4(xy, 0, 1); 14 | } 15 | float getWidth() { return 40.0; } 16 | vec2 getXY(vec2 xy) { return xy; }`, 17 | frag: ` 18 | precision lowp float; 19 | varying vec2 pos; 20 | void main () { 21 | // Convert the x-coordinate into a color 22 | gl_FragColor = vec4(0.6 + 0.4 * cos(8.0 * (pos.x - vec3(0, 1, 2) * 3.141 / 3.0)), 0.7); 23 | }`, 24 | // Turn off depth and turn on blending to make it very clear if we accidentally 25 | // draw end caps twice 26 | depth: { enable: false }, 27 | cull: {enable: true, face: 'back'}, 28 | blend: { 29 | enable: true, 30 | func: { srcRGB: "src alpha", srcAlpha: 1, dstRGB: "one minus src alpha", dstAlpha: 1 } 31 | }, 32 | }); 33 | 34 | const n = 31; 35 | const lineCount = 10; 36 | 37 | function xy (line, i) { 38 | let t = (i / (n - 1) * 2 - 1) * 0.9; 39 | const y = ((line + 0.5) / lineCount * 2 - 1) * 0.9; 40 | return [t, y + 1 / lineCount * Math.sin((t - line * 0.1) * 8.0)]; 41 | } 42 | 43 | // Start with a break in order to signal a cap 44 | const positions = [[NaN, NaN]]; 45 | 46 | for (let line = 0; line < lineCount; line++) { 47 | for (let i = 0; i < n; i++) { 48 | positions.push(xy(line, i)); 49 | } 50 | // Signal a cap after each line 51 | positions.push([NaN, NaN]); 52 | } 53 | 54 | // After this, render as normal! 55 | const lineData = { 56 | // Trigger the command to automatically insert caps at any break, signaled by a position with (w = 0) 57 | insertCaps: true, 58 | 59 | join: 'round', 60 | cap: 'round', 61 | vertexCount: positions.length, 62 | vertexAttributes: { 63 | xy: regl.buffer(positions), 64 | }, 65 | }; 66 | 67 | function draw () { 68 | regl.poll(); 69 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 70 | drawLines(lineData); 71 | } 72 | 73 | draw(); 74 | window.addEventListener('resize', draw); 75 | -------------------------------------------------------------------------------- /examples/postproject.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | const mat4 = glMatrix.mat4; 5 | 6 | // This example illustrates the postproject property. You may specify a function to be applied 7 | // *after* screne-space projection. Together with providing viewportSize, this allows you to 8 | // draw lines in whatever viewport you desire, then to project them to the screen. This useful, 9 | // for example, when drawing lines on a flat surface. It changes nothing if the lines are not 10 | // on a flat surface, though the joins may not look particularly good in the out-of-plane 11 | // dimension. 12 | const drawLines = reglLines(regl, { 13 | vert: ` 14 | precision highp float; 15 | 16 | // Use a vec2 attribute to construt the vec4 vertex position 17 | #pragma lines: attribute vec2 position; 18 | #pragma lines: position = getPosition(position); 19 | vec4 getPosition(vec2 position) { 20 | return vec4(position, 0, 1); 21 | } 22 | 23 | // Return the line width from a uniorm 24 | #pragma lines: width = getWidth(); 25 | uniform float width; 26 | float getWidth() { 27 | return width; 28 | } 29 | 30 | // Specify a projection function 31 | #pragma lines: postproject = postprojectPosition; 32 | uniform mat4 projectionView; 33 | vec4 postprojectPosition(vec4 position) { 34 | return projectionView * position; 35 | } 36 | `, 37 | frag: ` 38 | precision lowp float; 39 | void main () { 40 | gl_FragColor = vec4(1); 41 | }`, 42 | 43 | // Multiply the width by the pixel ratio for consistent width 44 | uniforms: { 45 | width: (ctx, props) => ctx.pixelRatio * props.width, 46 | projectionView: (ctx, props) => { 47 | //const aspect = ctx.viewportWidth / ctx.viewportHeight; 48 | const aspect = 2; 49 | const projection = mat4.perspective(mat4.create(), Math.PI / 4, aspect, 0.01, 10.0); 50 | const theta = ctx.time * 0.5; 51 | const r = 1.8; 52 | const eye = [ 53 | r * Math.cos(theta), 54 | r * Math.sin(theta), 55 | 0.5 56 | ]; 57 | const center = [0, 0, 0]; 58 | const up = [0, 0, 1]; 59 | const view = mat4.lookAt(mat4.create(), eye, center, up); 60 | return mat4.multiply(projection, projection, view); 61 | } 62 | }, 63 | }); 64 | 65 | // Construct an array of xy pairs 66 | const n = 501; 67 | const position = [...Array(n + 3).keys()] 68 | .map(i => i / n * Math.PI * 2) 69 | .map(t => { 70 | const r = 0.6 + 0.4 * Math.cos(t * 7.0); 71 | return [r * Math.cos(t), r * Math.sin(t)] 72 | }); 73 | 74 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 75 | // them on every draw call. 76 | const lineData = { 77 | width: 0.03, 78 | join: 'round', 79 | cap: 'round', 80 | vertexCount: position.length, 81 | vertexAttributes: { position: regl.buffer(position) }, 82 | 83 | // Screen-project in the 1 x 1 unit square. Since this size is divided out before 84 | // post-projection, this only affects interpretation of "widthV. 85 | viewportSize: [1, 1] 86 | }; 87 | 88 | regl.frame(() => { 89 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 90 | drawLines(lineData); 91 | }); 92 | -------------------------------------------------------------------------------- /examples/strided.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | 5 | // Instantiate a command for drawing lines 6 | const drawLines = reglLines(regl, { 7 | vert: ` 8 | precision highp float; 9 | 10 | uniform float pixelRatio; 11 | 12 | #pragma lines: attribute vec2 xy; 13 | #pragma lines: attribute vec4 color; 14 | #pragma lines: attribute float width; 15 | #pragma lines: position = getPosition(xy); 16 | #pragma lines: varying vec4 color = getColor(color); 17 | #pragma lines: width = getWidth(width); 18 | 19 | vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); } 20 | vec4 getColor(vec4 color) { return color; } 21 | float getWidth(float width) { return pixelRatio * width; } 22 | `, 23 | frag: ` 24 | precision lowp float; 25 | varying vec4 color; 26 | void main () { 27 | gl_FragColor = color; 28 | }`, 29 | uniforms: { 30 | pixelRatio: regl.context('pixelRatio') 31 | } 32 | }); 33 | 34 | // Construct an array of xy pairs 35 | const n = 101; 36 | const verticesU8 = new Uint8ClampedArray(n * 16); 37 | const verticesF32 = new Float32Array(verticesU8.buffer); 38 | for (let i = 0; i < n; i++) { 39 | let t = i / (n - 1); 40 | let x = (t * 2 - 1) * 0.8; 41 | let y = 0.8 * Math.sin(x * 2.0 * Math.PI); 42 | let width = 50.0 * (0.5 - 0.4 * Math.cos(Math.PI * 4 * x)); 43 | let color = [0, 1, 2].map(i => 0.5 + Math.cos(2 * (x - 2 * i * Math.PI / 3))); 44 | 45 | // x, y (float32): bytes 0-7 46 | // width (float32): bytes 8-11 47 | // color (float32): bytes 12-23 48 | verticesF32[i * 4] = x; 49 | verticesF32[i * 4 + 1] = y; 50 | verticesF32[i * 4 + 2] = width; 51 | verticesU8[i * 16 + 12] = color[0] * 255; 52 | verticesU8[i * 16 + 13] = color[1] * 255; 53 | verticesU8[i * 16 + 14] = color[2] * 255; 54 | verticesU8[i * 16 + 15] = 255; 55 | } 56 | 57 | // Pack the interleaved values into a buffer 58 | const verticesBuffer = regl.buffer(verticesU8); 59 | 60 | // Packing the endpoints into a buffer is a little trickier. We need blocks of sixteen 61 | // bytes for each vertex packed into an array for the first three vertices and the last 62 | // three (in reverse order), so basically vertices [0, 1, 2] and [n-1, n-2, n-3]. 63 | const endpointsU8 = new Uint8Array(2 * 3 * 16); 64 | for (let j = 0; j < 16; j++) { 65 | for (let i = 0; i < 3; i++) { 66 | endpointsU8[i * 16 + j] = verticesU8[i * 16 + j]; 67 | endpointsU8[(3 + i) * 16 + j] = verticesU8[(n - 1 - i) * 16 + j]; 68 | } 69 | } 70 | const endpointsBuffer = regl.buffer(endpointsU8); 71 | 72 | // Set up the data to be drawn. 73 | const lineData = { 74 | join: 'round', 75 | cap: 'round', 76 | vertexCount: n, 77 | vertexAttributes: { 78 | // Attributes are compatible with regl specification 79 | xy: { 80 | type: 'float32', 81 | buffer: verticesBuffer, 82 | offset: Float32Array.BYTES_PER_ELEMENT * 0, 83 | stride: Float32Array.BYTES_PER_ELEMENT * 4, 84 | // divisor: 1 // implicit (but configurable) 85 | }, 86 | width: { 87 | type: 'float32', 88 | buffer: verticesBuffer, 89 | offset: Float32Array.BYTES_PER_ELEMENT * 2, 90 | stride: Float32Array.BYTES_PER_ELEMENT * 4, 91 | }, 92 | color: { 93 | type: 'uint8', 94 | normalized: true, 95 | buffer: verticesBuffer, 96 | offset: Float32Array.BYTES_PER_ELEMENT * 3, 97 | stride: Float32Array.BYTES_PER_ELEMENT * 4, 98 | } 99 | }, 100 | endpointCount: 2, 101 | endpointAttributes: { 102 | xy: { 103 | type: 'float32', 104 | buffer: endpointsBuffer, 105 | offset: Float32Array.BYTES_PER_ELEMENT * 0, 106 | stride: Float32Array.BYTES_PER_ELEMENT * 4, 107 | }, 108 | width: { 109 | type: 'float32', 110 | buffer: endpointsBuffer, 111 | offset: Float32Array.BYTES_PER_ELEMENT * 2, 112 | stride: Float32Array.BYTES_PER_ELEMENT * 4, 113 | }, 114 | color: { 115 | type: 'uint8', 116 | normalized: true, 117 | buffer: endpointsBuffer, 118 | offset: Float32Array.BYTES_PER_ELEMENT * 3, 119 | stride: Float32Array.BYTES_PER_ELEMENT * 4, 120 | } 121 | } 122 | }; 123 | 124 | function draw () { 125 | regl.poll(); 126 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 127 | drawLines(lineData); 128 | } 129 | 130 | draw(); 131 | window.addEventListener('resize', draw); 132 | -------------------------------------------------------------------------------- /examples/vao.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'], 3 | optionalExtensions: ['OES_vertex_array_object'] 4 | }); 5 | 6 | if (!regl.hasExtension('OES_vertex_array_object')) { 7 | console.warn('Unable to load OES_vertex_array_object extension. VAOs will be emulated.'); 8 | } 9 | 10 | // This command illustrates usage of Vertex Array Objects (VAOs). The general idea of VAOs is that 11 | // you define the full state of strides and offsets up front to avoid having to configure it over 12 | // and over. 13 | // 14 | // In the context of regl-gpu-lines, it means that you move vertexAttributes and endpointAttributes 15 | // to a separate call to `drawLines.vao()`, then simply pass the resulting object when drawing lines 16 | // instead of vertexAttributes and endpointAttributes. 17 | const drawLines = reglLines(regl, { 18 | vert: ` 19 | precision highp float; 20 | 21 | // Use a vec2 attribute to construt the vec4 vertex position 22 | #pragma lines: attribute vec2 xy; 23 | #pragma lines: position = getPosition(xy); 24 | vec4 getPosition(vec2 xy) { 25 | return vec4(xy, 0, 1); 26 | } 27 | 28 | #pragma lines: attribute float orientation; 29 | #pragma lines: orientation = getOrientation(orientation) 30 | float getOrientation(float o) { return o; } 31 | 32 | // Return the line width from a uniorm 33 | #pragma lines: width = getWidth(); 34 | uniform float width; 35 | float getWidth() { 36 | return width; 37 | }`, 38 | frag: ` 39 | precision lowp float; 40 | void main () { 41 | gl_FragColor = vec4(1); 42 | }`, 43 | 44 | // Multiply the width by the pixel ratio for consistent width 45 | uniforms: { 46 | width: (ctx, props) => ctx.pixelRatio * props.width 47 | }, 48 | }); 49 | 50 | // Construct an array of xy pairs 51 | const n = 11; 52 | const xy = [...Array(n).keys()] 53 | .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8) 54 | .map(t => [t, 0.5 * Math.sin(8.0 * t)]); 55 | 56 | // Instead of passing vertex and endpoint attributes with line data, we instead allocate a Vertex 57 | // Array Object (VAO) up front, then pass the resulting object to the draw command. This prevents 58 | // having to compute and configure strides and offsets on every frame. 59 | const vao = drawLines.vao({ 60 | vertexAttributes: { 61 | xy: regl.buffer(xy) 62 | }, 63 | endpointAttributes: { 64 | // Note the use of flat() which is required for regl to correctly infer the dimensionality of 65 | // the data, which must be correctly passed to the VAO. 66 | orientation: regl.buffer([Array(3).fill(reglLines.CAP_START), Array(3).fill(reglLines.CAP_END)].flat()), 67 | xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()].flat()) 68 | } 69 | }); 70 | 71 | function draw () { 72 | // After the above step, line proceeds as normal, except instead of attributes, we pass it the 73 | // preallocated VAO object. 74 | const lineData = { 75 | width: 30, 76 | join: 'round', 77 | cap: 'round', 78 | vertexCount: xy.length, 79 | endpointCount: 2, 80 | vao 81 | }; 82 | 83 | regl.poll(); 84 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 85 | drawLines(lineData); 86 | } 87 | 88 | draw(); 89 | window.addEventListener('resize', draw); 90 | -------------------------------------------------------------------------------- /examples/variable-width.js: -------------------------------------------------------------------------------- 1 | const regl = createREGL({ 2 | extensions: ['ANGLE_instanced_arrays'] 3 | }); 4 | 5 | // Instantiate a command for drawing lines 6 | const drawLines = reglLines(regl, { 7 | vert: ` 8 | precision highp float; 9 | 10 | #pragma lines: attribute vec2 xy; 11 | 12 | // Assign a function for computing the vec4 position 13 | #pragma lines: position = getPosition(xy); 14 | 15 | // Assign a function for computing the float width 16 | #pragma lines: width = getWidth(xy); 17 | 18 | // Compute a varying value from the input attribute 19 | #pragma lines: varying float x = getX(xy); 20 | 21 | uniform float width; 22 | 23 | // Implement the functions above 24 | vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); } 25 | float getWidth(vec2 xy) { return width * (0.5 + 0.4 * cos(16.0 * xy.x)); } 26 | float getX(vec2 xy) { return xy.x; }`, 27 | frag: ` 28 | precision lowp float; 29 | varying float x; 30 | void main () { 31 | gl_FragColor = vec4(0.5 + cos(8.0 * (x - vec3(0, 1, 2) * 3.141 / 3.0)), 1); 32 | }`, 33 | 34 | // Additional regl command properties are valid 35 | uniforms: { 36 | width: regl.prop('width') 37 | }, 38 | 39 | depth: {enable: false} 40 | }); 41 | 42 | // Construct an array of xy pairs 43 | const n = 101; 44 | const xy = [...Array(n).keys()] 45 | .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8) 46 | .map(t => [t, 0.5 * Math.sin(8.0 * t)]); 47 | 48 | // Set up the data to be drawn. Note that we preallocate buffers and don't create 49 | // them on every draw call. 50 | const lineData = { 51 | join: 'round', 52 | cap: 'round', 53 | vertexCount: xy.length, 54 | vertexAttributes: { 55 | xy: regl.buffer(xy) 56 | }, 57 | endpointCount: 2, 58 | endpointAttributes: { 59 | xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()]) 60 | }, 61 | 62 | // Picked up by regl.prop('width') 63 | width: 50 64 | }; 65 | 66 | function draw () { 67 | regl.poll(); 68 | regl.clear({color: [0.2, 0.2, 0.2, 1]}); 69 | drawLines(lineData); 70 | } 71 | 72 | draw(); 73 | window.addEventListener('resize', draw); 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "regl-gpu-lines", 3 | "version": "2.4.1", 4 | "description": "Pure GPU, instanced, screen-projected lines for regl", 5 | "main": "dist/regl-gpu-lines.min.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/rreusser/regl-gpu-lines.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/rreusser/regl-gpu-lines/issues" 12 | }, 13 | "scripts": { 14 | "bundle": "npm run bundle:rollup && npm run bundle:minify && npm run bundle:minify:compat", 15 | "bundle:minify": "terser -cm -o dist/regl-gpu-lines.min.js dist/regl-gpu-lines.js && node scripts/compress.js < dist/regl-gpu-lines.min.js > dist/tmp.js && mv dist/tmp.js dist/regl-gpu-lines.min.js", 16 | "bundle:minify:compat": "terser -cm -o dist/regl-gpu-lines.compat.min.js dist/regl-gpu-lines.compat.js && node scripts/compress.js < dist/regl-gpu-lines.compat.min.js > dist/tmp.js && mv dist/tmp.js dist/regl-gpu-lines.compat.min.js", 17 | "bundle:rollup": "mkdir -p dist && rollup -c", 18 | "build:examples": "for i in examples/*.js; do node scripts/build-example $i; done", 19 | "build:test-page": "node scripts/build-test-page.js", 20 | "lint": "eslint src/*.js", 21 | "lint-fix": "eslint src/*.js --fix", 22 | "prepublishOnly": "npm run bundle && npm run test && npm run build:examples && npm run build:test-page", 23 | "test": "npm run lint && npm run test-render && npm run test-render-prod", 24 | "test-render": "ENV=development node test/render.js", 25 | "test-render-prod": "ENV=production node test/render.js", 26 | "serve-render-tests": "node scripts/serve-render-tests.js", 27 | "start": "node scripts/serve-example.js" 28 | }, 29 | "keywords": [ 30 | "regl", 31 | "webgl", 32 | "lines" 33 | ], 34 | "author": "Ricky Reusser", 35 | "license": "MIT", 36 | "devDependencies": { 37 | "@babel/core": "^7.15.8", 38 | "@babel/preset-env": "^7.15.8", 39 | "@rollup/plugin-babel": "^5.3.0", 40 | "@rollup/plugin-commonjs": "^21.0.1", 41 | "@rollup/plugin-json": "^4.1.0", 42 | "@rollup/plugin-node-resolve": "^13.0.6", 43 | "async": "^3.2.2", 44 | "babelify": "^10.0.0", 45 | "browserify": "^17.0.0", 46 | "budo": "^11.6.4", 47 | "controls-gui": "^2.0.0", 48 | "controls-state": "^2.0.0", 49 | "eslint": "^8.2.0", 50 | "get-pixels": "^3.3.3", 51 | "gl": "^6.0.2", 52 | "gl-matrix": "^3.4.3", 53 | "glob": "^7.2.0", 54 | "hyperstream": "^1.2.2", 55 | "insert-css": "^2.0.0", 56 | "ndarray": "^1.0.19", 57 | "ndarray-ops": "^1.2.2", 58 | "ndarray-scratch": "^1.2.0", 59 | "pixelmatch": "^5.2.1", 60 | "raf": "^3.4.1", 61 | "regl": "^2.1.0", 62 | "regl-camera": "^2.1.1", 63 | "rollup": "^2.58.3", 64 | "save-pixels": "^2.3.6", 65 | "simple-html-index": "^1.5.0", 66 | "stream-to-string": "^1.2.0", 67 | "string-to-stream": "^3.0.1", 68 | "tape": "^5.3.1", 69 | "terser": "^5.9.0" 70 | }, 71 | "dependencies": {} 72 | } 73 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import nodeResolve from '@rollup/plugin-node-resolve'; 4 | import { babel } from '@rollup/plugin-babel'; 5 | import json from '@rollup/plugin-json'; 6 | 7 | export default [ 8 | { 9 | input: 'src/index.js', 10 | output: 'dist/regl-gpu-lines.js', 11 | format: 'umd', 12 | name: 'reglLines', 13 | babelPresets: [], 14 | }, { 15 | input: 'src/index.js', 16 | output: 'dist/regl-gpu-lines.compat.js', 17 | format: 'umd', 18 | name: 'reglLines', 19 | babelPresets: ['@babel/preset-env'], 20 | }, 21 | ].map(bundle => ({ 22 | input: bundle.input, 23 | output: { 24 | file: bundle.output, 25 | format: bundle.format, 26 | name: bundle.name, 27 | }, 28 | plugins: [ 29 | nodeResolve({ 30 | browser: true 31 | }), 32 | commonjs(), 33 | babel({ 34 | babelHelpers: 'bundled', 35 | presets: bundle.babelPresets 36 | }), 37 | json() 38 | ] 39 | })); 40 | -------------------------------------------------------------------------------- /scripts/_example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | regl-gpu-lines Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /scripts/browser-fixture-renderer.js: -------------------------------------------------------------------------------- 1 | const renderFixture = require('../test/util/render-fixture.js'); 2 | const pixelmatch = require('pixelmatch'); 3 | 4 | const h = (type, props, c) => { 5 | const el = document.createElement(type); 6 | if (c) (Array.isArray(c) ? c : [c]).forEach(c => el.appendChild(typeof c === 'string' ? document.createTextNode(c) : c)); 7 | return Object.assign(el, props); 8 | } 9 | 10 | document.body.appendChild(h('style', {}, ` 11 | html { font-family: sans-serif; } 12 | p { line-height: 1.4em; max-width: 640px; } 13 | .actual { border: 1px solid #888 } 14 | .expected { border: 1px solid #23f; } 15 | .diff { border: 1px solid #f23; } 16 | .group { margin-bottom: 1em; } 17 | summary { cursor: pointer; } 18 | `)); 19 | 20 | const initialFixtureName = window.location.hash.replace(/^#/, '') || Object.keys(fixtures)[0]; 21 | 22 | const fixtureSelector = h('select', null, Object.keys(fixtures).map(value => 23 | h('option', {value, selected: value === initialFixtureName ? 'selected' : ''}, value) 24 | )); 25 | const expected = h('img', {className: 'expected', crossOrigin: 'Anonymous'}); 26 | const diff = h('canvas', {className: 'diff'}); 27 | const canvas = h('canvas', {className: 'actual'}); 28 | const fixtureDataLabel = h('summary'); 29 | const fixtureDataPre = h('pre'); 30 | const badCount = h('span', null, '0'); 31 | 32 | const prodMaterial = window.env === 'development' ? [] : [ 33 | h('h1', {}, 'regl-gpu-lines render tests'), 34 | h('a', {href: 'https://github.com/rreusser/regl-gpu-lines'}, '← Back to project page'), 35 | h('p', {}, [ 36 | 'This page uses ', 37 | h('a', {href: 'https://unpkg.com/regl-gpu-lines@latest'}, 'regl-gpu-lines@latest'), 38 | ' from ', 39 | h('a', {href: 'https://unpkg.com'}, 'unpkg.com'), 40 | ' to run tests live.' 41 | ]), 42 | ]; 43 | 44 | document.body.appendChild(h('div', {}, [ 45 | h('div', null, prodMaterial), 46 | h('div', {className: 'group'}, [fixtureSelector]), 47 | h('div', {className: 'group'}, [h('div', {}, 'Actual:'), canvas]), 48 | h('div', {className: 'group'}, [h('div', {}, 'Expected:'), expected]), 49 | h('div', {className: 'group'}, [h('div', {}, ['Diff (', badCount, ' incorrect pixels):']), diff]), 50 | h('details', {}, [fixtureDataLabel, h('code', {}, fixtureDataPre)]) 51 | ])); 52 | 53 | const regl = createREGL({ 54 | canvas, 55 | pixelRatio: 1, 56 | attributes: {antialias: false, preserveDrawingBuffer: true}, 57 | extensions: ['ANGLE_instanced_arrays'], 58 | optionalExtensions: ['OES_standard_derivatives'] 59 | }); 60 | 61 | fixtureSelector.addEventListener('input', e => executeFixture(e.target.value)); 62 | 63 | function flipPixels (data, width) { 64 | let tmp = new Uint8Array(width * 4); 65 | const height = data.length / (width * 4); 66 | for (let i = 0; i < height / 2; i++) { 67 | const idx1 = 4 * i * width; 68 | const idx2 = 4 * (height - i - 1) * width; 69 | const row1 = data.subarray(idx1, idx1 + width * 4); 70 | const row2 = data.subarray(idx2, idx2 + width * 4); 71 | tmp.set(row1); 72 | row1.set(row2); 73 | row2.set(tmp); 74 | } 75 | return data; 76 | } 77 | 78 | const expCanvas = h('canvas'); 79 | function compareImages(fixture, width, height) { 80 | const actualPixels = flipPixels(regl.read(), width); 81 | 82 | expCanvas.width = width; 83 | expCanvas.height = height; 84 | const expCtx = expCanvas.getContext('2d'); 85 | expCtx.drawImage(expected, 0, 0); 86 | const expectedPixels = expCtx.getImageData(0, 0, width, height).data; 87 | 88 | diff.width = width; 89 | diff.height = height; 90 | const diffCtx = diff.getContext('2d'); 91 | const diffImgData = diffCtx.getImageData(0, 0, width, height); 92 | 93 | const match = pixelmatch(actualPixels, expectedPixels, diffImgData.data, width, height); 94 | diffCtx.putImageData(diffImgData, 0, 0); 95 | 96 | badCount.textContent = match; 97 | } 98 | 99 | function getFixture (name) { 100 | if (window.env === 'development') { 101 | console.info(`Fetching local fixture: ${name}`); 102 | return fetch(`/test/fixtures/${name}/fixture.json`) 103 | .then(response => { 104 | if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`); 105 | return response.text(); 106 | }); 107 | } else { 108 | console.info(`Using fixture: ${name}`); 109 | return Promise.resolve(fixtures[name].fixture); 110 | } 111 | } 112 | 113 | function executeFixture (name) { 114 | getFixture(name).then(fixtureText => { 115 | if (!fixtureText) throw new Error(`Invalid or missing test name "${name}"`); 116 | const fixture = JSON.parse(fixtureText); 117 | window.location.hash = name; 118 | 119 | const {width, height} = fixture; 120 | canvas.width = width; 121 | canvas.height = height; 122 | canvas.style.width = `${width}px`; 123 | canvas.style.height = `${height}px`; 124 | renderFixture(regl, reglLines, fixture); 125 | 126 | fixtureDataLabel.textContent = `${name}/fixture.json`; 127 | fixtureDataPre.textContent = fixtureText; 128 | 129 | expected.onload = null; 130 | expected.src = ''; 131 | expected.onload = () => compareImages(fixture, width, height); 132 | if (window.env === 'development') { 133 | expected.src = `/test/fixtures/${name}/expected.png`; 134 | } else { 135 | expected.src = `https://raw.githubusercontent.com/rreusser/regl-gpu-lines/main/test/fixtures/${name}/expected.png`; 136 | } 137 | }); 138 | } 139 | 140 | executeFixture(initialFixtureName); 141 | -------------------------------------------------------------------------------- /scripts/build-example.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const hyperstream = require('hyperstream'); 4 | 5 | const entry = process.argv[2]; 6 | const output = path.join(__dirname, '..', 'docs', path.basename(entry, '.js') + '.html'); 7 | 8 | const htmlStream = fs.createReadStream(path.join(__dirname, '_example.html')); 9 | const content = fs.readFileSync(path.join(__dirname, '..', entry), 'utf8'); 10 | const outStream = fs.createWriteStream(output); 11 | 12 | htmlStream 13 | .pipe(hyperstream({ 14 | body: { 15 | _appendHtml: `\n 18 | 33 |
34 |
35 | Code 36 |

37 | ${content}
38 | 
39 |
40 |
41 | 45 | ` 46 | } 47 | })) 48 | .pipe(outStream); 49 | -------------------------------------------------------------------------------- /scripts/build-test-page.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const htmlIndex = require('simple-html-index'); 4 | const hyperstream = require('hyperstream'); 5 | const browserify = require('browserify'); 6 | const toString = require('stream-to-string'); 7 | const getFixtures = require('./get-fixtures.js'); 8 | 9 | toString( 10 | browserify().add(path.join(__dirname, 'browser-fixture-renderer.js')).bundle(), 11 | function (err, bundle) { 12 | if (err) throw new Error(err); 13 | 14 | htmlIndex({ 15 | title: 'regl-gpu-lines tests' 16 | }) 17 | .pipe(hyperstream({ 18 | head: { 19 | _appendHtml: ` 20 | 21 | 22 | 23 | ` 24 | }, 25 | body: { 26 | _appendHtml: ` 27 | 28 | 29 | ` 30 | } 31 | })) 32 | .pipe(fs.createWriteStream(path.join(__dirname, '..', 'docs', 'tests.html'))); 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /scripts/compress.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const toString = require('stream-to-string'); 4 | 5 | toString(process.stdin, function (err, str) { 6 | // Remove comments 7 | str = str.replace(/\/\/[^\\]*\\n/g, '\\n'); 8 | 9 | // Collapse repeated line breaks 10 | str = str.replace(/(\\n\s*){2,}/g, '\\n'); 11 | 12 | // Collapse space around line breaks 13 | str = str.replace(/(\s*\\n\s*){1,}/g, '\\n'); 14 | 15 | // Collapse space around symbols 16 | str = str.replace(/\s*(=|\+|-|\*|\/|==|>|<|\(|\)|,|\?|:|\|\|)\s*/g, '$1'); 17 | 18 | // Remove line breaks after certain symbols 19 | str = str.replace(/([;{}])\\n/g, '$1'); 20 | 21 | process.stdout.write(str); 22 | }); 23 | 24 | 25 | -------------------------------------------------------------------------------- /scripts/dummy.js: -------------------------------------------------------------------------------- 1 | // This file exists only because budo does not accept a dynamic stream input 2 | // for the entry point. Instead, we have to pass it this dummy file to get 3 | // browserify working, then use a plugin to add dynamic js content 4 | -------------------------------------------------------------------------------- /scripts/get-fixtures.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const glob = require('glob'); 4 | 5 | module.exports = getFixtures; 6 | 7 | function getFixtures () { 8 | const allFixtures = {}; 9 | const fixtureDir = path.join(__dirname, '..', 'test', 'fixtures'); 10 | glob.sync(path.join(fixtureDir, '**', 'fixture.json')) 11 | .forEach(test => { 12 | const name = path.dirname(path.relative(fixtureDir, test)); 13 | const testPath = path.join(fixtureDir, name, 'fixture.json'); 14 | const fixture = fs.readFileSync(testPath, 'utf8'); 15 | allFixtures[name] = {name, fixture}; 16 | }); 17 | return allFixtures; 18 | } 19 | -------------------------------------------------------------------------------- /scripts/serve-example.js: -------------------------------------------------------------------------------- 1 | const budo = require('budo'); 2 | const path = require('path'); 3 | const defaultIndex = require('simple-html-index'); 4 | const hyperstream = require('hyperstream'); 5 | const toStream = require('string-to-stream'); 6 | 7 | const entry = 'examples/' + process.argv[2] + '.js'; 8 | 9 | if (!entry) { 10 | console.error('Usage: npm start '); 11 | process.exit(1); 12 | } 13 | 14 | const preface = ` 15 | window.wrapGUI = require('controls-gui'); 16 | window.State = require('controls-state'); 17 | window.createREGL = require('regl'); 18 | window.glMatrix = require('gl-matrix'); 19 | window.reglLines = require('./src/index.js'); 20 | `; 21 | 22 | budo(path.join(__dirname, 'dummy.js'), { 23 | host: 'localhost', 24 | open: true, 25 | live: true, 26 | browserify: { 27 | plugin: [ 28 | b => b.add(toStream(preface)), 29 | b => b.add(path.join(__dirname, '..', entry)) 30 | ] 31 | }, 32 | }).on('connect', function (ev) { 33 | console.log('Server running on %s', ev.uri); 34 | console.log('LiveReload running on port %s', ev.livePort); 35 | }).on('update', function (buffer) { 36 | console.log('bundle - %d bytes', buffer.length); 37 | }); 38 | 39 | console.log('entry:', entry); 40 | -------------------------------------------------------------------------------- /scripts/serve-render-tests.js: -------------------------------------------------------------------------------- 1 | const budo = require('budo'); 2 | const path = require('path'); 3 | const defaultIndex = require('simple-html-index'); 4 | const hyperstream = require('hyperstream'); 5 | const toStream = require('string-to-stream'); 6 | const getFixtures = require('./get-fixtures.js'); 7 | 8 | const preface = ` 9 | window.fixtures = ${JSON.stringify(getFixtures())}; 10 | window.createREGL = require('regl'); 11 | window.reglLines = require('./src/index.js'); 12 | window.env = 'development'; 13 | `; 14 | 15 | budo(path.join(__dirname, 'dummy.js'), { 16 | //host: 'localhost', 17 | open: true, 18 | live: true, 19 | watchGlob: [ 20 | path.join(__dirname, '..', 'test', '**', 'fixture.json'), 21 | path.join(__dirname, '..', '**', '*.js'), 22 | ], 23 | browserify: { 24 | plugin: [ 25 | b => b.add(toStream(preface)), 26 | b => b.add(path.join(__dirname, 'browser-fixture-renderer.js')) 27 | ] 28 | }, 29 | forceDefaultIndex: true, 30 | defaultIndex: function () { 31 | return defaultIndex({ 32 | entry: 'dummy.js' 33 | }).pipe(hyperstream({ 34 | head: { 35 | _appendHtml: '' 36 | } 37 | })); 38 | }, 39 | middleware: [ 40 | function (req, res, next) { 41 | // Read every time rather than static file serving to ensure it's not cached 42 | if (/^\/test\/fixtures\/.*\/fixture\.json$/.test(req.url)) { 43 | fs.createReadStream(path.join('..', req.url)).pipe(res); 44 | } else { 45 | next() 46 | } 47 | } 48 | ] 49 | }).on('connect', function (ev) { 50 | console.log('Server running on %s', ev.uri); 51 | console.log('LiveReload running on port %s', ev.livePort); 52 | }).on('update', function (buffer) { 53 | console.log('bundle - %d bytes', buffer.length); 54 | }); 55 | -------------------------------------------------------------------------------- /src/constants/attr-usage.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NONE: 0, 3 | REGULAR: 1, 4 | EXTENDED: 2, 5 | PER_INSTANCE: 4 6 | }; 7 | -------------------------------------------------------------------------------- /src/constants/dtypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "int8": 5120, 3 | "uint8": 5121, 4 | "int16": 5122, 5 | "uint16": 5123, 6 | "int32": 5124, 7 | "uint32": 5125, 8 | "float": 5126, 9 | "float32": 5126 10 | } 11 | -------------------------------------------------------------------------------- /src/constants/dtypesizes.js: -------------------------------------------------------------------------------- 1 | const DTYPES_SIZES = []; 2 | DTYPES_SIZES[5120] = 1; // int8 3 | DTYPES_SIZES[5122] = 2; // int16 4 | DTYPES_SIZES[5124] = 4; // int32 5 | DTYPES_SIZES[5121] = 1; // uint8 6 | DTYPES_SIZES[5123] = 2; // uint16 7 | DTYPES_SIZES[5125] = 4; // uint32 8 | DTYPES_SIZES[5126] = 4; // float32 9 | 10 | module.exports = DTYPES_SIZES; 11 | -------------------------------------------------------------------------------- /src/constants/glsltypes.js: -------------------------------------------------------------------------------- 1 | const GLSL_TYPES = []; 2 | GLSL_TYPES[1] = 'float'; 3 | GLSL_TYPES[2] = 'vec2'; 4 | GLSL_TYPES[3] = 'vec3'; 5 | GLSL_TYPES[4] = 'vec4'; 6 | 7 | module.exports = GLSL_TYPES; 8 | -------------------------------------------------------------------------------- /src/constants/orientation.json: -------------------------------------------------------------------------------- 1 | { 2 | "CAP_START": 0, 3 | "CAP_END": 1, 4 | "CAP_SHORT": 2 5 | } 6 | -------------------------------------------------------------------------------- /src/create-attr-spec.js: -------------------------------------------------------------------------------- 1 | module.exports = createAttrSpecs; 2 | 3 | const ATTR_USAGE = require('./constants/attr-usage.js'); 4 | const DTYPES = require('./constants/dtypes.json'); 5 | const GLSL_TYPES = require('./constants/glsltypes.js'); 6 | const ORIENTATION = require('./constants/orientation.json'); 7 | const DTYPE_BY_CODE = new Map(Object.entries(DTYPES).map(a => a.reverse())); 8 | 9 | // This function returns regl props, used for constructing the attribute layout regl accessors 10 | // and corresponding GLSL up front. 11 | function createAttrSpecs (meta, regl, isEndpoints) { 12 | const suffixes = isEndpoints ? ['B', 'C', 'D'] : ['A', 'B', 'C', 'D']; 13 | const attrLines = []; 14 | const attrSpecList = []; 15 | 16 | meta.attrs.forEach((attr, attrName) => { 17 | const usage = isEndpoints ? attr.endpointUsage : attr.vertexUsage; 18 | if (!usage) return; 19 | 20 | const attrList = []; 21 | function emitAttr (index, suffix) { 22 | const attrOutName = attrName + suffix; 23 | attrList.push(attrOutName); 24 | 25 | if (isEndpoints) { 26 | const instanceStride = usage & ATTR_USAGE.PER_INSTANCE ? 1 : 3; 27 | attrSpecList.push({ 28 | name: attrOutName, 29 | spec: { 30 | buffer: (ctx, props) => props.buffers[attrName].buffer, 31 | offset: attr.isInstanceAttr 32 | ? (ctx, props) => props.buffers[attrName].offset + props.buffers[attrName].stride * index 33 | : (ctx, props) => props.buffers[attrName].offset + props.buffers[attrName].stride * (((props.orientation === ORIENTATION.CAP_START || !props.splitCaps) ? 0 : 3) + index), 34 | stride: (ctx, props) => props.buffers[attrName].stride * instanceStride * (props.splitCaps ? 2 : 1), 35 | divisor: (ctx, props) => (attr.isInstanceAttr ? 1 : props.instances) * props.buffers[attrName].divisor, 36 | normalized: (ctx, props) => props.buffers[attrName].normalized === undefined ? false : props.buffers[attrName].normalized, 37 | type: (ctx, props) => { 38 | const attr = props.buffers[attrName]; 39 | return DTYPE_BY_CODE.get(attr.type === undefined ? attr.buffer._buffer.dtype : attr.type); 40 | }, 41 | } 42 | }); 43 | } else { 44 | attrSpecList.push({ 45 | name: attrOutName, 46 | spec: { 47 | buffer: (ctx, props) => props.buffers[attrName].buffer, 48 | offset: (ctx, props) => props.buffers[attrName].offset + props.buffers[attrName].stride * index, 49 | stride: (ctx, props) => props.buffers[attrName].stride, 50 | divisor: (ctx, props) => (attr.isInstanceAttr ? 1 : props.instances) * props.buffers[attrName].divisor, 51 | normalized: (ctx, props) => props.buffers[attrName].normalized === undefined ? false : props.buffers[attrName].normalized, 52 | type: (ctx, props) => { 53 | const attr = props.buffers[attrName]; 54 | return DTYPE_BY_CODE.get(attr.type === undefined ? attr.buffer._buffer.dtype : attr.type); 55 | }, 56 | } 57 | }); 58 | } 59 | } 60 | 61 | if (usage & ATTR_USAGE.PER_INSTANCE) { 62 | emitAttr(0, ''); 63 | } 64 | if (usage & ATTR_USAGE.REGULAR || usage & ATTR_USAGE.EXTENDED) { 65 | for (let i = 0; i < suffixes.length; i++) { 66 | const suffix = suffixes[i]; 67 | if (!(usage & ATTR_USAGE.EXTENDED) && (suffix === 'D' || suffix === 'A')) continue; 68 | emitAttr(i, suffix); 69 | } 70 | } 71 | attrLines.push(`attribute ${GLSL_TYPES[attr.dimension]} ${attrList.join(', ')};`); 72 | }); 73 | 74 | meta.varyings.forEach((varying, varyingName) => { 75 | attrLines.push(`varying ${varying.returnType} ${varyingName};`); 76 | }); 77 | 78 | return { 79 | glsl: attrLines.join('\n'), 80 | attrs: attrSpecList 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /src/parse-pragmas.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ATTR_USAGE = require('./constants/attr-usage.js'); 4 | 5 | module.exports = parseShaderPragmas; 6 | 7 | const PRAGMA_REGEX = /^\s*#pragma\s+lines\s*:\s*([^;]*);?$/i; 8 | const ATTRIBUTE_REGEX = /^\s*(?:(instance)?)\s*attribute\s+(float|vec2|vec3|vec4)\s+([\w\d_]+)\s*$/i; 9 | const PROPERTY_REGEX = /^\s*(position|width|orientation)\s+=\s+([\w\d_]+)\s*\(([^)]*)\)\s*$/i; 10 | const VARYING_REGEX = /^\s*(?:(extrapolate)?)\s*varying\s+(float|vec2|vec3|vec4)\s+([\w\d_]+)\s*=\s*([\w\d_]+)\(([^)]*)\)\s*$/; 11 | const POSTPROJECT_REGEX = /^\s*postproject\s+=\s+([\w\d_]+)\s*$/i; 12 | 13 | const DIMENSION_GLSL_TYPES = { 14 | "float": 1, 15 | "vec2": 2, 16 | "vec3": 3, 17 | "vec4": 4 18 | }; 19 | 20 | function parseShaderPragmas (glsl) { 21 | const pragmas = []; 22 | const lines = glsl.split('\n'); 23 | for (let i = 0; i < lines.length; i++) { 24 | lines[i] = lines[i].replace(PRAGMA_REGEX, function (match, pragma) { 25 | pragmas.push(parsePragma(pragma)); 26 | return ''; 27 | }); 28 | } 29 | return { 30 | glsl: lines.join('\n').trim(), 31 | ...analyzePragmas(pragmas) 32 | }; 33 | } 34 | 35 | function parsePragma (pragma) { 36 | pragma = pragma.trim(); 37 | let match; 38 | if ((match = pragma.match(ATTRIBUTE_REGEX))) { 39 | const isInstanceAttr = !!match[1]; 40 | const dimension = DIMENSION_GLSL_TYPES[match[2]]; 41 | const name = match[3]; 42 | return {type: 'attribute', dimension, name, isInstanceAttr}; 43 | } else if ((match = pragma.match(PROPERTY_REGEX))) { 44 | const property = match[1]; 45 | const returnType = {width: 'float', position: 'vec4', orientation: 'bool'}[property]; 46 | const name = match[2]; 47 | const inputs = match[3].split(',').map(str => str.trim()).filter(x => !!x); 48 | const generate = (meta, label, prefix) => { 49 | return `${name}(${inputs.map(input => { 50 | const attrMeta = meta.attrs.get(input); 51 | if (attrMeta.isInstanceAttr) return input; 52 | return (prefix || '') + input + label; 53 | }).join(', ')})`; 54 | }; 55 | return {type: 'property', property, returnType, name, inputs, generate}; 56 | } else if ((match = pragma.match(VARYING_REGEX))) { 57 | const extrapolate = match[1] === 'extrapolate'; 58 | const returnType = match[2]; 59 | const name = match[3]; 60 | const getter = match[4]; 61 | const inputs = match[5].split(',').map(str => str.trim()).filter(x => !!x); 62 | const generate = (meta, interp, a, b) => { 63 | const clamped = extrapolate ? interp : `clamp(${interp},0.0,1.0)`; 64 | return `${name} = ${getter}(${inputs.map(input => `mix(${input + a}, ${input + b}, ${clamped})`).join(', ')});`; 65 | }; 66 | return {type: 'varying', returnType, name, getter, inputs, generate}; 67 | } else if ((match = pragma.match(POSTPROJECT_REGEX))) { 68 | const name = match[1]; 69 | return {type: 'postproject', name}; 70 | } else { 71 | throw new Error(`Unrecognized lines pragma: "${pragma}"`); 72 | } 73 | } 74 | 75 | function analyzePragmas (pragmas) { 76 | let postproject; 77 | const attrs = new Map(); 78 | const varyings = new Map(); 79 | for (const pragma of pragmas) { 80 | if (pragma.type === 'attribute') { 81 | attrs.set(pragma.name, pragma); 82 | pragma.vertexUsage = ATTR_USAGE.NONE; 83 | pragma.endpointUsage = ATTR_USAGE.NONE; 84 | } else if (pragma.type === 'varying') { 85 | varyings.set(pragma.name, pragma); 86 | } else if (pragma.type === 'postproject') { 87 | postproject = pragma.name; 88 | 89 | } 90 | } 91 | 92 | let width, position, orientation; 93 | for (const pragma of pragmas) { 94 | if (pragma.type !== 'property') continue; 95 | 96 | switch(pragma.property) { 97 | case 'width': 98 | if (width) throw new Error(`Unexpected duplicate pragma for property "${pragma.property}"`); 99 | width = pragma; 100 | break; 101 | case 'position': 102 | if (position) throw new Error(`Unexpected duplicate pragma for property "${pragma.property}"`); 103 | position = pragma; 104 | break; 105 | case 'orientation': 106 | if (orientation) throw new Error(`Unexpected duplicate pragma for property "${pragma.property}"`); 107 | orientation = pragma; 108 | break; 109 | default: 110 | throw new Error(`Invalid pragma property "${pragma.property}"`); 111 | } 112 | for (const input of pragma.inputs) { 113 | if (!attrs.has(input)) throw new Error(`Missing attribute ${input} of property ${pragma.property}`); 114 | } 115 | } 116 | for (const pragma of pragmas) { 117 | if (!pragma.inputs) continue; 118 | for (const input of pragma.inputs) { 119 | const inputAttr = attrs.get(input); 120 | if (inputAttr.isInstanceAttr) { 121 | inputAttr.vertexUsage = ATTR_USAGE.PER_INSTANCE; 122 | inputAttr.endpointUsage = ATTR_USAGE.PER_INSTANCE; 123 | } else { 124 | if (pragma.type === 'property' || pragma.type === 'varying') { 125 | if (pragma.property === 'position') { 126 | inputAttr.vertexUsage |= ATTR_USAGE.EXTENDED; 127 | inputAttr.endpointUsage |= ATTR_USAGE.EXTENDED; 128 | } else if (pragma.property === 'orientation') { 129 | inputAttr.endpointUsage |= ATTR_USAGE.PER_INSTANCE; 130 | } else { 131 | inputAttr.endpointUsage|= ATTR_USAGE.REGULAR; 132 | inputAttr.vertexUsage|= ATTR_USAGE.REGULAR; 133 | } 134 | } 135 | } 136 | } 137 | } 138 | 139 | return {varyings, attrs, width, position, orientation, postproject}; 140 | } 141 | 142 | -------------------------------------------------------------------------------- /src/sanitize-buffer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = sanitizeBufferInput; 4 | 5 | const DTYPE_SIZES = require('./constants/dtypesizes.js'); 6 | const DTYPES = require('./constants/dtypes.json'); 7 | 8 | function has(obj, prop) { 9 | return Object.prototype.hasOwnProperty.call(obj, prop); 10 | } 11 | 12 | // This function is run on every draw call in order to sanitize and configure the data layout 13 | function sanitizeBufferInput (metadata, buffersObj, isEndpoints) { 14 | // console.log('metadata:', metadata); 15 | // console.log('buffersObj:', buffersObj); 16 | // console.log('isEndpoints:', isEndpoints); 17 | const outputs = {}; 18 | 19 | if (!buffersObj) return outputs; 20 | 21 | for (let [attrName, attrMeta] of metadata.attrs) { 22 | const input = buffersObj[attrName]; 23 | const usage = isEndpoints ? attrMeta.endpointUsage : attrMeta.vertexUsage; 24 | 25 | if (!usage) continue; 26 | 27 | const output = { 28 | buffer: null, 29 | dimension: attrMeta.dimension, 30 | offset: 0, 31 | type: NaN, 32 | stride: NaN, 33 | divisor: 1, 34 | normalized: false, 35 | bytesPerElement: NaN 36 | }; 37 | 38 | if (!input) { 39 | throw new Error(`Missing buffer for ${isEndpoints ? 'endpoint' : 'vertex'} attribute '${attrName}'`); 40 | } else if (input._reglType === 'buffer') { 41 | output.buffer = input; 42 | output.type = output.buffer._buffer.dtype; 43 | } else if (input.buffer && input.buffer._reglType === 'buffer') { 44 | output.buffer = input.buffer; 45 | 46 | if (has(input, 'dimension') && input.dimension !== output.dimension) { 47 | throw new Error(`Size of attribute (${input.dimension}) does not match dimension specified in shader pragma (${attrMeta.dimension})`); 48 | } 49 | if (has(input, 'offset')) output.offset = input.offset; 50 | if (has(input, 'type')) { 51 | output.type = DTYPES[input.type]; 52 | } else { 53 | output.type = output.buffer._buffer.dtype; 54 | } 55 | if (has(input, 'divisor')) { 56 | output.divisor = input.divisor; 57 | } 58 | if (has(input, 'normalized')) { 59 | output.normalized = !!input.normalized; 60 | } 61 | if (has(input, 'stride')) output.stride = input.stride; 62 | } else { 63 | throw new Error(`Invalid buffer for attribute '${attrName}'. Be sure to wrap in regl.buffer().`); 64 | } 65 | 66 | output.bytesPerElement = DTYPE_SIZES[output.type]; 67 | 68 | if (Number.isNaN(output.stride)) { 69 | output.stride = output.bytesPerElement * attrMeta.dimension; 70 | } 71 | 72 | outputs[attrName] = output; 73 | } 74 | 75 | return outputs; 76 | } 77 | -------------------------------------------------------------------------------- /src/sanitize-in-list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function createSanitizer(label, list, dflt) { 4 | return function sanitizeValue (value) { 5 | if (!value) return dflt; 6 | if (list.indexOf(value) === -1) { 7 | throw new Error( 8 | `Invalid ${label} type. Valid options are: ${list.join(', ')}.` 9 | ); 10 | } 11 | return value; 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/miter/basic/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/basic/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/basic/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "threshold": 0.1, 5 | "command": { 6 | "vert": [ 7 | "precision highp float;", 8 | "#pragma lines: attribute vec2 xy", 9 | "#pragma lines: position = getPosition(xy)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }" 13 | ], 14 | "frag": [ 15 | "precision lowp float;", 16 | "void main () {", 17 | " gl_FragColor = vec4(0,0,0,0.5);", 18 | "}" 19 | ], 20 | "blend": { 21 | "enable": true, 22 | "func": { 23 | "srcRGB": "src alpha", 24 | "srcAlpha": 1, 25 | "dstRGB": "one minus src alpha", 26 | "dstAlpha": 1 27 | } 28 | }, 29 | "depth": { 30 | "enable": false 31 | } 32 | }, 33 | "vertexAttributes": { 34 | "xy": [ 35 | [-0.8, 0.0], 36 | [-0.5, 0.0], 37 | [0.3, 0.7], 38 | [0.8, 0.7], 39 | [-0.8, -0.7], 40 | [-0.3, -0.7], 41 | [0.5, 0.0], 42 | [0.8, 0.0] 43 | ] 44 | }, 45 | "endpointAttributes": { 46 | "xy": [ 47 | [ 48 | [-0.8, 0.0], 49 | [-0.5, 0.0], 50 | [0.3, 0.7] 51 | ], [ 52 | [0.8, 0.0], 53 | [0.5, 0.0], 54 | [-0.3, -0.7] 55 | ] 56 | ] 57 | }, 58 | "data": { 59 | "join": "miter", 60 | "cap": "square" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/fixtures/miter/dash/extrapolate/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/dash/extrapolate/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/dash/extrapolate/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float t", 9 | "#pragma lines: position = getPosition(xy)", 10 | "#pragma lines: width = getWidth()", 11 | "#pragma lines: extrapolate varying float t = getT(t)", 12 | "uniform float width;", 13 | "float getWidth() { return width; }", 14 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }", 15 | "float getT(float t) { return t; }" 16 | ], 17 | "frag": [ 18 | "precision highp float;", 19 | "varying float t;", 20 | "uniform float width;", 21 | "void main () {", 22 | " gl_FragColor = vec4(vec3(fract(t) > 0.5 ? 1.0 : 0.0), 0.5);", 23 | "}" 24 | ], 25 | "uniforms": { 26 | "width": 15 27 | }, 28 | "blend": { 29 | "enable": true, 30 | "func": { 31 | "srcRGB": "src alpha", 32 | "srcAlpha": 1, 33 | "dstRGB": "one minus src alpha", 34 | "dstAlpha": 1 35 | } 36 | }, 37 | "depth": { 38 | "enable": false 39 | } 40 | }, 41 | "vertexAttributes": { 42 | "xy": [ 43 | [-0.8, 0.0], 44 | [-0.5, 0.0], 45 | [0.3, 0.7], 46 | [0.8, 0.7], 47 | [-0.8, -0.7], 48 | [-0.3, -0.7], 49 | [0.5, 0.0], 50 | [0.8, 0.0] 51 | ], 52 | "t": [0, 4, 16, 22, 46, 52, 64, 68] 53 | }, 54 | "endpointAttributes": { 55 | "xy": [ 56 | [ 57 | [-0.8, 0.0], 58 | [-0.5, 0.0], 59 | [0.3, 0.7] 60 | ], [ 61 | [0.8, 0.0], 62 | [0.5, 0.0], 63 | [-0.3, -0.7] 64 | ] 65 | ], 66 | "t": [ 67 | [ 0, 4, 16 ], 68 | [ 68, 34, 52 ] 69 | ] 70 | }, 71 | "data": { 72 | "join": "miter", 73 | "cap": "square" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/fixtures/miter/dash/no-extrapolate/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/dash/no-extrapolate/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/dash/no-extrapolate/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float t", 9 | "#pragma lines: position = getPosition(xy)", 10 | "#pragma lines: width = getWidth()", 11 | "#pragma lines: varying float t = getT(t)", 12 | "uniform float width;", 13 | "float getWidth() { return width; }", 14 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }", 15 | "float getT(float t) { return t; }" 16 | ], 17 | "frag": [ 18 | "precision highp float;", 19 | "varying float t;", 20 | "uniform float width;", 21 | "void main () {", 22 | " gl_FragColor = vec4(vec3(fract(t) > 0.5 ? 1.0 : 0.0), 0.5);", 23 | "}" 24 | ], 25 | "uniforms": { 26 | "width": 15 27 | }, 28 | "blend": { 29 | "enable": true, 30 | "func": { 31 | "srcRGB": "src alpha", 32 | "srcAlpha": 1, 33 | "dstRGB": "one minus src alpha", 34 | "dstAlpha": 1 35 | } 36 | }, 37 | "depth": { 38 | "enable": false 39 | } 40 | }, 41 | "vertexAttributes": { 42 | "xy": [ 43 | [-0.8, 0.0], 44 | [-0.5, 0.0], 45 | [0.3, 0.7], 46 | [0.8, 0.7], 47 | [-0.8, -0.7], 48 | [-0.3, -0.7], 49 | [0.5, 0.0], 50 | [0.8, 0.0] 51 | ], 52 | "t": [0, 4, 16, 22, 46, 52, 64, 68] 53 | }, 54 | "endpointAttributes": { 55 | "xy": [ 56 | [ 57 | [-0.8, 0.0], 58 | [-0.5, 0.0], 59 | [0.3, 0.7] 60 | ], [ 61 | [0.8, 0.0], 62 | [0.5, 0.0], 63 | [-0.3, -0.7] 64 | ] 65 | ], 66 | "t": [ 67 | [ 0, 4, 16 ], 68 | [ 68, 34, 52 ] 69 | ] 70 | }, 71 | "data": { 72 | "join": "miter", 73 | "cap": "square" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/fixtures/miter/degenerate/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/degenerate/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/degenerate/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 128, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "float getWidth() { return 20.0; }", 11 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }" 12 | ], 13 | "frag": [ 14 | "precision lowp float;", 15 | "void main () {", 16 | " gl_FragColor = vec4(0,0,0,0.2);", 17 | "}" 18 | ], 19 | "blend": { 20 | "enable": true, 21 | "func": { 22 | "srcRGB": "src alpha", 23 | "srcAlpha": 1, 24 | "dstRGB": "one minus src alpha", 25 | "dstAlpha": 1 26 | } 27 | }, 28 | "depth": { 29 | "enable": false 30 | } 31 | }, 32 | "vertexAttributes": { 33 | "xy": [ 34 | [0, 0], 35 | [0.8, 0], 36 | [0,0], 37 | [0,-0.8], 38 | [0,0], 39 | [-0.8,0], 40 | [0,0], 41 | [0,0.8], 42 | [0,0], 43 | [0.6, 0.6], 44 | [0, 0], 45 | [-0.6, 0.6], 46 | [0, 0], 47 | [-0.6, -0.6], 48 | [0, 0], 49 | [0.6, -0.6], 50 | [0,0], 51 | [-0.1, 0.1] 52 | ] 53 | }, 54 | "endpointAttributes": { 55 | "xy": [ 56 | [ 57 | [0, 0], 58 | [0.8, 0], 59 | [0,0] 60 | ], [ 61 | [-0.1, 0.1], 62 | [0,0], 63 | [0.6, -0.6] 64 | ] 65 | ] 66 | }, 67 | "data": { 68 | "join": "miter", 69 | "cap": "square", 70 | "capResolution": 4, 71 | "joinResolution": 4 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/fixtures/miter/depth/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/depth/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/depth/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 128, 3 | "height": 64, 4 | "threshold": 0.1, 5 | "command": { 6 | "vert": [ 7 | "precision highp float;", 8 | "#pragma lines: attribute vec3 xyz", 9 | "#pragma lines: attribute vec3 color", 10 | "#pragma lines: position = getPosition(xyz)", 11 | "#pragma lines: varying vec3 color = getColor(color)", 12 | "#pragma lines: width = getWidth()", 13 | "float getWidth() { return 10.0; }", 14 | "vec3 getColor(vec3 color) { return color; }", 15 | "vec4 getPosition(vec3 xyz) { return vec4(xyz, 1); }" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "varying vec3 color;", 20 | "void main () {", 21 | " gl_FragColor = vec4(color,1);", 22 | "}" 23 | ], 24 | "depth": { 25 | "enable": true 26 | } 27 | }, 28 | "vertexAttributes": { 29 | "xyz": [ 30 | [-0.6, -0.6, 0.7], 31 | [0.0, 0.6, 0.7], 32 | [0.6, -0.6, 0.7], 33 | [0.6, 0.6, 0.7], 34 | [0.0, -0.6, 0.7], 35 | [-0.6, 0.6, -0.7] 36 | ], 37 | "color": [ 38 | [0.8,0.2,0.2], 39 | [0.8,0.2,0.2], 40 | [0.8,0.2,0.2], 41 | [0.2,0.2,0.8], 42 | [0.2,0.2,0.8], 43 | [0.2,0.2,0.8] 44 | ] 45 | }, 46 | "endpointAttributes": { 47 | "xyz": [ 48 | [ 49 | [-0.6, -0.6, 0.7], 50 | [0.0, 0.6, 0.7], 51 | [0.6, -0.6, 0.7] 52 | ], [ 53 | [-0.6, 0.6, -0.7], 54 | [0.0, -0.6, 0.7], 55 | [0.6, 0.6, 0.7] 56 | ] 57 | ], 58 | "color": [ 59 | [ 60 | [0.8,0.2,0.2], 61 | [0.8,0.2,0.2], 62 | [0.8,0.2,0.2] 63 | ], [ 64 | [0.2,0.2,0.8], 65 | [0.2,0.2,0.8], 66 | [0.2,0.2,0.8] 67 | ] 68 | ] 69 | }, 70 | "data": { 71 | "join": "miter", 72 | "cap": "square" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/nan/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/insert-caps/nan/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/nan/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "float getWidth() { return 20.0; }", 11 | "vec4 getPosition(vec2 xy) {", 12 | " return vec4(xy, 0, 1);", 13 | "}" 14 | ], 15 | "frag": [ 16 | "precision lowp float;", 17 | "void main () {", 18 | " gl_FragColor = vec4(0,0,0,0.5);", 19 | "}" 20 | ], 21 | "blend": { 22 | "enable": true, 23 | "func": { 24 | "srcRGB": "src alpha", 25 | "srcAlpha": 1, 26 | "dstRGB": "one minus src alpha", 27 | "dstAlpha": 1 28 | } 29 | }, 30 | "depth": { 31 | "enable": false 32 | } 33 | }, 34 | "vertexAttributes": { 35 | "xy": [ 36 | [null, null], 37 | 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [null, null], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [null, null], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5], 56 | 57 | [null, null] 58 | ] 59 | }, 60 | "data": { 61 | "insertCaps": true, 62 | "join": "miter", 63 | "cap": "round" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/none/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/insert-caps/none/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/none/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [0,0], 39 | 40 | [-0.8, 0.5], 41 | [-0.3, 0.7], 42 | [0.3, 0.5], 43 | [0.8, 0.7], 44 | 45 | [0,0], 46 | 47 | [-0.8, -0.1], 48 | [-0.3, 0.1], 49 | [0.3, -0.1], 50 | [0.8, 0.1], 51 | 52 | [0,0], 53 | 54 | [-0.8, -0.7], 55 | [-0.3, -0.5], 56 | [0.3, -0.7], 57 | [0.8, -0.5], 58 | 59 | [0,0] 60 | ], 61 | "break": [ 62 | 1, 63 | 0,0,0,0, 64 | 1, 65 | 0,0,0,0, 66 | 1, 67 | 0,0,0,0, 68 | 1 69 | ] 70 | }, 71 | "data": { 72 | "insertCaps": true, 73 | "join": "miter", 74 | "cap": "none" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/round/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/insert-caps/round/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/round/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [0,0], 39 | 40 | [-0.8, 0.5], 41 | [-0.3, 0.7], 42 | [0.3, 0.5], 43 | [0.8, 0.7], 44 | 45 | [0,0], 46 | 47 | [-0.8, -0.1], 48 | [-0.3, 0.1], 49 | [0.3, -0.1], 50 | [0.8, 0.1], 51 | 52 | [0,0], 53 | 54 | [-0.8, -0.7], 55 | [-0.3, -0.5], 56 | [0.3, -0.7], 57 | [0.8, -0.5], 58 | 59 | [0,0] 60 | ], 61 | "break": [ 62 | 1, 63 | 0,0,0,0, 64 | 1, 65 | 0,0,0,0, 66 | 1, 67 | 0,0,0,0, 68 | 1 69 | ] 70 | }, 71 | "data": { 72 | "insertCaps": true, 73 | "join": "miter", 74 | "cap": "round" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/square/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/insert-caps/square/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/insert-caps/square/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [0,0], 39 | 40 | [-0.8, 0.5], 41 | [-0.3, 0.7], 42 | [0.3, 0.5], 43 | [0.8, 0.7], 44 | 45 | [0,0], 46 | 47 | [-0.8, -0.1], 48 | [-0.3, 0.1], 49 | [0.3, -0.1], 50 | [0.8, 0.1], 51 | 52 | [0,0], 53 | 54 | [-0.8, -0.7], 55 | [-0.3, -0.5], 56 | [0.3, -0.7], 57 | [0.8, -0.5], 58 | 59 | [0,0] 60 | ], 61 | "break": [ 62 | 1, 63 | 0,0,0,0, 64 | 1, 65 | 0,0,0,0, 66 | 1, 67 | 0,0,0,0, 68 | 1 69 | ] 70 | }, 71 | "data": { 72 | "insertCaps": true, 73 | "join": "miter", 74 | "cap": "square" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/fixtures/miter/manual-caps/none/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/manual-caps/none/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/manual-caps/none/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [0,0], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [0,0], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5] 56 | ], 57 | "break": [ 58 | 0,0,0,0, 59 | 1, 60 | 0,0,0,0, 61 | 1, 62 | 0,0,0,0 63 | ] 64 | }, 65 | "endpointAttributes": { 66 | "xy": [ 67 | [ 68 | [-0.8, 0.5], 69 | [-0.3, 0.7], 70 | [0.3, 0.5] 71 | ], [ 72 | [0.8, 0.7], 73 | [0.3, 0.5], 74 | [-0.3, 0.7] 75 | ], [ 76 | [-0.8, -0.1], 77 | [-0.3, 0.1], 78 | [0.3, -0.1] 79 | ], [ 80 | [0.8, 0.1], 81 | [0.3, -0.1], 82 | [-0.3, 0.1] 83 | ], [ 84 | [-0.8, -0.7], 85 | [-0.3, -0.5], 86 | [0.3, -0.7] 87 | ], [ 88 | [0.8, -0.5], 89 | [0.3, -0.7], 90 | [-0.3, -0.5] 91 | ] 92 | ], 93 | "break": [ 94 | [0, 0, 0], 95 | [0, 0, 1], 96 | [0, 0, 1], 97 | [0, 0, 1], 98 | [0, 0, 1], 99 | [0, 0, 0] 100 | ] 101 | }, 102 | "data": { 103 | "join": "round", 104 | "cap": "none" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/miter/manual-caps/round/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/manual-caps/round/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/manual-caps/round/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [0,0], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [0,0], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5] 56 | ], 57 | "break": [ 58 | 0,0,0,0, 59 | 1, 60 | 0,0,0,0, 61 | 1, 62 | 0,0,0,0 63 | ] 64 | }, 65 | "endpointAttributes": { 66 | "xy": [ 67 | [ 68 | [-0.8, 0.5], 69 | [-0.3, 0.7], 70 | [0.3, 0.5] 71 | ], [ 72 | [0.8, 0.7], 73 | [0.3, 0.5], 74 | [-0.3, 0.7] 75 | ], [ 76 | [-0.8, -0.1], 77 | [-0.3, 0.1], 78 | [0.3, -0.1] 79 | ], [ 80 | [0.8, 0.1], 81 | [0.3, -0.1], 82 | [-0.3, 0.1] 83 | ], [ 84 | [-0.8, -0.7], 85 | [-0.3, -0.5], 86 | [0.3, -0.7] 87 | ], [ 88 | [0.8, -0.5], 89 | [0.3, -0.7], 90 | [-0.3, -0.5] 91 | ] 92 | ], 93 | "break": [ 94 | [0, 0, 0], 95 | [0, 0, 1], 96 | [0, 0, 1], 97 | [0, 0, 1], 98 | [0, 0, 1], 99 | [0, 0, 0] 100 | ] 101 | }, 102 | "data": { 103 | "join": "round", 104 | "cap": "round" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/miter/manual-caps/square/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/manual-caps/square/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/manual-caps/square/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [0,0], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [0,0], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5] 56 | ], 57 | "break": [ 58 | 0,0,0,0, 59 | 1, 60 | 0,0,0,0, 61 | 1, 62 | 0,0,0,0 63 | ] 64 | }, 65 | "endpointAttributes": { 66 | "xy": [ 67 | [ 68 | [-0.8, 0.5], 69 | [-0.3, 0.7], 70 | [0.3, 0.5] 71 | ], [ 72 | [0.8, 0.7], 73 | [0.3, 0.5], 74 | [-0.3, 0.7] 75 | ], [ 76 | [-0.8, -0.1], 77 | [-0.3, 0.1], 78 | [0.3, -0.1] 79 | ], [ 80 | [0.8, 0.1], 81 | [0.3, -0.1], 82 | [-0.3, 0.1] 83 | ], [ 84 | [-0.8, -0.7], 85 | [-0.3, -0.5], 86 | [0.3, -0.7] 87 | ], [ 88 | [0.8, -0.5], 89 | [0.3, -0.7], 90 | [-0.3, -0.5] 91 | ] 92 | ], 93 | "break": [ 94 | [0, 0, 0], 95 | [0, 0, 1], 96 | [0, 0, 1], 97 | [0, 0, 1], 98 | [0, 0, 1], 99 | [0, 0, 0] 100 | ] 101 | }, 102 | "data": { 103 | "join": "round", 104 | "cap": "square" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/miter/sdf/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/miter/sdf/expected.png -------------------------------------------------------------------------------- /test/fixtures/miter/sdf/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "uniform float width;", 11 | "float getWidth() { return width; }", 12 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }" 13 | ], 14 | "frag": [ 15 | "precision highp float;", 16 | "varying vec3 lineCoord;", 17 | "uniform float width;", 18 | "float linearstep(float a, float b, float x) { return clamp((x - a) / (b - a), 0.0, 1.0); }", 19 | "void main () {", 20 | " float sdf = 0.5 * width * max(abs(lineCoord.x), abs(lineCoord.y));", 21 | " float aa = linearstep(width * 0.5, width * 0.5 - 1.0, sdf);", 22 | " float border = linearstep(width * 0.5 - 4.5, width * 0.5 - 3.5, sdf);", 23 | " float alpha = aa * mix(0.2, 1.0, border);", 24 | " gl_FragColor = vec4(vec3(0), alpha);", 25 | "}" 26 | ], 27 | "uniforms": { 28 | "width": 20 29 | }, 30 | "blend": { 31 | "enable": true, 32 | "func": { 33 | "srcRGB": "src alpha", 34 | "srcAlpha": 1, 35 | "dstRGB": "one minus src alpha", 36 | "dstAlpha": 1 37 | } 38 | }, 39 | "depth": { 40 | "enable": false 41 | } 42 | }, 43 | "vertexAttributes": { 44 | "xy": [ 45 | [-0.8, 0.0], 46 | [-0.5, 0.0], 47 | [0.3, 0.7], 48 | [0.8, 0.7], 49 | [-0.8, -0.7], 50 | [-0.3, -0.7], 51 | [0.5, 0.0], 52 | [0.8, 0.0] 53 | ] 54 | }, 55 | "endpointAttributes": { 56 | "xy": [ 57 | [ 58 | [-0.8, 0.0], 59 | [-0.5, 0.0], 60 | [0.3, 0.7] 61 | ], [ 62 | [0.8, 0.0], 63 | [0.5, 0.0], 64 | [-0.3, -0.7] 65 | ] 66 | ] 67 | }, 68 | "data": { 69 | "join": "miter", 70 | "cap": "square" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/fixtures/round/basic/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/basic/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/basic/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "float getWidth() { return 20.0; }", 11 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }" 12 | ], 13 | "frag": [ 14 | "precision lowp float;", 15 | "void main () {", 16 | " gl_FragColor = vec4(0,0,0,0.5);", 17 | "}" 18 | ], 19 | "blend": { 20 | "enable": true, 21 | "func": { 22 | "srcRGB": "src alpha", 23 | "srcAlpha": 1, 24 | "dstRGB": "one minus src alpha", 25 | "dstAlpha": 1 26 | } 27 | }, 28 | "depth": { 29 | "enable": false 30 | } 31 | }, 32 | "vertexAttributes": { 33 | "xy": [ 34 | [-0.8, 0.0], 35 | [-0.5, 0.0], 36 | [0.3, 0.7], 37 | [0.8, 0.7], 38 | [-0.8, -0.7], 39 | [-0.3, -0.7], 40 | [0.5, 0.0], 41 | [0.8, 0.0] 42 | ] 43 | }, 44 | "endpointAttributes": { 45 | "xy": [ 46 | [ 47 | [-0.8, 0.0], 48 | [-0.5, 0.0], 49 | [0.3, 0.7] 50 | ], [ 51 | [0.8, 0.0], 52 | [0.5, 0.0], 53 | [-0.3, -0.7] 54 | ] 55 | ] 56 | }, 57 | "data": { 58 | "join": "round", 59 | "cap": "round" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/fixtures/round/dash/extrapolate/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/dash/extrapolate/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/dash/extrapolate/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float t", 9 | "#pragma lines: position = getPosition(xy)", 10 | "#pragma lines: width = getWidth()", 11 | "#pragma lines: extrapolate varying float t = getT(t)", 12 | "uniform float width;", 13 | "float getWidth() { return width; }", 14 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }", 15 | "float getT(float t) { return t; }" 16 | ], 17 | "frag": [ 18 | "precision highp float;", 19 | "varying float t;", 20 | "uniform float width;", 21 | "void main () {", 22 | " gl_FragColor = vec4(vec3(fract(t) > 0.5 ? 1.0 : 0.0), 0.5);", 23 | "}" 24 | ], 25 | "uniforms": { 26 | "width": 15 27 | }, 28 | "blend": { 29 | "enable": true, 30 | "func": { 31 | "srcRGB": "src alpha", 32 | "srcAlpha": 1, 33 | "dstRGB": "one minus src alpha", 34 | "dstAlpha": 1 35 | } 36 | }, 37 | "depth": { 38 | "enable": false 39 | } 40 | }, 41 | "vertexAttributes": { 42 | "xy": [ 43 | [-0.8, 0.0], 44 | [-0.5, 0.0], 45 | [0.3, 0.7], 46 | [0.8, 0.7], 47 | [-0.8, -0.7], 48 | [-0.3, -0.7], 49 | [0.5, 0.0], 50 | [0.8, 0.0] 51 | ], 52 | "t": [0, 4, 16, 22, 46, 52, 64, 68] 53 | }, 54 | "endpointAttributes": { 55 | "xy": [ 56 | [ 57 | [-0.8, 0.0], 58 | [-0.5, 0.0], 59 | [0.3, 0.7] 60 | ], [ 61 | [0.8, 0.0], 62 | [0.5, 0.0], 63 | [-0.3, -0.7] 64 | ] 65 | ], 66 | "t": [ 67 | [ 0, 4, 16 ], 68 | [ 68, 34, 52 ] 69 | ] 70 | }, 71 | "data": { 72 | "join": "round", 73 | "cap": "round" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/fixtures/round/dash/no-extrapolate/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/dash/no-extrapolate/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/dash/no-extrapolate/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float t", 9 | "#pragma lines: position = getPosition(xy)", 10 | "#pragma lines: width = getWidth()", 11 | "#pragma lines: varying float t = getT(t)", 12 | "uniform float width;", 13 | "float getWidth() { return width; }", 14 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }", 15 | "float getT(float t) { return t; }" 16 | ], 17 | "frag": [ 18 | "precision highp float;", 19 | "varying float t;", 20 | "uniform float width;", 21 | "void main () {", 22 | " gl_FragColor = vec4(vec3(fract(t) > 0.5 ? 1.0 : 0.0), 0.5);", 23 | "}" 24 | ], 25 | "uniforms": { 26 | "width": 15 27 | }, 28 | "blend": { 29 | "enable": true, 30 | "func": { 31 | "srcRGB": "src alpha", 32 | "srcAlpha": 1, 33 | "dstRGB": "one minus src alpha", 34 | "dstAlpha": 1 35 | } 36 | }, 37 | "depth": { 38 | "enable": false 39 | } 40 | }, 41 | "vertexAttributes": { 42 | "xy": [ 43 | [-0.8, 0.0], 44 | [-0.5, 0.0], 45 | [0.3, 0.7], 46 | [0.8, 0.7], 47 | [-0.8, -0.7], 48 | [-0.3, -0.7], 49 | [0.5, 0.0], 50 | [0.8, 0.0] 51 | ], 52 | "t": [0, 4, 16, 22, 46, 52, 64, 68] 53 | }, 54 | "endpointAttributes": { 55 | "xy": [ 56 | [ 57 | [-0.8, 0.0], 58 | [-0.5, 0.0], 59 | [0.3, 0.7] 60 | ], [ 61 | [0.8, 0.0], 62 | [0.5, 0.0], 63 | [-0.3, -0.7] 64 | ] 65 | ], 66 | "t": [ 67 | [ 0, 4, 16 ], 68 | [ 68, 34, 52 ] 69 | ] 70 | }, 71 | "data": { 72 | "join": "round", 73 | "cap": "round" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/fixtures/round/degenerate/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/degenerate/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/degenerate/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 128, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "float getWidth() { return 20.0; }", 11 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }" 12 | ], 13 | "frag": [ 14 | "precision lowp float;", 15 | "void main () {", 16 | " gl_FragColor = vec4(0,0,0,0.2);", 17 | "}" 18 | ], 19 | "blend": { 20 | "enable": true, 21 | "func": { 22 | "srcRGB": "src alpha", 23 | "srcAlpha": 1, 24 | "dstRGB": "one minus src alpha", 25 | "dstAlpha": 1 26 | } 27 | }, 28 | "depth": { 29 | "enable": false 30 | } 31 | }, 32 | "vertexAttributes": { 33 | "xy": [ 34 | [0, 0], 35 | [0.8, 0], 36 | [0,0], 37 | [0,-0.8], 38 | [0,0], 39 | [-0.8,0], 40 | [0,0], 41 | [0,0.8], 42 | [0,0], 43 | [0.6, 0.6], 44 | [0, 0], 45 | [-0.6, 0.6], 46 | [0, 0], 47 | [-0.6, -0.6], 48 | [0, 0], 49 | [0.6, -0.6], 50 | [0,0], 51 | [-0.1, 0.1] 52 | ] 53 | }, 54 | "endpointAttributes": { 55 | "xy": [ 56 | [ 57 | [0, 0], 58 | [0.8, 0], 59 | [0,0] 60 | ], [ 61 | [-0.1, 0.1], 62 | [0,0], 63 | [0.6, -0.6] 64 | ] 65 | ] 66 | }, 67 | "data": { 68 | "join": "round", 69 | "cap": "round", 70 | "capResolution": 4, 71 | "joinResolution": 4 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/fixtures/round/depth/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/depth/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/depth/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 128, 3 | "height": 64, 4 | "threshold": 0.1, 5 | "command": { 6 | "vert": [ 7 | "precision highp float;", 8 | "#pragma lines: attribute vec3 xyz", 9 | "#pragma lines: attribute vec3 color", 10 | "#pragma lines: position = getPosition(xyz)", 11 | "#pragma lines: varying vec3 color = getColor(color)", 12 | "#pragma lines: width = getWidth()", 13 | "float getWidth() { return 10.0; }", 14 | "vec3 getColor(vec3 color) { return color; }", 15 | "vec4 getPosition(vec3 xyz) { return vec4(xyz, 1); }" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "varying vec3 color;", 20 | "void main () {", 21 | " gl_FragColor = vec4(color,1);", 22 | "}" 23 | ], 24 | "depth": { 25 | "enable": true 26 | } 27 | }, 28 | "vertexAttributes": { 29 | "xyz": [ 30 | [-0.6, -0.6, 0.7], 31 | [0.0, 0.6, 0.7], 32 | [0.6, -0.6, 0.7], 33 | [0.6, 0.6, 0.7], 34 | [0.0, -0.6, 0.7], 35 | [-0.6, 0.6, -0.7] 36 | ], 37 | "color": [ 38 | [0.8,0.2,0.2], 39 | [0.8,0.2,0.2], 40 | [0.8,0.2,0.2], 41 | [0.2,0.2,0.8], 42 | [0.2,0.2,0.8], 43 | [0.2,0.2,0.8] 44 | ] 45 | }, 46 | "endpointAttributes": { 47 | "xyz": [ 48 | [ 49 | [-0.6, -0.6, 0.7], 50 | [0.0, 0.6, 0.7], 51 | [0.6, -0.6, 0.7] 52 | ], [ 53 | [-0.6, 0.6, -0.7], 54 | [0.0, -0.6, 0.7], 55 | [0.6, 0.6, 0.7] 56 | ] 57 | ], 58 | "color": [ 59 | [ 60 | [0.8,0.2,0.2], 61 | [0.8,0.2,0.2], 62 | [0.8,0.2,0.2] 63 | ], [ 64 | [0.2,0.2,0.8], 65 | [0.2,0.2,0.8], 66 | [0.2,0.2,0.8] 67 | ] 68 | ] 69 | }, 70 | "data": { 71 | "join": "round", 72 | "cap": "round" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/fixtures/round/insert-caps/none/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/insert-caps/none/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/insert-caps/none/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [0,0], 39 | 40 | [-0.8, 0.5], 41 | [-0.3, 0.7], 42 | [0.3, 0.5], 43 | [0.8, 0.7], 44 | 45 | [0,0], 46 | 47 | [-0.8, -0.1], 48 | [-0.3, 0.1], 49 | [0.3, -0.1], 50 | [0.8, 0.1], 51 | 52 | [0,0], 53 | 54 | [-0.8, -0.7], 55 | [-0.3, -0.5], 56 | [0.3, -0.7], 57 | [0.8, -0.5], 58 | 59 | [0,0] 60 | ], 61 | "break": [ 62 | 1, 63 | 0,0,0,0, 64 | 1, 65 | 0,0,0,0, 66 | 1, 67 | 0,0,0,0, 68 | 1 69 | ] 70 | }, 71 | "data": { 72 | "insertCaps": true, 73 | "join": "round", 74 | "cap": "none" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/fixtures/round/insert-caps/round/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/insert-caps/round/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/insert-caps/round/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | }, 35 | "cull": { 36 | "enable": false 37 | } 38 | }, 39 | "vertexAttributes": { 40 | "xy": [ 41 | [0, 0], 42 | 43 | [-0.8, -0.7], 44 | [-0.3, -0.5], 45 | [0.3, -0.7], 46 | [0.8, -0.5], 47 | 48 | [0, 0], 49 | 50 | [-0.8, -0.1], 51 | [-0.3, 0.1], 52 | [0.3, -0.1], 53 | [0.8, 0.1], 54 | 55 | [0, 0], 56 | 57 | [-0.8, 0.5], 58 | [-0.3, 0.7], 59 | [0.3, 0.5], 60 | [0.8, 0.7], 61 | 62 | [0, 0] 63 | ], 64 | "break": [ 65 | 1, 66 | 0,0,0,0, 67 | 1, 68 | 0,0,0,0, 69 | 1, 70 | 0,0,0,0, 71 | 1 72 | ] 73 | }, 74 | "data": { 75 | "insertCaps": true, 76 | "join": "round", 77 | "cap": "round" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/fixtures/round/insert-caps/square/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/insert-caps/square/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/insert-caps/square/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [0,0], 39 | 40 | [-0.8, 0.5], 41 | [-0.3, 0.7], 42 | [0.3, 0.5], 43 | [0.8, 0.7], 44 | 45 | [0,0], 46 | 47 | [-0.8, -0.1], 48 | [-0.3, 0.1], 49 | [0.3, -0.1], 50 | [0.8, 0.1], 51 | 52 | [0,0], 53 | 54 | [-0.8, -0.7], 55 | [-0.3, -0.5], 56 | [0.3, -0.7], 57 | [0.8, -0.5], 58 | 59 | [0,0] 60 | ], 61 | "break": [ 62 | 1, 63 | 0,0,0,0, 64 | 1, 65 | 0,0,0,0, 66 | 1, 67 | 0,0,0,0, 68 | 1 69 | ] 70 | }, 71 | "data": { 72 | "insertCaps": true, 73 | "join": "round", 74 | "cap": "square" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/fixtures/round/manual-caps/none/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/manual-caps/none/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/manual-caps/none/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [0,0], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [0,0], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5] 56 | ], 57 | "break": [ 58 | 0,0,0,0, 59 | 1, 60 | 0,0,0,0, 61 | 1, 62 | 0,0,0,0 63 | ] 64 | }, 65 | "endpointAttributes": { 66 | "xy": [ 67 | [ 68 | [-0.8, 0.5], 69 | [-0.3, 0.7], 70 | [0.3, 0.5] 71 | ], [ 72 | [0.8, 0.7], 73 | [0.3, 0.5], 74 | [-0.3, 0.7] 75 | ], [ 76 | [-0.8, -0.1], 77 | [-0.3, 0.1], 78 | [0.3, -0.1] 79 | ], [ 80 | [0.8, 0.1], 81 | [0.3, -0.1], 82 | [-0.3, 0.1] 83 | ], [ 84 | [-0.8, -0.7], 85 | [-0.3, -0.5], 86 | [0.3, -0.7] 87 | ], [ 88 | [0.8, -0.5], 89 | [0.3, -0.7], 90 | [-0.3, -0.5] 91 | ] 92 | ], 93 | "break": [ 94 | [0, 0, 0], 95 | [0, 0, 1], 96 | [0, 0, 1], 97 | [0, 0, 1], 98 | [0, 0, 1], 99 | [0, 0, 0] 100 | ] 101 | }, 102 | "data": { 103 | "join": "round", 104 | "cap": "none" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/round/manual-caps/round/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/manual-caps/round/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/manual-caps/round/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [0,0], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [0,0], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5] 56 | ], 57 | "break": [ 58 | 0,0,0,0, 59 | 1, 60 | 0,0,0,0, 61 | 1, 62 | 0,0,0,0 63 | ] 64 | }, 65 | "endpointAttributes": { 66 | "xy": [ 67 | [ 68 | [-0.8, 0.5], 69 | [-0.3, 0.7], 70 | [0.3, 0.5] 71 | ], [ 72 | [0.8, 0.7], 73 | [0.3, 0.5], 74 | [-0.3, 0.7] 75 | ], [ 76 | [-0.8, -0.1], 77 | [-0.3, 0.1], 78 | [0.3, -0.1] 79 | ], [ 80 | [0.8, 0.1], 81 | [0.3, -0.1], 82 | [-0.3, 0.1] 83 | ], [ 84 | [-0.8, -0.7], 85 | [-0.3, -0.5], 86 | [0.3, -0.7] 87 | ], [ 88 | [0.8, -0.5], 89 | [0.3, -0.7], 90 | [-0.3, -0.5] 91 | ] 92 | ], 93 | "break": [ 94 | [0, 0, 0], 95 | [0, 0, 1], 96 | [0, 0, 1], 97 | [0, 0, 1], 98 | [0, 0, 1], 99 | [0, 0, 0] 100 | ] 101 | }, 102 | "data": { 103 | "join": "round", 104 | "cap": "round" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/round/manual-caps/square/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/manual-caps/square/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/manual-caps/square/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: attribute float break", 9 | "#pragma lines: position = getPosition(xy, break)", 10 | "#pragma lines: width = getWidth()", 11 | "float getWidth() { return 20.0; }", 12 | "vec4 getPosition(vec2 xy, float isBreak) {", 13 | " if (isBreak > 0.0) return vec4(0);", 14 | " return vec4(xy, 0, 1);", 15 | "}" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,0.5);", 21 | "}" 22 | ], 23 | "blend": { 24 | "enable": true, 25 | "func": { 26 | "srcRGB": "src alpha", 27 | "srcAlpha": 1, 28 | "dstRGB": "one minus src alpha", 29 | "dstAlpha": 1 30 | } 31 | }, 32 | "depth": { 33 | "enable": false 34 | } 35 | }, 36 | "vertexAttributes": { 37 | "xy": [ 38 | [-0.8, 0.5], 39 | [-0.3, 0.7], 40 | [0.3, 0.5], 41 | [0.8, 0.7], 42 | 43 | [0,0], 44 | 45 | [-0.8, -0.1], 46 | [-0.3, 0.1], 47 | [0.3, -0.1], 48 | [0.8, 0.1], 49 | 50 | [0,0], 51 | 52 | [-0.8, -0.7], 53 | [-0.3, -0.5], 54 | [0.3, -0.7], 55 | [0.8, -0.5] 56 | ], 57 | "break": [ 58 | 0,0,0,0, 59 | 1, 60 | 0,0,0,0, 61 | 1, 62 | 0,0,0,0 63 | ] 64 | }, 65 | "endpointAttributes": { 66 | "xy": [ 67 | [ 68 | [-0.8, 0.5], 69 | [-0.3, 0.7], 70 | [0.3, 0.5] 71 | ], [ 72 | [0.8, 0.7], 73 | [0.3, 0.5], 74 | [-0.3, 0.7] 75 | ], [ 76 | [-0.8, -0.1], 77 | [-0.3, 0.1], 78 | [0.3, -0.1] 79 | ], [ 80 | [0.8, 0.1], 81 | [0.3, -0.1], 82 | [-0.3, 0.1] 83 | ], [ 84 | [-0.8, -0.7], 85 | [-0.3, -0.5], 86 | [0.3, -0.7] 87 | ], [ 88 | [0.8, -0.5], 89 | [0.3, -0.7], 90 | [-0.3, -0.5] 91 | ] 92 | ], 93 | "break": [ 94 | [0, 0, 0], 95 | [0, 0, 1], 96 | [0, 0, 1], 97 | [0, 0, 1], 98 | [0, 0, 1], 99 | [0, 0, 0] 100 | ] 101 | }, 102 | "data": { 103 | "join": "round", 104 | "cap": "square" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/round/postproject/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/postproject/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/postproject/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "#pragma lines: postproject = postprojectPosition", 11 | "uniform float width;", 12 | "uniform mat4 projectionView;", 13 | "float getWidth() { return width; }", 14 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }", 15 | "vec4 postprojectPosition(vec4 position) { return projectionView * position; }" 16 | ], 17 | "frag": [ 18 | "precision lowp float;", 19 | "void main () {", 20 | " gl_FragColor = vec4(0,0,0,1);", 21 | "}" 22 | ], 23 | "depth": { "enable": true }, 24 | "uniforms": { 25 | "width": 0.10, 26 | "projectionView": [ 27 | -0.022149166092276573, -0.646040678024292, -0.9652843475341797, -0.9633557200431824, 28 | 1.2069035768508911, -0.011856176890432835, -0.017714954912662506, -0.01767956092953682, 29 | 0, 2.3261380195617676, -0.26817968487739563, -0.26764386892318726, 30 | 0, 0, 1.8518742322921753, 1.8681541681289673 31 | ] 32 | } 33 | }, 34 | "vertexAttributes": { 35 | "xy": [ 36 | [1, 0], 37 | [-0.22252093395631434, 0.9749279121818236], 38 | [-0.9009688679024191, -0.433883739117558], 39 | [0.6234898018587334, -0.7818314824680299], 40 | [0.6234898018587337, 0.7818314824680297], 41 | [-0.9009688679024189, 0.43388373911755845], 42 | [-0.2225209339563148, -0.9749279121818235], 43 | [1, 0], 44 | [-0.22252093395631387, 0.9749279121818237], 45 | [-0.9009688679024194, -0.43388373911755757] 46 | ] 47 | }, 48 | "data": { 49 | "join": "round", 50 | "cap": "round", 51 | "viewportSize": [1, 1] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/fixtures/round/sdf/expected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/regl-gpu-lines/ec1cfb11ff3330461e0083215dbb5cb139129b5a/test/fixtures/round/sdf/expected.png -------------------------------------------------------------------------------- /test/fixtures/round/sdf/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "width": 256, 3 | "height": 128, 4 | "command": { 5 | "vert": [ 6 | "precision highp float;", 7 | "#pragma lines: attribute vec2 xy", 8 | "#pragma lines: position = getPosition(xy)", 9 | "#pragma lines: width = getWidth()", 10 | "uniform float width;", 11 | "float getWidth() { return width; }", 12 | "vec4 getPosition(vec2 xy) { return vec4(xy, 0, 1); }" 13 | ], 14 | "frag": [ 15 | "precision highp float;", 16 | "varying vec3 lineCoord;", 17 | "uniform float width;", 18 | "float linearstep(float a, float b, float x) { return clamp((x - a) / (b - a), 0.0, 1.0); }", 19 | "void main () {", 20 | " float sdf = 0.5 * width * length(lineCoord.xy);", 21 | " float aa = linearstep(width * 0.5, width * 0.5 - 1.0, sdf);", 22 | " float border = linearstep(width * 0.5 - 4.5, width * 0.5 - 3.5, sdf);", 23 | " float alpha = aa * mix(0.2, 1.0, border);", 24 | " gl_FragColor = vec4(vec3(0), alpha);", 25 | "}" 26 | ], 27 | "uniforms": { 28 | "width": 20 29 | }, 30 | "blend": { 31 | "enable": true, 32 | "func": { 33 | "srcRGB": "src alpha", 34 | "srcAlpha": 1, 35 | "dstRGB": "one minus src alpha", 36 | "dstAlpha": 1 37 | } 38 | }, 39 | "depth": { 40 | "enable": false 41 | } 42 | }, 43 | "vertexAttributes": { 44 | "xy": [ 45 | [-0.8, 0.0], 46 | [-0.5, 0.0], 47 | [0.3, 0.7], 48 | [0.8, 0.7], 49 | [-0.8, -0.7], 50 | [-0.3, -0.7], 51 | [0.5, 0.0], 52 | [0.8, 0.0] 53 | ] 54 | }, 55 | "endpointAttributes": { 56 | "xy": [ 57 | [ 58 | [-0.8, 0.0], 59 | [-0.5, 0.0], 60 | [0.3, 0.7] 61 | ], [ 62 | [0.8, 0.0], 63 | [0.5, 0.0], 64 | [-0.3, -0.7] 65 | ] 66 | ] 67 | }, 68 | "data": { 69 | "join": "round", 70 | "cap": "round" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/render.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const test = require('tape'); 4 | const createContext = require('./util/create-context.js'); 5 | const createREGL = require('regl/dist/regl.js'); 6 | const createDrawLinesDev = require('../src/index.js'); 7 | const createDrawLinesProd = require('../dist/regl-gpu-lines.min.js'); 8 | const savePixels = require('save-pixels'); 9 | const getPixels = require('get-pixels'); 10 | const ndarray = require('ndarray'); 11 | const glob = require('glob'); 12 | const async = require('async'); 13 | const pixelmatch = require('pixelmatch'); 14 | const pool = require('ndarray-scratch'); 15 | 16 | const UPDATE = process.env['UPDATE'] === '1'; 17 | const CI = process.env['CI'] === '1'; 18 | const ENV = (process.env['ENV'] || '').toUpperCase() === 'PRODUCTION' ? 'production' : 'development' 19 | const filter = process.env['FILTER'] ? new RegExp(process.env['FILTER']) : null; 20 | const renderFixture = require('./util/render-fixture.js'); 21 | 22 | let createDrawLines; 23 | console.log('ENV:', ENV); 24 | if (ENV === 'production') { 25 | createDrawLines = createDrawLinesProd; 26 | console.error('Testing against production bundle dist/regl-gpu-lines.min.js'); 27 | } else { 28 | createDrawLines = createDrawLinesDev; 29 | console.error('Testing against source dir, src/'); 30 | } 31 | 32 | const fixtureDir = path.join(__dirname, "fixtures"); 33 | const fixturePaths = glob.sync(path.join(fixtureDir, "**/fixture.json")); 34 | const gl = createContext(256, 256); 35 | 36 | test('run image tests', function (t) { 37 | for (const fixturePath of fixturePaths) { 38 | const relPath = path.relative(fixtureDir, fixturePath) 39 | if (filter && !filter.test(relPath)) continue; 40 | t.test(path.dirname(relPath), function (t) { 41 | const fixture = JSON.parse(fs.readFileSync(fixturePath, 'utf8')); 42 | 43 | const {width, height} = fixture; 44 | if (!width || !height) throw new Error(`Invalid dimensions, ${width} x ${height}`); 45 | gl.resize(width, height); 46 | 47 | const regl = createREGL({gl, extensions: ['ANGLE_instanced_arrays']}); 48 | 49 | renderFixture(regl, createDrawLines, fixture); 50 | 51 | const outputName = UPDATE ? 'expected.png' : 'actual.png'; 52 | const outputPath = path.join(path.dirname(fixturePath), outputName); 53 | const actualPixels = ndarray(regl.read(), [height, width, 4]).transpose(1, 0); 54 | 55 | regl.destroy(); 56 | 57 | if (!CI) { 58 | savePixels(actualPixels.step(1, -1), 'png') 59 | .pipe(fs.createWriteStream(outputPath)); 60 | } 61 | 62 | if (!UPDATE) { 63 | const expectedName = path.join(path.dirname(fixturePath), 'expected.png'); 64 | 65 | getPixels(expectedName, function (err, unflippedExpectedPixels) { 66 | if (err) return t.fail(err); 67 | 68 | const expectedPixels = pool.clone(unflippedExpectedPixels.step(1, -1).transpose(1, 0)); 69 | 70 | const diffData = new Uint8Array(width * height * 4); 71 | const badPixelCount = pixelmatch(actualPixels.data, expectedPixels.data, diffData, width, height, { 72 | threshold: fixture.threshold || 0.1, 73 | includeAA: true 74 | }); 75 | 76 | if (!CI || badPixelCount) { 77 | const diffPath = path.join(path.dirname(fixturePath), 'diff.png'); 78 | const diffPixels = ndarray(diffData, [height, width, 4]); 79 | savePixels(diffPixels.transpose(1, 0).step(1, -1), 'png') 80 | .pipe(fs.createWriteStream(diffPath)); 81 | } 82 | 83 | const result = !badPixelCount; 84 | const msg = `zero unmatched pixels${badPixelCount ? ` (got ${badPixelCount} unmatched)` : ''}`; 85 | if (fixture.skip) { 86 | t.skip(result, msg); 87 | } else { 88 | t.ok(result, msg); 89 | } 90 | 91 | t.end(); 92 | }) 93 | 94 | } else { 95 | t.skip(`Wrote expected image to ${path.join(path.basename(path.dirname(fixturePath)), outputName)}`); 96 | regl.destroy(); 97 | t.end(); 98 | } 99 | }); 100 | } 101 | }); 102 | -------------------------------------------------------------------------------- /test/util/create-context.js: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/regl-project/regl 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2016 Mikola Lysenko 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | if (typeof document !== 'undefined') { 25 | var canvas, opts, context 26 | 27 | var refreshCanvas = function () { 28 | if (canvas) canvas.remove() 29 | 30 | canvas = document.createElement('canvas') 31 | opts = { 32 | antialias: false, 33 | stencil: true, 34 | preserveDrawingBuffer: true 35 | } 36 | context = canvas.getContext('webgl', opts) || canvas.getContext('experimental-webgl', opts) 37 | canvas.style.position = 'fixed' 38 | canvas.style.top = '0' 39 | canvas.style.right = '0' 40 | canvas.style.width = '256px' 41 | canvas.style.height = '256px' 42 | document.body.appendChild(canvas) 43 | } 44 | 45 | refreshCanvas() 46 | 47 | module.exports = function (width, height) { 48 | canvas.width = width 49 | canvas.height = height 50 | return context 51 | } 52 | 53 | module.exports.refreshCanvas = refreshCanvas 54 | 55 | module.exports.resize = function (gl, w, h) { 56 | canvas.width = w 57 | canvas.height = h 58 | } 59 | 60 | module.exports.destroy = function (gl) { } 61 | } else { 62 | var CONTEXT = require('gl')(1, 1, { preserveDrawingBuffer: true }) 63 | var RESIZE = CONTEXT.getExtension('STACKGL_resize_drawingbuffer') 64 | 65 | module.exports = function (w, h) { 66 | RESIZE.resize(w, h) 67 | return CONTEXT 68 | } 69 | 70 | module.exports.resize = function (gl, w, h) { 71 | RESIZE.resize(w, h) 72 | } 73 | 74 | module.exports.destroy = function (gl) { 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/util/render-fixture.js: -------------------------------------------------------------------------------- 1 | module.exports = renderFixture; 2 | 3 | function replaceNullWithNaN (data) { 4 | for (let i = 0; i < data.length; i++) { 5 | if (Array.isArray(data[i])) { 6 | replaceNullWithNaN(data[i]); 7 | } else if (data[i] === null) { 8 | data[i] = NaN; 9 | } 10 | } 11 | return data; 12 | } 13 | 14 | function renderFixture(regl, createDrawLines, fixture) { 15 | const drawLines = createDrawLines(regl, { 16 | ...fixture.command, 17 | vert: fixture.command.vert.join('\n'), 18 | frag: fixture.command.frag.join('\n') 19 | }); 20 | regl.poll(); 21 | regl.clear({color: [1, 1, 1, 1], depth: 1}); 22 | 23 | const lineData = fixture.data ? {...fixture.data} : {}; 24 | lineData.vertexAttributes = {}; 25 | lineData.endpointAttributes = {}; 26 | 27 | 28 | if (fixture.vertexAttributes) { 29 | lineData.vertexAttributes = {}; 30 | for (const [name, attribute] of Object.entries(fixture.vertexAttributes)) { 31 | const sanitizedAttr = replaceNullWithNaN(attribute); 32 | 33 | lineData.vertexAttributes[name] = regl.buffer(sanitizedAttr); 34 | lineData.vertexCount = sanitizedAttr.length; 35 | } 36 | } 37 | 38 | if (fixture.endpointAttributes) { 39 | lineData.endpointAttributes = {}; 40 | for (const [name, attribute] of Object.entries(fixture.endpointAttributes)) { 41 | const sanitizedAttr = replaceNullWithNaN(attribute); 42 | 43 | // If endpoint data is provided, use it 44 | lineData.endpointAttributes[name] = regl.buffer(sanitizedAttr); 45 | lineData.endpointCount = sanitizedAttr.length; 46 | } 47 | } 48 | 49 | drawLines(lineData); 50 | } 51 | --------------------------------------------------------------------------------