autumnplot-gl 3.1.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 (56) hide show
  1. package/README.md +6 -11
  2. package/dist/110.autumnplot-gl.js +1 -1
  3. package/dist/110.autumnplot-gl.js.map +1 -1
  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 +53 -5
  8. package/lib/AutumnTypes.js +25 -1
  9. package/lib/Barbs.d.ts +23 -6
  10. package/lib/Barbs.js +20 -18
  11. package/lib/BillboardCollection.d.ts +16 -8
  12. package/lib/BillboardCollection.js +107 -59
  13. package/lib/Color.d.ts +57 -0
  14. package/lib/Color.js +163 -0
  15. package/lib/ColorBar.d.ts +12 -1
  16. package/lib/ColorBar.js +9 -7
  17. package/lib/Colormap.d.ts +19 -18
  18. package/lib/Colormap.js +84 -23
  19. package/lib/Contour.d.ts +42 -11
  20. package/lib/Contour.js +67 -58
  21. package/lib/ContourCreator.d.ts +4 -0
  22. package/lib/ContourCreator.js +2 -1
  23. package/lib/Fill.d.ts +27 -16
  24. package/lib/Fill.js +105 -83
  25. package/lib/Grid.d.ts +125 -29
  26. package/lib/Grid.js +303 -95
  27. package/lib/Hodographs.d.ts +24 -6
  28. package/lib/Hodographs.js +28 -24
  29. package/lib/Map.js +1 -1
  30. package/lib/Paintball.d.ts +6 -5
  31. package/lib/Paintball.js +38 -32
  32. package/lib/ParticleTracer.d.ts +19 -0
  33. package/lib/ParticleTracer.js +37 -0
  34. package/lib/PlotComponent.d.ts +6 -7
  35. package/lib/PlotComponent.js +17 -7
  36. package/lib/PlotLayer.d.ts +4 -4
  37. package/lib/PlotLayer.worker.d.ts +1 -2
  38. package/lib/PlotLayer.worker.js +22 -57
  39. package/lib/PolylineCollection.d.ts +18 -9
  40. package/lib/PolylineCollection.js +124 -89
  41. package/lib/RawField.d.ts +76 -23
  42. package/lib/RawField.js +138 -29
  43. package/lib/ShaderManager.d.ts +12 -0
  44. package/lib/ShaderManager.js +58 -0
  45. package/lib/StationPlot.d.ts +145 -0
  46. package/lib/StationPlot.js +205 -0
  47. package/lib/TextCollection.d.ts +12 -8
  48. package/lib/TextCollection.js +113 -71
  49. package/lib/cpp/marchingsquares.js +483 -585
  50. package/lib/cpp/marchingsquares.wasm +0 -0
  51. package/lib/cpp/marchingsquares_embind.d.ts +23 -3
  52. package/lib/index.d.ts +7 -4
  53. package/lib/index.js +5 -3
  54. package/lib/utils.d.ts +5 -8
  55. package/lib/utils.js +12 -83
  56. package/package.json +2 -2
Binary file
@@ -1,7 +1,5 @@
1
1
  import { Float16Array } from "@petamoriken/float16";
2
2
  interface WindProfile {
3
- lat: number;
4
- lon: number;
5
3
  jlat: number;
6
4
  ilon: number;
7
5
  smu: number;
@@ -34,7 +32,57 @@ type Polyline = {
34
32
  };
35
33
  type WebGLAnyRenderingContext = WebGLRenderingContext | WebGL2RenderingContext;
36
34
  declare function isWebGL2Ctx(gl: WebGLAnyRenderingContext): gl is WebGL2RenderingContext;
37
- type TypedArray = Float16Array | Float32Array;
35
+ type TypedArray = Float16Array | Float32Array | Uint8Array;
36
+ type TypedArrayStr = 'float16' | 'float32' | 'uint8';
38
37
  type ContourData = Record<number, [number, number][][]>;
39
- export { isWebGL2Ctx };
40
- export type { WindProfile, BillboardSpec, Polyline, LineData, WebGLAnyRenderingContext, TypedArray, ContourData };
38
+ type mat4 = number[] | Float32Array | Float64Array;
39
+ type RenderShaderData = {
40
+ vertexShaderPrelude: string;
41
+ define: string;
42
+ variantName: string;
43
+ };
44
+ type MapLibreRendererDataProjection = {
45
+ clippingPlane: [number, number, number, number];
46
+ fallbackMatrix: mat4;
47
+ mainMatrix: mat4;
48
+ projectionTransition: number;
49
+ tileMercatorCoords: [number, number, number, number];
50
+ };
51
+ type MapLibreRendererDataShader = RenderShaderData;
52
+ type MapLibreRendererData = {
53
+ defaultProjectionData: MapLibreRendererDataProjection;
54
+ farZ: number;
55
+ fov: number;
56
+ modelViewProjectionMatrix: mat4;
57
+ nearZ: number;
58
+ projectionMatrix: mat4;
59
+ shaderData: MapLibreRendererDataShader;
60
+ };
61
+ type RenderMethodArg = mat4 | MapLibreRendererData;
62
+ type RendererDataProjection = {
63
+ clippingPlane: number[];
64
+ fallbackMatrix: number[];
65
+ mainMatrix: number[];
66
+ projectionTransition: number;
67
+ tileMercatorCoords: number[];
68
+ };
69
+ type RendererDataShader = RenderShaderData;
70
+ type RendererDataMapLibre = {
71
+ type: 'maplibre';
72
+ defaultProjectionData: RendererDataProjection;
73
+ farZ: number;
74
+ fov: number;
75
+ modelViewProjectionMatrix: number[];
76
+ nearZ: number;
77
+ projectionMatrix: number[];
78
+ shaderData: RendererDataShader;
79
+ };
80
+ type RendererDataAutumn = {
81
+ type: 'autumn';
82
+ mainMatrix: number[];
83
+ shaderData: null;
84
+ };
85
+ type RendererData = RendererDataMapLibre | RendererDataAutumn;
86
+ declare function getRendererData(arg: RenderMethodArg): RendererData;
87
+ export { isWebGL2Ctx, getRendererData };
88
+ export type { WindProfile, BillboardSpec, Polyline, LineData, WebGLAnyRenderingContext, TypedArray, TypedArrayStr, ContourData, RenderMethodArg, RendererData, RenderShaderData };
@@ -1,4 +1,28 @@
1
1
  function isWebGL2Ctx(gl) {
2
2
  return gl.getParameter(gl.VERSION).includes('WebGL 2.0');
3
3
  }
4
- export { isWebGL2Ctx };
4
+ function isMapLibreRenderArg(obj) {
5
+ return 'modelViewProjectionMatrix' in obj && 'defaultProjectionData' in obj && 'mainMatrix' in obj.defaultProjectionData;
6
+ }
7
+ function getRendererData(arg) {
8
+ if (isMapLibreRenderArg(arg)) {
9
+ return {
10
+ type: 'maplibre',
11
+ defaultProjectionData: {
12
+ clippingPlane: [...arg.defaultProjectionData.clippingPlane],
13
+ fallbackMatrix: [...arg.defaultProjectionData.fallbackMatrix],
14
+ mainMatrix: [...arg.defaultProjectionData.mainMatrix],
15
+ projectionTransition: arg.defaultProjectionData.projectionTransition,
16
+ tileMercatorCoords: [...arg.defaultProjectionData.tileMercatorCoords]
17
+ },
18
+ farZ: arg.farZ,
19
+ fov: arg.fov,
20
+ modelViewProjectionMatrix: [...arg.modelViewProjectionMatrix],
21
+ nearZ: arg.nearZ,
22
+ projectionMatrix: [...arg.projectionMatrix],
23
+ shaderData: arg.shaderData
24
+ };
25
+ }
26
+ return { type: 'autumn', mainMatrix: [...arg], shaderData: null };
27
+ }
28
+ export { isWebGL2Ctx, getRendererData };
package/lib/Barbs.d.ts CHANGED
@@ -1,13 +1,29 @@
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
+ import { ColorMap } from "./Colormap";
6
+ import { Grid } from "./Grid";
5
7
  interface BarbsOptions {
6
8
  /**
7
9
  * The color to use for the barbs as a hex color string;.
8
10
  * @default '#000000'
9
11
  */
10
12
  color?: string;
13
+ /**
14
+ * A color map to use to color the barbs by magnitude. Specifying cmap overrides the color argument.
15
+ */
16
+ cmap?: ColorMap | null;
17
+ /**
18
+ * The width of the lines to use for the barbs
19
+ * @default 2
20
+ */
21
+ line_width?: number;
22
+ /**
23
+ * A multiplier for the barb size
24
+ * @default 1
25
+ */
26
+ barb_size_multiplier?: number;
11
27
  /**
12
28
  * How much to thin the barbs at zoom level 1 on the map. This effectively means to plot every `n`th barb in the i and j directions, where `n` =
13
29
  * `thin_fac`. `thin_fac` should be a power of 2.
@@ -23,23 +39,24 @@ interface BarbsOptions {
23
39
  * const vector_field = new RawVectorField(grid, u_data, v_data);
24
40
  * const barbs = new Barbs(vector_field, {color: '#000000', thin_fac: 16});
25
41
  */
26
- declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
42
+ declare class Barbs<ArrayType extends TypedArray, GridType extends Grid, MapType extends MapLikeType> extends PlotComponent<MapType> {
27
43
  /** The vector field */
28
44
  private fields;
29
45
  readonly opts: Required<BarbsOptions>;
30
- private readonly color_rgb;
46
+ private readonly color;
31
47
  private gl_elems;
48
+ private barb_texture;
32
49
  /**
33
50
  * Create a field of wind barbs
34
51
  * @param fields - The vector field to plot as barbs
35
52
  * @param opts - Options for creating the wind barbs
36
53
  */
37
- constructor(fields: RawVectorField<ArrayType>, opts: BarbsOptions);
54
+ constructor(fields: RawVectorField<ArrayType, GridType>, opts: BarbsOptions);
38
55
  /**
39
56
  * Update the field displayed as barbs
40
57
  * @param fields - The new field to display as barbs
41
58
  */
42
- updateField(fields: RawVectorField<ArrayType>): Promise<void>;
59
+ updateField(fields: RawVectorField<ArrayType, GridType>): Promise<void>;
43
60
  /**
44
61
  * @internal
45
62
  * Add the barb field to a map
@@ -49,7 +66,7 @@ declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> e
49
66
  * @internal
50
67
  * Render the barb field
51
68
  */
52
- render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array): void;
69
+ render(gl: WebGLAnyRenderingContext, matrix: RenderMethodArg): void;
53
70
  }
54
71
  export default Barbs;
55
72
  export type { BarbsOptions };
package/lib/Barbs.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { PlotComponent } from "./PlotComponent";
2
2
  import { BillboardCollection } from './BillboardCollection';
3
- import { hex2rgb, normalizeOptions } from './utils';
4
- const BARB_DIMS = {
3
+ import { normalizeOptions } from './utils';
4
+ import { Color } from "./Color";
5
+ const BASE_BARB_DIMS = {
5
6
  BB_WIDTH: 85,
6
7
  BB_HEIGHT: 256,
7
8
  BB_TEX_WIDTH: 1024,
@@ -10,12 +11,13 @@ const BARB_DIMS = {
10
11
  BB_MAG_WRAP: 60,
11
12
  BB_MAG_BIN_SIZE: 5,
12
13
  };
13
- function _createBarbTexture() {
14
+ const LINE_WIDTH_MULTIPLIER = 4;
15
+ function createBarbTexture(dimensions, line_width) {
14
16
  let canvas = document.createElement('canvas');
15
- canvas.width = BARB_DIMS.BB_TEX_WIDTH;
16
- canvas.height = BARB_DIMS.BB_TEX_HEIGHT;
17
+ canvas.width = dimensions.BB_TEX_WIDTH;
18
+ canvas.height = dimensions.BB_TEX_HEIGHT;
17
19
  function drawWindBarb(ctx, tipx, tipy, mag) {
18
- const elem_full_size = BARB_DIMS.BB_WIDTH / 2 - 4;
20
+ const elem_full_size = dimensions.BB_WIDTH / 2 - 4;
19
21
  const elem_spacing = elem_full_size / 2;
20
22
  if (mag < 2.5) {
21
23
  ctx.beginPath();
@@ -87,18 +89,20 @@ function _createBarbTexture() {
87
89
  if (ctx === null) {
88
90
  throw "Could not get rendering context for the wind barb canvas";
89
91
  }
90
- ctx.lineWidth = 8;
92
+ ctx.lineWidth = line_width;
91
93
  ctx.miterLimit = 4;
92
- for (let ibarb = 0; ibarb <= BARB_DIMS.BB_MAG_MAX; ibarb += BARB_DIMS.BB_MAG_BIN_SIZE) {
93
- const x_pos = (ibarb % BARB_DIMS.BB_MAG_WRAP) / BARB_DIMS.BB_MAG_BIN_SIZE * BARB_DIMS.BB_WIDTH + BARB_DIMS.BB_WIDTH / 2;
94
- const y_pos = Math.floor(ibarb / BARB_DIMS.BB_MAG_WRAP) * BARB_DIMS.BB_HEIGHT + BARB_DIMS.BB_WIDTH / 2;
94
+ for (let ibarb = 0; ibarb <= dimensions.BB_MAG_MAX; ibarb += dimensions.BB_MAG_BIN_SIZE) {
95
+ const x_pos = (ibarb % dimensions.BB_MAG_WRAP) / dimensions.BB_MAG_BIN_SIZE * dimensions.BB_WIDTH + dimensions.BB_WIDTH / 2;
96
+ const y_pos = Math.floor(ibarb / dimensions.BB_MAG_WRAP) * dimensions.BB_HEIGHT + dimensions.BB_WIDTH / 2;
95
97
  drawWindBarb(ctx, x_pos, y_pos, ibarb);
96
98
  }
97
99
  return canvas;
98
100
  }
99
- let BARB_TEXTURE = null;
100
101
  const barb_opt_defaults = {
101
102
  color: '#000000',
103
+ cmap: null,
104
+ line_width: 2,
105
+ barb_size_multiplier: 1,
102
106
  thin_fac: 1
103
107
  };
104
108
  /**
@@ -119,8 +123,8 @@ class Barbs extends PlotComponent {
119
123
  super();
120
124
  this.fields = fields;
121
125
  this.opts = normalizeOptions(opts, barb_opt_defaults);
122
- const color_rgb = hex2rgb(this.opts.color);
123
- this.color_rgb = [color_rgb[0], color_rgb[1], color_rgb[2]];
126
+ this.color = Color.fromHex(this.opts.color);
127
+ this.barb_texture = createBarbTexture(BASE_BARB_DIMS, this.opts.line_width / this.opts.barb_size_multiplier * LINE_WIDTH_MULTIPLIER);
124
128
  this.gl_elems = null;
125
129
  }
126
130
  /**
@@ -142,11 +146,9 @@ class Barbs extends PlotComponent {
142
146
  gl.getExtension('OES_texture_float');
143
147
  gl.getExtension('OES_texture_float_linear');
144
148
  const map_max_zoom = map.getMaxZoom();
145
- if (BARB_TEXTURE === null) {
146
- BARB_TEXTURE = _createBarbTexture();
147
- }
148
- const barb_image = { format: gl.RGBA, type: gl.UNSIGNED_BYTE, image: BARB_TEXTURE, mag_filter: gl.NEAREST };
149
- const barb_billboards = new BillboardCollection(this.fields, this.opts.thin_fac, map_max_zoom, barb_image, BARB_DIMS, this.color_rgb, 0.1);
149
+ const barb_image = { format: gl.RGBA, type: gl.UNSIGNED_BYTE, image: this.barb_texture, mag_filter: gl.NEAREST };
150
+ const cmap = this.opts.cmap === null ? undefined : this.opts.cmap;
151
+ const barb_billboards = new BillboardCollection(this.fields, this.opts.thin_fac, map_max_zoom, barb_image, BASE_BARB_DIMS, 0.1 * this.opts.barb_size_multiplier, { color: this.color, cmap: cmap });
150
152
  await barb_billboards.setup(gl);
151
153
  this.gl_elems = {
152
154
  map: map, barb_billboards: barb_billboards
@@ -1,21 +1,29 @@
1
- import { BillboardSpec, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
1
+ import { BillboardSpec, RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
2
+ import { Color } from "./Color";
3
+ import { ColorMap } from "./Colormap";
4
+ import { Grid } from "./Grid";
2
5
  import { RawVectorField } from "./RawField";
3
6
  import { WGLTextureSpec } from "autumn-wgl";
4
- declare class BillboardCollection<ArrayType extends TypedArray> {
7
+ interface BillboardCollectionOpts {
8
+ color?: Color;
9
+ cmap?: ColorMap;
10
+ rotate_with_map?: boolean;
11
+ }
12
+ declare class BillboardCollection<ArrayType extends TypedArray, GridType extends Grid> {
5
13
  private field;
6
14
  readonly spec: BillboardSpec;
7
- readonly color: [number, number, number];
15
+ readonly color: Color;
16
+ readonly cmap: ColorMap | null;
17
+ readonly rotate_with_map: boolean;
8
18
  readonly size_multiplier: number;
9
19
  readonly thin_fac: number;
10
20
  readonly max_zoom: number;
11
21
  readonly billboard_image: WGLTextureSpec;
12
22
  private gl_elems;
13
23
  private wind_textures;
14
- private readonly trim_inaccessible;
15
- private show_field;
16
- constructor(field: RawVectorField<ArrayType>, thin_fac: number, max_zoom: number, billboard_image: WGLTextureSpec, billboard_spec: BillboardSpec, billboard_color: [number, number, number], billboard_size_mult: number);
17
- 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;
18
26
  setup(gl: WebGLAnyRenderingContext): Promise<void>;
19
- 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;
20
28
  }
21
29
  export { BillboardCollection };
@@ -1,25 +1,32 @@
1
- import { getGLFormatTypeAlignment } from "./PlotComponent";
2
- import { WGLProgram, WGLTexture } from "autumn-wgl";
3
- const billboard_vertex_shader_src = `uniform mat4 u_matrix;
1
+ import { getRendererData } from "./AutumnTypes";
2
+ import { Color } from "./Color";
3
+ import { ColorMapGPUInterface } from "./Colormap";
4
+ import { WGLBuffer, WGLTexture } from "autumn-wgl";
5
+ import { ShaderProgramManager } from "./ShaderManager";
6
+ const billboard_vertex_shader_src = `#version 300 es
7
+
4
8
  uniform int u_offset;
5
9
 
6
- attribute vec3 a_pos;
7
- attribute vec2 a_tex_coord;
10
+ in vec2 a_pos;
11
+ in vec2 a_tex_coord;
12
+ in lowp float a_geom;
8
13
  uniform lowp float u_bb_size;
9
14
  uniform lowp float u_map_aspect;
10
15
  uniform lowp float u_zoom;
11
- uniform highp float u_map_bearing;
12
16
  uniform lowp float u_bb_width;
13
17
  uniform lowp float u_bb_height;
14
18
  uniform highp float u_bb_mag_bin_size;
15
-
16
19
  uniform highp float u_bb_mag_wrap;
20
+ uniform int u_rotate_with_map;
17
21
 
18
22
  uniform sampler2D u_u_sampler;
19
23
  uniform sampler2D u_v_sampler;
20
24
  uniform sampler2D u_rot_sampler;
21
25
 
22
- varying highp vec2 v_tex_coord;
26
+ out highp vec2 v_tex_coord;
27
+ #ifdef COLORMAP
28
+ out highp float v_mag;
29
+ #endif
23
30
 
24
31
  mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
25
32
  return mat4(x_scale, 0.0, 0.0, 0.0,
@@ -52,18 +59,34 @@ void main() {
52
59
  float globe_width = 1.;
53
60
  vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
54
61
 
55
- vec4 pivot_pos = u_matrix * vec4(a_pos.xy + globe_offset, 0.0, 1.0);
56
- highp float zoom_corner = a_pos.z;
57
- lowp float min_zoom = floor(zoom_corner / 4.0);
58
- 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
+ }
73
+
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);
59
79
 
60
- highp float u = texture2D(u_u_sampler, a_tex_coord).r;
61
- highp float v = texture2D(u_v_sampler, a_tex_coord).r;
62
- highp float rot = texture2D(u_rot_sampler, a_tex_coord).r;
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;
63
83
 
64
84
  lowp float bb_aspect = u_bb_width / u_bb_height;
65
85
  lowp float ang = (abs(u) < 1e-6 && abs(v) < 1e-6) ? 0. : atan(v, u) - 3.141592654 / 2.0;
66
86
  highp float mag = length(vec2(u, v));
87
+ #ifdef COLORMAP
88
+ v_mag = mag;
89
+ #endif
67
90
  mag = floor(mag / u_bb_mag_bin_size + 0.5) * u_bb_mag_bin_size;
68
91
 
69
92
  vec4 offset = vec4(0.0, 0.0, 0.0, 0.0);
@@ -90,58 +113,62 @@ void main() {
90
113
  texcoord = tex_loc + vec2(u_bb_width, u_bb_height);
91
114
  }
92
115
 
93
- mat4 barb_rotation = rotationZMatrix(ang + radians(u_map_bearing) - rot);
94
- mat4 map_stretch_matrix = scalingMatrix(1.0, 1. / u_map_aspect, 1.0);
116
+ mat4 barb_rotation = rotationZMatrix(ang - globe_rotation - rot);
95
117
  offset = map_stretch_matrix * barb_rotation * offset;
96
118
  }
97
119
 
98
120
  gl_Position = pivot_pos + offset;
99
121
  v_tex_coord = texcoord;
100
122
  }`
101
- 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;
126
+
127
+ #ifdef COLORMAP
128
+ in highp float v_mag;
129
+ #else
130
+ uniform lowp vec4 u_bb_color;
131
+ #endif
102
132
 
103
133
  uniform sampler2D u_sampler;
104
- uniform lowp vec3 u_bb_color;
134
+
135
+ out highp vec4 fragColor;
105
136
 
106
137
  void main() {
107
- lowp vec4 tex_color = texture2D(u_sampler, v_tex_coord);
108
- gl_FragColor = vec4(u_bb_color, tex_color.a);
138
+ lowp vec4 tex_color = texture(u_sampler, v_tex_coord);
139
+
140
+ lowp vec4 color;
141
+ #ifdef COLORMAP
142
+ color = apply_colormap(v_mag);
143
+ #else
144
+ color = u_bb_color;
145
+ #endif
146
+
147
+ color.a *= tex_color.a;
148
+ fragColor = color;
109
149
  }`
110
- class BillboardCollectionGLElems {
111
- }
112
150
  class BillboardCollection {
113
- constructor(field, thin_fac, max_zoom, billboard_image, billboard_spec, billboard_color, billboard_size_mult) {
151
+ constructor(field, thin_fac, max_zoom, billboard_image, billboard_spec, billboard_size_mult, opts) {
152
+ opts = opts === undefined ? {} : opts;
153
+ this.color = opts.color === undefined ? new Color([0, 0, 0, 1]) : opts.color;
154
+ this.cmap = opts.cmap === undefined ? null : opts.cmap;
155
+ this.rotate_with_map = opts.rotate_with_map === undefined ? true : opts.rotate_with_map;
114
156
  this.field = field;
115
157
  this.spec = billboard_spec;
116
- this.color = billboard_color;
117
158
  this.size_multiplier = billboard_size_mult;
118
159
  this.thin_fac = thin_fac;
119
160
  this.max_zoom = max_zoom;
120
161
  this.billboard_image = billboard_image;
121
162
  this.gl_elems = null;
122
163
  this.wind_textures = null;
123
- const n_density_tiers = Math.log2(thin_fac);
124
- const n_inaccessible_tiers = Math.max(n_density_tiers + 1 - max_zoom, 0);
125
- this.trim_inaccessible = Math.pow(2, n_inaccessible_tiers);
126
- this.show_field = true;
127
164
  }
128
165
  updateField(field) {
129
166
  this.field = field;
130
167
  if (this.gl_elems === null)
131
168
  return;
132
169
  const gl = this.gl_elems.gl;
133
- const data = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
134
- const { u: u_thin, v: v_thin } = data.getTextureData();
135
- this.show_field = u_thin !== null;
136
- const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(u_thin instanceof Float32Array));
137
- const u_image = { 'format': format, 'type': type,
138
- 'width': data.grid.ni, 'height': data.grid.nj, 'image': u_thin,
139
- 'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
140
- };
141
- const v_image = { 'format': format, 'type': type,
142
- 'width': data.grid.ni, 'height': data.grid.nj, 'image': v_thin,
143
- 'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
144
- };
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);
145
172
  if (this.wind_textures === null) {
146
173
  this.wind_textures = { u: new WGLTexture(gl, u_image), v: new WGLTexture(gl, v_image) };
147
174
  }
@@ -151,34 +178,55 @@ class BillboardCollection {
151
178
  }
152
179
  }
153
180
  async setup(gl) {
154
- const program = new WGLProgram(gl, billboard_vertex_shader_src, billboard_fragment_shader_src);
155
- const thinned_field = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
156
- const { vertices, texcoords } = await thinned_field.grid.getWGLBillboardBuffers(gl, this.thin_fac / this.trim_inaccessible, this.max_zoom);
157
- 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');
158
186
  const texture = new WGLTexture(gl, this.billboard_image);
159
- this.gl_elems = { gl: gl, program: program, vertices: vertices, texcoords: texcoords, texture: texture, proj_rot_texture: proj_rotation_tex };
187
+ const shader_defines = [];
188
+ let fragment_src = billboard_fragment_shader_src;
189
+ let cmap_gpu = null;
190
+ if (this.cmap !== null) {
191
+ fragment_src = ColorMapGPUInterface.applyShader(fragment_src);
192
+ cmap_gpu = new ColorMapGPUInterface(this.cmap);
193
+ cmap_gpu.setupShaderVariables(gl, gl.NEAREST);
194
+ shader_defines.push('COLORMAP');
195
+ }
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 };
160
199
  }
161
- render(gl, matrix, [map_width, map_height], map_zoom, map_bearing, map_pitch) {
162
- 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)
163
202
  return;
164
- if (matrix instanceof Float32Array)
165
- matrix = [...matrix];
203
+ const render_data = getRendererData(arg);
204
+ const program = this.gl_elems.shader_manager.getShaderProgram(gl, render_data.shaderData);
166
205
  const gl_elems = this.gl_elems;
167
206
  const bb_size = this.spec.BB_HEIGHT * (map_height / map_width) * this.size_multiplier;
168
207
  const bb_width = this.spec.BB_WIDTH / this.spec.BB_TEX_WIDTH;
169
208
  const bb_height = this.spec.BB_HEIGHT / this.spec.BB_TEX_HEIGHT;
170
- 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,
171
210
  'u_bb_mag_bin_size': this.spec.BB_MAG_BIN_SIZE, 'u_bb_mag_wrap': this.spec.BB_MAG_WRAP, 'u_offset': 0,
172
- 'u_bb_color': this.color, '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 });
213
+ if (gl_elems.cmap_gpu !== null) {
214
+ gl_elems.cmap_gpu.bindShaderVariables(program);
215
+ }
216
+ else {
217
+ program.setUniforms({ 'u_bb_color': this.color.toRGBATuple() });
218
+ }
173
219
  gl.enable(gl.BLEND);
174
220
  gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
175
- gl_elems.program.draw();
176
- gl_elems.program.setUniforms({ 'u_offset': -2 });
177
- gl_elems.program.draw();
178
- gl_elems.program.setUniforms({ 'u_offset': -1 });
179
- gl_elems.program.draw();
180
- gl_elems.program.setUniforms({ 'u_offset': 1 });
181
- 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
+ }
182
230
  }
183
231
  }
184
232
  export { BillboardCollection };
package/lib/Color.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ declare class Color {
2
+ private rgba;
3
+ /**
4
+ * Create a new color object
5
+ * @param rgba - An RGBA tuple of floats between 0 and 1
6
+ */
7
+ constructor(rgba: [number, number, number, number]);
8
+ /**
9
+ * The red component of the color as a float value between 0 and 1
10
+ */
11
+ get r(): number;
12
+ /**
13
+ * The green component of the color as a float value between 0 and 1
14
+ */
15
+ get g(): number;
16
+ /**
17
+ * The blue component of the color as a float value between 0 and 1
18
+ */
19
+ get b(): number;
20
+ /**
21
+ * The alpha component (opacity) of the color as a float value between 0 and 1
22
+ */
23
+ get a(): number;
24
+ /**
25
+ * @param opacity - The new alpha component (opacity)
26
+ * @returns A new color with the alpha component set to opacity.
27
+ */
28
+ withOpacity(opacity: number): Color;
29
+ /**
30
+ * @returns The color as an RGB hex string (e.g., '#dedbef')
31
+ */
32
+ toRGBHex(): string;
33
+ /**
34
+ * @returns The color as an RGBA hex string (e.g., '#dedbefff')
35
+ */
36
+ toRGBAHex(): string;
37
+ /**
38
+ * @returns The color as an RGBA float tuple
39
+ */
40
+ toRGBATuple(): [number, number, number, number];
41
+ /**
42
+ * @returns The color as a tuple of HSV values
43
+ */
44
+ toHSVTuple(): [number, number, number];
45
+ /**
46
+ * @param hex - An RGB or RGBA hex string to parse
47
+ * @returns a new Color object
48
+ */
49
+ static fromHex(hex: string): Color;
50
+ /**
51
+ * @param hsv - A tuple of HSV values
52
+ * @returns a new Color object
53
+ */
54
+ static fromHSVTuple(hsv: [number, number, number]): Color;
55
+ static normalizeColor(color: Color | string): Color;
56
+ }
57
+ export { Color };