gridviz 3.0.30 → 3.0.32
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/dist/gridviz.js +324 -242
- package/dist/gridviz.min.js +1 -1
- package/package.json +1 -1
- package/src/core/Map.js +5 -1
- package/src/style/ShadingStyle.js +15 -4
- package/src/style/SideStyle.js +8 -3
- package/src/style/SideTanakaStyle.js +3 -1
- package/src/style/SquareColorCategoryWebGLStyle.js +218 -30
- package/src/utils/WebGLSquareColoringAdvanced.js +46 -49
- package/src/utils/scale.js +22 -0
- package/src/utils/webGLUtils.js +5 -4
- package/src/style/SideTanakaStyle___OLD.js +0 -66
- package/src/utils/WebGLSquareColoringCatAdvanced.js +0 -133
package/package.json
CHANGED
package/src/core/Map.js
CHANGED
|
@@ -76,6 +76,7 @@ export class Map {
|
|
|
76
76
|
this.legend = opts.legendContainer
|
|
77
77
|
? select(opts.legendContainer) // Wrap the provided HTML node in a D3 selection
|
|
78
78
|
: null
|
|
79
|
+
this._legendProvidedByUser = !!opts.legendContainer
|
|
79
80
|
if (!this.legend) this.initialiseLegend()
|
|
80
81
|
|
|
81
82
|
//tooltip
|
|
@@ -536,7 +537,10 @@ export class Map {
|
|
|
536
537
|
this.geoCanvas.canvas.remove()
|
|
537
538
|
|
|
538
539
|
// remove legend
|
|
539
|
-
|
|
540
|
+
// only remove legend if gridviz created it
|
|
541
|
+
if (!this._legendProvidedByUser) {
|
|
542
|
+
this.legend?.remove()
|
|
543
|
+
}
|
|
540
544
|
|
|
541
545
|
// remove tooltip
|
|
542
546
|
this.tooltip.tooltip?.remove()
|
|
@@ -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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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 + ')'
|
package/src/style/SideStyle.js
CHANGED
|
@@ -124,12 +124,17 @@ export class SideStyle extends Style {
|
|
|
124
124
|
* @param {boolean} center Set to true so that the side coordinate are those of its center point rather than its left/bottom point (the side x,y coordinates are those of the left point for horizontal sides, and of the bottom point for vertical sides)
|
|
125
125
|
* @returns { Array.<Side> }
|
|
126
126
|
*/
|
|
127
|
-
static buildSides(cells, resolution, withHorizontal = true, withVertical = true, center = true) {
|
|
127
|
+
static buildSides(cells, resolution, withHorizontal = true, withVertical = true, center = true, eps = 0.01) {
|
|
128
128
|
/** @type { Array.<Side> } */
|
|
129
129
|
const sides = []
|
|
130
130
|
|
|
131
|
+
// half resolution
|
|
131
132
|
const r2 = center ? resolution / 2 : 0
|
|
132
133
|
|
|
134
|
+
// epsilon
|
|
135
|
+
eps = resolution * eps
|
|
136
|
+
const abs = Math.abs
|
|
137
|
+
|
|
133
138
|
//make horizontal sides
|
|
134
139
|
//sort cells by x and y
|
|
135
140
|
cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
|
|
@@ -137,7 +142,7 @@ export class SideStyle extends Style {
|
|
|
137
142
|
for (let i = 1; i < cells.length; i++) {
|
|
138
143
|
let c2 = cells[i]
|
|
139
144
|
|
|
140
|
-
if (c1.y + resolution
|
|
145
|
+
if (abs(c1.y + resolution - c2.y) < eps && abs(c1.x - c2.x) < eps)
|
|
141
146
|
//cells in same column and touch along horizontal side
|
|
142
147
|
//make shared side
|
|
143
148
|
sides.push({
|
|
@@ -176,7 +181,7 @@ export class SideStyle extends Style {
|
|
|
176
181
|
for (let i = 1; i < cells.length; i++) {
|
|
177
182
|
let c2 = cells[i]
|
|
178
183
|
|
|
179
|
-
if (c1.x + resolution
|
|
184
|
+
if (abs(c1.x + resolution - c2.x) < eps && abs(c1.y - c2.y) < eps)
|
|
180
185
|
//cells in same row and touch along vertical side
|
|
181
186
|
//make shared side
|
|
182
187
|
sides.push({
|
|
@@ -10,6 +10,8 @@ import { SideStyle } from './SideStyle.js'
|
|
|
10
10
|
* @typedef {function(Array.<Side>,number, number):*} SideViewScale */
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
+
* @see https://manifold.net/doc/mfd9/example__tanaka_contours.htm
|
|
14
|
+
*
|
|
13
15
|
* @module style
|
|
14
16
|
* @author Julien Gaffuri
|
|
15
17
|
*/
|
|
@@ -25,7 +27,7 @@ export class SideTanakaStyle extends Style {
|
|
|
25
27
|
|
|
26
28
|
/** A function returning the width of a cell side, in geo unit
|
|
27
29
|
* @type {function(Side, number, number, number, object):number} */
|
|
28
|
-
this.width = opts.width || ((side, sideValue, resolution, z, sidesScale) => Math.abs(sideValue) * Math.min(2 * z, resolution / 3))
|
|
30
|
+
this.width = opts.width || ((side, sideValue, resolution, z, sidesScale) => Math.abs(sideValue) * z) //Math.min(2 * z, resolution / 3))
|
|
29
31
|
|
|
30
32
|
/** A function returning the length of a cell side, in geo unit
|
|
31
33
|
* @type {function(Side, number, number, object):number} */
|
|
@@ -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 {
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
//
|
|
97
|
-
const
|
|
98
|
-
|
|
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(
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
113
|
+
fshBuff.push(' float t = vt;')
|
|
120
114
|
}
|
|
121
115
|
} else {
|
|
122
|
-
|
|
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)
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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; }
|
|
149
|
-
|
|
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)
|
|
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
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
|
package/src/utils/scale.js
CHANGED
|
@@ -151,6 +151,28 @@ export const viewScaleColorQuantile = (opts) => {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Generic function for view scale quantile classifier
|
|
157
|
+
*
|
|
158
|
+
* @param {{ valueFunction:function(import("../core/Dataset").Cell):number, classNumber?:number }} opts
|
|
159
|
+
* @returns {function(Array.<import("../core/Dataset").Cell>):ColorScale}
|
|
160
|
+
*/
|
|
161
|
+
export const viewScaleQuantileClassifer = (opts) => {
|
|
162
|
+
const valueFunction = opts.valueFunction // the function that return the cell value from a cell
|
|
163
|
+
const classNumber = opts.classNumber || 12 // the class number
|
|
164
|
+
|
|
165
|
+
const scale = scaleQuantile().range(Array.from( { length: classNumber }, (_, i) => i ))
|
|
166
|
+
return (cells) => {
|
|
167
|
+
scale.domain(cells.map(valueFunction))
|
|
168
|
+
scale.breaks = scale.quantiles()
|
|
169
|
+
return scale
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
154
176
|
/**
|
|
155
177
|
* Combine view scale functions
|
|
156
178
|
*
|
package/src/utils/webGLUtils.js
CHANGED
|
@@ -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
|
/**
|