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.
Files changed (48) hide show
  1. package/lib/AutumnTypes.d.ts +53 -5
  2. package/lib/AutumnTypes.js +25 -1
  3. package/lib/Barbs.d.ts +6 -5
  4. package/lib/BillboardCollection.d.ts +8 -7
  5. package/lib/BillboardCollection.js +69 -58
  6. package/lib/Color.d.ts +1 -0
  7. package/lib/Color.js +3 -0
  8. package/lib/ColorBar.d.ts +10 -0
  9. package/lib/ColorBar.js +4 -2
  10. package/lib/Colormap.js +8 -8
  11. package/lib/Contour.d.ts +18 -8
  12. package/lib/Contour.js +17 -54
  13. package/lib/ContourCreator.d.ts +4 -0
  14. package/lib/ContourCreator.js +2 -1
  15. package/lib/Fill.d.ts +26 -14
  16. package/lib/Fill.js +97 -50
  17. package/lib/Grid.d.ts +124 -29
  18. package/lib/Grid.js +297 -94
  19. package/lib/Hodographs.d.ts +9 -8
  20. package/lib/Hodographs.js +14 -11
  21. package/lib/Map.js +1 -1
  22. package/lib/Paintball.d.ts +6 -5
  23. package/lib/Paintball.js +35 -30
  24. package/lib/ParticleTracer.d.ts +19 -0
  25. package/lib/ParticleTracer.js +37 -0
  26. package/lib/PlotComponent.d.ts +6 -7
  27. package/lib/PlotComponent.js +8 -3
  28. package/lib/PlotLayer.d.ts +3 -3
  29. package/lib/PlotLayer.worker.d.ts +1 -2
  30. package/lib/PlotLayer.worker.js +15 -50
  31. package/lib/PolylineCollection.d.ts +5 -3
  32. package/lib/PolylineCollection.js +60 -37
  33. package/lib/RawField.d.ts +76 -23
  34. package/lib/RawField.js +138 -29
  35. package/lib/ShaderManager.d.ts +12 -0
  36. package/lib/ShaderManager.js +58 -0
  37. package/lib/StationPlot.d.ts +136 -25
  38. package/lib/StationPlot.js +192 -60
  39. package/lib/TextCollection.d.ts +9 -6
  40. package/lib/TextCollection.js +94 -62
  41. package/lib/cpp/marchingsquares.js +483 -585
  42. package/lib/cpp/marchingsquares.wasm +0 -0
  43. package/lib/cpp/marchingsquares_embind.d.ts +23 -3
  44. package/lib/index.d.ts +4 -3
  45. package/lib/index.js +4 -3
  46. package/lib/utils.d.ts +4 -1
  47. package/lib/utils.js +12 -1
  48. 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 { kdTree } from 'kd-tree-javascript';
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, matrix) {
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, matrix, [map_width, map_height], zoom, bearing, pitch));
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('{range}', '0-255').replace('{fontstack}', this.opts.font_face);
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.01 * Math.pow(2, 7 - map_max_zoom);
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
- if (min_label_lon === null || pt_lon < min_label_lon)
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 tree = new kdTree(label_pos, (a, b) => Math.hypot(a.lon - b.lon, a.lat - b.lat), ['lon', 'lat']);
200
- if (min_label_lon === null || min_label_lat === null || max_label_lon === null || max_label_lat === null) {
201
- return;
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, label_pos, font_url, tc_opts);
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, matrix) {
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, matrix, [map_width, map_height], map_zoom);
222
+ this.text_collection.render(gl, arg, [map_width, map_height], map_zoom);
260
223
  }
261
224
  }
262
225
  export default Contour;
@@ -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 };
@@ -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 map to use when creating the fills */
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>): Promise<void>;
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, matrix: number[] | Float32Array): void;
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>): Promise<void>;
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>): Promise<void>;
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, getGLFormatTypeAlignment } from './PlotComponent';
1
+ import { PlotComponent } from './PlotComponent';
2
2
  import { ColorMap, ColorMapGPUInterface } from './Colormap';
3
- import { WGLProgram, WGLTexture } from 'autumn-wgl';
3
+ import { WGLTexture } from 'autumn-wgl';
4
+ import { RawScalarField } from './RawField';
5
+ import { getRendererData } from './AutumnTypes';
4
6
  import { normalizeOptions } from './utils';
5
- const contourfill_vertex_shader_src = `uniform mat4 u_matrix;
7
+ import { ShaderProgramManager } from './ShaderManager';
8
+ const contourfill_vertex_shader_src = `#version 300 es
9
+
6
10
  uniform int u_offset;
7
11
 
8
- attribute vec2 a_pos;
9
- attribute vec2 a_tex_coord;
12
+ in vec2 a_pos;
13
+ in vec2 a_tex_coord;
10
14
 
11
- varying highp vec2 v_tex_coord;
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 = u_matrix * vec4(a_pos + globe_offset, 0.0, 1.0);
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 = `varying highp vec2 v_tex_coord;
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
- bool isnan(highp float val) {
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 = texture2D(u_fill_sampler, v_tex_coord).r;
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
- gl_FragColor = color;
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.cmap_gpu = new ColorMapGPUInterface(this.opts.cmap);
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 filtes in a subclass`;
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 tex_data = this.field.getTextureData();
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.setupShaderVariables(gl, this.cmap_mag_filter);
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, map: map, program: program, vertices: vertices, texcoords: texcoords,
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, matrix) {
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
- if (matrix instanceof Float32Array)
101
- matrix = [...matrix];
102
- gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_matrix': matrix, 'u_opacity': this.opts.opacity, 'u_offset': 0 }, { 'u_fill_sampler': this.fill_texture });
103
- this.cmap_gpu.bindShaderVariables(gl_elems.program);
104
- gl.enable(gl.BLEND);
105
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
106
- gl_elems.program.draw();
107
- gl_elems.program.setUniforms({ 'u_offset': -2 });
108
- gl_elems.program.draw();
109
- gl_elems.program.setUniforms({ 'u_offset': -1 });
110
- gl_elems.program.draw();
111
- gl_elems.program.setUniforms({ 'u_offset': 1 });
112
- gl_elems.program.draw();
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