autumnplot-gl 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/812.autumnplot-gl.js +2 -0
- package/dist/812.autumnplot-gl.js.map +1 -0
- package/dist/983.autumnplot-gl.js +1 -1
- package/dist/983.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/AutumnTypes.d.ts +6 -4
- package/lib/AutumnTypes.js +4 -1
- package/lib/Barbs.d.ts +11 -2
- package/lib/Barbs.js +9 -0
- package/lib/BillboardCollection.d.ts +2 -2
- package/lib/BillboardCollection.js +14 -14
- package/lib/ColorBar.d.ts +6 -1
- package/lib/ColorBar.js +10 -4
- package/lib/Colormap.d.ts +8 -1
- package/lib/Colormap.js +24 -1
- package/lib/Contour.d.ts +16 -1
- package/lib/Contour.js +14 -2
- package/lib/{ContourCreator.d.ts → ContourCreator.worker.d.ts} +10 -11
- package/lib/{ContourCreator.js → ContourCreator.worker.js} +15 -14
- package/lib/Fill.d.ts +29 -11
- package/lib/Fill.js +38 -18
- package/lib/Hodographs.d.ts +13 -3
- package/lib/Hodographs.js +11 -1
- package/lib/Map.d.ts +3 -4
- package/lib/Map.js +49 -51
- package/lib/Paintball.d.ts +13 -5
- package/lib/Paintball.js +96 -46
- package/lib/PlotComponent.d.ts +8 -3
- package/lib/PlotComponent.js +35 -1
- package/lib/PlotLayer.worker.js +1 -1
- package/lib/RawField.d.ts +221 -27
- package/lib/RawField.js +405 -58
- package/lib/StationPlot.d.ts +22 -6
- package/lib/StationPlot.js +88 -22
- package/lib/TextCollection.d.ts +5 -0
- package/lib/TextCollection.js +79 -9
- package/lib/WasmInterface.d.ts +7 -0
- package/lib/WasmInterface.js +11 -0
- package/lib/WorkerPool.d.ts +8 -0
- package/lib/WorkerPool.js +77 -0
- package/lib/cpp/marchingsquares.js +127 -13
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +16 -3
- package/lib/grids/AutoZoom.d.ts +21 -0
- package/lib/grids/AutoZoom.js +63 -0
- package/lib/grids/DomainBuffer.d.ts +14 -0
- package/lib/grids/DomainBuffer.js +16 -0
- package/lib/grids/Geostationary.d.ts +35 -0
- package/lib/grids/Geostationary.js +47 -0
- package/lib/grids/Grid.d.ts +36 -0
- package/lib/grids/Grid.js +12 -0
- package/lib/grids/GridCoordinates.d.ts +10 -0
- package/lib/grids/GridCoordinates.js +64 -0
- package/lib/grids/LambertGrid.d.ts +73 -0
- package/lib/grids/LambertGrid.js +92 -0
- package/lib/grids/PlateCarreeGrid.d.ts +46 -0
- package/lib/grids/PlateCarreeGrid.js +55 -0
- package/lib/grids/PlateCarreeRotatedGrid.d.ts +53 -0
- package/lib/grids/PlateCarreeRotatedGrid.js +65 -0
- package/lib/grids/RadarSweepGrid.d.ts +46 -0
- package/lib/grids/RadarSweepGrid.js +74 -0
- package/lib/grids/StructuredGrid.d.ts +49 -0
- package/lib/grids/StructuredGrid.js +103 -0
- package/lib/grids/UnstructuredGrid.d.ts +56 -0
- package/lib/grids/UnstructuredGrid.js +102 -0
- package/lib/index.d.ts +15 -4
- package/lib/index.js +15 -7
- package/lib/utils.d.ts +11 -2
- package/lib/utils.js +63 -1
- package/package.json +3 -2
- package/lib/Grid.d.ts +0 -270
- package/lib/Grid.js +0 -600
- package/lib/ParticleTracer.d.ts +0 -19
- package/lib/ParticleTracer.js +0 -37
package/lib/StationPlot.js
CHANGED
|
@@ -76,8 +76,35 @@ function positionToAlignmentAndOffset(pos, off_size) {
|
|
|
76
76
|
}
|
|
77
77
|
return { horizontal_align: ha, vertical_align: va, offset_x: xoff, offset_y: yoff };
|
|
78
78
|
}
|
|
79
|
+
function symbolCategory(symbol) {
|
|
80
|
+
const sym = symbol.toLowerCase();
|
|
81
|
+
if (sym.includes('fzra') || (sym.includes('fz') && sym.includes('ra')))
|
|
82
|
+
return 'freezing_rain';
|
|
83
|
+
if (sym.includes('pl'))
|
|
84
|
+
return 'sleet';
|
|
85
|
+
if (sym.includes('sn') || sym.includes('sg') || sym.includes('gs'))
|
|
86
|
+
return 'snow';
|
|
87
|
+
if (sym.includes('ra') || sym.includes('dz'))
|
|
88
|
+
return 'rain';
|
|
89
|
+
if (sym.includes('du') || sym.includes('ds') || sym.includes('ss') || sym.includes('blsa') || sym.includes('bldu') || sym.includes('blpy'))
|
|
90
|
+
return 'blowing_dust';
|
|
91
|
+
if (sym.includes('ts') || sym.includes('thdr'))
|
|
92
|
+
return 'thunder';
|
|
93
|
+
if (sym.includes('fg'))
|
|
94
|
+
return 'fog';
|
|
95
|
+
return 'none';
|
|
96
|
+
}
|
|
79
97
|
/**
|
|
80
98
|
* Station model plots for observed data
|
|
99
|
+
*
|
|
100
|
+
* ## Grid Compatibility
|
|
101
|
+
* - :white_check_mark: `PlateCarreeGrid`
|
|
102
|
+
* - :white_check_mark: `PlateCarreeRotatedGrid`
|
|
103
|
+
* - :white_check_mark: `LambertGrid`
|
|
104
|
+
* - :white_check_mark: `UnstructuredGrid`
|
|
105
|
+
* - :x: `RadarSweepGrid`
|
|
106
|
+
* - :x: `Geostationary`
|
|
107
|
+
*
|
|
81
108
|
* @example
|
|
82
109
|
* // Specify how to set up the station plot
|
|
83
110
|
* const station_plot_locs = {
|
|
@@ -120,32 +147,51 @@ class StationPlot extends PlotComponent {
|
|
|
120
147
|
if (font_url_template === undefined)
|
|
121
148
|
throw "The map style doesn't have any glyph information. Please pass the font_url_template option to StationPlot";
|
|
122
149
|
const font_url = font_url_template.replace('{fontstack}', this.opts.font_face);
|
|
150
|
+
const coords = this.field.grid.getEarthCoords();
|
|
151
|
+
const zoom = this.field.grid.getMinVisibleZoom(this.opts.thin_fac);
|
|
123
152
|
let ibarb = 0;
|
|
124
|
-
|
|
153
|
+
let sub_component_promises = [];
|
|
154
|
+
Object.entries(this.opts.config).forEach(async ([k_, config]) => {
|
|
125
155
|
const k = k_;
|
|
126
156
|
if (config.type == 'number' || config.type == 'string') {
|
|
127
157
|
const pos = config.pos;
|
|
128
158
|
const color = config.color === undefined ? Color.fromHex('#000000') : Color.normalizeColor(config.color);
|
|
129
159
|
const halo_color = config.halo_color === undefined ? Color.fromHex('#ffffff') : Color.normalizeColor(config.halo_color);
|
|
130
160
|
const halo = config.halo === undefined ? true : config.halo;
|
|
131
|
-
|
|
132
|
-
const zoom = this.field.grid.getMinVisibleZoom(this.opts.thin_fac);
|
|
161
|
+
let cmap = null;
|
|
133
162
|
let text_specs;
|
|
134
163
|
if (config.type == 'number') {
|
|
164
|
+
cmap = config.cmap === undefined ? null : config.cmap;
|
|
135
165
|
const comp = this.field.getScalar(k);
|
|
136
166
|
const formatter = config.formatter === undefined ? (val) => val === null ? 'null' : val.toString() : config.formatter;
|
|
137
|
-
text_specs = comp.map((v, i) =>
|
|
167
|
+
text_specs = comp.map((v, i) => {
|
|
168
|
+
const spec = {
|
|
169
|
+
text: formatter(v),
|
|
170
|
+
lat: coords.lats[i],
|
|
171
|
+
lon: coords.lons[i],
|
|
172
|
+
min_zoom: zoom[i],
|
|
173
|
+
};
|
|
174
|
+
if (v !== null)
|
|
175
|
+
spec.data_value = v;
|
|
176
|
+
return spec;
|
|
177
|
+
});
|
|
138
178
|
}
|
|
139
179
|
else {
|
|
140
180
|
const comp = this.field.getStrings(k);
|
|
141
|
-
text_specs = comp.map((v, i) => ({
|
|
181
|
+
text_specs = comp.map((v, i) => ({
|
|
182
|
+
text: v === null ? '' : v,
|
|
183
|
+
lat: coords.lats[i],
|
|
184
|
+
lon: coords.lons[i],
|
|
185
|
+
min_zoom: zoom[i],
|
|
186
|
+
}));
|
|
142
187
|
}
|
|
143
188
|
const tc_opts = {
|
|
144
189
|
...positionToAlignmentAndOffset(pos),
|
|
145
190
|
font_size: this.opts.font_size, halo: halo,
|
|
146
191
|
text_color: color, halo_color: halo_color,
|
|
192
|
+
cmap: cmap
|
|
147
193
|
};
|
|
148
|
-
|
|
194
|
+
sub_component_promises.push([TextCollection.make(gl, text_specs, font_url, tc_opts)]);
|
|
149
195
|
}
|
|
150
196
|
else if (config.type == 'barb') {
|
|
151
197
|
const comp = this.field.getVector(k);
|
|
@@ -154,29 +200,49 @@ class StationPlot extends PlotComponent {
|
|
|
154
200
|
}
|
|
155
201
|
else if (config.type == 'symbol') {
|
|
156
202
|
const pos = config.pos;
|
|
157
|
-
const color = config.color === undefined ?
|
|
203
|
+
const color = config.color === undefined ? '#000000' : config.color;
|
|
158
204
|
const halo_color = config.halo_color === undefined ? Color.fromHex('#ffffff') : Color.normalizeColor(config.halo_color);
|
|
159
205
|
const halo = config.halo === undefined ? true : config.halo;
|
|
160
206
|
const comp = this.field.getStrings(k);
|
|
161
|
-
const coords = this.field.grid.getEarthCoords();
|
|
162
|
-
const zoom = this.field.grid.getMinVisibleZoom(this.opts.thin_fac);
|
|
163
207
|
const wxsym_font_url = font_url_template.replace('{fontstack}', 'wx_symbols');
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
208
|
+
const text_spec_data = [];
|
|
209
|
+
comp.forEach((v, i) => {
|
|
210
|
+
const v_cat = v === null ? 'none' : symbolCategory(v);
|
|
211
|
+
const text_color = typeof color === 'string' ? color : color(v, v_cat);
|
|
212
|
+
const text_spec_data_filtered = text_spec_data.filter(tsd => tsd.color == text_color);
|
|
213
|
+
let text_spec_color;
|
|
214
|
+
if (text_spec_data_filtered.length == 0) {
|
|
215
|
+
text_spec_color = { specs: [], color: text_color };
|
|
216
|
+
text_spec_data.push(text_spec_color);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
text_spec_color = text_spec_data_filtered[0];
|
|
220
|
+
}
|
|
221
|
+
text_spec_color.specs.push({
|
|
222
|
+
text: v === null ? '' : String.fromCharCode(SYMBOLS[v]),
|
|
223
|
+
lat: coords.lats[i],
|
|
224
|
+
lon: coords.lons[i],
|
|
225
|
+
min_zoom: zoom[i],
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
const promises = [];
|
|
229
|
+
text_spec_data.forEach(tsd => {
|
|
230
|
+
const tc_opts = {
|
|
231
|
+
...positionToAlignmentAndOffset(pos),
|
|
232
|
+
font_size: this.opts.font_size, halo: halo,
|
|
233
|
+
text_color: Color.normalizeColor(tsd.color), halo_color: halo_color,
|
|
234
|
+
};
|
|
235
|
+
if (tc_opts.offset_x !== undefined)
|
|
236
|
+
tc_opts.offset_x -= 3;
|
|
237
|
+
promises.push(TextCollection.make(gl, tsd.specs, wxsym_font_url, tc_opts));
|
|
238
|
+
});
|
|
239
|
+
sub_component_promises.push(promises);
|
|
174
240
|
}
|
|
175
241
|
else {
|
|
176
242
|
throw `Unknown station plot configuration type ${config.type}`;
|
|
177
243
|
}
|
|
178
244
|
});
|
|
179
|
-
this.text_components =
|
|
245
|
+
this.text_components = await Promise.all(sub_component_promises.map(p => Promise.all(p)));
|
|
180
246
|
map.triggerRepaint();
|
|
181
247
|
}
|
|
182
248
|
/** @internal */
|
|
@@ -209,12 +275,12 @@ class StationPlot extends PlotComponent {
|
|
|
209
275
|
const map_height = gl_elems.map.getCanvas().height;
|
|
210
276
|
const map_zoom = gl_elems.map.getZoom();
|
|
211
277
|
let itext = 0, ibarb = 0;
|
|
212
|
-
Object.values(this.opts.config).forEach(comp => {
|
|
278
|
+
Object.values(this.opts.config).forEach((comp, idx) => {
|
|
213
279
|
if (comp.type == 'barb') {
|
|
214
280
|
barb_components[ibarb++].render(gl, arg);
|
|
215
281
|
}
|
|
216
282
|
else {
|
|
217
|
-
text_components[itext++].render(gl, arg, [map_width, map_height], map_zoom);
|
|
283
|
+
text_components[itext++].forEach(tc => tc.render(gl, arg, [map_width, map_height], map_zoom));
|
|
218
284
|
}
|
|
219
285
|
});
|
|
220
286
|
}
|
package/lib/TextCollection.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RenderMethodArg, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
2
2
|
import { Color } from "./Color";
|
|
3
|
+
import { ColorMap, ColorMapGPUInterface } from "./Colormap";
|
|
3
4
|
import { ShaderProgramManager } from "./ShaderManager";
|
|
4
5
|
import { WGLBuffer, WGLTexture } from "autumn-wgl";
|
|
5
6
|
interface TextSpec {
|
|
@@ -7,6 +8,7 @@ interface TextSpec {
|
|
|
7
8
|
lon: number;
|
|
8
9
|
text: string;
|
|
9
10
|
min_zoom?: number;
|
|
11
|
+
data_value?: number;
|
|
10
12
|
}
|
|
11
13
|
type HorizontalAlign = 'left' | 'center' | 'right';
|
|
12
14
|
type VerticalAlign = 'baseline' | 'middle' | 'top';
|
|
@@ -15,6 +17,7 @@ interface TextCollectionOptions {
|
|
|
15
17
|
vertical_align?: VerticalAlign;
|
|
16
18
|
font_size?: number;
|
|
17
19
|
text_color?: Color;
|
|
20
|
+
cmap?: ColorMap | null;
|
|
18
21
|
halo_color?: Color;
|
|
19
22
|
halo?: boolean;
|
|
20
23
|
offset_x?: number;
|
|
@@ -25,7 +28,9 @@ declare class TextCollection {
|
|
|
25
28
|
readonly anchors: WGLBuffer;
|
|
26
29
|
readonly offsets: WGLBuffer;
|
|
27
30
|
readonly texcoords: WGLBuffer;
|
|
31
|
+
readonly data: WGLBuffer | null;
|
|
28
32
|
readonly texture: WGLTexture;
|
|
33
|
+
readonly cmap_gpu: ColorMapGPUInterface | null;
|
|
29
34
|
readonly opts: Required<TextCollectionOptions>;
|
|
30
35
|
private constructor();
|
|
31
36
|
static make(gl: WebGLAnyRenderingContext, text_locs: TextSpec[], fontstack_url_template: string, opts?: TextCollectionOptions): Promise<TextCollection>;
|
package/lib/TextCollection.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRendererData, isWebGL2Ctx } from "./AutumnTypes";
|
|
2
2
|
import { Color } from "./Color";
|
|
3
|
+
import { ColorMap, ColorMapGPUInterface } from "./Colormap";
|
|
3
4
|
import { LngLat } from "./Map";
|
|
4
5
|
import { ShaderProgramManager } from "./ShaderManager";
|
|
5
6
|
import { Cache, normalizeOptions } from "./utils";
|
|
@@ -18,8 +19,16 @@ in vec3 a_pos;
|
|
|
18
19
|
in vec2 a_offset;
|
|
19
20
|
in vec2 a_tex_coord;
|
|
20
21
|
|
|
22
|
+
#ifdef DATA
|
|
23
|
+
in highp float a_value;
|
|
24
|
+
#endif
|
|
25
|
+
|
|
21
26
|
out highp vec2 v_tex_coord;
|
|
22
27
|
|
|
28
|
+
#ifdef DATA
|
|
29
|
+
out highp float v_value;
|
|
30
|
+
#endif
|
|
31
|
+
|
|
23
32
|
mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
|
|
24
33
|
return mat4(x_scale, 0.0, 0.0, 0.0,
|
|
25
34
|
0.0, y_scale, 0.0, 0.0,
|
|
@@ -41,14 +50,25 @@ void main() {
|
|
|
41
50
|
|
|
42
51
|
gl_Position = projectTile(a_pos.xy + globe_offset) + u_font_size / 12. * 1.5 * map_stretch_matrix * vec4(offset, 0., 0.);
|
|
43
52
|
v_tex_coord = a_tex_coord;
|
|
53
|
+
|
|
54
|
+
#ifdef DATA
|
|
55
|
+
v_value = a_value;
|
|
56
|
+
#endif
|
|
44
57
|
}`
|
|
45
58
|
const text_fragment_shader_src = `#version 300 es
|
|
46
59
|
|
|
47
60
|
in highp vec2 v_tex_coord;
|
|
61
|
+
|
|
62
|
+
#ifdef DATA
|
|
63
|
+
in highp float v_value;
|
|
64
|
+
#endif
|
|
65
|
+
|
|
48
66
|
uniform sampler2D u_sdf_sampler;
|
|
49
67
|
uniform int u_is_halo;
|
|
50
68
|
|
|
69
|
+
#ifndef DATA
|
|
51
70
|
uniform lowp vec4 u_text_color;
|
|
71
|
+
#endif
|
|
52
72
|
uniform lowp vec4 u_halo_color;
|
|
53
73
|
|
|
54
74
|
#define SDF_FILL 0.75
|
|
@@ -62,7 +82,13 @@ void main() {
|
|
|
62
82
|
lowp float step_width = 0.08;
|
|
63
83
|
lowp float alpha = smoothstep(SDF_FILL - step_width, SDF_FILL + step_width, sdf_val);
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
#ifdef DATA
|
|
86
|
+
lowp vec4 text_color = apply_colormap(v_value);
|
|
87
|
+
#else
|
|
88
|
+
lowp vec4 text_color = u_text_color;
|
|
89
|
+
#endif
|
|
90
|
+
|
|
91
|
+
lowp vec4 color = u_is_halo == 1 ? u_halo_color : text_color;
|
|
66
92
|
|
|
67
93
|
if (u_is_halo == 1) {
|
|
68
94
|
alpha = min(smoothstep(SDF_HALO - step_width, SDF_HALO + step_width, sdf_val), 1.0 - alpha);
|
|
@@ -171,6 +197,7 @@ const text_collection_opt_defaults = {
|
|
|
171
197
|
vertical_align: 'baseline',
|
|
172
198
|
font_size: 12,
|
|
173
199
|
text_color: new Color([0, 0, 0, 1]),
|
|
200
|
+
cmap: null,
|
|
174
201
|
halo_color: new Color([0, 0, 0, 1]),
|
|
175
202
|
halo: false,
|
|
176
203
|
offset_x: 0,
|
|
@@ -179,6 +206,7 @@ const text_collection_opt_defaults = {
|
|
|
179
206
|
class TextCollection {
|
|
180
207
|
constructor(gl, text_locs, font_atlas, opts) {
|
|
181
208
|
this.opts = normalizeOptions(opts, text_collection_opt_defaults);
|
|
209
|
+
const text_color_hex = this.opts.text_color === undefined ? new Color([0, 0, 0, 1]) : this.opts.text_color;
|
|
182
210
|
const is_webgl2 = isWebGL2Ctx(gl);
|
|
183
211
|
const format = is_webgl2 ? gl.R8 : gl.LUMINANCE;
|
|
184
212
|
const type = gl.UNSIGNED_BYTE;
|
|
@@ -196,11 +224,15 @@ class TextCollection {
|
|
|
196
224
|
const anchor_data = new Float32Array(n_verts * 3);
|
|
197
225
|
const offset_data = new Float32Array(n_verts * 2);
|
|
198
226
|
const tc_data = new Float32Array(n_verts * 2);
|
|
199
|
-
|
|
227
|
+
const value_data = new Float32Array(n_verts);
|
|
228
|
+
let i_anch = 0, i_off = 0, i_tc = 0, i_dat = 0;
|
|
229
|
+
let has_data = false;
|
|
200
230
|
text_locs.forEach(loc => {
|
|
201
231
|
const { lat, lon, text } = loc;
|
|
202
232
|
const min_zoom = loc.min_zoom === undefined ? 0 : loc.min_zoom;
|
|
233
|
+
const data_value = loc.data_value === undefined ? NaN : loc.data_value;
|
|
203
234
|
const { x: anchor_x, y: anchor_y } = new LngLat(lon, lat).toMercatorCoord();
|
|
235
|
+
has_data = has_data || loc.data_value !== undefined;
|
|
204
236
|
let x_offset = this.opts.offset_x;
|
|
205
237
|
let y_offset = this.opts.offset_y;
|
|
206
238
|
const init_i_off = i_off;
|
|
@@ -254,6 +286,12 @@ class TextCollection {
|
|
|
254
286
|
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
255
287
|
tc_data[i_tc++] = (glyph_info.atlas_i + glyph_info.width) / font_atlas.atlas_width;
|
|
256
288
|
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
289
|
+
value_data[i_dat++] = data_value;
|
|
290
|
+
value_data[i_dat++] = data_value;
|
|
291
|
+
value_data[i_dat++] = data_value;
|
|
292
|
+
value_data[i_dat++] = data_value;
|
|
293
|
+
value_data[i_dat++] = data_value;
|
|
294
|
+
value_data[i_dat++] = data_value;
|
|
257
295
|
x_offset += glyph_info.advance - glyph_info.left;
|
|
258
296
|
}
|
|
259
297
|
if (this.opts.horizontal_align == 'center') {
|
|
@@ -277,18 +315,40 @@ class TextCollection {
|
|
|
277
315
|
}
|
|
278
316
|
}
|
|
279
317
|
});
|
|
280
|
-
|
|
318
|
+
const shader_defines = [];
|
|
319
|
+
let fragment_src = text_fragment_shader_src;
|
|
320
|
+
if (has_data) {
|
|
321
|
+
shader_defines.push('DATA');
|
|
322
|
+
this.data = new WGLBuffer(gl, value_data, 1, gl.TRIANGLE_STRIP);
|
|
323
|
+
const cmap = this.opts.cmap === null ? new ColorMap([0, 1], [text_color_hex], { overflow_color: text_color_hex, underflow_color: text_color_hex }) : this.opts.cmap;
|
|
324
|
+
this.cmap_gpu = new ColorMapGPUInterface(cmap);
|
|
325
|
+
this.cmap_gpu.setupShaderVariables(gl, gl.NEAREST);
|
|
326
|
+
fragment_src = ColorMapGPUInterface.applyShader(fragment_src);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
this.data = null;
|
|
330
|
+
this.cmap_gpu = null;
|
|
331
|
+
}
|
|
332
|
+
this.shader_manager = new ShaderProgramManager(text_vertex_shader_src, fragment_src, shader_defines);
|
|
281
333
|
this.anchors = new WGLBuffer(gl, anchor_data, 3, gl.TRIANGLE_STRIP);
|
|
282
334
|
this.offsets = new WGLBuffer(gl, offset_data, 2, gl.TRIANGLE_STRIP);
|
|
283
335
|
this.texcoords = new WGLBuffer(gl, tc_data, 2, gl.TRIANGLE_STRIP);
|
|
336
|
+
this.data = has_data ? new WGLBuffer(gl, value_data, 1, gl.TRIANGLE_STRIP) : null;
|
|
284
337
|
}
|
|
285
338
|
static async make(gl, text_locs, fontstack_url_template, opts) {
|
|
286
339
|
const FONT_GROUP_SIZE = 256;
|
|
287
340
|
const characters = text_locs.map(tl => [...tl.text]).flat().map(c => c.charCodeAt(0)).filter((c, i, ary) => ary.indexOf(c) == i);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
341
|
+
let stack_start, stack_end;
|
|
342
|
+
if (characters.length > 0) {
|
|
343
|
+
const char_code_min = Math.min(...characters);
|
|
344
|
+
const char_code_max = Math.max(...characters);
|
|
345
|
+
stack_start = Math.floor(char_code_min / FONT_GROUP_SIZE) * FONT_GROUP_SIZE;
|
|
346
|
+
stack_end = Math.floor(char_code_max / FONT_GROUP_SIZE) * FONT_GROUP_SIZE;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
stack_start = 0;
|
|
350
|
+
stack_end = 0;
|
|
351
|
+
}
|
|
292
352
|
const fontstack_urls = [];
|
|
293
353
|
for (let istack = stack_start; istack <= stack_end; istack += FONT_GROUP_SIZE) {
|
|
294
354
|
fontstack_urls.push(fontstack_url_template.replace('{range}', `${istack}-${istack + FONT_GROUP_SIZE - 1}`));
|
|
@@ -301,13 +361,23 @@ class TextCollection {
|
|
|
301
361
|
render(gl, arg, [map_width, map_height], map_zoom) {
|
|
302
362
|
const render_data = getRendererData(arg);
|
|
303
363
|
const program = this.shader_manager.getShaderProgram(gl, render_data.shaderData);
|
|
364
|
+
const attributes = { 'a_pos': this.anchors, 'a_offset': this.offsets, 'a_tex_coord': this.texcoords };
|
|
304
365
|
const uniforms = {
|
|
305
366
|
'u_map_width': map_width, 'u_map_height': map_height, 'u_map_zoom': map_zoom, 'u_font_size': this.opts.font_size,
|
|
306
|
-
'
|
|
367
|
+
'u_halo_color': this.opts.halo_color.toRGBATuple(), 'u_offset': 0,
|
|
307
368
|
...this.shader_manager.getShaderUniforms(render_data)
|
|
308
369
|
};
|
|
370
|
+
if (this.data !== null) {
|
|
371
|
+
attributes['a_value'] = this.data;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
uniforms['u_text_color'] = this.opts.text_color.toRGBATuple();
|
|
375
|
+
}
|
|
309
376
|
uniforms['u_is_halo'] = this.opts.halo ? 1 : 0;
|
|
310
|
-
program.use(
|
|
377
|
+
program.use(attributes, uniforms, { 'u_sdf_sampler': this.texture });
|
|
378
|
+
if (this.cmap_gpu !== null) {
|
|
379
|
+
this.cmap_gpu.bindShaderVariables(program);
|
|
380
|
+
}
|
|
311
381
|
gl.enable(gl.BLEND);
|
|
312
382
|
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
313
383
|
program.draw();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MarchingSquaresModule } from './cpp/marchingsquares';
|
|
2
|
+
import './cpp/marchingsquares.wasm';
|
|
3
|
+
interface InitMSModuleOpts {
|
|
4
|
+
document_script?: string;
|
|
5
|
+
}
|
|
6
|
+
declare function initMSModule(opts: InitMSModuleOpts): Promise<MarchingSquaresModule>;
|
|
7
|
+
export { initMSModule };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Should rename this eventually
|
|
2
|
+
import Module from './cpp/marchingsquares';
|
|
3
|
+
import './cpp/marchingsquares.wasm';
|
|
4
|
+
let msm_promise = null;
|
|
5
|
+
function initMSModule(opts) {
|
|
6
|
+
if (msm_promise === null) {
|
|
7
|
+
msm_promise = Module({ 'locateFile': (fname, dir) => (opts.document_script === undefined ? dir : opts.document_script) + fname });
|
|
8
|
+
}
|
|
9
|
+
return msm_promise;
|
|
10
|
+
}
|
|
11
|
+
export { initMSModule };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
type Promisify<T> = (T extends (...args: infer TArguments) => infer TReturn ? (...args: TArguments) => Promise<TReturn> : unknown);
|
|
3
|
+
type WorkerPool<T> = {
|
|
4
|
+
[K in keyof T]: Promisify<T[K]>;
|
|
5
|
+
};
|
|
6
|
+
declare function createWorkerPool<T>(workers: Worker[], init?: (wkr: Comlink.Remote<T>) => void): WorkerPool<T>;
|
|
7
|
+
export { createWorkerPool };
|
|
8
|
+
export type { WorkerPool };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
class WorkerPool_ {
|
|
3
|
+
constructor(workers, init) {
|
|
4
|
+
this.workers = workers.map(wkr => ({ worker: Comlink.wrap(wkr), is_busy: false }));
|
|
5
|
+
if (init) {
|
|
6
|
+
this.workers.forEach(wkr => init(wkr.worker));
|
|
7
|
+
}
|
|
8
|
+
this.queue = new Map();
|
|
9
|
+
}
|
|
10
|
+
call(path, args, callback) {
|
|
11
|
+
const worker_idx = this.workers.map(((w, iw) => [w, iw])).filter(([w, iw]) => !w.is_busy)[0];
|
|
12
|
+
if (worker_idx === undefined) {
|
|
13
|
+
this.enqueue(path, args, callback);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const [worker, iw] = worker_idx;
|
|
17
|
+
const dequeueAndStart = () => {
|
|
18
|
+
if (this.queueSize(path) > 0) {
|
|
19
|
+
const queue_data = this.dequeue(path);
|
|
20
|
+
if (queue_data === undefined)
|
|
21
|
+
return;
|
|
22
|
+
this.startRun(worker, path, queue_data.args).then(queue_data.callback).then(dequeueAndStart);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
this.startRun(worker, path, args).then(callback).then(dequeueAndStart);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
enqueue(path, args, callback) {
|
|
29
|
+
const path_str = path.join(".");
|
|
30
|
+
const old_queue = this.queue.get(path_str);
|
|
31
|
+
const new_queue = old_queue ? old_queue : [];
|
|
32
|
+
new_queue.push({ args: args, callback: callback });
|
|
33
|
+
this.queue.set(path_str, new_queue);
|
|
34
|
+
}
|
|
35
|
+
dequeue(path) {
|
|
36
|
+
const path_str = path.join(".");
|
|
37
|
+
const queue = this.queue.get(path_str);
|
|
38
|
+
if (!queue) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const elem = queue.shift();
|
|
42
|
+
this.queue.set(path_str, queue);
|
|
43
|
+
return elem;
|
|
44
|
+
}
|
|
45
|
+
queueSize(path) {
|
|
46
|
+
const path_str = path.join(".");
|
|
47
|
+
const queue = this.queue.get(path_str);
|
|
48
|
+
if (!queue) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
return queue.length;
|
|
52
|
+
}
|
|
53
|
+
async startRun(worker, path, args) {
|
|
54
|
+
let rem = worker.worker;
|
|
55
|
+
path.forEach(p => rem = rem[p]);
|
|
56
|
+
worker.is_busy = true;
|
|
57
|
+
const ret = await rem(...args);
|
|
58
|
+
worker.is_busy = false;
|
|
59
|
+
return ret;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function createProxy(pool, path, target) {
|
|
63
|
+
const proxy = new Proxy(target, {
|
|
64
|
+
get(tgt, prop) {
|
|
65
|
+
return createProxy(pool, [...path, prop], () => { });
|
|
66
|
+
},
|
|
67
|
+
apply(tgt, this_arg, func_args) {
|
|
68
|
+
return new Promise((resolve, reject) => pool.call(path, func_args, resolve));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return proxy;
|
|
72
|
+
}
|
|
73
|
+
function createWorkerPool(workers, init) {
|
|
74
|
+
const pool = new WorkerPool_(workers, init);
|
|
75
|
+
return createProxy(pool, [], () => { });
|
|
76
|
+
}
|
|
77
|
+
export { createWorkerPool };
|