gridviz 3.0.29 → 3.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gridviz",
3
- "version": "3.0.29",
3
+ "version": "3.0.31",
4
4
  "description": "Visualization tool for gridded statistics",
5
5
  "keywords": [
6
6
  "statistics",
@@ -42,14 +42,25 @@ export class ShadingStyle extends SideStyle {
42
42
  // compute maximum side value for normalization
43
43
  this.viewScale = sides => max(sides, s => sideValue(s))
44
44
 
45
- const colorTopLeft = opts.colorTopLeft || '255,255,255'
46
- const colorBottomRight = opts.colorBottomRight || '0,0,0'
47
- const scale = opts.scale || (t=>t)
45
+ // get colors
46
+ let colorTopLeft = opts.colorTopLeft || '255,255,255'
47
+ let colorBottomRight = opts.colorBottomRight || '0,0,0'
48
+
49
+ // revert colors (to revert the relief, show depressions as hills)
50
+ const revert = opts.revert == undefined? false : opts.revert
51
+ if(revert) {
52
+ let a = colorTopLeft
53
+ colorTopLeft = colorBottomRight
54
+ colorBottomRight = a
55
+ }
56
+
57
+ //
58
+ const scale = opts.scale
48
59
 
49
60
  this.color = (side, resolution, z, max) => {
50
61
  if (side.v == 0) return
51
62
  let coeff = Math.abs(side.v / max)
52
- coeff = scale(coeff)
63
+ if(scale) coeff = scale(coeff)
53
64
  if ((side.v < 0 && side.or === 'h') || (side.v > 0 && side.or === 'v'))
54
65
  return 'rgba(' + colorTopLeft + ',' + coeff + ')'
55
66
  return 'rgba(' + colorBottomRight + ',' + coeff + ')'
@@ -35,9 +35,17 @@ export class SideTanakaStyle extends Style {
35
35
  this.colorDark = opts.colorDark || '#111'
36
36
  // the bright color: for side facing the light (coming from NW)
37
37
  this.colorBright = opts.colorBright || '#ddd'
38
+ //
39
+ this.revert = opts.revert == undefined? false : opts.revert
38
40
 
39
41
  /** Set to A or true so that the side is drawn as a diamond */
40
42
  this.diamond = opts.diamond
43
+
44
+ /* Determines what to do for limit sides, between a cell with value and one with no value
45
+ * steep: the cell value absence is equivalent to 0. It shows potentially a steep limit then.
46
+ * skip: no side is drawn
47
+ * step: a side with a 1 step */
48
+ this.limit = opts.limit || "steep"
41
49
  }
42
50
 
43
51
  /**
@@ -61,8 +69,8 @@ export class SideTanakaStyle extends Style {
61
69
  const cl1 = side.c1 ? classifier(side.c1) : undefined
62
70
  const cl2 = side.c2 ? classifier(side.c2) : undefined
63
71
  if (cl1 === undefined && cl2 === undefined) return undefined
64
- if (cl1 === undefined) return cl2
65
- if (cl2 === undefined) return -cl1
72
+ if (cl1 === undefined) return this.limit=="none"? undefined : this.limit=="steep"? cl2 : Math.sign(cl2)
73
+ if (cl2 === undefined) return this.limit=="none"? undefined : this.limit=="steep"? -cl1 : Math.sign(-cl1)
66
74
  return cl2 - cl1
67
75
  }
68
76
 
@@ -78,6 +86,8 @@ export class SideTanakaStyle extends Style {
78
86
  //draw sides
79
87
  ctx.lineCap = 'butt'
80
88
  const r2 = resolution * 0.5
89
+ const cd = this.revert? this.colorBright : this.colorDark
90
+ const cb = this.revert? this.colorDark : this.colorBright
81
91
  for (let side of sides) {
82
92
 
83
93
  //get side value
@@ -86,7 +96,7 @@ export class SideTanakaStyle extends Style {
86
96
 
87
97
  //color
88
98
  /** @type {string|undefined} */
89
- const col = ((v < 0 && side.or === 'h') || (v > 0 && side.or === 'v')) ? this.colorBright : this.colorDark
99
+ const col = ((v < 0 && side.or === 'h') || (v > 0 && side.or === 'v')) ? cb : cd
90
100
  if (!col || col == 'none') continue
91
101
 
92
102
  if (this.diamond) {
@@ -2,8 +2,8 @@
2
2
  'use strict'
3
3
 
4
4
  import { Style } from '../core/Style.js'
5
- import { makeWebGLCanvas } from '../utils/webGLUtils.js'
6
- import { WebGLSquareColoringCatAdvanced } from '../utils/WebGLSquareColoringCatAdvanced.js'
5
+ import { initShaderProgram, createShader, makeWebGLCanvas } from '../utils/webGLUtils.js'
6
+ import { color } from 'd3-color'
7
7
 
8
8
  /**
9
9
  * Style based on webGL
@@ -21,8 +21,8 @@ export class SquareColorCategoryWebGLStyle extends Style {
21
21
 
22
22
  /**
23
23
  * A function returning the category code of the cell, for coloring.
24
- * @type {function(import('../core/Dataset.js').Cell):string} */
25
- this.code = opts.code
24
+ * @type {function(import('../core/Dataset.js').Cell, number, number, viewScale):string} */
25
+ this.code = opts.code // (c, resolution, z, viewScale) => "code1"
26
26
 
27
27
  /**
28
28
  * The dictionary (code -> color) which gives the color of each category code.
@@ -45,38 +45,105 @@ export class SquareColorCategoryWebGLStyle extends Style {
45
45
  * @type {function(number,number):number} */
46
46
  this.size = opts.size // (resolution, z) => ...
47
47
 
48
- /**
49
- * @private
50
- * @type { WebGLSquareColoringCatAdvanced } */
51
- this.wgp = new WebGLSquareColoringCatAdvanced(this.colors)
48
+ /** * @type {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number }}*/
49
+ this.cvWGL = undefined
50
+ this.programm = undefined
51
+
52
+ this.x = undefined
53
+ this.y = undefined
54
+ this.z = undefined
55
+ this.cellsNb = undefined
52
56
  }
53
57
 
54
- /**
55
- * @param {Array.<import("../core/Dataset.js").Cell>} cells
56
- * @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
57
- * @param {number} resolution
58
- */
59
- draw(cells, geoCanvas, resolution) {
60
- //filter
61
- if (this.filter) cells = cells.filter(this.filter)
58
+ init(w, h) {
59
+ this.cvWGL = makeWebGLCanvas(w + '', h + '')
60
+ if (!this.cvWGL) { console.error('No webGL'); return }
62
61
 
63
- //
64
- const z = geoCanvas.view.z
62
+ const gl = this.cvWGL.gl
63
+
64
+ //draw
65
+ const vectorShader = `
66
+ attribute vec2 pos;
67
+ uniform float sizePix;
68
+ uniform mat3 mat;
69
+ attribute float i;
70
+ varying float vi;
71
+ void main() {
72
+ gl_Position = vec4(mat * vec3(pos, 1.0), 1.0);
73
+ gl_PointSize = sizePix;
74
+ vi = i;
75
+ }`
76
+ /** @type {WebGLShader} */
77
+ const vShader = createShader(gl, gl.VERTEX_SHADER, vectorShader)
78
+
79
+ const fragmentShader = `
80
+ precision highp float;
81
+ varying float vi;
82
+ uniform sampler2D colorLUT;
83
+ uniform float lutSize;
84
+ void main(void) {
85
+ float idx = floor(vi + 0.5);
86
+ float u = (idx + 0.5) / lutSize;
87
+ gl_FragColor = texture2D(colorLUT, vec2(u, 0.5));
88
+ }`
89
+ /** @type {WebGLShader} */
90
+ const fShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShader)
91
+
92
+ /** @type {WebGLProgram} */
93
+ this.program = initShaderProgram(gl, vShader, fShader)
94
+ gl.useProgram(this.program)
95
+ }
96
+
97
+ bindColors() {
98
+ const gl = this.cvWGL.gl
99
+ const lutSize = this.colors.length;
100
+ const lutData = new Uint8Array(lutSize * 4); // RGBA for each entry
65
101
 
102
+ // Fill lutData with your color values (e.g., rainbow, grayscale, etc.)
103
+ for (let i = 0; i < lutSize; i++) {
104
+ const c = color(this.colors[i])
105
+ lutData[i * 4] = +c.r; // R
106
+ lutData[i * 4 + 1] = c.g; // G
107
+ lutData[i * 4 + 2] = c.b; // B
108
+ lutData[i * 4 + 3] = c?.opacity * 255; // A
109
+ }
110
+
111
+ // Create and bind texture
112
+ const lutTexture = gl.createTexture();
113
+ gl.bindTexture(gl.TEXTURE_2D, lutTexture);
114
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, lutSize, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, lutData);
115
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
116
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
117
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
118
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
119
+
120
+ // Get uniform locations
121
+ const uColorLUT = gl.getUniformLocation(this.program, 'colorLUT');
122
+ const uLutSize = gl.getUniformLocation(this.program, 'lutSize');
123
+
124
+ // Set uniform values
125
+ gl.uniform1i(uColorLUT, 0); // Texture unit 0
126
+ gl.uniform1f(uLutSize, lutSize);
127
+
128
+ // Bind the texture to texture unit 0
129
+ gl.activeTexture(gl.TEXTURE0);
130
+ gl.bindTexture(gl.TEXTURE_2D, lutTexture);
131
+ }
132
+
133
+ bindVertices(cells, resolution, z, viewScale) {
134
+ const gl = this.cvWGL.gl
66
135
  //add vertice and fragment data
67
136
  const r2 = resolution / 2
68
- let c,
69
- nb = cells.length
137
+ let c, nb = cells.length
70
138
  const verticesBuffer = []
71
139
  const iBuffer = []
72
140
  for (let i = 0; i < nb; i++) {
73
141
  c = cells[i]
74
- const cat = this.code(c)
142
+ const cat = this.code(c, resolution, z, viewScale)
75
143
  if (cat == undefined) {
76
144
  console.log('Unexpected category: ' + cat)
77
145
  continue
78
146
  }
79
- /** @type {number} */
80
147
  const i_ = this.catToI[cat]
81
148
  if (isNaN(+i_)) {
82
149
  console.log('Unexpected category index: ' + cat + ' ' + i_)
@@ -86,22 +153,143 @@ export class SquareColorCategoryWebGLStyle extends Style {
86
153
  iBuffer.push(+i_)
87
154
  }
88
155
 
156
+ //bind vertice data
157
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
158
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesBuffer), gl.STATIC_DRAW)
159
+ const position = gl.getAttribLocation(this.program, 'pos')
160
+ gl.vertexAttribPointer(
161
+ position,
162
+ 2, //numComponents
163
+ gl.FLOAT, //type
164
+ false, //normalise
165
+ 0, //stride
166
+ 0 //offset
167
+ )
168
+ gl.enableVertexAttribArray(position)
169
+
170
+ //i data
171
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
172
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(iBuffer), gl.STATIC_DRAW)
173
+ const i = gl.getAttribLocation(this.program, 'i')
174
+ gl.vertexAttribPointer(i, 1, gl.FLOAT, false, 0, 0)
175
+ gl.enableVertexAttribArray(i)
176
+ }
177
+
178
+
179
+
180
+ // check if the vertices have to be bound again
181
+ mapContentChanged(v, cellsNb) {
182
+ if (v.x == this.x && v.y == this.y && v.z == this.z && this.cellsNb == cellsNb) return false
183
+ this.x = v.x
184
+ this.y = v.y
185
+ this.z = v.z
186
+ this.cellsNb = cellsNb
187
+ return true
188
+ }
189
+
190
+
191
+ /**
192
+ * @param {Array.<import("../core/Dataset.js").Cell>} cells
193
+ * @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
194
+ * @param {number} resolution
195
+ */
196
+ draw(cells, geoCanvas, resolution) {
197
+ //filter
198
+ if (this.filter) cells = cells.filter(this.filter)
199
+
200
+ //
201
+ const z = geoCanvas.view.z
202
+
203
+ //get view scale
204
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
205
+
89
206
  //create canvas and webgl renderer
90
- const cvWGL = makeWebGLCanvas(geoCanvas.w + '', geoCanvas.h + '')
91
- if (!cvWGL) {
92
- console.error('No webGL')
93
- return
207
+ if (!this.cvWGL || geoCanvas.w != this.cvWGL.width || geoCanvas.h != this.cvWGL.height) {
208
+ this.init(geoCanvas.w, geoCanvas.h)
209
+ this.bindColors()
94
210
  }
211
+ const gl = this.cvWGL.gl
212
+ const canvas = this.cvWGL.canvas
95
213
 
96
- //draw
97
- const sizeGeo = this.size ? this.size(resolution, z) : resolution + 0.2 * z
98
- this.wgp.draw(cvWGL.gl, verticesBuffer, iBuffer, geoCanvas.getWebGLTransform(), sizeGeo / z)
214
+ //bind sizePix
215
+ const sizePix = this.size ? this.size(resolution, z) / z : resolution / z + 0.2
216
+ gl.uniform1f(gl.getUniformLocation(this.program, 'sizePix'), 1.0 * sizePix)
217
+
218
+ //
219
+ if (this.mapContentChanged(geoCanvas.view, cells.length))
220
+ //bind vertices
221
+ this.bindVertices(cells, resolution, z, viewScale)
222
+ //transformation
223
+ gl.uniformMatrix3fv(gl.getUniformLocation(this.program, 'mat'), false, new Float32Array(geoCanvas.getWebGLTransform()))
224
+
225
+ // Enable the depth test
226
+ //gl.enable(gl.DEPTH_TEST);
227
+ // Clear the color buffer bit
228
+ gl.clear(gl.COLOR_BUFFER_BIT)
229
+ // Set the view port
230
+ //gl.viewport(0, 0, cg.w, cg.h);
231
+
232
+ gl.drawArrays(gl.POINTS, 0, cells.length)
99
233
 
100
234
  //draw in canvas geo
101
235
  geoCanvas.initCanvasTransform()
102
- geoCanvas.offscreenCtx.drawImage(cvWGL.canvas, 0, 0)
236
+ geoCanvas.offscreenCtx.drawImage(canvas, 0, 0)
103
237
 
104
238
  //update legends
105
239
  this.updateLegends({ style: this, resolution: resolution, z: z })
106
240
  }
107
241
  }
242
+
243
+
244
+
245
+ // early tests for webgl2 migration
246
+
247
+ /*
248
+ function getVectorShader2() {
249
+ return `
250
+ #version 300 es
251
+ precision highp float;
252
+
253
+ in vec2 pos;
254
+ in int i;
255
+
256
+ uniform float sizePix;
257
+ uniform mat3 mat;
258
+
259
+ flat out int vi;
260
+
261
+ void main() {
262
+ gl_Position = vec4(mat * vec3(pos, 1.0), 1.0);
263
+ gl_PointSize = sizePix;
264
+ vi = i;
265
+ }
266
+ `
267
+ }
268
+
269
+
270
+ function getFragmentShader2(colors) {
271
+
272
+ //prepare fragment shader code
273
+ //declare the uniform and other variables
274
+ const out = []
275
+ out.push('#version 300 es\nprecision highp float;\nflat in int vi;\n')
276
+
277
+ //add color uniforms
278
+ //uniform vec4 colors[12];
279
+ out.push('uniform vec4 colors[')
280
+ out.push(colors.length)
281
+ out.push('];\n')
282
+
283
+ out.push('out vec4 fragColor;\n')
284
+
285
+ //start the main function
286
+ //void main() { fragColor = colors[clamp(vi, 0, 11)]; }
287
+ out.push('void main() { fragColor = colors[vi]; }\n')
288
+
289
+ /** Fragment shader program
290
+ const fshString = out.join('')
291
+ console.log(fshString)
292
+ return fshString
293
+ }
294
+
295
+ */
@@ -53,86 +53,85 @@ export class WebGLSquareColoringAdvanced {
53
53
 
54
54
  //prepare fragment shader code
55
55
  //declare the uniform and other variables
56
- let fshString =
57
- '' +
58
- 'precision mediump float;\n' +
59
- 'varying float vt;\n' +
60
- 'uniform float alpha;\n' +
61
- (() => {
62
- const out = []
63
- for (let i = 0; i < colors.length; i++) out.push('uniform vec4 c' + i + ';\n')
64
- return out.join('')
65
- })() +
66
- //start the main function, apply the stretching of t
67
- 'void main(void) {\n'
68
-
56
+ const fshBuff = []
57
+ fshBuff.push('precision mediump float;')
58
+ fshBuff.push('varying float vt;')
59
+ fshBuff.push('uniform float alpha;')
60
+ for (let i = 0; i < colors.length; i++) fshBuff.push('uniform vec4 c' + i + ';')
61
+ //start the main function
62
+ fshBuff.push('void main(void) {')
63
+
64
+ // apply the stretching of t
69
65
  if (stretching) {
70
66
  if (stretching.fun == 'pow')
71
67
  //sPow = (t, alpha = 3) => Math.pow(t, alpha);
72
- fshString += ' float t = pow(vt, alpha);\n'
68
+ fshBuff.push(' float t = pow(vt, alpha);')
73
69
  else if (stretching.fun == 'powInv')
74
70
  //sPowRev = (t, alpha = 3) => 1 - Math.pow(1 - t, 1 / alpha);
75
- fshString += ' float t = 1.0-pow(1.0-vt, 1.0/alpha);\n'
71
+ fshBuff.push(' float t = 1.0-pow(1.0-vt, 1.0/alpha);')
76
72
  else if (stretching.fun == 'exp')
77
73
  //sExp = (t, alpha = 3) => alpha == 0 ? t : (Math.exp(t * alpha) - 1) / (Math.exp(alpha) - 1);
78
- fshString +=
74
+ fshBuff.push(
79
75
  stretching.alpha == 0
80
76
  ? `float t = vt;`
81
- : ' float t = (exp(vt * alpha) - 1.0) / (exp(alpha) - 1.0);\n'
77
+ : ' float t = (exp(vt * alpha) - 1.0) / (exp(alpha) - 1.0);')
82
78
  else if (stretching.fun == 'log')
83
79
  //sExpRev = (t, alpha = 3) => alpha == 0 ? t : 1 - (1 / alpha) * Math.log(Math.exp(alpha) * (1 - t) + t);
84
- fshString +=
80
+ fshBuff.push(
85
81
  stretching.alpha == 0
86
82
  ? `float t = vt;`
87
- : ' float t = 1.0 - (1.0 / alpha) * log(exp(alpha) * (1.0 - vt) + vt);\n'
83
+ : ' float t = 1.0 - (1.0 / alpha) * log(exp(alpha) * (1.0 - vt) + vt);')
88
84
  else if (stretching.fun == 'circle') {
89
85
  if (stretching.alpha == 0)
90
86
  //if (alpha == 0) return t;
91
- fshString += ' float t = vt;\n'
87
+ fshBuff.push(' float t = vt;')
92
88
  else if (stretching.alpha == 1)
93
89
  // if (alpha == 1) return Math.sqrt(2 * t - t * t);
94
- fshString += ' float t = sqrt(vt * (2.0 - vt));\n'
90
+ fshBuff.push(' float t = sqrt(vt * (2.0 - vt));')
95
91
  else {
96
92
  //const a = alpha / (1 - alpha);
97
93
  //return Math.sqrt(1 / (a * a) + t * (2 / a + 2 - t)) - 1 / a;
98
- fshString +=
99
- ' float a = alpha / (1.0 - alpha);\n' +
100
- ' float t = sqrt(1.0 / (a * a) + vt * ( 2.0/a + 2.0 - vt )) - 1.0 / a;\n'
94
+ fshBuff.push(' float a = alpha / (1.0 - alpha);')
95
+ fshBuff.push(' float t = sqrt(1.0 / (a * a) + vt * ( 2.0/a + 2.0 - vt )) - 1.0 / a;')
101
96
  }
102
97
  } else if (stretching.fun == 'circleInv') {
103
98
  // 1 - sCircleLow(1 - t, alpha)
104
99
  if (stretching.alpha == 0)
105
100
  //if (alpha == 0) return t;
106
- fshString += ' float t = vt;\n'
101
+ fshBuff.push(' float t = vt;')
107
102
  else if (stretching.alpha == 1)
108
103
  // if (alpha == 1) return Math.sqrt(2 * t - t * t);
109
- fshString += ' float t = 1.0 - sqrt((1.0 - vt) * (1.0 + vt));\n'
104
+ fshBuff.push(' float t = 1.0 - sqrt((1.0 - vt) * (1.0 + vt));')
110
105
  else {
111
106
  //const a = alpha / (1 - alpha);
112
107
  //return Math.sqrt(1 / (a * a) + (2 * t) / a + 2 * t - t * t) - 1 / a;
113
- fshString +=
114
- ' float a = alpha / (1.0 - alpha);\n' +
115
- ' float t = 1.0 - sqrt(1.0 / (a * a) + (1.0-vt) * ( 2.0/a + 1.0 + vt )) + 1.0 / a;\n'
108
+ fshBuff.push(' float a = alpha / (1.0 - alpha);')
109
+ fshBuff.push(' float t = 1.0 - sqrt(1.0 / (a * a) + (1.0-vt) * ( 2.0/a + 1.0 + vt )) + 1.0 / a;')
116
110
  }
117
111
  } else {
118
112
  console.error('Unexpected stretching function code: ' + stretching.fun)
119
- fshString += ' float t = vt;\n'
113
+ fshBuff.push(' float t = vt;')
120
114
  }
121
115
  } else {
122
- fshString += ' float t = vt;\n'
116
+ fshBuff.push(' float t = vt;')
123
117
  }
124
118
 
125
119
  //choose initial and final colors, and adjust t value
126
- if (colors.length == 1) fshString += ' vec4 cI=c0;\n vec4 cF=c0;\n'
127
- else if (colors.length == 2) fshString += ' vec4 cI=c0;\n vec4 cF=c1;\n'
128
- else {
120
+ if (colors.length == 1) {
121
+ fshBuff.push(' vec4 cI=c0;')
122
+ fshBuff.push(' vec4 cF=c0;')
123
+ }
124
+ else if (colors.length == 2) {
125
+ fshBuff.push(' vec4 cI=c0;')
126
+ fshBuff.push(' vec4 cF=c1;')
127
+ } else {
129
128
  const nb = colors.length - 1
130
129
  const nbs = nb + '.0'
131
- fshString += ' vec4 cI;\n'
132
- fshString += ' vec4 cF;\n'
133
- fshString += ' if(t<1.0/' + nbs + ') { cI=c0; cF=c1; t=t*' + nbs + '; }\n'
130
+ fshBuff.push(' vec4 cI;')
131
+ fshBuff.push(' vec4 cF;')
132
+ fshBuff.push(' if(t<1.0/' + nbs + ') { cI=c0; cF=c1; t=t*' + nbs + '; }')
134
133
  for (let i = 2; i < nb; i++)
135
- fshString +=
134
+ fshBuff.push(
136
135
  ' else if(t<' +
137
136
  i +
138
137
  '.0/' +
@@ -145,16 +144,17 @@ export class WebGLSquareColoringAdvanced {
145
144
  nbs +
146
145
  '*t-' +
147
146
  (i - 1) +
148
- '.0; }\n'
149
- fshString +=
150
- ' else { cI=c' + (nb - 1) + '; cF=c' + nb + '; t=' + nbs + '*t-' + (nb - 1) + '.0; }\n'
147
+ '.0; }')
148
+ fshBuff.push(' else { cI=c' + (nb - 1) + '; cF=c' + nb + '; t=' + nbs + '*t-' + (nb - 1) + '.0; }')
151
149
  }
152
150
 
153
151
  //one single color
154
- if (colors.length == 1) fshString += ' gl_FragColor = vec4(c0[0], c0[1], c0[2], c0[3]);}\n'
152
+ if (colors.length == 1) fshBuff.push(' gl_FragColor = vec4(c0[0], c0[1], c0[2], c0[3]);}')
155
153
  //set interpolated color, between initial and final one
156
- else fshString += ' gl_FragColor = mix(cI, cF, t);}\n'
154
+ else fshBuff.push(' gl_FragColor = mix(cI, cF, t);}')
157
155
 
156
+ let fshString = fshBuff.join("")
157
+ //let fshString = fshBuff.join("\n")
158
158
  //console.log(fshString)
159
159
 
160
160
  /** @type {WebGLShader} */
@@ -180,12 +180,9 @@ export class WebGLSquareColoringAdvanced {
180
180
  let opacity = c.opacity
181
181
  if (c.opacity == 1 && globalOpacity != undefined) opacity = globalOpacity
182
182
 
183
- gl.uniform4fv(gl.getUniformLocation(this.program, 'c' + i), [
184
- +c.r / 255.0,
185
- +c.g / 255.0,
186
- +c.b / 255.0,
187
- +opacity,
188
- ])
183
+ const colData = [+c.r / 255.0, +c.g / 255.0, +c.b / 255.0, +opacity]
184
+ //console.log(i, colors[i], c, colData)
185
+ gl.uniform4fv(gl.getUniformLocation(this.program, 'c' + i), colData)
189
186
  }
190
187
  }
191
188
 
@@ -5,18 +5,19 @@
5
5
  * @param {string} width
6
6
  * @param {string} height
7
7
  * @param {object} opts
8
- * @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext}}
8
+ * @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number }}
9
9
  */
10
10
  export function makeWebGLCanvas(width, height, opts = {}) {
11
11
  const canvas = document.createElement('canvas')
12
12
  canvas.setAttribute('width', width)
13
13
  canvas.setAttribute('height', height)
14
+ const version2 = (opts && +opts.version==2)? "2" : ""
14
15
  /** @type {WebGLRenderingContext} */
15
- const gl = canvas.getContext('webgl', opts)
16
+ const gl = canvas.getContext('webgl' + version2, opts)
16
17
  if (!gl) {
17
- throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.')
18
+ throw new Error('Unable to initialize WebGL'+version2+'. Your browser or machine may not support it.')
18
19
  }
19
- return { canvas: canvas, gl: gl }
20
+ return { canvas: canvas, gl: gl, width: width, height: height }
20
21
  }
21
22
 
22
23
  /**