autumnplot-gl 3.2.0 → 4.0.0-beta
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/lib/AutumnTypes.d.ts +53 -5
- package/lib/AutumnTypes.js +25 -1
- package/lib/Barbs.d.ts +6 -5
- package/lib/BillboardCollection.d.ts +8 -7
- package/lib/BillboardCollection.js +69 -58
- package/lib/Color.d.ts +1 -0
- package/lib/Color.js +3 -0
- package/lib/ColorBar.d.ts +10 -0
- package/lib/ColorBar.js +4 -2
- package/lib/Colormap.js +8 -8
- package/lib/Contour.d.ts +18 -8
- package/lib/Contour.js +17 -54
- package/lib/ContourCreator.d.ts +4 -0
- package/lib/ContourCreator.js +2 -1
- package/lib/Fill.d.ts +26 -14
- package/lib/Fill.js +97 -50
- package/lib/Grid.d.ts +124 -29
- package/lib/Grid.js +297 -94
- package/lib/Hodographs.d.ts +9 -8
- package/lib/Hodographs.js +14 -11
- package/lib/Map.js +1 -1
- package/lib/Paintball.d.ts +6 -5
- package/lib/Paintball.js +35 -30
- package/lib/ParticleTracer.d.ts +19 -0
- package/lib/ParticleTracer.js +37 -0
- package/lib/PlotComponent.d.ts +6 -7
- package/lib/PlotComponent.js +8 -3
- package/lib/PlotLayer.d.ts +3 -3
- package/lib/PlotLayer.worker.d.ts +1 -2
- package/lib/PlotLayer.worker.js +15 -50
- package/lib/PolylineCollection.d.ts +5 -3
- package/lib/PolylineCollection.js +60 -37
- package/lib/RawField.d.ts +76 -23
- package/lib/RawField.js +138 -29
- package/lib/ShaderManager.d.ts +12 -0
- package/lib/ShaderManager.js +58 -0
- package/lib/StationPlot.d.ts +136 -25
- package/lib/StationPlot.js +192 -60
- package/lib/TextCollection.d.ts +9 -6
- package/lib/TextCollection.js +94 -62
- package/lib/cpp/marchingsquares.js +483 -585
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +23 -3
- package/lib/index.d.ts +4 -3
- package/lib/index.js +4 -3
- package/lib/utils.d.ts +4 -1
- package/lib/utils.js +12 -1
- package/package.json +2 -2
package/lib/Contour.js
CHANGED
|
@@ -4,14 +4,15 @@ import { PolylineCollection, isLineStyle } from './PolylineCollection';
|
|
|
4
4
|
import { TextCollection } from './TextCollection';
|
|
5
5
|
import { Color } from './Color';
|
|
6
6
|
import { normalizeOptions } from './utils';
|
|
7
|
-
import {
|
|
7
|
+
import { UnstructuredGrid } from './Grid';
|
|
8
8
|
const contour_opt_defaults = {
|
|
9
9
|
color: '#000000',
|
|
10
10
|
cmap: null,
|
|
11
11
|
interval: 1,
|
|
12
12
|
levels: null,
|
|
13
13
|
line_width: 2,
|
|
14
|
-
line_style: '-'
|
|
14
|
+
line_style: '-',
|
|
15
|
+
quad_as_tri: false
|
|
15
16
|
};
|
|
16
17
|
/**
|
|
17
18
|
* A field of contoured data.
|
|
@@ -87,7 +88,7 @@ class Contour extends PlotComponent {
|
|
|
87
88
|
}
|
|
88
89
|
async getContours() {
|
|
89
90
|
const levels = this.opts.levels === null ? undefined : this.opts.levels;
|
|
90
|
-
return await this.field.getContours({ interval: this.opts.interval, levels: levels });
|
|
91
|
+
return await this.field.getContours({ interval: this.opts.interval, levels: levels, quad_as_tri: this.opts.quad_as_tri });
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* @internal
|
|
@@ -103,18 +104,16 @@ class Contour extends PlotComponent {
|
|
|
103
104
|
* @internal
|
|
104
105
|
* Render the contours
|
|
105
106
|
*/
|
|
106
|
-
render(gl,
|
|
107
|
+
render(gl, arg) {
|
|
107
108
|
if (this.gl_elems === null || this.contours === null)
|
|
108
109
|
return;
|
|
109
110
|
const gl_elems = this.gl_elems;
|
|
110
|
-
if (matrix instanceof Float32Array)
|
|
111
|
-
matrix = [...matrix];
|
|
112
111
|
const zoom = gl_elems.map.getZoom();
|
|
113
112
|
const map_width = gl_elems.map.getCanvas().width;
|
|
114
113
|
const map_height = gl_elems.map.getCanvas().height;
|
|
115
114
|
const bearing = gl_elems.map.getBearing();
|
|
116
115
|
const pitch = gl_elems.map.getPitch();
|
|
117
|
-
this.contours.forEach(cnt => cnt.render(gl,
|
|
116
|
+
this.contours.forEach(cnt => cnt.render(gl, arg, [map_width, map_height], zoom, bearing, pitch));
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
const contour_label_opt_defaults = {
|
|
@@ -124,7 +123,8 @@ const contour_label_opt_defaults = {
|
|
|
124
123
|
font_url_template: '',
|
|
125
124
|
text_color: '#000000',
|
|
126
125
|
halo_color: '#000000',
|
|
127
|
-
halo: false
|
|
126
|
+
halo: false,
|
|
127
|
+
density: 1
|
|
128
128
|
};
|
|
129
129
|
class ContourLabels extends PlotComponent {
|
|
130
130
|
constructor(contours, opts) {
|
|
@@ -146,14 +146,13 @@ class ContourLabels extends PlotComponent {
|
|
|
146
146
|
const font_url_template = this.opts.font_url_template == '' ? map_style.glyphs : this.opts.font_url_template;
|
|
147
147
|
if (font_url_template === undefined)
|
|
148
148
|
throw "The map style doesn't have any glyph information. Please pass the font_url_template option to ContourLabels";
|
|
149
|
-
const font_url = font_url_template.replace('{
|
|
149
|
+
const font_url = font_url_template.replace('{fontstack}', this.opts.font_face);
|
|
150
150
|
const label_pos = [];
|
|
151
151
|
const contour_data = await this.contours.getContours();
|
|
152
152
|
const contour_levels = Object.keys(contour_data).map(parseFloat);
|
|
153
153
|
contour_levels.sort((a, b) => a - b);
|
|
154
154
|
const map_max_zoom = map.getMaxZoom();
|
|
155
|
-
const contour_label_spacing = 0.
|
|
156
|
-
let min_label_lat = null, max_label_lat = null, min_label_lon = null, max_label_lon = null;
|
|
155
|
+
const contour_label_spacing = 0.006 / this.opts.density * Math.pow(2, 7 - map_max_zoom);
|
|
157
156
|
Object.entries(contour_data).forEach(([level, contours]) => {
|
|
158
157
|
const icntr = (parseFloat(level) - contour_levels[0]);
|
|
159
158
|
const level_str = level.toString();
|
|
@@ -182,57 +181,21 @@ class ContourLabels extends PlotComponent {
|
|
|
182
181
|
const alpha = (target_dist - dist[idist - 1]) / (dist[idist] - dist[idist - 1]);
|
|
183
182
|
const pt_lon = (1 - alpha) * pt1[0] + alpha * pt2[0];
|
|
184
183
|
const pt_lat = (1 - alpha) * pt1[1] + alpha * pt2[1];
|
|
185
|
-
|
|
186
|
-
min_label_lon = pt_lon;
|
|
187
|
-
if (max_label_lon === null || pt_lon > max_label_lon)
|
|
188
|
-
max_label_lon = pt_lon;
|
|
189
|
-
if (min_label_lat === null || pt_lat < min_label_lat)
|
|
190
|
-
min_label_lat = pt_lat;
|
|
191
|
-
if (max_label_lat === null || pt_lat > max_label_lat)
|
|
192
|
-
max_label_lat = pt_lat;
|
|
193
|
-
label_pos.push({ lon: pt_lon, lat: pt_lat, min_zoom: map_max_zoom, text: level_str });
|
|
184
|
+
label_pos.push({ coord: { lon: pt_lon, lat: pt_lat }, text: level_str });
|
|
194
185
|
n_labels_placed++;
|
|
195
186
|
}
|
|
196
187
|
}
|
|
197
188
|
});
|
|
198
189
|
});
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
const { x: min_label_x, y: max_label_y } = new LngLat(min_label_lon, min_label_lat).toMercatorCoord();
|
|
204
|
-
const { x: max_label_x, y: min_label_y } = new LngLat(max_label_lon, max_label_lat).toMercatorCoord();
|
|
205
|
-
const thin_grid_width = max_label_x - min_label_x;
|
|
206
|
-
const thin_grid_height = max_label_y - min_label_y;
|
|
207
|
-
const ni_thin_grid = Math.round(4 * thin_grid_width / contour_label_spacing);
|
|
208
|
-
const nj_thin_grid = Math.round(4 * thin_grid_height / contour_label_spacing);
|
|
209
|
-
const thin_grid_xs = [];
|
|
210
|
-
const thin_grid_ys = [];
|
|
211
|
-
for (let idx = 0; idx < ni_thin_grid; idx++) {
|
|
212
|
-
thin_grid_xs.push(min_label_x + (idx / ni_thin_grid) * thin_grid_width);
|
|
213
|
-
}
|
|
214
|
-
for (let jdy = 0; jdy < nj_thin_grid; jdy++) {
|
|
215
|
-
thin_grid_ys.push(min_label_y + (jdy / nj_thin_grid) * thin_grid_height);
|
|
216
|
-
}
|
|
217
|
-
let skip = 1;
|
|
218
|
-
for (let zoom = map_max_zoom - 1; zoom >= 0; zoom--) {
|
|
219
|
-
for (let idx = 0; idx < ni_thin_grid; idx += skip) {
|
|
220
|
-
for (let jdy = 0; jdy < nj_thin_grid; jdy += skip) {
|
|
221
|
-
const grid_x = thin_grid_xs[idx];
|
|
222
|
-
const grid_y = thin_grid_ys[jdy];
|
|
223
|
-
const ll = LngLat.fromMercatorCoord(grid_x, grid_y);
|
|
224
|
-
const [label, dist] = tree.nearest({ lon: ll.lng, lat: ll.lat, min_zoom: 0, text: "" }, 1)[0];
|
|
225
|
-
label.min_zoom = zoom;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
skip *= 2;
|
|
229
|
-
}
|
|
190
|
+
const label_grid = new UnstructuredGrid(label_pos.map(lp => lp.coord));
|
|
191
|
+
const min_zoom = label_grid.getMinVisibleZoom(4);
|
|
192
|
+
const text_specs = label_pos.map((lp, ilp) => ({ ...lp.coord, min_zoom: min_zoom[ilp], text: lp.text }));
|
|
230
193
|
const tc_opts = {
|
|
231
194
|
horizontal_align: 'center', vertical_align: 'middle', font_size: this.opts.font_size,
|
|
232
195
|
halo: this.opts.halo,
|
|
233
196
|
text_color: Color.fromHex(this.opts.text_color), halo_color: Color.fromHex(this.opts.halo_color),
|
|
234
197
|
};
|
|
235
|
-
this.text_collection = await TextCollection.make(gl,
|
|
198
|
+
this.text_collection = await TextCollection.make(gl, text_specs, font_url, tc_opts);
|
|
236
199
|
map.triggerRepaint();
|
|
237
200
|
}
|
|
238
201
|
/**
|
|
@@ -249,14 +212,14 @@ class ContourLabels extends PlotComponent {
|
|
|
249
212
|
* @internal
|
|
250
213
|
* Render the contour labels
|
|
251
214
|
*/
|
|
252
|
-
render(gl,
|
|
215
|
+
render(gl, arg) {
|
|
253
216
|
if (this.gl_elems === null || this.text_collection === null)
|
|
254
217
|
return;
|
|
255
218
|
const gl_elems = this.gl_elems;
|
|
256
219
|
const map_width = gl_elems.map.getCanvas().width;
|
|
257
220
|
const map_height = gl_elems.map.getCanvas().height;
|
|
258
221
|
const map_zoom = gl_elems.map.getZoom();
|
|
259
|
-
this.text_collection.render(gl,
|
|
222
|
+
this.text_collection.render(gl, arg, [map_width, map_height], map_zoom);
|
|
260
223
|
}
|
|
261
224
|
}
|
|
262
225
|
export default Contour;
|
package/lib/ContourCreator.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ interface FieldContourOpts {
|
|
|
12
12
|
* Contour the field at these specific levels.
|
|
13
13
|
*/
|
|
14
14
|
levels?: number[];
|
|
15
|
+
/**
|
|
16
|
+
* Add triangles in the contouring, which takes longer and generates more detailed (not necessarily smoother or better) contours
|
|
17
|
+
*/
|
|
18
|
+
quad_as_tri?: boolean;
|
|
15
19
|
}
|
|
16
20
|
declare function contourCreator<ArrayType extends TypedArray>(data: ArrayType, grid: Grid, opts: FieldContourOpts): Promise<ContourData>;
|
|
17
21
|
export { contourCreator, initMSModule };
|
package/lib/ContourCreator.js
CHANGED
|
@@ -12,12 +12,13 @@ async function contourCreator(data, grid, opts) {
|
|
|
12
12
|
throw "Must supply either an interval or levels to contourCreator()";
|
|
13
13
|
}
|
|
14
14
|
const interval = opts.interval === undefined ? 0 : opts.interval;
|
|
15
|
+
const quad_as_tri = opts.quad_as_tri === undefined ? false : opts.quad_as_tri;
|
|
15
16
|
const msm = await initMSModule();
|
|
16
17
|
const grid_coords = grid.getGridCoords();
|
|
17
18
|
const getContourLevels = data instanceof Float32Array ? msm.getContourLevelsFloat32 : msm.getContourLevelsFloat16;
|
|
18
19
|
const makeContours = data instanceof Float32Array ? msm.makeContoursFloat32 : msm.makeContoursFloat16;
|
|
19
20
|
const levels = opts.levels === undefined ? getContourLevels(data, grid.ni, grid.nj, interval) : opts.levels;
|
|
20
|
-
const contours = makeContours(data, grid_coords.x, grid_coords.y, levels, (x, y) => grid.transform(x, y, { inverse: true }));
|
|
21
|
+
const contours = makeContours(data, grid_coords.x, grid_coords.y, levels, (x, y) => grid.transform(x, y, { inverse: true }), quad_as_tri);
|
|
21
22
|
return contours;
|
|
22
23
|
}
|
|
23
24
|
export { contourCreator, initMSModule };
|
package/lib/Fill.d.ts
CHANGED
|
@@ -2,10 +2,16 @@ import { PlotComponent } from './PlotComponent';
|
|
|
2
2
|
import { ColorMap } from './Colormap';
|
|
3
3
|
import { RawScalarField } from './RawField';
|
|
4
4
|
import { MapLikeType } from './Map';
|
|
5
|
-
import { TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
|
|
5
|
+
import { RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
|
|
6
|
+
import { StructuredGrid } from './Grid';
|
|
6
7
|
interface ContourFillOptions {
|
|
7
|
-
/** The color
|
|
8
|
-
cmap: ColorMap;
|
|
8
|
+
/** The color maps to use when creating the fills */
|
|
9
|
+
cmap: ColorMap | ColorMap[];
|
|
10
|
+
/**
|
|
11
|
+
* A mask specifying where to use each color map. This should be on the same grid as the RawScalarField passed.
|
|
12
|
+
* A 1 in the mask means to use the first colormap, a 2 means to use the second colormap, etc.
|
|
13
|
+
*/
|
|
14
|
+
cmap_mask?: Uint8Array | null;
|
|
9
15
|
/**
|
|
10
16
|
* The opacity for the filled contours
|
|
11
17
|
* @default 1
|
|
@@ -14,25 +20,31 @@ interface ContourFillOptions {
|
|
|
14
20
|
}
|
|
15
21
|
interface RasterOptions {
|
|
16
22
|
/** The color map to use when creating the raster plot */
|
|
17
|
-
cmap: ColorMap;
|
|
23
|
+
cmap: ColorMap | ColorMap[];
|
|
24
|
+
/**
|
|
25
|
+
* A mask specifying where to use each color map. This should be on the same grid as the RawScalarField passed.
|
|
26
|
+
* A 1 in the mask means to use the first colormap, a 2 means to use the second colormap, etc.
|
|
27
|
+
*/
|
|
28
|
+
cmap_mask?: Uint8Array | null;
|
|
18
29
|
/**
|
|
19
30
|
* The opacity for the raster plot
|
|
20
31
|
* @default 1
|
|
21
32
|
*/
|
|
22
33
|
opacity?: number;
|
|
23
34
|
}
|
|
24
|
-
declare class PlotComponentFill<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
35
|
+
declare class PlotComponentFill<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
25
36
|
private field;
|
|
26
37
|
readonly opts: Required<ContourFillOptions>;
|
|
27
38
|
private readonly cmap_gpu;
|
|
28
39
|
private gl_elems;
|
|
29
40
|
private fill_texture;
|
|
41
|
+
private mask_texture;
|
|
30
42
|
protected image_mag_filter: number | null;
|
|
31
43
|
protected cmap_mag_filter: number | null;
|
|
32
|
-
constructor(field: RawScalarField<ArrayType>, opts: ContourFillOptions);
|
|
33
|
-
updateField(field: RawScalarField<ArrayType
|
|
44
|
+
constructor(field: RawScalarField<ArrayType, GridType>, opts: ContourFillOptions);
|
|
45
|
+
updateField(field: RawScalarField<ArrayType, GridType>, mask?: Uint8Array): Promise<void>;
|
|
34
46
|
onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
|
|
35
|
-
render(gl: WebGLAnyRenderingContext,
|
|
47
|
+
render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
|
|
36
48
|
}
|
|
37
49
|
/**
|
|
38
50
|
* A raster (i.e. pixel) plot
|
|
@@ -40,18 +52,18 @@ declare class PlotComponentFill<ArrayType extends TypedArray, MapType extends Ma
|
|
|
40
52
|
* // Create a raster plot with the provided color map
|
|
41
53
|
* const raster = new Raster(wind_speed_field, {cmap: color_map});
|
|
42
54
|
*/
|
|
43
|
-
declare class Raster<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, MapType> {
|
|
55
|
+
declare class Raster<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, GridType, MapType> {
|
|
44
56
|
/**
|
|
45
57
|
* Create a raster plot
|
|
46
58
|
* @param field - The field to create the raster plot from
|
|
47
59
|
* @param opts - Options for creating the raster plot
|
|
48
60
|
*/
|
|
49
|
-
constructor(field: RawScalarField<ArrayType>, opts: RasterOptions);
|
|
61
|
+
constructor(field: RawScalarField<ArrayType, GridType>, opts: RasterOptions);
|
|
50
62
|
/**
|
|
51
63
|
* Update the data displayed as a raster plot
|
|
52
64
|
* @param field - The new field to display as a raster plot
|
|
53
65
|
*/
|
|
54
|
-
updateField(field: RawScalarField<ArrayType
|
|
66
|
+
updateField(field: RawScalarField<ArrayType, GridType>, mask?: Uint8Array): Promise<void>;
|
|
55
67
|
/**
|
|
56
68
|
* @internal
|
|
57
69
|
* Add the raster plot to a map
|
|
@@ -69,18 +81,18 @@ declare class Raster<ArrayType extends TypedArray, MapType extends MapLikeType>
|
|
|
69
81
|
* // Create a field of filled contours with the provided color map
|
|
70
82
|
* const fill = new ContourFill(wind_speed_field, {cmap: color_map});
|
|
71
83
|
*/
|
|
72
|
-
declare class ContourFill<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, MapType> {
|
|
84
|
+
declare class ContourFill<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, GridType, MapType> {
|
|
73
85
|
/**
|
|
74
86
|
* Create a filled contoured field
|
|
75
87
|
* @param field - The field to create filled contours from
|
|
76
88
|
* @param opts - Options for creating the filled contours
|
|
77
89
|
*/
|
|
78
|
-
constructor(field: RawScalarField<ArrayType>, opts: ContourFillOptions);
|
|
90
|
+
constructor(field: RawScalarField<ArrayType, GridType>, opts: ContourFillOptions);
|
|
79
91
|
/**
|
|
80
92
|
* Update the data displayed as filled contours
|
|
81
93
|
* @param field - The new field to display as filled contours
|
|
82
94
|
*/
|
|
83
|
-
updateField(field: RawScalarField<ArrayType
|
|
95
|
+
updateField(field: RawScalarField<ArrayType, GridType>, mask?: Uint8Array): Promise<void>;
|
|
84
96
|
/**
|
|
85
97
|
* @internal
|
|
86
98
|
* Add the filled contours to a map
|
package/lib/Fill.js
CHANGED
|
@@ -1,49 +1,70 @@
|
|
|
1
|
-
import { PlotComponent
|
|
1
|
+
import { PlotComponent } from './PlotComponent';
|
|
2
2
|
import { ColorMap, ColorMapGPUInterface } from './Colormap';
|
|
3
|
-
import {
|
|
3
|
+
import { WGLTexture } from 'autumn-wgl';
|
|
4
|
+
import { RawScalarField } from './RawField';
|
|
5
|
+
import { getRendererData } from './AutumnTypes';
|
|
4
6
|
import { normalizeOptions } from './utils';
|
|
5
|
-
|
|
7
|
+
import { ShaderProgramManager } from './ShaderManager';
|
|
8
|
+
const contourfill_vertex_shader_src = `#version 300 es
|
|
9
|
+
|
|
6
10
|
uniform int u_offset;
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
in vec2 a_pos;
|
|
13
|
+
in vec2 a_tex_coord;
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
out highp vec2 v_tex_coord;
|
|
12
16
|
|
|
13
17
|
void main() {
|
|
14
18
|
float globe_width = 1.;
|
|
15
19
|
vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
|
|
16
20
|
|
|
17
|
-
gl_Position =
|
|
21
|
+
gl_Position = projectTile(a_pos.xy + globe_offset);
|
|
18
22
|
v_tex_coord = a_tex_coord;
|
|
19
23
|
}`
|
|
20
|
-
const contourfill_fragment_shader_src =
|
|
24
|
+
const contourfill_fragment_shader_src = `#version 300 es
|
|
25
|
+
|
|
26
|
+
in highp vec2 v_tex_coord;
|
|
21
27
|
|
|
22
28
|
uniform sampler2D u_fill_sampler;
|
|
29
|
+
|
|
30
|
+
#ifdef MASK
|
|
31
|
+
uniform sampler2D u_mask_sampler;
|
|
32
|
+
#endif
|
|
33
|
+
|
|
23
34
|
uniform highp float u_opacity;
|
|
35
|
+
#ifdef MASK
|
|
36
|
+
uniform int u_mask_val;
|
|
37
|
+
#endif
|
|
24
38
|
|
|
25
|
-
|
|
26
|
-
return ( val < 0.0 || 0.0 < val || val == 0.0 ) ? false : true;
|
|
27
|
-
}
|
|
39
|
+
out highp vec4 fragColor;
|
|
28
40
|
|
|
29
41
|
void main() {
|
|
30
|
-
highp float fill_val =
|
|
42
|
+
highp float fill_val = texture(u_fill_sampler, v_tex_coord).r;
|
|
43
|
+
|
|
44
|
+
int draw_mask = 1;
|
|
45
|
+
|
|
46
|
+
#ifdef MASK
|
|
47
|
+
highp float mask_val = texture(u_mask_sampler, v_tex_coord).r;
|
|
48
|
+
draw_mask = int(mask_val * 255.0) == u_mask_val ? 1 : 0;
|
|
49
|
+
#endif
|
|
31
50
|
|
|
32
|
-
if (isnan(fill_val)) {
|
|
51
|
+
if (isnan(fill_val) || draw_mask == 0) {
|
|
33
52
|
discard;
|
|
34
53
|
}
|
|
35
54
|
|
|
36
55
|
lowp vec4 color = apply_colormap(fill_val);
|
|
37
56
|
color.a = color.a * u_opacity;
|
|
38
|
-
|
|
57
|
+
fragColor = color;
|
|
39
58
|
}`
|
|
40
59
|
const default_cmap = new ColorMap([0, 1], ['#000000'], { overflow_color: '#000000', underflow_color: '#000000' });
|
|
41
60
|
const contour_fill_opt_defaults = {
|
|
42
|
-
cmap: default_cmap,
|
|
61
|
+
cmap: [default_cmap],
|
|
62
|
+
cmap_mask: null,
|
|
43
63
|
opacity: 1,
|
|
44
64
|
};
|
|
45
65
|
const raster_opt_defaults = {
|
|
46
|
-
cmap: default_cmap,
|
|
66
|
+
cmap: [default_cmap],
|
|
67
|
+
cmap_mask: null,
|
|
47
68
|
opacity: 1,
|
|
48
69
|
};
|
|
49
70
|
class PlotComponentFill extends PlotComponent {
|
|
@@ -51,65 +72,91 @@ class PlotComponentFill extends PlotComponent {
|
|
|
51
72
|
super();
|
|
52
73
|
this.field = field;
|
|
53
74
|
this.opts = normalizeOptions(opts, contour_fill_opt_defaults);
|
|
54
|
-
this.
|
|
75
|
+
this.opts.cmap = Array.isArray(this.opts.cmap) ? this.opts.cmap : [this.opts.cmap];
|
|
76
|
+
this.cmap_gpu = this.opts.cmap.map(cm => new ColorMapGPUInterface(cm));
|
|
55
77
|
this.gl_elems = null;
|
|
56
78
|
this.fill_texture = null;
|
|
79
|
+
this.mask_texture = null;
|
|
57
80
|
this.image_mag_filter = null;
|
|
58
81
|
this.cmap_mag_filter = null;
|
|
59
82
|
}
|
|
60
|
-
async updateField(field) {
|
|
83
|
+
async updateField(field, mask) {
|
|
61
84
|
this.field = field;
|
|
62
85
|
if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
|
|
63
|
-
throw `Implement magnification
|
|
86
|
+
throw `Implement magnification filters in a subclass`;
|
|
64
87
|
}
|
|
65
88
|
if (this.gl_elems === null)
|
|
66
89
|
return;
|
|
67
90
|
const gl = this.gl_elems.gl;
|
|
68
91
|
const map = this.gl_elems.map;
|
|
69
|
-
const
|
|
70
|
-
const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(tex_data instanceof Float32Array));
|
|
71
|
-
const fill_image = { 'format': format, 'type': type,
|
|
72
|
-
'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': tex_data,
|
|
73
|
-
'mag_filter': this.image_mag_filter, 'row_alignment': row_alignment,
|
|
74
|
-
};
|
|
92
|
+
const fill_image = this.field.getWGLTextureSpec(gl, this.image_mag_filter);
|
|
75
93
|
if (this.fill_texture === null) {
|
|
76
94
|
this.fill_texture = new WGLTexture(gl, fill_image);
|
|
77
95
|
}
|
|
78
96
|
else {
|
|
79
97
|
this.fill_texture.setImageData(fill_image);
|
|
80
98
|
}
|
|
99
|
+
if (mask !== undefined) {
|
|
100
|
+
if (this.opts.cmap_mask === null) {
|
|
101
|
+
console.warn("A mask was passed to updateField on a Fill component that didn't have a mask. The updated mask will be ignored.");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const mask_field = new RawScalarField(this.field.grid, mask);
|
|
105
|
+
const mask_image = mask_field.getWGLTextureSpec(gl, gl.NEAREST);
|
|
106
|
+
if (this.mask_texture === null) {
|
|
107
|
+
this.mask_texture = new WGLTexture(gl, mask_image);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.mask_texture.setImageData(mask_image);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
81
113
|
map.triggerRepaint();
|
|
82
114
|
}
|
|
83
115
|
async onAdd(map, gl) {
|
|
84
116
|
// Basic procedure for the filled contours inspired by https://blog.mbq.me/webgl-weather-globe/
|
|
85
|
-
if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
|
|
86
|
-
throw `Implement magnification filtes in a subclass`;
|
|
87
|
-
}
|
|
88
|
-
const program = new WGLProgram(gl, contourfill_vertex_shader_src, ColorMapGPUInterface.applyShader(contourfill_fragment_shader_src));
|
|
89
117
|
const { vertices: vertices, texcoords: texcoords } = await this.field.grid.getWGLBuffers(gl);
|
|
90
|
-
this.cmap_gpu.
|
|
118
|
+
this.cmap_gpu.forEach(cmg => {
|
|
119
|
+
if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
|
|
120
|
+
throw `Implement magnification filters in a subclass`;
|
|
121
|
+
}
|
|
122
|
+
cmg.setupShaderVariables(gl, this.cmap_mag_filter);
|
|
123
|
+
});
|
|
124
|
+
const shader_defines = [];
|
|
125
|
+
if (this.opts.cmap_mask !== null) {
|
|
126
|
+
shader_defines.push('MASK');
|
|
127
|
+
}
|
|
128
|
+
const shader_manger = new ShaderProgramManager(contourfill_vertex_shader_src, ColorMapGPUInterface.applyShader(contourfill_fragment_shader_src), shader_defines);
|
|
91
129
|
this.gl_elems = {
|
|
92
|
-
gl: gl,
|
|
130
|
+
gl: gl, shader_manager: shader_manger, map: map, vertices: vertices, texcoords: texcoords,
|
|
93
131
|
};
|
|
94
|
-
this.updateField(this.field);
|
|
132
|
+
this.updateField(this.field, this.opts.cmap_mask === null ? undefined : this.opts.cmap_mask);
|
|
95
133
|
}
|
|
96
|
-
render(gl,
|
|
134
|
+
render(gl, arg) {
|
|
97
135
|
if (this.gl_elems === null || this.fill_texture === null)
|
|
98
136
|
return;
|
|
99
137
|
const gl_elems = this.gl_elems;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.cmap_gpu.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
const render_data = getRendererData(arg);
|
|
139
|
+
const program = this.gl_elems.shader_manager.getShaderProgram(gl, render_data.shaderData);
|
|
140
|
+
program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_opacity': this.opts.opacity, ...this.gl_elems.shader_manager.getShaderUniforms(render_data) }, { 'u_fill_sampler': this.fill_texture });
|
|
141
|
+
this.cmap_gpu.forEach((cmg, icmg) => {
|
|
142
|
+
program.setUniforms({ 'u_offset': 0 });
|
|
143
|
+
if (this.opts.cmap_mask !== null && this.mask_texture !== null) {
|
|
144
|
+
program.setUniforms({ 'u_mask_val': icmg + 1 });
|
|
145
|
+
program.bindTextures({ 'u_mask_sampler': this.mask_texture });
|
|
146
|
+
}
|
|
147
|
+
cmg.bindShaderVariables(program);
|
|
148
|
+
gl.enable(gl.BLEND);
|
|
149
|
+
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
150
|
+
program.draw();
|
|
151
|
+
if (render_data.type != 'maplibre' || !render_data.shaderData.define.includes('GLOBE')) {
|
|
152
|
+
program.setUniforms({ 'u_offset': -2 });
|
|
153
|
+
program.draw();
|
|
154
|
+
program.setUniforms({ 'u_offset': -1 });
|
|
155
|
+
program.draw();
|
|
156
|
+
program.setUniforms({ 'u_offset': 1 });
|
|
157
|
+
program.draw();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
113
160
|
}
|
|
114
161
|
}
|
|
115
162
|
/**
|
|
@@ -131,8 +178,8 @@ class Raster extends PlotComponentFill {
|
|
|
131
178
|
* Update the data displayed as a raster plot
|
|
132
179
|
* @param field - The new field to display as a raster plot
|
|
133
180
|
*/
|
|
134
|
-
async updateField(field) {
|
|
135
|
-
await super.updateField(field);
|
|
181
|
+
async updateField(field, mask) {
|
|
182
|
+
await super.updateField(field, mask);
|
|
136
183
|
}
|
|
137
184
|
/**
|
|
138
185
|
* @internal
|
|
@@ -170,8 +217,8 @@ class ContourFill extends PlotComponentFill {
|
|
|
170
217
|
* Update the data displayed as filled contours
|
|
171
218
|
* @param field - The new field to display as filled contours
|
|
172
219
|
*/
|
|
173
|
-
async updateField(field) {
|
|
174
|
-
await super.updateField(field);
|
|
220
|
+
async updateField(field, mask) {
|
|
221
|
+
await super.updateField(field, mask);
|
|
175
222
|
}
|
|
176
223
|
/**
|
|
177
224
|
* @internal
|