autumnplot-gl 3.2.0 → 4.0.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 (59) hide show
  1. package/README.md +11 -207
  2. package/dist/983.autumnplot-gl.js +2 -0
  3. package/dist/983.autumnplot-gl.js.map +1 -0
  4. package/dist/autumnplot-gl.js +1 -1
  5. package/dist/autumnplot-gl.js.map +1 -1
  6. package/dist/marchingsquares.wasm +0 -0
  7. package/lib/AutumnTypes.d.ts +85 -6
  8. package/lib/AutumnTypes.js +28 -1
  9. package/lib/Barbs.d.ts +7 -5
  10. package/lib/BillboardCollection.d.ts +8 -7
  11. package/lib/BillboardCollection.js +69 -58
  12. package/lib/Color.d.ts +2 -0
  13. package/lib/Color.js +4 -0
  14. package/lib/ColorBar.d.ts +19 -0
  15. package/lib/ColorBar.js +9 -6
  16. package/lib/Colormap.d.ts +1 -0
  17. package/lib/Colormap.js +8 -8
  18. package/lib/Contour.d.ts +28 -8
  19. package/lib/Contour.js +27 -54
  20. package/lib/ContourCreator.d.ts +9 -1
  21. package/lib/ContourCreator.js +5 -4
  22. package/lib/Fill.d.ts +28 -14
  23. package/lib/Fill.js +97 -50
  24. package/lib/Grid.d.ts +132 -30
  25. package/lib/Grid.js +355 -99
  26. package/lib/Hodographs.d.ts +15 -8
  27. package/lib/Hodographs.js +48 -30
  28. package/lib/Map.d.ts +14 -1
  29. package/lib/Map.js +60 -4
  30. package/lib/Paintball.d.ts +7 -5
  31. package/lib/Paintball.js +36 -31
  32. package/lib/ParticleTracer.d.ts +19 -0
  33. package/lib/ParticleTracer.js +37 -0
  34. package/lib/PlotComponent.d.ts +7 -7
  35. package/lib/PlotComponent.js +9 -3
  36. package/lib/PlotLayer.d.ts +5 -5
  37. package/lib/PlotLayer.js +2 -2
  38. package/lib/PlotLayer.worker.d.ts +1 -2
  39. package/lib/PlotLayer.worker.js +22 -51
  40. package/lib/PolylineCollection.d.ts +5 -3
  41. package/lib/PolylineCollection.js +60 -37
  42. package/lib/RawField.d.ts +78 -23
  43. package/lib/RawField.js +147 -31
  44. package/lib/ShaderManager.d.ts +12 -0
  45. package/lib/ShaderManager.js +58 -0
  46. package/lib/StationPlot.d.ts +187 -25
  47. package/lib/StationPlot.js +209 -60
  48. package/lib/TextCollection.d.ts +9 -6
  49. package/lib/TextCollection.js +97 -62
  50. package/lib/cpp/marchingsquares.js +483 -585
  51. package/lib/cpp/marchingsquares.wasm +0 -0
  52. package/lib/cpp/marchingsquares_embind.d.ts +23 -3
  53. package/lib/index.d.ts +12 -5
  54. package/lib/index.js +8 -5
  55. package/lib/utils.d.ts +4 -1
  56. package/lib/utils.js +12 -1
  57. package/package.json +3 -3
  58. package/dist/110.autumnplot-gl.js +0 -2
  59. package/dist/110.autumnplot-gl.js.map +0 -1
Binary file
@@ -1,15 +1,37 @@
1
1
  import { Float16Array } from "@petamoriken/float16";
2
- interface WindProfile {
3
- lat: number;
4
- lon: number;
2
+ /** A wind profile with a storm-motion for plotting storm-relative hodographs */
3
+ interface StormRelativeWindProfile {
4
+ /** The grid index in the j direction (ignored for unstructured grids) */
5
5
  jlat: number;
6
+ /** The grid index in the i direction */
6
7
  ilon: number;
8
+ /** u component of storm motion in kts */
7
9
  smu: number;
10
+ /** v component of storm motion in kts */
8
11
  smv: number;
12
+ /** Ground-relative u winds in kts (will be converted to storm-relative during plotting) */
9
13
  u: Float32Array;
14
+ /** Ground-relative v winds in kts (will be converted to storm-relative during plotting) */
10
15
  v: Float32Array;
16
+ /** Height of each data point in km */
11
17
  z: Float32Array;
12
18
  }
19
+ /** A wind profile without a storm motion for plotting ground-relative hodographs */
20
+ interface GroundRelativeWindProfile {
21
+ /** The grid index in the j direction (ignored for unstructured grids) */
22
+ jlat: number;
23
+ /** The grid index in the i direction */
24
+ ilon: number;
25
+ /** Ground-relative u winds in kts */
26
+ u: Float32Array;
27
+ /** Ground-relative v winds in kts */
28
+ v: Float32Array;
29
+ /** Height of each data point in km */
30
+ z: Float32Array;
31
+ }
32
+ /** Different types of wind profiles for {@link Hodographs} components */
33
+ type WindProfile = StormRelativeWindProfile | GroundRelativeWindProfile;
34
+ declare function isStormRelativeWindProfile(obj: any): obj is StormRelativeWindProfile;
13
35
  interface BillboardSpec {
14
36
  BB_WIDTH: number;
15
37
  BB_HEIGHT: number;
@@ -32,9 +54,66 @@ type Polyline = {
32
54
  data?: Float32Array;
33
55
  zoom?: Float32Array;
34
56
  };
57
+ /** WebGL rendering contexts (either WebGL1 or WebGL2) */
35
58
  type WebGLAnyRenderingContext = WebGLRenderingContext | WebGL2RenderingContext;
36
59
  declare function isWebGL2Ctx(gl: WebGLAnyRenderingContext): gl is WebGL2RenderingContext;
37
- type TypedArray = Float16Array | Float32Array;
60
+ /** Javascript typed arrays for use in raw fields */
61
+ type TypedArray = Float16Array | Float32Array | Uint8Array;
62
+ type TypedArrayStr = 'float16' | 'float32' | 'uint8';
63
+ /**
64
+ * The result of contouring a field
65
+ *
66
+ * Each property is a different contour level giving an array of contours, and each contour is an array of [longitude, latitude].
67
+ */
38
68
  type ContourData = Record<number, [number, number][][]>;
39
- export { isWebGL2Ctx };
40
- export type { WindProfile, BillboardSpec, Polyline, LineData, WebGLAnyRenderingContext, TypedArray, ContourData };
69
+ type mat4 = number[] | Float32Array | Float64Array;
70
+ type RenderShaderData = {
71
+ vertexShaderPrelude: string;
72
+ define: string;
73
+ variantName: string;
74
+ };
75
+ type MapLibreRendererDataProjection = {
76
+ clippingPlane: [number, number, number, number];
77
+ fallbackMatrix: mat4;
78
+ mainMatrix: mat4;
79
+ projectionTransition: number;
80
+ tileMercatorCoords: [number, number, number, number];
81
+ };
82
+ type MapLibreRendererDataShader = RenderShaderData;
83
+ type MapLibreRendererData = {
84
+ defaultProjectionData: MapLibreRendererDataProjection;
85
+ farZ: number;
86
+ fov: number;
87
+ modelViewProjectionMatrix: mat4;
88
+ nearZ: number;
89
+ projectionMatrix: mat4;
90
+ shaderData: MapLibreRendererDataShader;
91
+ };
92
+ type RenderMethodArg = mat4 | MapLibreRendererData;
93
+ type RendererDataProjection = {
94
+ clippingPlane: number[];
95
+ fallbackMatrix: number[];
96
+ mainMatrix: number[];
97
+ projectionTransition: number;
98
+ tileMercatorCoords: number[];
99
+ };
100
+ type RendererDataShader = RenderShaderData;
101
+ type RendererDataMapLibre = {
102
+ type: 'maplibre';
103
+ defaultProjectionData: RendererDataProjection;
104
+ farZ: number;
105
+ fov: number;
106
+ modelViewProjectionMatrix: number[];
107
+ nearZ: number;
108
+ projectionMatrix: number[];
109
+ shaderData: RendererDataShader;
110
+ };
111
+ type RendererDataAutumn = {
112
+ type: 'autumn';
113
+ mainMatrix: number[];
114
+ shaderData: null;
115
+ };
116
+ type RendererData = RendererDataMapLibre | RendererDataAutumn;
117
+ declare function getRendererData(arg: RenderMethodArg): RendererData;
118
+ export { isWebGL2Ctx, getRendererData, isStormRelativeWindProfile };
119
+ export type { WindProfile, StormRelativeWindProfile, GroundRelativeWindProfile, BillboardSpec, Polyline, LineData, WebGLAnyRenderingContext, TypedArray, TypedArrayStr, ContourData, RenderMethodArg, RendererData, RenderShaderData };
@@ -1,4 +1,31 @@
1
+ function isStormRelativeWindProfile(obj) {
2
+ return 'smu' in obj && 'smv' in obj;
3
+ }
1
4
  function isWebGL2Ctx(gl) {
2
5
  return gl.getParameter(gl.VERSION).includes('WebGL 2.0');
3
6
  }
4
- export { isWebGL2Ctx };
7
+ function isMapLibreRenderArg(obj) {
8
+ return 'modelViewProjectionMatrix' in obj && 'defaultProjectionData' in obj && 'mainMatrix' in obj.defaultProjectionData;
9
+ }
10
+ function getRendererData(arg) {
11
+ if (isMapLibreRenderArg(arg)) {
12
+ return {
13
+ type: 'maplibre',
14
+ defaultProjectionData: {
15
+ clippingPlane: [...arg.defaultProjectionData.clippingPlane],
16
+ fallbackMatrix: [...arg.defaultProjectionData.fallbackMatrix],
17
+ mainMatrix: [...arg.defaultProjectionData.mainMatrix],
18
+ projectionTransition: arg.defaultProjectionData.projectionTransition,
19
+ tileMercatorCoords: [...arg.defaultProjectionData.tileMercatorCoords]
20
+ },
21
+ farZ: arg.farZ,
22
+ fov: arg.fov,
23
+ modelViewProjectionMatrix: [...arg.modelViewProjectionMatrix],
24
+ nearZ: arg.nearZ,
25
+ projectionMatrix: [...arg.projectionMatrix],
26
+ shaderData: arg.shaderData
27
+ };
28
+ }
29
+ return { type: 'autumn', mainMatrix: [...arg], shaderData: null };
30
+ }
31
+ export { isWebGL2Ctx, getRendererData, isStormRelativeWindProfile };
package/lib/Barbs.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { PlotComponent } from "./PlotComponent";
2
2
  import { RawVectorField } from "./RawField";
3
3
  import { MapLikeType } from "./Map";
4
- import { TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
4
+ import { RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
5
5
  import { ColorMap } from "./Colormap";
6
+ import { Grid } from "./Grid";
7
+ /** Options for {@link Barbs} components */
6
8
  interface BarbsOptions {
7
9
  /**
8
10
  * The color to use for the barbs as a hex color string;.
@@ -38,7 +40,7 @@ interface BarbsOptions {
38
40
  * const vector_field = new RawVectorField(grid, u_data, v_data);
39
41
  * const barbs = new Barbs(vector_field, {color: '#000000', thin_fac: 16});
40
42
  */
41
- declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
43
+ declare class Barbs<ArrayType extends TypedArray, GridType extends Grid, MapType extends MapLikeType> extends PlotComponent<MapType> {
42
44
  /** The vector field */
43
45
  private fields;
44
46
  readonly opts: Required<BarbsOptions>;
@@ -50,12 +52,12 @@ declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> e
50
52
  * @param fields - The vector field to plot as barbs
51
53
  * @param opts - Options for creating the wind barbs
52
54
  */
53
- constructor(fields: RawVectorField<ArrayType>, opts: BarbsOptions);
55
+ constructor(fields: RawVectorField<ArrayType, GridType>, opts: BarbsOptions);
54
56
  /**
55
57
  * Update the field displayed as barbs
56
58
  * @param fields - The new field to display as barbs
57
59
  */
58
- updateField(fields: RawVectorField<ArrayType>): Promise<void>;
60
+ updateField(fields: RawVectorField<ArrayType, GridType>): Promise<void>;
59
61
  /**
60
62
  * @internal
61
63
  * Add the barb field to a map
@@ -65,7 +67,7 @@ declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> e
65
67
  * @internal
66
68
  * Render the barb field
67
69
  */
68
- render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array): void;
70
+ render(gl: WebGLAnyRenderingContext, matrix: RenderMethodArg): void;
69
71
  }
70
72
  export default Barbs;
71
73
  export type { BarbsOptions };
@@ -1,28 +1,29 @@
1
- import { BillboardSpec, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
1
+ import { BillboardSpec, RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
2
2
  import { Color } from "./Color";
3
3
  import { ColorMap } from "./Colormap";
4
+ import { Grid } from "./Grid";
4
5
  import { RawVectorField } from "./RawField";
5
6
  import { WGLTextureSpec } from "autumn-wgl";
6
7
  interface BillboardCollectionOpts {
7
8
  color?: Color;
8
9
  cmap?: ColorMap;
10
+ rotate_with_map?: boolean;
9
11
  }
10
- declare class BillboardCollection<ArrayType extends TypedArray> {
12
+ declare class BillboardCollection<ArrayType extends TypedArray, GridType extends Grid> {
11
13
  private field;
12
14
  readonly spec: BillboardSpec;
13
15
  readonly color: Color;
14
16
  readonly cmap: ColorMap | null;
17
+ readonly rotate_with_map: boolean;
15
18
  readonly size_multiplier: number;
16
19
  readonly thin_fac: number;
17
20
  readonly max_zoom: number;
18
21
  readonly billboard_image: WGLTextureSpec;
19
22
  private gl_elems;
20
23
  private wind_textures;
21
- private readonly trim_inaccessible;
22
- private show_field;
23
- constructor(field: RawVectorField<ArrayType>, thin_fac: number, max_zoom: number, billboard_image: WGLTextureSpec, billboard_spec: BillboardSpec, billboard_size_mult: number, opts?: BillboardCollectionOpts);
24
- updateField(field: RawVectorField<ArrayType>): void;
24
+ constructor(field: RawVectorField<ArrayType, GridType>, thin_fac: number, max_zoom: number, billboard_image: WGLTextureSpec, billboard_spec: BillboardSpec, billboard_size_mult: number, opts?: BillboardCollectionOpts);
25
+ updateField(field: RawVectorField<ArrayType, GridType>): void;
25
26
  setup(gl: WebGLAnyRenderingContext): Promise<void>;
26
- render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array, [map_width, map_height]: [number, number], map_zoom: number, map_bearing: number, map_pitch: number): void;
27
+ render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg, [map_width, map_height]: [number, number], map_zoom: number, map_bearing: number, map_pitch: number): void;
27
28
  }
28
29
  export { BillboardCollection };
@@ -1,29 +1,31 @@
1
+ import { getRendererData } from "./AutumnTypes";
1
2
  import { Color } from "./Color";
2
3
  import { ColorMapGPUInterface } from "./Colormap";
3
- import { getGLFormatTypeAlignment } from "./PlotComponent";
4
- import { WGLProgram, WGLTexture } from "autumn-wgl";
5
- const billboard_vertex_shader_src = `uniform mat4 u_matrix;
4
+ import { WGLBuffer, WGLTexture } from "autumn-wgl";
5
+ import { ShaderProgramManager } from "./ShaderManager";
6
+ const billboard_vertex_shader_src = `#version 300 es
7
+
6
8
  uniform int u_offset;
7
9
 
8
- attribute vec3 a_pos;
9
- attribute vec2 a_tex_coord;
10
+ in vec2 a_pos;
11
+ in vec2 a_tex_coord;
12
+ in lowp float a_geom;
10
13
  uniform lowp float u_bb_size;
11
14
  uniform lowp float u_map_aspect;
12
15
  uniform lowp float u_zoom;
13
- uniform highp float u_map_bearing;
14
16
  uniform lowp float u_bb_width;
15
17
  uniform lowp float u_bb_height;
16
18
  uniform highp float u_bb_mag_bin_size;
17
-
18
19
  uniform highp float u_bb_mag_wrap;
20
+ uniform int u_rotate_with_map;
19
21
 
20
22
  uniform sampler2D u_u_sampler;
21
23
  uniform sampler2D u_v_sampler;
22
24
  uniform sampler2D u_rot_sampler;
23
25
 
24
- varying highp vec2 v_tex_coord;
26
+ out highp vec2 v_tex_coord;
25
27
  #ifdef COLORMAP
26
- varying highp float v_mag;
28
+ out highp float v_mag;
27
29
  #endif
28
30
 
29
31
  mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
@@ -57,14 +59,27 @@ void main() {
57
59
  float globe_width = 1.;
58
60
  vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
59
61
 
60
- vec4 pivot_pos = u_matrix * vec4(a_pos.xy + globe_offset, 0.0, 1.0);
61
- highp float zoom_corner = a_pos.z;
62
- lowp float min_zoom = floor(zoom_corner / 4.0);
63
- lowp float corner = mod(zoom_corner, 4.0);
62
+ mat4 map_stretch_matrix = scalingMatrix(1.0, 1. / u_map_aspect, 1.0);
63
+
64
+ vec4 pivot_pos = projectTile(a_pos.xy + globe_offset);
65
+ float globe_rotation = 0.0;
66
+
67
+ if (u_rotate_with_map == 1) {
68
+ vec4 pivot_pos_ihat = projectTile(a_pos.xy + globe_offset + vec2(1e-5, 0.));
69
+
70
+ vec2 pivot_east = normalize((inverse(map_stretch_matrix) * (pivot_pos_ihat - pivot_pos)).xy);
71
+ globe_rotation = atan(pivot_east.x, pivot_east.y) - 3.141592654 / 2.0;
72
+ }
64
73
 
65
- highp float u = texture2D(u_u_sampler, a_tex_coord).r;
66
- highp float v = texture2D(u_v_sampler, a_tex_coord).r;
67
- highp float rot = texture2D(u_rot_sampler, a_tex_coord).r;
74
+ highp float min_zoom = floor(a_tex_coord.x);
75
+ lowp float corner = a_geom;
76
+
77
+ vec2 data_texcoord = a_tex_coord;
78
+ data_texcoord.x = fract(data_texcoord.x);
79
+
80
+ highp float u = texture(u_u_sampler, data_texcoord).r;
81
+ highp float v = texture(u_v_sampler, data_texcoord).r;
82
+ highp float rot = texture(u_rot_sampler, data_texcoord).r;
68
83
 
69
84
  lowp float bb_aspect = u_bb_width / u_bb_height;
70
85
  lowp float ang = (abs(u) < 1e-6 && abs(v) < 1e-6) ? 0. : atan(v, u) - 3.141592654 / 2.0;
@@ -98,26 +113,29 @@ void main() {
98
113
  texcoord = tex_loc + vec2(u_bb_width, u_bb_height);
99
114
  }
100
115
 
101
- mat4 barb_rotation = rotationZMatrix(ang + radians(u_map_bearing) - rot);
102
- mat4 map_stretch_matrix = scalingMatrix(1.0, 1. / u_map_aspect, 1.0);
116
+ mat4 barb_rotation = rotationZMatrix(ang - globe_rotation - rot);
103
117
  offset = map_stretch_matrix * barb_rotation * offset;
104
118
  }
105
119
 
106
120
  gl_Position = pivot_pos + offset;
107
121
  v_tex_coord = texcoord;
108
122
  }`
109
- const billboard_fragment_shader_src = `varying highp vec2 v_tex_coord;
123
+ const billboard_fragment_shader_src = `#version 300 es
124
+
125
+ in highp vec2 v_tex_coord;
110
126
 
111
127
  #ifdef COLORMAP
112
- varying highp float v_mag;
128
+ in highp float v_mag;
113
129
  #else
114
130
  uniform lowp vec4 u_bb_color;
115
131
  #endif
116
132
 
117
133
  uniform sampler2D u_sampler;
118
134
 
135
+ out highp vec4 fragColor;
136
+
119
137
  void main() {
120
- lowp vec4 tex_color = texture2D(u_sampler, v_tex_coord);
138
+ lowp vec4 tex_color = texture(u_sampler, v_tex_coord);
121
139
 
122
140
  lowp vec4 color;
123
141
  #ifdef COLORMAP
@@ -127,13 +145,14 @@ void main() {
127
145
  #endif
128
146
 
129
147
  color.a *= tex_color.a;
130
- gl_FragColor = color;
148
+ fragColor = color;
131
149
  }`
132
150
  class BillboardCollection {
133
151
  constructor(field, thin_fac, max_zoom, billboard_image, billboard_spec, billboard_size_mult, opts) {
134
152
  opts = opts === undefined ? {} : opts;
135
153
  this.color = opts.color === undefined ? new Color([0, 0, 0, 1]) : opts.color;
136
154
  this.cmap = opts.cmap === undefined ? null : opts.cmap;
155
+ this.rotate_with_map = opts.rotate_with_map === undefined ? true : opts.rotate_with_map;
137
156
  this.field = field;
138
157
  this.spec = billboard_spec;
139
158
  this.size_multiplier = billboard_size_mult;
@@ -142,28 +161,14 @@ class BillboardCollection {
142
161
  this.billboard_image = billboard_image;
143
162
  this.gl_elems = null;
144
163
  this.wind_textures = null;
145
- const n_density_tiers = Math.log2(thin_fac);
146
- const n_inaccessible_tiers = Math.max(n_density_tiers + 1 - max_zoom, 0);
147
- this.trim_inaccessible = Math.pow(2, n_inaccessible_tiers);
148
- this.show_field = true;
149
164
  }
150
165
  updateField(field) {
151
166
  this.field = field;
152
167
  if (this.gl_elems === null)
153
168
  return;
154
169
  const gl = this.gl_elems.gl;
155
- const data = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
156
- const { u: u_thin, v: v_thin } = data.getTextureData();
157
- this.show_field = u_thin !== null;
158
- const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(u_thin instanceof Float32Array));
159
- const u_image = { 'format': format, 'type': type,
160
- 'width': data.grid.ni, 'height': data.grid.nj, 'image': u_thin,
161
- 'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
162
- };
163
- const v_image = { 'format': format, 'type': type,
164
- 'width': data.grid.ni, 'height': data.grid.nj, 'image': v_thin,
165
- 'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
166
- };
170
+ const data = this.field.getThinnedField(this.thin_fac, this.max_zoom);
171
+ const { u: u_image, v: v_image } = data.getWGLTextureSpecs(gl, gl.NEAREST);
167
172
  if (this.wind_textures === null) {
168
173
  this.wind_textures = { u: new WGLTexture(gl, u_image), v: new WGLTexture(gl, v_image) };
169
174
  }
@@ -173,9 +178,11 @@ class BillboardCollection {
173
178
  }
174
179
  }
175
180
  async setup(gl) {
176
- const thinned_field = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
177
- const { vertices, texcoords } = await thinned_field.grid.getWGLBillboardBuffers(gl, this.thin_fac / this.trim_inaccessible, this.max_zoom);
178
- const { rotation: proj_rotation_tex } = thinned_field.grid.getVectorRotationTexture(gl);
181
+ const thinned_grid = this.field.grid.getThinnedGrid(this.thin_fac, this.max_zoom);
182
+ const geom_verts = new Float32Array([0., 1., 2., 3.]);
183
+ const geom_buffer = new WGLBuffer(gl, geom_verts, 1, gl.TRIANGLE_STRIP);
184
+ const { vertices, texcoords } = await thinned_grid.getWGLBillboardBuffers(gl, this.thin_fac, this.max_zoom);
185
+ const { rotation: proj_rotation_tex } = thinned_grid.getVectorRotationTexture(gl, this.field.relative_to == 'earth');
179
186
  const texture = new WGLTexture(gl, this.billboard_image);
180
187
  const shader_defines = [];
181
188
  let fragment_src = billboard_fragment_shader_src;
@@ -186,36 +193,40 @@ class BillboardCollection {
186
193
  cmap_gpu.setupShaderVariables(gl, gl.NEAREST);
187
194
  shader_defines.push('COLORMAP');
188
195
  }
189
- const program = new WGLProgram(gl, billboard_vertex_shader_src, fragment_src, { define: shader_defines });
190
- this.gl_elems = { gl: gl, program: program, vertices: vertices, texcoords: texcoords, texture: texture, proj_rot_texture: proj_rotation_tex, cmap_gpu: cmap_gpu };
196
+ const shader_manager = new ShaderProgramManager(billboard_vertex_shader_src, fragment_src, shader_defines);
197
+ this.gl_elems = { gl: gl, shader_manager: shader_manager, geom_vertices: geom_buffer, vertices: vertices, texcoords: texcoords, texture: texture,
198
+ proj_rot_texture: proj_rotation_tex, cmap_gpu: cmap_gpu };
191
199
  }
192
- render(gl, matrix, [map_width, map_height], map_zoom, map_bearing, map_pitch) {
193
- if (this.gl_elems === null || this.wind_textures === null || !this.show_field)
200
+ render(gl, arg, [map_width, map_height], map_zoom, map_bearing, map_pitch) {
201
+ if (this.gl_elems === null || this.wind_textures === null)
194
202
  return;
195
- if (matrix instanceof Float32Array)
196
- matrix = [...matrix];
203
+ const render_data = getRendererData(arg);
204
+ const program = this.gl_elems.shader_manager.getShaderProgram(gl, render_data.shaderData);
197
205
  const gl_elems = this.gl_elems;
198
206
  const bb_size = this.spec.BB_HEIGHT * (map_height / map_width) * this.size_multiplier;
199
207
  const bb_width = this.spec.BB_WIDTH / this.spec.BB_TEX_WIDTH;
200
208
  const bb_height = this.spec.BB_HEIGHT / this.spec.BB_TEX_HEIGHT;
201
- gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_bb_size': bb_size, 'u_bb_width': bb_width, 'u_bb_height': bb_height,
209
+ program.use({ 'a_geom': gl_elems.geom_vertices, 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_bb_size': bb_size, 'u_bb_width': bb_width, 'u_bb_height': bb_height,
202
210
  'u_bb_mag_bin_size': this.spec.BB_MAG_BIN_SIZE, 'u_bb_mag_wrap': this.spec.BB_MAG_WRAP, 'u_offset': 0,
203
- 'u_matrix': matrix, 'u_map_aspect': map_height / map_width, 'u_zoom': map_zoom, 'u_map_bearing': map_bearing }, { 'u_sampler': gl_elems.texture, 'u_u_sampler': this.wind_textures.u, 'u_v_sampler': this.wind_textures.v, 'u_rot_sampler': gl_elems.proj_rot_texture });
211
+ 'u_map_aspect': map_height / map_width, 'u_zoom': map_zoom, 'u_rotate_with_map': this.rotate_with_map ? 1 : 0,
212
+ ...this.gl_elems.shader_manager.getShaderUniforms(render_data) }, { 'u_sampler': gl_elems.texture, 'u_u_sampler': this.wind_textures.u, 'u_v_sampler': this.wind_textures.v, 'u_rot_sampler': gl_elems.proj_rot_texture });
204
213
  if (gl_elems.cmap_gpu !== null) {
205
- gl_elems.cmap_gpu.bindShaderVariables(gl_elems.program);
214
+ gl_elems.cmap_gpu.bindShaderVariables(program);
206
215
  }
207
216
  else {
208
- gl_elems.program.setUniforms({ 'u_bb_color': this.color.toRGBATuple() });
217
+ program.setUniforms({ 'u_bb_color': this.color.toRGBATuple() });
209
218
  }
210
219
  gl.enable(gl.BLEND);
211
220
  gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
212
- gl_elems.program.draw();
213
- gl_elems.program.setUniforms({ 'u_offset': -2 });
214
- gl_elems.program.draw();
215
- gl_elems.program.setUniforms({ 'u_offset': -1 });
216
- gl_elems.program.draw();
217
- gl_elems.program.setUniforms({ 'u_offset': 1 });
218
- gl_elems.program.draw();
221
+ program.draw();
222
+ if (render_data.type != 'maplibre' || !render_data.shaderData.define.includes('GLOBE')) {
223
+ program.setUniforms({ 'u_offset': -2 });
224
+ program.draw();
225
+ program.setUniforms({ 'u_offset': -1 });
226
+ program.draw();
227
+ program.setUniforms({ 'u_offset': 1 });
228
+ program.draw();
229
+ }
219
230
  }
220
231
  }
221
232
  export { BillboardCollection };
package/lib/Color.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /** A class for handling colors and translations between different color spaces */
1
2
  declare class Color {
2
3
  private rgba;
3
4
  /**
@@ -52,5 +53,6 @@ declare class Color {
52
53
  * @returns a new Color object
53
54
  */
54
55
  static fromHSVTuple(hsv: [number, number, number]): Color;
56
+ static normalizeColor(color: Color | string): Color;
55
57
  }
56
58
  export { Color };
package/lib/Color.js CHANGED
@@ -78,6 +78,7 @@ const hsv2rgb = (hsv) => {
78
78
  }
79
79
  return [r_prime + m, g_prime + m, b_prime + m];
80
80
  };
81
+ /** A class for handling colors and translations between different color spaces */
81
82
  class Color {
82
83
  /**
83
84
  * Create a new color object
@@ -156,5 +157,8 @@ class Color {
156
157
  const rgb = hsv2rgb(hsv);
157
158
  return new Color([rgb[0], rgb[1], rgb[2], 1]);
158
159
  }
160
+ static normalizeColor(color) {
161
+ return color instanceof Color ? color : Color.fromHex(color);
162
+ }
159
163
  }
160
164
  export { Color };
package/lib/ColorBar.d.ts CHANGED
@@ -1,10 +1,23 @@
1
1
  import { ColorMap } from "./Colormap";
2
2
  import { Color } from "./Color";
3
+ /** The orientation for color bars (horizontal or vertical) */
3
4
  type ColorbarOrientation = 'horizontal' | 'vertical';
5
+ /** Which side of a color bar the ticks are on */
4
6
  type ColorbarTickDirection = 'top' | 'bottom' | 'left' | 'right';
7
+ /** Options for {@link ColorBar}s */
5
8
  interface ColorBarOptions {
6
9
  /** The label to place along the color bar */
7
10
  label?: string;
11
+ /**
12
+ * The size in pixels along the long axis of the colorbar
13
+ * @default 600
14
+ */
15
+ size_long?: number;
16
+ /**
17
+ * The size in pixels along the short axis of the colorbar
18
+ * @default size_long / 9
19
+ */
20
+ size_short?: number;
8
21
  /**
9
22
  * An array of numbers to use as the tick locations.
10
23
  * @default Use all the levels in the color map provided to {@link makeColorBar}.
@@ -31,6 +44,11 @@ interface ColorBarOptions {
31
44
  * @default 12
32
45
  */
33
46
  ticklabelsize?: number;
47
+ /**
48
+ * The color for the color bar outline and the text
49
+ * @default '#000000'
50
+ */
51
+ outline_and_text_color?: string;
34
52
  }
35
53
  /**
36
54
  * Make an SVG containing a color bar. The color bar can either be oriented horizontal or vertical, and a label can be provided.
@@ -46,6 +64,7 @@ interface ColorBarOptions {
46
64
  * document.getElementById('colorbar-container').appendChild(svg);
47
65
  */
48
66
  declare function makeColorBar(colormap: ColorMap, opts: ColorBarOptions): SVGElement;
67
+ /** Options for {@link makePaintballKey | makePaintballKey()} */
49
68
  interface PaintballKeyOptions {
50
69
  /**
51
70
  * The number of columns of entries in the key
package/lib/ColorBar.js CHANGED
@@ -30,6 +30,9 @@ function makeColorBar(colormap, opts) {
30
30
  const orientation = opts.orientation || 'vertical';
31
31
  const fontface = opts.fontface || 'sans-serif';
32
32
  const tickfontsize = opts.ticklabelsize || 12;
33
+ const size_long = opts.size_long || 600;
34
+ const size_short = opts.size_short || size_long / 9;
35
+ const outline_and_text_color = opts.outline_and_text_color || '#000000';
33
36
  const tick_dir = opts.tick_direction || (orientation == 'vertical' ? 'left' : 'bottom');
34
37
  if (orientation == 'vertical' && (tick_dir == 'top' || tick_dir == 'bottom') ||
35
38
  orientation == 'horizontal' && (tick_dir == 'left' || tick_dir == 'right')) {
@@ -41,8 +44,8 @@ function makeColorBar(colormap, opts) {
41
44
  const chars_left = getNChar(ticks[0]);
42
45
  const chars_right = getNChar(ticks[ticks.length - 1]);
43
46
  const need_overflow = colormap.underflow_color !== null || colormap.overflow_color !== null;
44
- const bar_long_size = 600;
45
- const bar_cross_size = bar_long_size / 9;
47
+ const bar_long_size = size_long;
48
+ const bar_cross_size = size_short;
46
49
  const bar_long_pad = Math.max(orientation == 'horizontal' ? Math.max(chars_left, chars_right) * 6 : 8, need_overflow ? bar_cross_size / (2 * Math.sqrt(3)) : 0);
47
50
  const bar_cross_pad = 3;
48
51
  const bar_thickness = 10;
@@ -141,7 +144,7 @@ function makeColorBar(colormap, opts) {
141
144
  else {
142
145
  lineattrs = tick_dir == 'bottom' ? { y2: 6 } : { y2: -6 };
143
146
  }
144
- createElement('line', { ...lineattrs, stroke: '#000000', 'stroke-width': 1.5 }, gtick);
147
+ createElement('line', { ...lineattrs, stroke: outline_and_text_color, 'stroke-width': 1.5 }, gtick);
145
148
  let textattrs;
146
149
  if (orientation == 'vertical') {
147
150
  textattrs = tick_dir == 'left' ? { x: -9, dy: '0.32em' } : { x: 9, dy: '0.32em' };
@@ -149,7 +152,7 @@ function makeColorBar(colormap, opts) {
149
152
  else {
150
153
  textattrs = tick_dir == 'bottom' ? { y: 9, dy: '0.8em' } : { y: -9, dy: '0em' };
151
154
  }
152
- const text = createElement('text', { ...textattrs, fill: '#000000', style: `font-family: ${fontface}; font-size: ${tickfontsize}pt` }, gtick);
155
+ const text = createElement('text', { ...textattrs, fill: outline_and_text_color, style: `font-family: ${fontface}; font-size: ${tickfontsize}pt` }, gtick);
153
156
  text.textContent = level.toString();
154
157
  });
155
158
  // Draw the outline
@@ -164,7 +167,7 @@ function makeColorBar(colormap, opts) {
164
167
  }
165
168
  const outline_attrs = {
166
169
  points: point_list,
167
- stroke: '#000000',
170
+ stroke: outline_and_text_color,
168
171
  'stroke-width': 1.5,
169
172
  fill: 'none'
170
173
  };
@@ -177,7 +180,7 @@ function makeColorBar(colormap, opts) {
177
180
  else {
178
181
  labelattrs = tick_dir == 'bottom' ? { transform: `translate(${width / 2}, ${height - 5})` } : { transform: `translate(${width / 2}, 15)` };
179
182
  }
180
- const label_elem = createElement('text', { ...labelattrs, fill: '#000000', 'text-anchor': 'middle', style: `font-family: ${fontface};` }, root);
183
+ const label_elem = createElement('text', { ...labelattrs, fill: outline_and_text_color, 'text-anchor': 'middle', style: `font-family: ${fontface};` }, root);
181
184
  label_elem.textContent = label;
182
185
  return root;
183
186
  }
package/lib/Colormap.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { WGLProgram, WGLTexture } from "autumn-wgl";
2
2
  import { WebGLAnyRenderingContext } from "./AutumnTypes";
3
3
  import { Color } from "./Color";
4
+ /** Options for {@link ColorMap}s */
4
5
  interface ColorMapOptions {
5
6
  /** The color to use for areas where the value is above the highest value in the color map */
6
7
  overflow_color?: Color | string;
package/lib/Colormap.js CHANGED
@@ -8,6 +8,7 @@ import { Float16Array } from "@petamoriken/float16";
8
8
  import { WGLTexture } from "autumn-wgl";
9
9
  import { getGLFormatTypeAlignment } from "./PlotComponent";
10
10
  import { Color } from "./Color";
11
+ import { mergeShaderCode } from "./utils";
11
12
  const colormap_shader_src = `
12
13
  uniform sampler2D u_cmap_sampler;
13
14
  uniform sampler2D u_cmap_nonlin_sampler;
@@ -30,8 +31,8 @@ lowp vec4 apply_colormap(highp float value) {
30
31
  else {
31
32
  lowp float index_buffer = 1. / (2. * float(u_n_index));
32
33
  normed_val = index_buffer + normed_val * (1. - 2. * index_buffer);
33
- highp float nonlin_val = texture2D(u_cmap_nonlin_sampler, vec2(normed_val, 0.5)).r;
34
- color = texture2D(u_cmap_sampler, vec2(nonlin_val, 0.5));
34
+ highp float nonlin_val = texture(u_cmap_nonlin_sampler, vec2(normed_val, 0.5)).r;
35
+ color = texture(u_cmap_sampler, vec2(nonlin_val, 0.5));
35
36
  }
36
37
 
37
38
  return color;
@@ -48,12 +49,11 @@ class ColorMap {
48
49
  if (levels.length != colors.length + 1) {
49
50
  throw `Mismatch between number of levels (${levels.length}) and number of colors (${colors.length}; expected ${levels.length - 1})`;
50
51
  }
51
- const normalizeColor = (c) => c instanceof Color ? c : Color.fromHex(c);
52
52
  this.levels = levels;
53
- this.colors = colors.map(c => normalizeColor(c));
53
+ this.colors = colors.map(c => Color.normalizeColor(c));
54
54
  opts = opts === undefined ? {} : opts;
55
- this.overflow_color = opts.overflow_color === undefined ? null : normalizeColor(opts.overflow_color);
56
- this.underflow_color = opts.underflow_color === undefined ? null : normalizeColor(opts.underflow_color);
55
+ this.overflow_color = opts.overflow_color === undefined ? null : Color.normalizeColor(opts.overflow_color);
56
+ this.underflow_color = opts.underflow_color === undefined ? null : Color.normalizeColor(opts.underflow_color);
57
57
  }
58
58
  /**
59
59
  * @returns an array of hex color strings
@@ -195,12 +195,12 @@ class ColorMapGPUInterface {
195
195
  this.gl_elems = null;
196
196
  }
197
197
  static applyShader(shader_src) {
198
- return colormap_shader_src + "\n" + shader_src;
198
+ return mergeShaderCode(colormap_shader_src, shader_src);
199
199
  }
200
200
  setupShaderVariables(gl, mag_filter) {
201
201
  const index_map = makeIndexMap(this.colormap);
202
202
  const cmap_image = makeTextureImage(this.colormap);
203
- const { format: format_nonlin, type: type_nonlin, row_alignment: row_alignment_nonlin } = getGLFormatTypeAlignment(gl, true);
203
+ const { format: format_nonlin, type: type_nonlin, row_alignment: row_alignment_nonlin } = getGLFormatTypeAlignment(gl, 'float16');
204
204
  const cmap_image_spec = { 'format': gl.RGBA, 'type': gl.UNSIGNED_BYTE, 'image': cmap_image, 'mag_filter': mag_filter };
205
205
  const cmap_texture = new WGLTexture(gl, cmap_image_spec);
206
206
  const cmap_nonlin_image = { 'format': format_nonlin, 'type': type_nonlin,