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.
- package/README.md +6 -11
- package/dist/110.autumnplot-gl.js +1 -1
- package/dist/110.autumnplot-gl.js.map +1 -1
- package/dist/autumnplot-gl.js +1 -1
- package/dist/autumnplot-gl.js.map +1 -1
- package/dist/marchingsquares.wasm +0 -0
- package/lib/Barbs.d.ts +18 -2
- package/lib/Barbs.js +25 -19
- package/lib/BillboardCollection.d.ts +9 -2
- package/lib/BillboardCollection.js +46 -9
- package/lib/Color.d.ts +56 -0
- package/lib/Color.js +160 -0
- package/lib/ColorBar.d.ts +2 -1
- package/lib/ColorBar.js +5 -5
- package/lib/Colormap.d.ts +19 -18
- package/lib/Colormap.js +81 -20
- package/lib/Contour.d.ts +25 -6
- package/lib/Contour.js +61 -12
- package/lib/ContourCreator.js +4 -40
- package/lib/Fill.d.ts +2 -4
- package/lib/Fill.js +29 -45
- package/lib/Grid.d.ts +1 -0
- package/lib/Grid.js +6 -1
- package/lib/Hodographs.d.ts +19 -3
- package/lib/Hodographs.js +23 -20
- package/lib/Paintball.d.ts +2 -2
- package/lib/Paintball.js +9 -6
- package/lib/PlotComponent.js +9 -4
- package/lib/PlotLayer.d.ts +1 -1
- package/lib/PlotLayer.worker.js +10 -7
- package/lib/PolylineCollection.d.ts +13 -6
- package/lib/PolylineCollection.js +76 -64
- package/lib/StationPlot.d.ts +34 -0
- package/lib/StationPlot.js +73 -0
- package/lib/TextCollection.d.ts +3 -2
- package/lib/TextCollection.js +21 -11
- package/lib/cpp/marchingsquares.js +558 -1261
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +4 -45
- package/lib/index.d.ts +4 -2
- package/lib/index.js +2 -1
- package/lib/utils.d.ts +2 -8
- package/lib/utils.js +1 -83
- 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
|
|
30
|
-
readonly
|
|
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 {
|
|
4
|
-
|
|
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
|
-
|
|
14
|
+
const LINE_WIDTH_MULTIPLIER = 4;
|
|
15
|
+
function createBarbTexture(dimensions, line_width) {
|
|
14
16
|
let canvas = document.createElement('canvas');
|
|
15
|
-
canvas.width =
|
|
16
|
-
canvas.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 =
|
|
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 =
|
|
92
|
+
ctx.lineWidth = line_width;
|
|
91
93
|
ctx.miterLimit = 4;
|
|
92
|
-
for (let ibarb = 0; ibarb <=
|
|
93
|
-
const x_pos = (ibarb %
|
|
94
|
-
const y_pos = Math.floor(ibarb /
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
this.color =
|
|
119
|
-
this.
|
|
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
|
-
|
|
142
|
-
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
'
|
|
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 {
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
223
|
-
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 {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 };
|