autumnplot-gl 2.2.3 → 3.1.0

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 (49) hide show
  1. package/README.md +65 -8
  2. package/dist/110.autumnplot-gl.js +2 -0
  3. package/dist/110.autumnplot-gl.js.map +1 -0
  4. package/dist/autumnplot-gl.js +3 -0
  5. package/dist/autumnplot-gl.js.LICENSE.txt +12 -0
  6. package/dist/autumnplot-gl.js.map +1 -0
  7. package/dist/marchingsquares.wasm +0 -0
  8. package/lib/AutumnTypes.d.ts +14 -13
  9. package/lib/Barbs.d.ts +10 -5
  10. package/lib/Barbs.js +22 -5
  11. package/lib/BillboardCollection.d.ts +11 -7
  12. package/lib/BillboardCollection.js +52 -30
  13. package/lib/ColorBar.js +51 -7
  14. package/lib/Colormap.d.ts +14 -3
  15. package/lib/Colormap.js +63 -12
  16. package/lib/Contour.d.ts +73 -18
  17. package/lib/Contour.js +180 -148
  18. package/lib/ContourCreator.d.ts +18 -0
  19. package/lib/ContourCreator.js +23 -0
  20. package/lib/Fill.d.ts +18 -7
  21. package/lib/Fill.js +73 -41
  22. package/lib/Grid.d.ts +167 -0
  23. package/lib/Grid.js +339 -0
  24. package/lib/Hodographs.d.ts +13 -6
  25. package/lib/Hodographs.js +58 -71
  26. package/lib/Map.d.ts +14 -3
  27. package/lib/Map.js +9 -0
  28. package/lib/Paintball.d.ts +11 -5
  29. package/lib/Paintball.js +35 -15
  30. package/lib/PlotComponent.d.ts +5 -5
  31. package/lib/PlotLayer.d.ts +12 -12
  32. package/lib/PlotLayer.js +16 -14
  33. package/lib/PlotLayer.worker.d.ts +2 -2
  34. package/lib/PlotLayer.worker.js +105 -66
  35. package/lib/PolylineCollection.d.ts +20 -9
  36. package/lib/PolylineCollection.js +158 -32
  37. package/lib/RawField.d.ts +11 -167
  38. package/lib/RawField.js +37 -383
  39. package/lib/TextCollection.d.ts +31 -0
  40. package/lib/TextCollection.js +295 -0
  41. package/lib/cpp/marchingsquares.d.ts +6 -0
  42. package/lib/cpp/marchingsquares.js +3449 -0
  43. package/lib/cpp/marchingsquares.wasm +0 -0
  44. package/lib/cpp/marchingsquares_embind.d.ts +6 -0
  45. package/lib/index.d.ts +13 -6
  46. package/lib/index.js +12 -3
  47. package/lib/utils.d.ts +5 -3
  48. package/lib/utils.js +17 -6
  49. package/package.json +14 -9
package/lib/Fill.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { PlotComponent, getGLFormatTypeAlignment } from './PlotComponent';
2
- import { makeTextureImage } from './Colormap';
2
+ import { ColorMap, makeIndexMap, makeTextureImage } from './Colormap';
3
3
  import { WGLProgram, WGLTexture } from 'autumn-wgl';
4
- import { Float16Array } from '@petamoriken/float16';
4
+ import { hex2rgb, normalizeOptions } from './utils';
5
5
  const contourfill_vertex_shader_src = `uniform mat4 u_matrix;
6
6
  uniform int u_offset;
7
7
 
@@ -25,6 +25,8 @@ uniform sampler2D u_cmap_nonlin_sampler;
25
25
  uniform highp float u_cmap_min;
26
26
  uniform highp float u_cmap_max;
27
27
  uniform highp float u_opacity;
28
+ uniform highp vec4 u_underflow_color;
29
+ uniform highp vec4 u_overflow_color;
28
30
  uniform int u_n_index;
29
31
 
30
32
  void main() {
@@ -32,45 +34,63 @@ void main() {
32
34
  highp float fill_val = texture2D(u_fill_sampler, v_tex_coord).r;
33
35
  lowp float normed_val = (fill_val - u_cmap_min) / (u_cmap_max - u_cmap_min);
34
36
 
35
- if (normed_val < 0.0 || normed_val > 1.0) {
36
- discard;
37
+ lowp vec4 color;
38
+ if (normed_val < 0.0) {
39
+ color = u_underflow_color;
40
+ }
41
+ else if (normed_val > 1.0) {
42
+ color = u_overflow_color;
43
+ }
44
+ else {
45
+ normed_val = index_buffer + normed_val * (1. - 2. * index_buffer);
46
+ highp float nonlin_val = texture2D(u_cmap_nonlin_sampler, vec2(normed_val, 0.5)).r;
47
+ color = texture2D(u_cmap_sampler, vec2(nonlin_val, 0.5));
37
48
  }
38
49
 
39
- normed_val = index_buffer + normed_val * (1. - 2. * index_buffer);
40
- highp float nonlin_val = texture2D(u_cmap_nonlin_sampler, vec2(normed_val, 0.5)).r;
41
- lowp vec4 color = texture2D(u_cmap_sampler, vec2(nonlin_val, 0.5));
42
50
  color.a = color.a * u_opacity;
43
51
  gl_FragColor = color;
44
52
  }`
53
+ const default_cmap = new ColorMap([0, 1], ['#000000'], { overflow_color: '#000000', underflow_color: '#000000' });
54
+ const contour_fill_opt_defaults = {
55
+ cmap: default_cmap,
56
+ opacity: 1,
57
+ };
58
+ const raster_opt_defaults = {
59
+ cmap: default_cmap,
60
+ opacity: 1,
61
+ };
45
62
  class PlotComponentFill extends PlotComponent {
46
63
  constructor(field, opts) {
47
64
  super();
48
65
  this.field = field;
49
- this.cmap = opts.cmap;
50
- this.opacity = opts.opacity || 1.;
51
- this.cmap_image = makeTextureImage(this.cmap);
52
- const levels = this.cmap.levels;
53
- const n_lev = levels.length - 1;
54
- // Build a texture to account for nonlinear colormaps (basically inverts the relationship between
55
- // the normalized index and the normalized level)
56
- const n_nonlin = 101;
57
- const map_norm = [];
58
- for (let i = 0; i < n_nonlin; i++) {
59
- map_norm.push(i / (n_nonlin - 1));
60
- }
61
- const input_norm = levels.map((lev, ilev) => ilev / n_lev);
62
- const cmap_norm = levels.map(lev => (lev - levels[0]) / (levels[n_lev] - levels[0]));
63
- const inv_cmap_norm = map_norm.map(lev => {
64
- let jlev;
65
- for (jlev = 0; !(cmap_norm[jlev] <= lev && lev <= cmap_norm[jlev + 1]); jlev++) { }
66
- const alpha = (lev - cmap_norm[jlev]) / (cmap_norm[jlev + 1] - cmap_norm[jlev]);
67
- return input_norm[jlev] * (1 - alpha) + input_norm[jlev + 1] * alpha;
68
- });
69
- this.index_map = new Float16Array(inv_cmap_norm);
66
+ this.opts = normalizeOptions(opts, contour_fill_opt_defaults);
67
+ this.cmap_image = makeTextureImage(this.opts.cmap);
68
+ this.index_map = makeIndexMap(this.opts.cmap);
70
69
  this.gl_elems = null;
70
+ this.fill_texture = null;
71
71
  this.image_mag_filter = null;
72
72
  this.cmap_mag_filter = null;
73
73
  }
74
+ async updateField(field) {
75
+ this.field = field;
76
+ if (this.gl_elems === null)
77
+ return;
78
+ const gl = this.gl_elems.gl;
79
+ const map = this.gl_elems.map;
80
+ const tex_data = this.field.getTextureData();
81
+ const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(tex_data instanceof Float32Array));
82
+ const fill_image = { 'format': format, 'type': type,
83
+ 'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': tex_data,
84
+ 'mag_filter': this.image_mag_filter, 'row_alignment': row_alignment,
85
+ };
86
+ if (this.fill_texture === null) {
87
+ this.fill_texture = new WGLTexture(gl, fill_image);
88
+ }
89
+ else {
90
+ this.fill_texture.setImageData(fill_image);
91
+ }
92
+ map.triggerRepaint();
93
+ }
74
94
  async onAdd(map, gl) {
75
95
  // Basic procedure for the filled contours inspired by https://blog.mbq.me/webgl-weather-globe/
76
96
  if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
@@ -80,12 +100,6 @@ class PlotComponentFill extends PlotComponent {
80
100
  const { vertices: verts_buf, texcoords: tex_coords_buf } = await this.field.grid.getWGLBuffers(gl);
81
101
  const vertices = verts_buf;
82
102
  const texcoords = tex_coords_buf;
83
- const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, this.field.isFloat16());
84
- const fill_image = { 'format': format, 'type': type,
85
- 'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': this.field.getTextureData(),
86
- 'mag_filter': this.image_mag_filter, 'row_alignment': row_alignment,
87
- };
88
- const fill_texture = new WGLTexture(gl, fill_image);
89
103
  const cmap_image = { 'format': gl.RGBA, 'type': gl.UNSIGNED_BYTE, 'image': this.cmap_image, 'mag_filter': this.cmap_mag_filter };
90
104
  const cmap_texture = new WGLTexture(gl, cmap_image);
91
105
  const { format: format_nonlin, type: type_nonlin, row_alignment: row_alignment_nonlin } = getGLFormatTypeAlignment(gl, true);
@@ -96,18 +110,22 @@ class PlotComponentFill extends PlotComponent {
96
110
  };
97
111
  const cmap_nonlin_texture = new WGLTexture(gl, cmap_nonlin_image);
98
112
  this.gl_elems = {
99
- program: program, vertices: vertices, texcoords: texcoords,
100
- fill_texture: fill_texture, cmap_texture: cmap_texture, cmap_nonlin_texture: cmap_nonlin_texture,
113
+ gl: gl, map: map, program: program, vertices: vertices, texcoords: texcoords,
114
+ cmap_texture: cmap_texture, cmap_nonlin_texture: cmap_nonlin_texture,
101
115
  };
116
+ this.updateField(this.field);
102
117
  }
103
118
  render(gl, matrix) {
104
- if (this.gl_elems === null)
119
+ if (this.gl_elems === null || this.fill_texture === null)
105
120
  return;
106
121
  const gl_elems = this.gl_elems;
107
122
  if (matrix instanceof Float32Array)
108
123
  matrix = [...matrix];
109
- gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_cmap_min': this.cmap.levels[0], 'u_cmap_max': this.cmap.levels[this.cmap.levels.length - 1], 'u_matrix': matrix, 'u_opacity': this.opacity,
110
- 'u_n_index': this.index_map.length, 'u_offset': 0 }, { 'u_fill_sampler': gl_elems.fill_texture, 'u_cmap_sampler': gl_elems.cmap_texture, 'u_cmap_nonlin_sampler': gl_elems.cmap_nonlin_texture });
124
+ const cmap = this.opts.cmap;
125
+ const underflow_color = cmap.underflow_color === null ? [0, 0, 0, 0] : hex2rgb(cmap.underflow_color.color).concat(cmap.underflow_color.opacity);
126
+ const overflow_color = cmap.overflow_color === null ? [0, 0, 0, 0] : hex2rgb(cmap.overflow_color.color).concat(cmap.overflow_color.opacity);
127
+ gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_cmap_min': cmap.levels[0], 'u_cmap_max': cmap.levels[cmap.levels.length - 1], 'u_matrix': matrix, 'u_opacity': this.opts.opacity,
128
+ 'u_n_index': this.index_map.length, 'u_underflow_color': underflow_color, 'u_overflow_color': overflow_color, 'u_offset': 0 }, { 'u_fill_sampler': this.fill_texture, 'u_cmap_sampler': gl_elems.cmap_texture, 'u_cmap_nonlin_sampler': gl_elems.cmap_nonlin_texture });
111
129
  gl.enable(gl.BLEND);
112
130
  gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
113
131
  gl_elems.program.draw();
@@ -134,6 +152,13 @@ class Raster extends PlotComponentFill {
134
152
  constructor(field, opts) {
135
153
  super(field, opts);
136
154
  }
155
+ /**
156
+ * Update the data displayed as a raster plot
157
+ * @param field - The new field to display as a raster plot
158
+ */
159
+ async updateField(field) {
160
+ await super.updateField(field);
161
+ }
137
162
  /**
138
163
  * @internal
139
164
  * Add the raster plot to a map
@@ -141,7 +166,7 @@ class Raster extends PlotComponentFill {
141
166
  async onAdd(map, gl) {
142
167
  this.image_mag_filter = gl.NEAREST;
143
168
  this.cmap_mag_filter = gl.LINEAR;
144
- super.onAdd(map, gl);
169
+ await super.onAdd(map, gl);
145
170
  }
146
171
  /**
147
172
  * @internal
@@ -166,6 +191,13 @@ class ContourFill extends PlotComponentFill {
166
191
  constructor(field, opts) {
167
192
  super(field, opts);
168
193
  }
194
+ /**
195
+ * Update the data displayed as filled contours
196
+ * @param field - The new field to display as filled contours
197
+ */
198
+ async updateField(field) {
199
+ await super.updateField(field);
200
+ }
169
201
  /**
170
202
  * @internal
171
203
  * Add the filled contours to a map
@@ -173,7 +205,7 @@ class ContourFill extends PlotComponentFill {
173
205
  async onAdd(map, gl) {
174
206
  this.image_mag_filter = gl.LINEAR;
175
207
  this.cmap_mag_filter = gl.NEAREST;
176
- super.onAdd(map, gl);
208
+ await super.onAdd(map, gl);
177
209
  }
178
210
  /**
179
211
  * @internal
package/lib/Grid.d.ts ADDED
@@ -0,0 +1,167 @@
1
+ import { WGLBuffer, WGLTexture } from "autumn-wgl";
2
+ import { WebGLAnyRenderingContext } from "./AutumnTypes";
3
+ interface EarthCoords {
4
+ lons: Float32Array;
5
+ lats: Float32Array;
6
+ }
7
+ interface GridCoords {
8
+ x: Float32Array;
9
+ y: Float32Array;
10
+ }
11
+ type GridType = 'latlon' | 'latlonrot' | 'lcc';
12
+ declare abstract class Grid {
13
+ readonly type: GridType;
14
+ readonly ni: number;
15
+ readonly nj: number;
16
+ readonly is_conformal: boolean;
17
+ private readonly buffer_cache;
18
+ private readonly billboard_buffer_cache;
19
+ private readonly vector_rotation_cache;
20
+ constructor(type: GridType, is_conformal: boolean, ni: number, nj: number);
21
+ abstract copy(opts?: {
22
+ ni?: number;
23
+ nj?: number;
24
+ }): Grid;
25
+ abstract getEarthCoords(): EarthCoords;
26
+ abstract getGridCoords(): GridCoords;
27
+ abstract transform(x: number, y: number, opts?: {
28
+ inverse?: boolean;
29
+ }): [number, number];
30
+ abstract getThinnedGrid(thin_x: number, thin_y: number): Grid;
31
+ getWGLBuffers(gl: WebGLAnyRenderingContext): Promise<{
32
+ vertices: WGLBuffer;
33
+ texcoords: WGLBuffer;
34
+ cellsize: WGLBuffer;
35
+ }>;
36
+ getWGLBillboardBuffers(gl: WebGLAnyRenderingContext, thin_fac: number, max_zoom: number): Promise<{
37
+ vertices: WGLBuffer;
38
+ texcoords: WGLBuffer;
39
+ }>;
40
+ getVectorRotationTexture(gl: WebGLAnyRenderingContext): {
41
+ rotation: WGLTexture;
42
+ };
43
+ }
44
+ /** A plate carree (a.k.a. lat/lon) grid with uniform grid spacing */
45
+ declare class PlateCarreeGrid extends Grid {
46
+ readonly ll_lon: number;
47
+ readonly ll_lat: number;
48
+ readonly ur_lon: number;
49
+ readonly ur_lat: number;
50
+ private readonly ll_cache;
51
+ private readonly gc_cache;
52
+ /**
53
+ * Create a plate carree grid
54
+ * @param ni - The number of grid points in the i (longitude) direction
55
+ * @param nj - The number of grid points in the j (latitude) direction
56
+ * @param ll_lon - The longitude of the lower left corner of the grid
57
+ * @param ll_lat - The latitude of the lower left corner of the grid
58
+ * @param ur_lon - The longitude of the upper right corner of the grid
59
+ * @param ur_lat - The latitude of the upper right corner of the grid
60
+ */
61
+ constructor(ni: number, nj: number, ll_lon: number, ll_lat: number, ur_lon: number, ur_lat: number);
62
+ copy(opts?: {
63
+ ni?: number;
64
+ nj?: number;
65
+ ll_lon?: number;
66
+ ll_lat?: number;
67
+ ur_lon?: number;
68
+ ur_lat?: number;
69
+ }): PlateCarreeGrid;
70
+ /**
71
+ * Get a list of longitudes and latitudes on the grid (internal method)
72
+ */
73
+ getEarthCoords(): EarthCoords;
74
+ getGridCoords(): GridCoords;
75
+ transform(x: number, y: number, opts?: {
76
+ inverse?: boolean;
77
+ }): [number, number];
78
+ getThinnedGrid(thin_x: number, thin_y: number): PlateCarreeGrid;
79
+ }
80
+ /** A rotated lat-lon (plate carree) grid with uniform grid spacing */
81
+ declare class PlateCarreeRotatedGrid extends Grid {
82
+ readonly np_lon: number;
83
+ readonly np_lat: number;
84
+ readonly lon_shift: number;
85
+ readonly ll_lon: number;
86
+ readonly ll_lat: number;
87
+ readonly ur_lon: number;
88
+ readonly ur_lat: number;
89
+ private readonly llrot;
90
+ private readonly ll_cache;
91
+ private readonly gc_cache;
92
+ /**
93
+ * Create a Lambert conformal conic grid
94
+ * @param ni - The number of grid points in the i (longitude) direction
95
+ * @param nj - The number of grid points in the j (latitude) direction
96
+ * @param np_lon - The longitude of the north pole for the rotated grid
97
+ * @param np_lat - The latitude of the north pole for the rotated grid
98
+ * @param lon_shift - The angle around the rotated north pole to shift the central meridian
99
+ * @param ll_lon - The longitude of the lower left corner of the grid (on the rotated earth)
100
+ * @param ll_lat - The latitude of the lower left corner of the grid (on the rotated earth)
101
+ * @param ur_lon - The longitude of the upper right corner of the grid (on the rotated earth)
102
+ * @param ur_lat - The latitude of the upper right corner of the grid (on the rotated earth)
103
+ */
104
+ constructor(ni: number, nj: number, np_lon: number, np_lat: number, lon_shift: number, ll_lon: number, ll_lat: number, ur_lon: number, ur_lat: number);
105
+ copy(opts?: {
106
+ ni?: number;
107
+ nj?: number;
108
+ ll_lon?: number;
109
+ ll_lat?: number;
110
+ ur_lon?: number;
111
+ ur_lat?: number;
112
+ }): PlateCarreeRotatedGrid;
113
+ /**
114
+ * Get a list of longitudes and latitudes on the grid (internal method)
115
+ */
116
+ getEarthCoords(): EarthCoords;
117
+ getGridCoords(): GridCoords;
118
+ transform(x: number, y: number, opts?: {
119
+ inverse?: boolean;
120
+ }): [number, number];
121
+ getThinnedGrid(thin_x: number, thin_y: number): PlateCarreeRotatedGrid;
122
+ }
123
+ /** A Lambert conformal conic grid with uniform grid spacing */
124
+ declare class LambertGrid extends Grid {
125
+ readonly lon_0: number;
126
+ readonly lat_0: number;
127
+ readonly lat_std: [number, number];
128
+ readonly ll_x: number;
129
+ readonly ll_y: number;
130
+ readonly ur_x: number;
131
+ readonly ur_y: number;
132
+ private readonly lcc;
133
+ private readonly ll_cache;
134
+ private readonly gc_cache;
135
+ /**
136
+ * Create a Lambert conformal conic grid
137
+ * @param ni - The number of grid points in the i (longitude) direction
138
+ * @param nj - The number of grid points in the j (latitude) direction
139
+ * @param lon_0 - The standard longitude for the projection; this is also the center longitude for the projection
140
+ * @param lat_0 - The center latitude for the projection
141
+ * @param lat_std - The standard latitudes for the projection
142
+ * @param ll_x - The x coordinate in projection space of the lower-left corner of the grid
143
+ * @param ll_y - The y coordinate in projection space of the lower-left corner of the grid
144
+ * @param ur_x - The x coordinate in projection space of the upper-right corner of the grid
145
+ * @param ur_y - The y coordinate in projection space of the upper-right corner of the grid
146
+ */
147
+ constructor(ni: number, nj: number, lon_0: number, lat_0: number, lat_std: [number, number], ll_x: number, ll_y: number, ur_x: number, ur_y: number);
148
+ copy(opts?: {
149
+ ni?: number;
150
+ nj?: number;
151
+ ll_x?: number;
152
+ ll_y?: number;
153
+ ur_x?: number;
154
+ ur_y?: number;
155
+ }): LambertGrid;
156
+ /**
157
+ * Get a list of longitudes and latitudes on the grid (internal method)
158
+ */
159
+ getEarthCoords(): EarthCoords;
160
+ getGridCoords(): GridCoords;
161
+ transform(x: number, y: number, opts?: {
162
+ inverse?: boolean;
163
+ }): [number, number];
164
+ getThinnedGrid(thin_x: number, thin_y: number): LambertGrid;
165
+ }
166
+ export { Grid, PlateCarreeGrid, PlateCarreeRotatedGrid, LambertGrid };
167
+ export type { GridType };