autumnplot-gl 3.0.0 → 3.2.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 (44) 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/Barbs.d.ts +18 -2
  8. package/lib/Barbs.js +25 -19
  9. package/lib/BillboardCollection.d.ts +9 -2
  10. package/lib/BillboardCollection.js +46 -9
  11. package/lib/Color.d.ts +56 -0
  12. package/lib/Color.js +160 -0
  13. package/lib/ColorBar.d.ts +2 -1
  14. package/lib/ColorBar.js +5 -5
  15. package/lib/Colormap.d.ts +19 -18
  16. package/lib/Colormap.js +81 -20
  17. package/lib/Contour.d.ts +25 -6
  18. package/lib/Contour.js +61 -12
  19. package/lib/ContourCreator.js +4 -40
  20. package/lib/Fill.d.ts +2 -4
  21. package/lib/Fill.js +29 -45
  22. package/lib/Grid.d.ts +1 -0
  23. package/lib/Grid.js +6 -1
  24. package/lib/Hodographs.d.ts +19 -3
  25. package/lib/Hodographs.js +23 -20
  26. package/lib/Paintball.d.ts +2 -2
  27. package/lib/Paintball.js +9 -6
  28. package/lib/PlotComponent.js +9 -4
  29. package/lib/PlotLayer.d.ts +1 -1
  30. package/lib/PlotLayer.worker.js +10 -7
  31. package/lib/PolylineCollection.d.ts +13 -6
  32. package/lib/PolylineCollection.js +76 -64
  33. package/lib/StationPlot.d.ts +34 -0
  34. package/lib/StationPlot.js +73 -0
  35. package/lib/TextCollection.d.ts +3 -2
  36. package/lib/TextCollection.js +21 -11
  37. package/lib/cpp/marchingsquares.js +558 -1261
  38. package/lib/cpp/marchingsquares.wasm +0 -0
  39. package/lib/cpp/marchingsquares_embind.d.ts +4 -45
  40. package/lib/index.d.ts +4 -2
  41. package/lib/index.js +2 -1
  42. package/lib/utils.d.ts +2 -8
  43. package/lib/utils.js +1 -83
  44. package/package.json +2 -2
Binary file
package/lib/Barbs.d.ts CHANGED
@@ -2,12 +2,27 @@ import { PlotComponent } from "./PlotComponent";
2
2
  import { RawVectorField } from "./RawField";
3
3
  import { MapLikeType } from "./Map";
4
4
  import { TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
5
+ import { ColorMap } from "./Colormap";
5
6
  interface BarbsOptions {
6
7
  /**
7
8
  * The color to use for the barbs as a hex color string;.
8
9
  * @default '#000000'
9
10
  */
10
11
  color?: string;
12
+ /**
13
+ * A color map to use to color the barbs by magnitude. Specifying cmap overrides the color argument.
14
+ */
15
+ cmap?: ColorMap | null;
16
+ /**
17
+ * The width of the lines to use for the barbs
18
+ * @default 2
19
+ */
20
+ line_width?: number;
21
+ /**
22
+ * A multiplier for the barb size
23
+ * @default 1
24
+ */
25
+ barb_size_multiplier?: number;
11
26
  /**
12
27
  * 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
28
  * `thin_fac`. `thin_fac` should be a power of 2.
@@ -26,9 +41,10 @@ interface BarbsOptions {
26
41
  declare class Barbs<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
27
42
  /** The vector field */
28
43
  private fields;
29
- readonly color: [number, number, number];
30
- readonly thin_fac: number;
44
+ readonly opts: Required<BarbsOptions>;
45
+ private readonly color;
31
46
  private gl_elems;
47
+ private barb_texture;
32
48
  /**
33
49
  * Create a field of wind barbs
34
50
  * @param fields - The vector field to plot as barbs
package/lib/Barbs.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { PlotComponent } from "./PlotComponent";
2
2
  import { BillboardCollection } from './BillboardCollection';
3
- import { hex2rgba } 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,16 +89,22 @@ 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;
101
+ const barb_opt_defaults = {
102
+ color: '#000000',
103
+ cmap: null,
104
+ line_width: 2,
105
+ barb_size_multiplier: 1,
106
+ thin_fac: 1
107
+ };
100
108
  /**
101
109
  * A class representing a field of wind barbs. The barbs are automatically thinned based on the zoom level on the map; the user only has to provide a
102
110
  * thinning factor at zoom level 1.
@@ -114,9 +122,9 @@ class Barbs extends PlotComponent {
114
122
  constructor(fields, opts) {
115
123
  super();
116
124
  this.fields = fields;
117
- const color = hex2rgba(opts.color || '#000000');
118
- this.color = [color[0], color[1], color[2]];
119
- this.thin_fac = opts.thin_fac || 1;
125
+ this.opts = normalizeOptions(opts, barb_opt_defaults);
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);
120
128
  this.gl_elems = null;
121
129
  }
122
130
  /**
@@ -138,11 +146,9 @@ class Barbs extends PlotComponent {
138
146
  gl.getExtension('OES_texture_float');
139
147
  gl.getExtension('OES_texture_float_linear');
140
148
  const map_max_zoom = map.getMaxZoom();
141
- if (BARB_TEXTURE === null) {
142
- BARB_TEXTURE = _createBarbTexture();
143
- }
144
- const barb_image = { format: gl.RGBA, type: gl.UNSIGNED_BYTE, image: BARB_TEXTURE, mag_filter: gl.NEAREST };
145
- const barb_billboards = new BillboardCollection(this.fields, this.thin_fac, map_max_zoom, barb_image, BARB_DIMS, this.color, 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 });
146
152
  await barb_billboards.setup(gl);
147
153
  this.gl_elems = {
148
154
  map: map, barb_billboards: barb_billboards
@@ -1,10 +1,17 @@
1
1
  import { BillboardSpec, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
2
+ import { Color } from "./Color";
3
+ import { ColorMap } from "./Colormap";
2
4
  import { RawVectorField } from "./RawField";
3
5
  import { WGLTextureSpec } from "autumn-wgl";
6
+ interface BillboardCollectionOpts {
7
+ color?: Color;
8
+ cmap?: ColorMap;
9
+ }
4
10
  declare class BillboardCollection<ArrayType extends TypedArray> {
5
11
  private field;
6
12
  readonly spec: BillboardSpec;
7
- readonly color: [number, number, number];
13
+ readonly color: Color;
14
+ readonly cmap: ColorMap | null;
8
15
  readonly size_multiplier: number;
9
16
  readonly thin_fac: number;
10
17
  readonly max_zoom: number;
@@ -13,7 +20,7 @@ declare class BillboardCollection<ArrayType extends TypedArray> {
13
20
  private wind_textures;
14
21
  private readonly trim_inaccessible;
15
22
  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);
23
+ constructor(field: RawVectorField<ArrayType>, thin_fac: number, max_zoom: number, billboard_image: WGLTextureSpec, billboard_spec: BillboardSpec, billboard_size_mult: number, opts?: BillboardCollectionOpts);
17
24
  updateField(field: RawVectorField<ArrayType>): void;
18
25
  setup(gl: WebGLAnyRenderingContext): Promise<void>;
19
26
  render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array, [map_width, map_height]: [number, number], map_zoom: number, map_bearing: number, map_pitch: number): void;
@@ -1,3 +1,5 @@
1
+ import { Color } from "./Color";
2
+ import { ColorMapGPUInterface } from "./Colormap";
1
3
  import { getGLFormatTypeAlignment } from "./PlotComponent";
2
4
  import { WGLProgram, WGLTexture } from "autumn-wgl";
3
5
  const billboard_vertex_shader_src = `uniform mat4 u_matrix;
@@ -20,6 +22,9 @@ uniform sampler2D u_v_sampler;
20
22
  uniform sampler2D u_rot_sampler;
21
23
 
22
24
  varying highp vec2 v_tex_coord;
25
+ #ifdef COLORMAP
26
+ varying highp float v_mag;
27
+ #endif
23
28
 
24
29
  mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
25
30
  return mat4(x_scale, 0.0, 0.0, 0.0,
@@ -64,6 +69,9 @@ void main() {
64
69
  lowp float bb_aspect = u_bb_width / u_bb_height;
65
70
  lowp float ang = (abs(u) < 1e-6 && abs(v) < 1e-6) ? 0. : atan(v, u) - 3.141592654 / 2.0;
66
71
  highp float mag = length(vec2(u, v));
72
+ #ifdef COLORMAP
73
+ v_mag = mag;
74
+ #endif
67
75
  mag = floor(mag / u_bb_mag_bin_size + 0.5) * u_bb_mag_bin_size;
68
76
 
69
77
  vec4 offset = vec4(0.0, 0.0, 0.0, 0.0);
@@ -100,20 +108,34 @@ void main() {
100
108
  }`
101
109
  const billboard_fragment_shader_src = `varying highp vec2 v_tex_coord;
102
110
 
111
+ #ifdef COLORMAP
112
+ varying highp float v_mag;
113
+ #else
114
+ uniform lowp vec4 u_bb_color;
115
+ #endif
116
+
103
117
  uniform sampler2D u_sampler;
104
- uniform lowp vec3 u_bb_color;
105
118
 
106
119
  void main() {
107
120
  lowp vec4 tex_color = texture2D(u_sampler, v_tex_coord);
108
- gl_FragColor = vec4(u_bb_color, tex_color.a);
121
+
122
+ lowp vec4 color;
123
+ #ifdef COLORMAP
124
+ color = apply_colormap(v_mag);
125
+ #else
126
+ color = u_bb_color;
127
+ #endif
128
+
129
+ color.a *= tex_color.a;
130
+ gl_FragColor = color;
109
131
  }`
110
- class BillboardCollectionGLElems {
111
- }
112
132
  class BillboardCollection {
113
- constructor(field, thin_fac, max_zoom, billboard_image, billboard_spec, billboard_color, billboard_size_mult) {
133
+ constructor(field, thin_fac, max_zoom, billboard_image, billboard_spec, billboard_size_mult, opts) {
134
+ opts = opts === undefined ? {} : opts;
135
+ this.color = opts.color === undefined ? new Color([0, 0, 0, 1]) : opts.color;
136
+ this.cmap = opts.cmap === undefined ? null : opts.cmap;
114
137
  this.field = field;
115
138
  this.spec = billboard_spec;
116
- this.color = billboard_color;
117
139
  this.size_multiplier = billboard_size_mult;
118
140
  this.thin_fac = thin_fac;
119
141
  this.max_zoom = max_zoom;
@@ -151,12 +173,21 @@ class BillboardCollection {
151
173
  }
152
174
  }
153
175
  async setup(gl) {
154
- const program = new WGLProgram(gl, billboard_vertex_shader_src, billboard_fragment_shader_src);
155
176
  const thinned_field = this.field.getThinnedField(this.trim_inaccessible, this.trim_inaccessible);
156
177
  const { vertices, texcoords } = await thinned_field.grid.getWGLBillboardBuffers(gl, this.thin_fac / this.trim_inaccessible, this.max_zoom);
157
178
  const { rotation: proj_rotation_tex } = thinned_field.grid.getVectorRotationTexture(gl);
158
179
  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 };
180
+ const shader_defines = [];
181
+ let fragment_src = billboard_fragment_shader_src;
182
+ let cmap_gpu = null;
183
+ if (this.cmap !== null) {
184
+ fragment_src = ColorMapGPUInterface.applyShader(fragment_src);
185
+ cmap_gpu = new ColorMapGPUInterface(this.cmap);
186
+ cmap_gpu.setupShaderVariables(gl, gl.NEAREST);
187
+ shader_defines.push('COLORMAP');
188
+ }
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 };
160
191
  }
161
192
  render(gl, matrix, [map_width, map_height], map_zoom, map_bearing, map_pitch) {
162
193
  if (this.gl_elems === null || this.wind_textures === null || !this.show_field)
@@ -169,7 +200,13 @@ class BillboardCollection {
169
200
  const bb_height = this.spec.BB_HEIGHT / this.spec.BB_TEX_HEIGHT;
170
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,
171
202
  '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 });
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 });
204
+ if (gl_elems.cmap_gpu !== null) {
205
+ gl_elems.cmap_gpu.bindShaderVariables(gl_elems.program);
206
+ }
207
+ else {
208
+ gl_elems.program.setUniforms({ 'u_bb_color': this.color.toRGBATuple() });
209
+ }
173
210
  gl.enable(gl.BLEND);
174
211
  gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
175
212
  gl_elems.program.draw();
package/lib/Color.d.ts ADDED
@@ -0,0 +1,56 @@
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
+ }
56
+ export { Color };
package/lib/Color.js ADDED
@@ -0,0 +1,160 @@
1
+ const hex2rgba = (hexstr, out_type) => {
2
+ out_type = out_type === undefined ? 'float' : out_type;
3
+ const match = hexstr.match(/#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?/i);
4
+ if (match === null) {
5
+ throw `Got '${hexstr}' in hex2rgba, which does not look like a hex color`;
6
+ }
7
+ let rgba = match.slice(1).filter(c => c !== undefined).map(c => parseInt(c, 16));
8
+ if (out_type == 'float') {
9
+ rgba = rgba.map(c => c / 255);
10
+ }
11
+ return rgba[3] === undefined ? [rgba[0], rgba[1], rgba[2], 1] : [rgba[0], rgba[1], rgba[2], rgba[3]];
12
+ };
13
+ const rgba2hex = (rgba, in_type) => {
14
+ in_type = in_type === undefined ? 'float' : in_type;
15
+ let rgba_ = rgba;
16
+ if (in_type == 'float') {
17
+ rgba_ = rgba_.map(c => Math.round(c * 255));
18
+ }
19
+ return '#' + rgba_.map(c => c.toString(16).padStart(2, '0').toUpperCase()).join('');
20
+ };
21
+ const rgb2hsv = (rgb) => {
22
+ const [r, g, b] = rgb;
23
+ const Cmax = Math.max(r, g, b);
24
+ const Cmin = Math.min(r, g, b);
25
+ const Delta = Cmax - Cmin;
26
+ let H;
27
+ if (Delta == 0) {
28
+ H = 0;
29
+ }
30
+ else if (Cmax == r) {
31
+ H = 60 * ((g - b) / Delta) % 6;
32
+ }
33
+ else if (Cmax == g) {
34
+ H = 60 * ((b - r) / Delta + 2);
35
+ }
36
+ else if (Cmax == b) {
37
+ H = 60 * ((r - g) / Delta + 4);
38
+ }
39
+ else {
40
+ throw "You've messed something up in rgb2hsv()";
41
+ }
42
+ let S = Cmax == 0 ? 0 : Delta / Cmax;
43
+ let V = Cmax;
44
+ return [H, S, V];
45
+ };
46
+ const hsv2rgb = (hsv) => {
47
+ const [H, S, V] = hsv;
48
+ const C = V * S;
49
+ const X = C * (1 - Math.abs(H / 60 % 2 - 1));
50
+ const m = V - C;
51
+ let r_prime, g_prime, b_prime;
52
+ if (0 <= H && H < 60) {
53
+ r_prime = C;
54
+ g_prime = X, b_prime = 0;
55
+ }
56
+ else if (60 <= H && H < 120) {
57
+ r_prime = X;
58
+ g_prime = C, b_prime = 0;
59
+ }
60
+ else if (120 <= H && H < 180) {
61
+ r_prime = 0;
62
+ g_prime = C, b_prime = X;
63
+ }
64
+ else if (180 <= H && H < 240) {
65
+ r_prime = 0;
66
+ g_prime = X, b_prime = C;
67
+ }
68
+ else if (240 <= H && H < 300) {
69
+ r_prime = X;
70
+ g_prime = 0, b_prime = C;
71
+ }
72
+ else if (300 <= H && H < 360) {
73
+ r_prime = C;
74
+ g_prime = 0, b_prime = X;
75
+ }
76
+ else {
77
+ throw "H is out of bounds in hsv2rgb";
78
+ }
79
+ return [r_prime + m, g_prime + m, b_prime + m];
80
+ };
81
+ class Color {
82
+ /**
83
+ * Create a new color object
84
+ * @param rgba - An RGBA tuple of floats between 0 and 1
85
+ */
86
+ constructor(rgba) {
87
+ this.rgba = rgba;
88
+ }
89
+ /**
90
+ * The red component of the color as a float value between 0 and 1
91
+ */
92
+ get r() {
93
+ return this.rgba[0];
94
+ }
95
+ /**
96
+ * The green component of the color as a float value between 0 and 1
97
+ */
98
+ get g() {
99
+ return this.rgba[1];
100
+ }
101
+ /**
102
+ * The blue component of the color as a float value between 0 and 1
103
+ */
104
+ get b() {
105
+ return this.rgba[2];
106
+ }
107
+ /**
108
+ * The alpha component (opacity) of the color as a float value between 0 and 1
109
+ */
110
+ get a() {
111
+ return this.rgba[3];
112
+ }
113
+ /**
114
+ * @param opacity - The new alpha component (opacity)
115
+ * @returns A new color with the alpha component set to opacity.
116
+ */
117
+ withOpacity(opacity) {
118
+ return new Color([this.r, this.g, this.b, opacity]);
119
+ }
120
+ /**
121
+ * @returns The color as an RGB hex string (e.g., '#dedbef')
122
+ */
123
+ toRGBHex() {
124
+ return this.toRGBAHex().slice(0, -2);
125
+ }
126
+ /**
127
+ * @returns The color as an RGBA hex string (e.g., '#dedbefff')
128
+ */
129
+ toRGBAHex() {
130
+ return rgba2hex(this.rgba);
131
+ }
132
+ /**
133
+ * @returns The color as an RGBA float tuple
134
+ */
135
+ toRGBATuple() {
136
+ return this.rgba;
137
+ }
138
+ /**
139
+ * @returns The color as a tuple of HSV values
140
+ */
141
+ toHSVTuple() {
142
+ return rgb2hsv([this.r, this.g, this.b]);
143
+ }
144
+ /**
145
+ * @param hex - An RGB or RGBA hex string to parse
146
+ * @returns a new Color object
147
+ */
148
+ static fromHex(hex) {
149
+ return new Color(hex2rgba(hex));
150
+ }
151
+ /**
152
+ * @param hsv - A tuple of HSV values
153
+ * @returns a new Color object
154
+ */
155
+ static fromHSVTuple(hsv) {
156
+ const rgb = hsv2rgb(hsv);
157
+ return new Color([rgb[0], rgb[1], rgb[2], 1]);
158
+ }
159
+ }
160
+ export { Color };
package/lib/ColorBar.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Color, ColorMap } from "./Colormap";
1
+ import { ColorMap } from "./Colormap";
2
+ import { Color } from "./Color";
2
3
  type ColorbarOrientation = 'horizontal' | 'vertical';
3
4
  type ColorbarTickDirection = 'top' | 'bottom' | 'left' | 'right';
4
5
  interface ColorBarOptions {
package/lib/ColorBar.js CHANGED
@@ -97,7 +97,7 @@ function makeColorBar(colormap, opts) {
97
97
  else {
98
98
  attrs = { x: bar_left + bar_width * icolor / n_colors, y: bar_top, width: bar_width / n_colors, height: bar_height };
99
99
  }
100
- createElement('rect', { ...attrs, fill: color.color, opacity: color.opacity }, gbar);
100
+ createElement('rect', { ...attrs, fill: color.toRGBHex(), opacity: color.a }, gbar);
101
101
  });
102
102
  // Make the overflow and underflow triangles
103
103
  if (colormap.underflow_color !== null) {
@@ -108,7 +108,7 @@ function makeColorBar(colormap, opts) {
108
108
  else {
109
109
  point_list = `${bar_left} ${bar_bottom}, ${bar_low_arrow} ${bar_middle}, ${bar_left} ${bar_top}, ${bar_left} ${bar_bottom}`;
110
110
  }
111
- const underflow_attrs = { points: point_list, fill: colormap.underflow_color.color, opacity: colormap.underflow_color.opacity };
111
+ const underflow_attrs = { points: point_list, fill: colormap.underflow_color.toRGBHex(), opacity: colormap.underflow_color.a };
112
112
  createElement('polygon', underflow_attrs, gbar);
113
113
  }
114
114
  if (colormap.overflow_color !== null) {
@@ -119,7 +119,7 @@ function makeColorBar(colormap, opts) {
119
119
  else {
120
120
  point_list = `${bar_right} ${bar_top}, ${bar_high_arrow} ${bar_middle}, ${bar_right} ${bar_bottom}, ${bar_right} ${bar_top}`;
121
121
  }
122
- const overflow_attrs = { points: point_list, fill: colormap.overflow_color.color, opacity: colormap.overflow_color.opacity };
122
+ const overflow_attrs = { points: point_list, fill: colormap.overflow_color.toRGBHex(), opacity: colormap.overflow_color.a };
123
123
  createElement('polygon', overflow_attrs, gbar);
124
124
  }
125
125
  // Make the ticks marks and labels
@@ -219,8 +219,8 @@ function makePaintballKey(colors, labels, opts) {
219
219
  const icol = Math.floor(icolor / n_rows);
220
220
  let opacity = 1.;
221
221
  if (typeof color != 'string') {
222
- opacity = color.opacity;
223
- color = color.color;
222
+ opacity = color.a;
223
+ color = color.toRGBHex();
224
224
  }
225
225
  const x = swatch_width_pad + icol * (swatch_width + swatch_text_pad + swatch_text_space + swatch_width_pad);
226
226
  const y = swatch_height_pad + irow * (swatch_height + swatch_height_pad);
package/lib/Colormap.d.ts CHANGED
@@ -1,14 +1,10 @@
1
- import { Float16Array } from "@petamoriken/float16";
2
- interface Color {
3
- /** The color as a hex color string */
4
- color: string;
5
- /** The opacity as a number from 0 to 1 */
6
- opacity: number;
7
- }
1
+ import { WGLProgram, WGLTexture } from "autumn-wgl";
2
+ import { WebGLAnyRenderingContext } from "./AutumnTypes";
3
+ import { Color } from "./Color";
8
4
  interface ColorMapOptions {
9
- /** The color to use for areas where the value is below the lowest value in the color map */
10
- overflow_color?: Color | string;
11
5
  /** The color to use for areas where the value is above the highest value in the color map */
6
+ overflow_color?: Color | string;
7
+ /** The color to use for areas where the value is below the lowest value in the color map */
12
8
  underflow_color?: Color | string;
13
9
  }
14
10
  /** A mapping from values to colors */
@@ -71,12 +67,17 @@ declare const redblue: (level_min: number, level_max: number, n_colors: number)
71
67
  * @returns a Colormap object
72
68
  */
73
69
  declare const bluered: (level_min: number, level_max: number, n_colors: number) => ColorMap;
74
- /**
75
- * Make a canvas image corresponding to a color map
76
- * @param colormap - The color map to use
77
- * @returns A canvas element containing each color of the color map
78
- */
79
- declare function makeTextureImage(colormap: ColorMap): HTMLCanvasElement;
80
- declare function makeIndexMap(colormap: ColorMap): Float16Array;
81
- export { ColorMap, bluered, redblue, pw_speed500mb, pw_speed850mb, pw_cape, pw_t2m, pw_td2m, nws_storm_clear_refl, makeTextureImage, makeIndexMap };
82
- export type { Color, ColorMapOptions };
70
+ interface ColorMapGLElems {
71
+ cmap_texture: WGLTexture;
72
+ cmap_nonlin_texture: WGLTexture;
73
+ }
74
+ declare class ColorMapGPUInterface {
75
+ readonly colormap: ColorMap;
76
+ gl_elems: ColorMapGLElems | null;
77
+ constructor(colormap: ColorMap);
78
+ static applyShader(shader_src: string): string;
79
+ setupShaderVariables(gl: WebGLAnyRenderingContext, mag_filter: number): void;
80
+ bindShaderVariables(program: WGLProgram): void;
81
+ }
82
+ export { ColorMap, ColorMapGPUInterface, bluered, redblue, pw_speed500mb, pw_speed850mb, pw_cape, pw_t2m, pw_td2m, nws_storm_clear_refl };
83
+ export type { ColorMapOptions };