gridviz 3.0.35 → 3.0.37
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/README.md +129 -129
- package/dist/gridviz.js +21056 -17487
- package/dist/gridviz.min.js +2 -2
- package/package.json +74 -74
- package/src/button/Button.js +67 -67
- package/src/button/FullscreenButton.js +121 -121
- package/src/button/ZoomButtons.js +103 -103
- package/src/core/Dataset.js +232 -154
- package/src/core/Drawable.js +78 -78
- package/src/core/GeoCanvas.js +443 -443
- package/src/core/Layer.js +23 -23
- package/src/core/Legend.js +108 -108
- package/src/core/Map.js +650 -568
- package/src/core/MultiResolutionDataset.js +93 -93
- package/src/core/Style.js +98 -98
- package/src/core/Tooltip.js +182 -182
- package/src/dataset/COGeo.js +25 -25
- package/src/dataset/CSVGrid.js +112 -112
- package/src/dataset/GeoTIFF.js +122 -122
- package/src/dataset/JSGrid.js +55 -55
- package/src/dataset/TiledGrid.js +311 -302
- package/src/dataset/worker.js +18 -18
- package/src/index.js +79 -79
- package/src/layer/BackgroundLayer.js +173 -173
- package/src/layer/BackgroundLayerImage.js +81 -81
- package/src/layer/BackgroundLayerWMS.js +104 -104
- package/src/layer/GeoJSONLayer.js +276 -276
- package/src/layer/GridLayer.js +166 -166
- package/src/layer/LabelLayer.js +194 -194
- package/src/legend/ColorCategoryLegend.js +109 -109
- package/src/legend/ColorDiscreteLegend.js +100 -100
- package/src/legend/ColorLegend.js +115 -115
- package/src/legend/OrientationLegend.js +89 -89
- package/src/legend/SizeLegend.js +224 -224
- package/src/legend/TernaryLegend.js +302 -302
- package/src/style/CompositionStyle.js +291 -291
- package/src/style/DotDensityStyle.js +133 -133
- package/src/style/ImageStyle.js +100 -100
- package/src/style/Interpolator.js +258 -258
- package/src/style/IsoFenceStyle.js +217 -217
- package/src/style/JoyPlotStyle.js +180 -180
- package/src/style/LegoStyle.js +132 -132
- package/src/style/MosaicStyle.js +99 -99
- package/src/style/NinjaStarStyle.js +102 -102
- package/src/style/PillarStyle.js +207 -207
- package/src/style/SegmentStyle.js +105 -105
- package/src/style/ShadingRayStyle.js +280 -280
- package/src/style/ShadingStyle.js +72 -72
- package/src/style/ShapeColorSizeStyle.js +147 -147
- package/src/style/SideCategoryStyle.js +112 -112
- package/src/style/SideStyle.js +217 -217
- package/src/style/SideTanakaStyle.js +154 -154
- package/src/style/SquareColorCategoryWebGLStyle.js +311 -294
- package/src/style/SquareColorWebGLStyle.js +126 -126
- package/src/style/StrokeStyle.js +135 -135
- package/src/style/TextStyle.js +110 -110
- package/src/style/TimeSeriesStyle.js +222 -222
- package/src/utils/WebGLRectangleColoring.js +106 -106
- package/src/utils/WebGLSquareColoring.js +114 -118
- package/src/utils/WebGLSquareColoringAdvanced.js +223 -227
- package/src/utils/WebGLSquareColoringKS.js +153 -153
- package/src/utils/scale.js +255 -255
- package/src/utils/stretching.js +120 -120
- package/src/utils/ternary.js +208 -208
- package/src/utils/utils.js +125 -125
- package/src/utils/webGLUtils.js +178 -73
- package/src/utils/zoomUtils.js +142 -142
package/src/utils/utils.js
CHANGED
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { min, max, extent } from 'd3-array'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Get the class id from a value and class break values
|
|
8
|
-
*
|
|
9
|
-
* @param {number} v the value
|
|
10
|
-
* @param {Array.<number>} breaks the breaks
|
|
11
|
-
* @returns The class id, from 0 to breaks.length
|
|
12
|
-
* @deprecated use getClassifier instead.
|
|
13
|
-
*/
|
|
14
|
-
export function getClass(v, breaks) {
|
|
15
|
-
if (!breaks) return
|
|
16
|
-
if (breaks.length == 0) return 0
|
|
17
|
-
if (v <= breaks[0]) return 0
|
|
18
|
-
for (let i = 1; i < breaks.length; i++) if (breaks[i - 1] < v && v <= breaks[i]) return i
|
|
19
|
-
return breaks.length
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
//take 'nice' value (power of ten, or multiple)
|
|
23
|
-
export function nice(v, multiples = [8, 6, 5, 4, 2.5, 2]) {
|
|
24
|
-
//compute bigger power of ten below
|
|
25
|
-
const v_ = Math.pow(10, Math.floor(Math.log10(v)))
|
|
26
|
-
for (let multiple of multiples) if (v_ * multiple <= v) return v_ * multiple
|
|
27
|
-
return v_
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* A grid cell.
|
|
32
|
-
* @typedef {{x: number, y: number}} Cell */
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Index cells by y and x
|
|
36
|
-
* @param {Array.<Cell>} cells
|
|
37
|
-
* @param {Function} [fun]
|
|
38
|
-
* @returns {Object}
|
|
39
|
-
*/
|
|
40
|
-
export function cellsToGrid(cells, fun) {
|
|
41
|
-
/** @type {Object} */
|
|
42
|
-
const ind = {}
|
|
43
|
-
for (const cell of cells) {
|
|
44
|
-
let row = ind[cell.y]
|
|
45
|
-
if (!row) {
|
|
46
|
-
row = {}
|
|
47
|
-
ind[cell.y] = row
|
|
48
|
-
}
|
|
49
|
-
row[cell.x] = fun ? fun(cell) : cell
|
|
50
|
-
}
|
|
51
|
-
return ind
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Cells to grid.
|
|
58
|
-
*
|
|
59
|
-
* @param {Array.<Cell>} cells
|
|
60
|
-
* @param {number} resolution
|
|
61
|
-
* @param {Function} [fun]
|
|
62
|
-
* @returns {Object}
|
|
63
|
-
* Grid[i][j] is the value for line i and column j.
|
|
64
|
-
* Numbered from top to bottom, from left to right.
|
|
65
|
-
* Properties x0 and y0 are the geo coordinates of the lower left corner of the top left cell.
|
|
66
|
-
* Properties minI, maxI, minJ, maxJ are the extent of the grid.
|
|
67
|
-
*/
|
|
68
|
-
export function cellsToMatrix(cells, resolution, fun) {
|
|
69
|
-
|
|
70
|
-
// get cells extent
|
|
71
|
-
const [minx, maxx] = extent(cells, c => c.x)
|
|
72
|
-
const [miny, maxy] = extent(cells, c => c.y)
|
|
73
|
-
|
|
74
|
-
// get grid dimension
|
|
75
|
-
const rows = Math.ceil((maxy - miny) / resolution) + 1
|
|
76
|
-
const cols = Math.ceil((maxx - minx) / resolution) + 1
|
|
77
|
-
|
|
78
|
-
// make empty grid
|
|
79
|
-
const grid = Array.from({ length: rows }, () => new Array(cols).fill(undefined));
|
|
80
|
-
grid.x0 = minx
|
|
81
|
-
grid.y0 = maxy
|
|
82
|
-
grid.resolution = resolution
|
|
83
|
-
|
|
84
|
-
for (const c of cells) {
|
|
85
|
-
const i = Math.floor((maxy - c.y) / resolution)
|
|
86
|
-
const j = Math.floor((c.x - minx) / resolution)
|
|
87
|
-
const v = fun ? fun(c) : c
|
|
88
|
-
grid[i][j] = v
|
|
89
|
-
}
|
|
90
|
-
return grid
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/*
|
|
96
|
-
//no longer used
|
|
97
|
-
export function loadImage(src) {
|
|
98
|
-
return new Promise((resolve, reject) => {
|
|
99
|
-
const img = new Image();
|
|
100
|
-
img.onload = function () { resolve(img); };
|
|
101
|
-
img.onerror = function () { reject(new Error('Error loading image')); };
|
|
102
|
-
img.src = src;
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
*/
|
|
106
|
-
|
|
107
|
-
/*
|
|
108
|
-
export let monitor = false
|
|
109
|
-
|
|
110
|
-
let previousDate
|
|
111
|
-
export function monitorDuration(message) {
|
|
112
|
-
const nowDate = Date.now()
|
|
113
|
-
|
|
114
|
-
//first call
|
|
115
|
-
if (!previousDate) {
|
|
116
|
-
previousDate = nowDate
|
|
117
|
-
console.log(previousDate, message)
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const d = nowDate - previousDate
|
|
122
|
-
previousDate = nowDate
|
|
123
|
-
console.log(d, message)
|
|
124
|
-
}
|
|
125
|
-
*/
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { min, max, extent } from 'd3-array'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get the class id from a value and class break values
|
|
8
|
+
*
|
|
9
|
+
* @param {number} v the value
|
|
10
|
+
* @param {Array.<number>} breaks the breaks
|
|
11
|
+
* @returns The class id, from 0 to breaks.length
|
|
12
|
+
* @deprecated use getClassifier instead.
|
|
13
|
+
*/
|
|
14
|
+
export function getClass(v, breaks) {
|
|
15
|
+
if (!breaks) return
|
|
16
|
+
if (breaks.length == 0) return 0
|
|
17
|
+
if (v <= breaks[0]) return 0
|
|
18
|
+
for (let i = 1; i < breaks.length; i++) if (breaks[i - 1] < v && v <= breaks[i]) return i
|
|
19
|
+
return breaks.length
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//take 'nice' value (power of ten, or multiple)
|
|
23
|
+
export function nice(v, multiples = [8, 6, 5, 4, 2.5, 2]) {
|
|
24
|
+
//compute bigger power of ten below
|
|
25
|
+
const v_ = Math.pow(10, Math.floor(Math.log10(v)))
|
|
26
|
+
for (let multiple of multiples) if (v_ * multiple <= v) return v_ * multiple
|
|
27
|
+
return v_
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A grid cell.
|
|
32
|
+
* @typedef {{x: number, y: number}} Cell */
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Index cells by y and x
|
|
36
|
+
* @param {Array.<Cell>} cells
|
|
37
|
+
* @param {Function} [fun]
|
|
38
|
+
* @returns {Object}
|
|
39
|
+
*/
|
|
40
|
+
export function cellsToGrid(cells, fun) {
|
|
41
|
+
/** @type {Object} */
|
|
42
|
+
const ind = {}
|
|
43
|
+
for (const cell of cells) {
|
|
44
|
+
let row = ind[cell.y]
|
|
45
|
+
if (!row) {
|
|
46
|
+
row = {}
|
|
47
|
+
ind[cell.y] = row
|
|
48
|
+
}
|
|
49
|
+
row[cell.x] = fun ? fun(cell) : cell
|
|
50
|
+
}
|
|
51
|
+
return ind
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Cells to grid.
|
|
58
|
+
*
|
|
59
|
+
* @param {Array.<Cell>} cells
|
|
60
|
+
* @param {number} resolution
|
|
61
|
+
* @param {Function} [fun]
|
|
62
|
+
* @returns {Object}
|
|
63
|
+
* Grid[i][j] is the value for line i and column j.
|
|
64
|
+
* Numbered from top to bottom, from left to right.
|
|
65
|
+
* Properties x0 and y0 are the geo coordinates of the lower left corner of the top left cell.
|
|
66
|
+
* Properties minI, maxI, minJ, maxJ are the extent of the grid.
|
|
67
|
+
*/
|
|
68
|
+
export function cellsToMatrix(cells, resolution, fun) {
|
|
69
|
+
|
|
70
|
+
// get cells extent
|
|
71
|
+
const [minx, maxx] = extent(cells, c => c.x)
|
|
72
|
+
const [miny, maxy] = extent(cells, c => c.y)
|
|
73
|
+
|
|
74
|
+
// get grid dimension
|
|
75
|
+
const rows = Math.ceil((maxy - miny) / resolution) + 1
|
|
76
|
+
const cols = Math.ceil((maxx - minx) / resolution) + 1
|
|
77
|
+
|
|
78
|
+
// make empty grid
|
|
79
|
+
const grid = Array.from({ length: rows }, () => new Array(cols).fill(undefined));
|
|
80
|
+
grid.x0 = minx
|
|
81
|
+
grid.y0 = maxy
|
|
82
|
+
grid.resolution = resolution
|
|
83
|
+
|
|
84
|
+
for (const c of cells) {
|
|
85
|
+
const i = Math.floor((maxy - c.y) / resolution)
|
|
86
|
+
const j = Math.floor((c.x - minx) / resolution)
|
|
87
|
+
const v = fun ? fun(c) : c
|
|
88
|
+
grid[i][j] = v
|
|
89
|
+
}
|
|
90
|
+
return grid
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
//no longer used
|
|
97
|
+
export function loadImage(src) {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
const img = new Image();
|
|
100
|
+
img.onload = function () { resolve(img); };
|
|
101
|
+
img.onerror = function () { reject(new Error('Error loading image')); };
|
|
102
|
+
img.src = src;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/*
|
|
108
|
+
export let monitor = false
|
|
109
|
+
|
|
110
|
+
let previousDate
|
|
111
|
+
export function monitorDuration(message) {
|
|
112
|
+
const nowDate = Date.now()
|
|
113
|
+
|
|
114
|
+
//first call
|
|
115
|
+
if (!previousDate) {
|
|
116
|
+
previousDate = nowDate
|
|
117
|
+
console.log(previousDate, message)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const d = nowDate - previousDate
|
|
122
|
+
previousDate = nowDate
|
|
123
|
+
console.log(d, message)
|
|
124
|
+
}
|
|
125
|
+
*/
|
package/src/utils/webGLUtils.js
CHANGED
|
@@ -1,73 +1,178 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @param {string} width
|
|
6
|
-
* @param {string} height
|
|
7
|
-
* @param {object} opts
|
|
8
|
-
* @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number }}
|
|
9
|
-
*/
|
|
10
|
-
export function makeWebGLCanvas(width, height, opts = {}) {
|
|
11
|
-
const canvas = document.createElement('canvas')
|
|
12
|
-
canvas.setAttribute('width', width)
|
|
13
|
-
canvas.setAttribute('height', height)
|
|
14
|
-
const version2 = (opts && +opts.version==2)? "2" : ""
|
|
15
|
-
/** @type {WebGLRenderingContext} */
|
|
16
|
-
const gl = canvas.getContext('webgl' + version2, opts)
|
|
17
|
-
if (!gl) {
|
|
18
|
-
throw new Error('Unable to initialize WebGL'+version2+'. Your browser or machine may not support it.')
|
|
19
|
-
}
|
|
20
|
-
return { canvas: canvas, gl: gl, width: width, height: height }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} width
|
|
6
|
+
* @param {string} height
|
|
7
|
+
* @param {object} opts
|
|
8
|
+
* @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number }}
|
|
9
|
+
*/
|
|
10
|
+
export function makeWebGLCanvas(width, height, opts = {}) {
|
|
11
|
+
const canvas = document.createElement('canvas')
|
|
12
|
+
canvas.setAttribute('width', width)
|
|
13
|
+
canvas.setAttribute('height', height)
|
|
14
|
+
const version2 = (opts && +opts.version==2)? "2" : ""
|
|
15
|
+
/** @type {WebGLRenderingContext} */
|
|
16
|
+
const gl = canvas.getContext('webgl' + version2, opts)
|
|
17
|
+
if (!gl) {
|
|
18
|
+
throw new Error('Unable to initialize WebGL'+version2+'. Your browser or machine may not support it.')
|
|
19
|
+
}
|
|
20
|
+
return { canvas: canvas, gl: gl, width: width, height: height }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The shared, off-page WebGL canvases, one per set of context options.
|
|
25
|
+
* Styles draw into one of these and copy the result onto the map's canvas, one style at a
|
|
26
|
+
* time, so they never need a context each. Browsers keep only ~16 live WebGL contexts and
|
|
27
|
+
* drop the oldest beyond that, so one context per style instance (or worse, per draw)
|
|
28
|
+
* eventually blanks other WebGL content on the page.
|
|
29
|
+
* @type {Map<string, {canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number}>} */
|
|
30
|
+
const sharedCanvases = new Map()
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the shared WebGL canvas for these context options, resized to width x height.
|
|
34
|
+
* Resizing keeps the context (and its compiled programs and buffers); only its drawing
|
|
35
|
+
* buffer is reallocated. A lost context is replaced.
|
|
36
|
+
*
|
|
37
|
+
* @param {number} width
|
|
38
|
+
* @param {number} height
|
|
39
|
+
* @param {object} [opts] WebGL context attributes, e.g. { premultipliedAlpha: false }
|
|
40
|
+
* @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number }}
|
|
41
|
+
*/
|
|
42
|
+
export function getSharedWebGLCanvas(width, height, opts = undefined) {
|
|
43
|
+
const key = JSON.stringify(opts || {})
|
|
44
|
+
let cv = sharedCanvases.get(key)
|
|
45
|
+
if (!cv || cv.gl.isContextLost()) {
|
|
46
|
+
cv = makeWebGLCanvas(width + '', height + '', opts)
|
|
47
|
+
cv.width = +width
|
|
48
|
+
cv.height = +height
|
|
49
|
+
sharedCanvases.set(key, cv)
|
|
50
|
+
}
|
|
51
|
+
if (cv.width != width || cv.height != height) {
|
|
52
|
+
cv.canvas.width = width
|
|
53
|
+
cv.canvas.height = height
|
|
54
|
+
cv.width = +width
|
|
55
|
+
cv.height = +height
|
|
56
|
+
}
|
|
57
|
+
cv.gl.viewport(0, 0, cv.width, cv.height)
|
|
58
|
+
return cv
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @type {WeakMap<WebGLRenderingContext, {programs: Map<string, WebGLProgram>, buffers: Map<string, WebGLBuffer>, owners: Map<string, any>}>} */
|
|
62
|
+
const glResources = new WeakMap()
|
|
63
|
+
function resourcesOf(gl) {
|
|
64
|
+
let r = glResources.get(gl)
|
|
65
|
+
if (!r) {
|
|
66
|
+
r = { programs: new Map(), buffers: new Map(), owners: new Map() }
|
|
67
|
+
glResources.set(gl, r)
|
|
68
|
+
}
|
|
69
|
+
return r
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A shader program for these sources, compiled once per context and reused.
|
|
74
|
+
*
|
|
75
|
+
* @param {WebGLRenderingContext} gl
|
|
76
|
+
* @param {string} vertexSource
|
|
77
|
+
* @param {string} fragmentSource
|
|
78
|
+
* @returns {WebGLProgram}
|
|
79
|
+
*/
|
|
80
|
+
export function getProgram(gl, vertexSource, fragmentSource) {
|
|
81
|
+
const programs = resourcesOf(gl).programs
|
|
82
|
+
const key = vertexSource + '\u0000' + fragmentSource
|
|
83
|
+
let program = programs.get(key)
|
|
84
|
+
if (!program) {
|
|
85
|
+
program = initShaderProgram(
|
|
86
|
+
gl,
|
|
87
|
+
createShader(gl, gl.VERTEX_SHADER, vertexSource),
|
|
88
|
+
createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)
|
|
89
|
+
)
|
|
90
|
+
programs.set(key, program)
|
|
91
|
+
}
|
|
92
|
+
return program
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A named buffer of a context, created once and reused: refill it with bufferData instead
|
|
97
|
+
* of creating a new buffer per draw (buffers are never garbage collected while their
|
|
98
|
+
* context lives).
|
|
99
|
+
*
|
|
100
|
+
* @param {WebGLRenderingContext} gl
|
|
101
|
+
* @param {string} name
|
|
102
|
+
* @returns {WebGLBuffer}
|
|
103
|
+
*/
|
|
104
|
+
export function getBuffer(gl, name) {
|
|
105
|
+
const buffers = resourcesOf(gl).buffers
|
|
106
|
+
let buffer = buffers.get(name)
|
|
107
|
+
if (!buffer) {
|
|
108
|
+
buffer = gl.createBuffer()
|
|
109
|
+
buffers.set(name, buffer)
|
|
110
|
+
}
|
|
111
|
+
return buffer
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Who last filled a named buffer, so a style can tell whether the data it uploaded is
|
|
116
|
+
* still there or another style has overwritten it since.
|
|
117
|
+
* @param {WebGLRenderingContext} gl
|
|
118
|
+
* @param {string} name
|
|
119
|
+
* @param {any} [owner] sets the owner when given
|
|
120
|
+
* @returns {any} the owner
|
|
121
|
+
*/
|
|
122
|
+
export function bufferOwner(gl, name, owner = undefined) {
|
|
123
|
+
const owners = resourcesOf(gl).owners
|
|
124
|
+
if (owner !== undefined) owners.set(name, owner)
|
|
125
|
+
return owners.get(name)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Initialize a shader program, so WebGL knows how to draw our data
|
|
130
|
+
*
|
|
131
|
+
* @param {WebGLRenderingContext} gl
|
|
132
|
+
* @param {...WebGLShader} shaders
|
|
133
|
+
* @returns {WebGLProgram}
|
|
134
|
+
*/
|
|
135
|
+
export function initShaderProgram(gl, ...shaders) {
|
|
136
|
+
/** @type {WebGLProgram|null} */
|
|
137
|
+
const program = gl.createProgram()
|
|
138
|
+
if (program == null) throw new Error('Cannot create webGL program')
|
|
139
|
+
for (const shader of shaders) gl.attachShader(program, shader)
|
|
140
|
+
gl.linkProgram(program)
|
|
141
|
+
if (gl.getProgramParameter(program, gl.LINK_STATUS)) return program
|
|
142
|
+
throw new Error(gl.getProgramInfoLog(program) || 'Cannot create webGL program (2)')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Creates a shader of the given type, uploads the source and compiles it.
|
|
147
|
+
*
|
|
148
|
+
* @param {WebGLRenderingContext} gl
|
|
149
|
+
* @param {number} type
|
|
150
|
+
* @param {...string} sources
|
|
151
|
+
* @returns {WebGLShader}
|
|
152
|
+
*/
|
|
153
|
+
export function createShader(gl, type, ...sources) {
|
|
154
|
+
/** @type {WebGLShader|null} */
|
|
155
|
+
const shader = gl.createShader(type)
|
|
156
|
+
if (shader == null) throw new Error('Cannot create webGL shader')
|
|
157
|
+
gl.shaderSource(shader, sources.join('\n'))
|
|
158
|
+
gl.compileShader(shader)
|
|
159
|
+
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader
|
|
160
|
+
throw new Error(gl.getShaderInfoLog(shader) || 'Cannot create webGL shader (2)')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check if webGL is supported
|
|
165
|
+
*
|
|
166
|
+
* @returns {boolean}
|
|
167
|
+
*/
|
|
168
|
+
export function checkWebGLSupport() {
|
|
169
|
+
try {
|
|
170
|
+
const canvas = document.createElement('canvas')
|
|
171
|
+
return !!(
|
|
172
|
+
!!window.WebGLRenderingContext &&
|
|
173
|
+
(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
|
|
174
|
+
)
|
|
175
|
+
} catch (err) {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
}
|