autumnplot-gl 3.2.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 (48) hide show
  1. package/lib/AutumnTypes.d.ts +53 -5
  2. package/lib/AutumnTypes.js +25 -1
  3. package/lib/Barbs.d.ts +6 -5
  4. package/lib/BillboardCollection.d.ts +8 -7
  5. package/lib/BillboardCollection.js +69 -58
  6. package/lib/Color.d.ts +1 -0
  7. package/lib/Color.js +3 -0
  8. package/lib/ColorBar.d.ts +10 -0
  9. package/lib/ColorBar.js +4 -2
  10. package/lib/Colormap.js +8 -8
  11. package/lib/Contour.d.ts +18 -8
  12. package/lib/Contour.js +17 -54
  13. package/lib/ContourCreator.d.ts +4 -0
  14. package/lib/ContourCreator.js +2 -1
  15. package/lib/Fill.d.ts +26 -14
  16. package/lib/Fill.js +97 -50
  17. package/lib/Grid.d.ts +124 -29
  18. package/lib/Grid.js +297 -94
  19. package/lib/Hodographs.d.ts +9 -8
  20. package/lib/Hodographs.js +14 -11
  21. package/lib/Map.js +1 -1
  22. package/lib/Paintball.d.ts +6 -5
  23. package/lib/Paintball.js +35 -30
  24. package/lib/ParticleTracer.d.ts +19 -0
  25. package/lib/ParticleTracer.js +37 -0
  26. package/lib/PlotComponent.d.ts +6 -7
  27. package/lib/PlotComponent.js +8 -3
  28. package/lib/PlotLayer.d.ts +3 -3
  29. package/lib/PlotLayer.worker.d.ts +1 -2
  30. package/lib/PlotLayer.worker.js +15 -50
  31. package/lib/PolylineCollection.d.ts +5 -3
  32. package/lib/PolylineCollection.js +60 -37
  33. package/lib/RawField.d.ts +76 -23
  34. package/lib/RawField.js +138 -29
  35. package/lib/ShaderManager.d.ts +12 -0
  36. package/lib/ShaderManager.js +58 -0
  37. package/lib/StationPlot.d.ts +136 -25
  38. package/lib/StationPlot.js +192 -60
  39. package/lib/TextCollection.d.ts +9 -6
  40. package/lib/TextCollection.js +94 -62
  41. package/lib/cpp/marchingsquares.js +483 -585
  42. package/lib/cpp/marchingsquares.wasm +0 -0
  43. package/lib/cpp/marchingsquares_embind.d.ts +23 -3
  44. package/lib/index.d.ts +4 -3
  45. package/lib/index.js +4 -3
  46. package/lib/utils.d.ts +4 -1
  47. package/lib/utils.js +12 -1
  48. package/package.json +2 -2
@@ -1,23 +1,24 @@
1
- import { isWebGL2Ctx } from "./AutumnTypes";
1
+ import { getRendererData, isWebGL2Ctx } from "./AutumnTypes";
2
2
  import { Color } from "./Color";
3
3
  import { LngLat } from "./Map";
4
+ import { ShaderProgramManager } from "./ShaderManager";
4
5
  import { Cache, normalizeOptions } from "./utils";
5
- import { WGLBuffer, WGLProgram, WGLTexture } from "autumn-wgl";
6
+ import { WGLBuffer, WGLTexture } from "autumn-wgl";
6
7
  import Protobuf from 'pbf';
7
8
  import potpack from "potpack";
8
- const text_vertex_shader_src = `
9
- uniform mat4 u_matrix;
9
+ const text_vertex_shader_src = `#version 300 es
10
+
10
11
  uniform int u_offset;
11
12
  uniform highp float u_map_width;
12
13
  uniform highp float u_map_height;
13
14
  uniform highp float u_map_zoom;
14
15
  uniform highp float u_font_size;
15
16
 
16
- attribute vec3 a_pos;
17
- attribute vec2 a_offset;
18
- attribute vec2 a_tex_coord;
17
+ in vec3 a_pos;
18
+ in vec2 a_offset;
19
+ in vec2 a_tex_coord;
19
20
 
20
- varying highp vec2 v_tex_coord;
21
+ out highp vec2 v_tex_coord;
21
22
 
22
23
  mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
23
24
  return mat4(x_scale, 0.0, 0.0, 0.0,
@@ -38,11 +39,12 @@ void main() {
38
39
 
39
40
  mat4 map_stretch_matrix = scalingMatrix(u_map_height / u_map_width, 1., 1.);
40
41
 
41
- gl_Position = u_matrix * vec4(a_pos.xy + globe_offset, 0.0, 1.0) + u_font_size / 12. * 1.5 * map_stretch_matrix * vec4(offset, 0., 0.);
42
+ gl_Position = projectTile(a_pos.xy + globe_offset) + u_font_size / 12. * 1.5 * map_stretch_matrix * vec4(offset, 0., 0.);
42
43
  v_tex_coord = a_tex_coord;
43
44
  }`
44
- const text_fragment_shader_src = `
45
- varying highp vec2 v_tex_coord;
45
+ const text_fragment_shader_src = `#version 300 es
46
+
47
+ in highp vec2 v_tex_coord;
46
48
  uniform sampler2D u_sdf_sampler;
47
49
  uniform int u_is_halo;
48
50
 
@@ -52,8 +54,10 @@ uniform lowp vec4 u_halo_color;
52
54
  #define SDF_FILL 0.75
53
55
  #define SDF_HALO 0.45
54
56
 
57
+ out highp vec4 fragColor;
58
+
55
59
  void main() {
56
- highp float sdf_val = texture2D(u_sdf_sampler, v_tex_coord).r;
60
+ highp float sdf_val = texture(u_sdf_sampler, v_tex_coord).r;
57
61
 
58
62
  lowp float step_width = 0.08;
59
63
  lowp float alpha = smoothstep(SDF_FILL - step_width, SDF_FILL + step_width, sdf_val);
@@ -65,9 +69,8 @@ void main() {
65
69
  }
66
70
 
67
71
  color.a *= alpha;
68
- gl_FragColor = color;
72
+ fragColor = color;
69
73
  }`
70
- const program_cache = new Cache((gl) => new WGLProgram(gl, text_vertex_shader_src, text_fragment_shader_src));
71
74
  const PADDING = 3;
72
75
  function parseFontPBF(data) {
73
76
  const readGlyph = (tag, glyph, pbf) => {
@@ -125,6 +128,7 @@ function createAtlas(pbf_glyphs) {
125
128
  const { w: img_width, h: img_height } = potpack(bins);
126
129
  const atlas_data = new Uint8Array(img_width * img_height);
127
130
  const glyphs = {};
131
+ let max_glyph_height = 0;
128
132
  glyph_bins.forEach(glyph_bin => {
129
133
  const { bin, glyph } = glyph_bin;
130
134
  if (bin.x === undefined || bin.y === undefined)
@@ -133,6 +137,7 @@ function createAtlas(pbf_glyphs) {
133
137
  id: glyph.id, width: glyph.width, height: glyph.height, left: glyph.left, top: glyph.top,
134
138
  atlas_i: bin.x, atlas_j: bin.y, advance: glyph.advance
135
139
  };
140
+ max_glyph_height = Math.max(max_glyph_height, glyph.height);
136
141
  for (let i = 0; i < glyph.width; i++) {
137
142
  for (let j = 0; j < glyph.height; j++) {
138
143
  const glyph_idx = i + glyph.width * j;
@@ -142,38 +147,46 @@ function createAtlas(pbf_glyphs) {
142
147
  }
143
148
  });
144
149
  const glyph_M = glyphs['M'.charCodeAt(0)];
145
- const baseline = glyph_M.height - glyph_M.top;
146
- const top = -glyph_M.top;
150
+ const baseline = glyph_M === undefined ? max_glyph_height : glyph_M.height - glyph_M.top;
151
+ const top = glyph_M === undefined ? 0 : -glyph_M.top;
147
152
  return { atlas: atlas_data, atlas_width: img_width, atlas_height: img_height, baseline: baseline, top: top, glyph_info: glyphs };
148
153
  }
149
- async function getFontAtlas(url) {
150
- const resp = await fetch(url);
151
- const blob = await resp.blob();
152
- const data_buffer = await blob.arrayBuffer();
153
- // Parse the PBF and get the glyph data
154
- const glyphs = parseFontPBF(new Uint8Array(data_buffer));
154
+ const FONT_ATLAS_CACHE = new Cache(async (urls) => {
155
+ const promises = urls.map(async (url) => {
156
+ const resp = await fetch(url);
157
+ const blob = await resp.blob();
158
+ const data_buffer = await blob.arrayBuffer();
159
+ // Parse the PBF and get the glyph data
160
+ return parseFontPBF(new Uint8Array(data_buffer));
161
+ });
162
+ const glyphs = (await Promise.all(promises)).flat();
155
163
  // Create an atlas for the glyphs
156
164
  return createAtlas(glyphs);
157
- }
165
+ });
158
166
  const text_collection_opt_defaults = {
159
167
  horizontal_align: 'left',
160
168
  vertical_align: 'baseline',
161
169
  font_size: 12,
162
170
  text_color: new Color([0, 0, 0, 1]),
163
171
  halo_color: new Color([0, 0, 0, 1]),
164
- halo: false
172
+ halo: false,
173
+ offset_x: 0,
174
+ offset_y: 0
165
175
  };
166
176
  class TextCollection {
167
177
  constructor(gl, text_locs, font_atlas, opts) {
168
- this.program = program_cache.getValue(gl);
169
178
  this.opts = normalizeOptions(opts, text_collection_opt_defaults);
170
179
  const is_webgl2 = isWebGL2Ctx(gl);
171
180
  const format = is_webgl2 ? gl.R8 : gl.LUMINANCE;
172
181
  const type = gl.UNSIGNED_BYTE;
173
182
  const row_alignment = 1;
183
+ const empty_atlas = font_atlas.atlas_width == 0 || font_atlas.atlas_height == 0 || font_atlas.atlas.length == 0;
184
+ const atlas_width = empty_atlas ? 1 : font_atlas.atlas_width;
185
+ const atlas_height = empty_atlas ? 1 : font_atlas.atlas_height;
186
+ const atlas_data = empty_atlas ? new Uint8Array([0, 0, 0, 0]) : font_atlas.atlas;
174
187
  const image = {
175
- 'format': format, 'type': type, 'width': font_atlas.atlas_width, 'height': font_atlas.atlas_height,
176
- 'image': font_atlas.atlas, 'row_alignment': row_alignment, 'mag_filter': gl.LINEAR
188
+ 'format': format, 'type': type, 'width': atlas_width, 'height': atlas_height,
189
+ 'image': atlas_data, 'row_alignment': row_alignment, 'mag_filter': gl.LINEAR
177
190
  };
178
191
  this.texture = new WGLTexture(gl, image);
179
192
  const n_verts = text_locs.map(tl => tl.text.length).reduce((a, b) => a + b, 0) * 6;
@@ -185,7 +198,8 @@ class TextCollection {
185
198
  const { lat, lon, text } = loc;
186
199
  const min_zoom = loc.min_zoom === undefined ? 0 : loc.min_zoom;
187
200
  const { x: anchor_x, y: anchor_y } = new LngLat(lon, lat).toMercatorCoord();
188
- let x_offset = 0;
201
+ let x_offset = this.opts.offset_x;
202
+ let y_offset = this.opts.offset_y;
189
203
  const init_i_off = i_off;
190
204
  for (let i = 0; i < text.length; i++) {
191
205
  const glyph_code = text.charCodeAt(i);
@@ -214,17 +228,17 @@ class TextCollection {
214
228
  anchor_data[i_anch++] = anchor_y;
215
229
  anchor_data[i_anch++] = min_zoom;
216
230
  offset_data[i_off++] = x_offset;
217
- offset_data[i_off++] = font_atlas.baseline + glyph_info.top - glyph_info.height;
231
+ offset_data[i_off++] = y_offset + font_atlas.baseline + glyph_info.top - glyph_info.height;
218
232
  offset_data[i_off++] = x_offset;
219
- offset_data[i_off++] = font_atlas.baseline + glyph_info.top - glyph_info.height;
233
+ offset_data[i_off++] = y_offset + font_atlas.baseline + glyph_info.top - glyph_info.height;
220
234
  offset_data[i_off++] = x_offset + glyph_info.width;
221
- offset_data[i_off++] = font_atlas.baseline + glyph_info.top - glyph_info.height;
235
+ offset_data[i_off++] = y_offset + font_atlas.baseline + glyph_info.top - glyph_info.height;
222
236
  offset_data[i_off++] = x_offset;
223
- offset_data[i_off++] = font_atlas.baseline + glyph_info.top;
237
+ offset_data[i_off++] = y_offset + font_atlas.baseline + glyph_info.top;
224
238
  offset_data[i_off++] = x_offset + glyph_info.width;
225
- offset_data[i_off++] = font_atlas.baseline + glyph_info.top;
239
+ offset_data[i_off++] = y_offset + font_atlas.baseline + glyph_info.top;
226
240
  offset_data[i_off++] = x_offset + glyph_info.width;
227
- offset_data[i_off++] = font_atlas.baseline + glyph_info.top;
241
+ offset_data[i_off++] = y_offset + font_atlas.baseline + glyph_info.top;
228
242
  tc_data[i_tc++] = glyph_info.atlas_i / font_atlas.atlas_width;
229
243
  tc_data[i_tc++] = (glyph_info.atlas_j + glyph_info.height) / font_atlas.atlas_height;
230
244
  tc_data[i_tc++] = glyph_info.atlas_i / font_atlas.atlas_width;
@@ -241,12 +255,12 @@ class TextCollection {
241
255
  }
242
256
  if (this.opts.horizontal_align == 'center') {
243
257
  for (let i = init_i_off; i < init_i_off + text.length * 12; i += 2) {
244
- offset_data[i] -= x_offset / 2;
258
+ offset_data[i] -= (x_offset - this.opts.offset_x) / 2;
245
259
  }
246
260
  }
247
261
  else if (this.opts.horizontal_align == 'right') {
248
262
  for (let i = init_i_off; i < init_i_off + text.length * 12; i += 2) {
249
- offset_data[i] -= x_offset;
263
+ offset_data[i] -= (x_offset - this.opts.offset_x);
250
264
  }
251
265
  }
252
266
  if (this.opts.vertical_align == 'top') {
@@ -260,45 +274,63 @@ class TextCollection {
260
274
  }
261
275
  }
262
276
  });
277
+ this.shader_manager = new ShaderProgramManager(text_vertex_shader_src, text_fragment_shader_src, []);
263
278
  this.anchors = new WGLBuffer(gl, anchor_data, 3, gl.TRIANGLE_STRIP);
264
279
  this.offsets = new WGLBuffer(gl, offset_data, 2, gl.TRIANGLE_STRIP);
265
280
  this.texcoords = new WGLBuffer(gl, tc_data, 2, gl.TRIANGLE_STRIP);
266
281
  }
267
- static async make(gl, text_locs, fontstack_url, opts) {
268
- const atlas = await getFontAtlas(fontstack_url);
282
+ static async make(gl, text_locs, fontstack_url_template, opts) {
283
+ const FONT_GROUP_SIZE = 256;
284
+ const characters = text_locs.map(tl => [...tl.text]).flat().map(c => c.charCodeAt(0)).filter((c, i, ary) => ary.indexOf(c) == i);
285
+ const char_code_min = Math.min(...characters);
286
+ const char_code_max = Math.max(...characters);
287
+ const stack_start = Math.floor(char_code_min / FONT_GROUP_SIZE) * FONT_GROUP_SIZE;
288
+ const stack_end = Math.floor(char_code_max / FONT_GROUP_SIZE) * FONT_GROUP_SIZE;
289
+ const fontstack_urls = [];
290
+ for (let istack = stack_start; istack <= stack_end; istack += FONT_GROUP_SIZE) {
291
+ fontstack_urls.push(fontstack_url_template.replace('{range}', `${istack}-${istack + FONT_GROUP_SIZE - 1}`));
292
+ }
293
+ const atlas = await FONT_ATLAS_CACHE.getValue(fontstack_urls);
294
+ if (atlas.atlas_height == 0 || atlas.atlas_width == 0 || atlas.atlas.length == 0)
295
+ console.warn(`No font data from '${fontstack_url_template}'`);
269
296
  return new TextCollection(gl, text_locs, atlas, opts);
270
297
  }
271
- render(gl, matrix, [map_width, map_height], map_zoom) {
298
+ render(gl, arg, [map_width, map_height], map_zoom) {
299
+ const render_data = getRendererData(arg);
300
+ const program = this.shader_manager.getShaderProgram(gl, render_data.shaderData);
272
301
  const uniforms = {
273
- 'u_matrix': matrix, 'u_map_width': map_width, 'u_map_height': map_height, 'u_map_zoom': map_zoom, 'u_font_size': this.opts.font_size,
274
- 'u_text_color': this.opts.text_color.toRGBATuple(), 'u_halo_color': this.opts.halo_color.toRGBATuple(), 'u_offset': 0
302
+ 'u_map_width': map_width, 'u_map_height': map_height, 'u_map_zoom': map_zoom, 'u_font_size': this.opts.font_size,
303
+ 'u_text_color': this.opts.text_color.toRGBATuple(), 'u_halo_color': this.opts.halo_color.toRGBATuple(), 'u_offset': 0,
304
+ ...this.shader_manager.getShaderUniforms(render_data)
275
305
  };
276
306
  uniforms['u_is_halo'] = this.opts.halo ? 1 : 0;
277
- this.program.use({ 'a_pos': this.anchors, 'a_offset': this.offsets, 'a_tex_coord': this.texcoords }, uniforms, { 'u_sdf_sampler': this.texture });
307
+ program.use({ 'a_pos': this.anchors, 'a_offset': this.offsets, 'a_tex_coord': this.texcoords }, uniforms, { 'u_sdf_sampler': this.texture });
278
308
  gl.enable(gl.BLEND);
279
309
  gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
280
- this.program.draw();
281
- if (this.opts.halo) {
282
- this.program.setUniforms({ 'u_is_halo': 0 });
283
- this.program.draw();
284
- }
285
- this.program.setUniforms({ 'u_offset': -2, 'u_is_halo': this.opts.halo ? 1 : 0 });
286
- this.program.draw();
287
- if (this.opts.halo) {
288
- this.program.setUniforms({ 'u_is_halo': 0 });
289
- this.program.draw();
290
- }
291
- this.program.setUniforms({ 'u_offset': -1, 'u_is_halo': this.opts.halo ? 1 : 0 });
292
- this.program.draw();
310
+ program.draw();
293
311
  if (this.opts.halo) {
294
- this.program.setUniforms({ 'u_is_halo': 0 });
295
- this.program.draw();
312
+ program.setUniforms({ 'u_is_halo': 0 });
313
+ program.draw();
296
314
  }
297
- this.program.setUniforms({ 'u_offset': 1, 'u_is_halo': this.opts.halo ? 1 : 0 });
298
- this.program.draw();
299
- if (this.opts.halo) {
300
- this.program.setUniforms({ 'u_is_halo': 0 });
301
- this.program.draw();
315
+ if (render_data.type != 'maplibre' || !render_data.shaderData.define.includes('GLOBE')) {
316
+ program.setUniforms({ 'u_offset': -2, 'u_is_halo': this.opts.halo ? 1 : 0 });
317
+ program.draw();
318
+ if (this.opts.halo) {
319
+ program.setUniforms({ 'u_is_halo': 0 });
320
+ program.draw();
321
+ }
322
+ program.setUniforms({ 'u_offset': -1, 'u_is_halo': this.opts.halo ? 1 : 0 });
323
+ program.draw();
324
+ if (this.opts.halo) {
325
+ program.setUniforms({ 'u_is_halo': 0 });
326
+ program.draw();
327
+ }
328
+ program.setUniforms({ 'u_offset': 1, 'u_is_halo': this.opts.halo ? 1 : 0 });
329
+ program.draw();
330
+ if (this.opts.halo) {
331
+ program.setUniforms({ 'u_is_halo': 0 });
332
+ program.draw();
333
+ }
302
334
  }
303
335
  }
304
336
  }