├── .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 |
--------------------------------------------------------------------------------