autumnplot-gl 2.2.3 → 3.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 +65 -8
- package/dist/110.autumnplot-gl.js +2 -0
- package/dist/110.autumnplot-gl.js.map +1 -0
- package/dist/autumnplot-gl.js +3 -0
- package/dist/autumnplot-gl.js.LICENSE.txt +12 -0
- package/dist/autumnplot-gl.js.map +1 -0
- package/dist/marchingsquares.wasm +0 -0
- package/lib/AutumnTypes.d.ts +14 -13
- package/lib/Barbs.d.ts +10 -5
- package/lib/Barbs.js +22 -5
- package/lib/BillboardCollection.d.ts +11 -7
- package/lib/BillboardCollection.js +52 -30
- package/lib/ColorBar.js +51 -7
- package/lib/Colormap.d.ts +14 -3
- package/lib/Colormap.js +63 -12
- package/lib/Contour.d.ts +73 -18
- package/lib/Contour.js +180 -148
- package/lib/ContourCreator.d.ts +18 -0
- package/lib/ContourCreator.js +23 -0
- package/lib/Fill.d.ts +18 -7
- package/lib/Fill.js +73 -41
- package/lib/Grid.d.ts +167 -0
- package/lib/Grid.js +339 -0
- package/lib/Hodographs.d.ts +13 -6
- package/lib/Hodographs.js +58 -71
- package/lib/Map.d.ts +14 -3
- package/lib/Map.js +9 -0
- package/lib/Paintball.d.ts +11 -5
- package/lib/Paintball.js +35 -15
- package/lib/PlotComponent.d.ts +5 -5
- package/lib/PlotLayer.d.ts +12 -12
- package/lib/PlotLayer.js +16 -14
- package/lib/PlotLayer.worker.d.ts +2 -2
- package/lib/PlotLayer.worker.js +105 -66
- package/lib/PolylineCollection.d.ts +20 -9
- package/lib/PolylineCollection.js +158 -32
- package/lib/RawField.d.ts +11 -167
- package/lib/RawField.js +37 -383
- package/lib/TextCollection.d.ts +31 -0
- package/lib/TextCollection.js +295 -0
- package/lib/cpp/marchingsquares.d.ts +6 -0
- package/lib/cpp/marchingsquares.js +3449 -0
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +6 -0
- package/lib/index.d.ts +13 -6
- package/lib/index.js +12 -3
- package/lib/utils.d.ts +5 -3
- package/lib/utils.js +17 -6
- package/package.json +14 -9
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { isWebGL2Ctx } from "./AutumnTypes";
|
|
2
|
+
import { LngLat } from "./Map";
|
|
3
|
+
import { Cache, normalizeOptions } from "./utils";
|
|
4
|
+
import { WGLBuffer, WGLProgram, WGLTexture } from "autumn-wgl";
|
|
5
|
+
import Protobuf from 'pbf';
|
|
6
|
+
import potpack from "potpack";
|
|
7
|
+
const text_vertex_shader_src = `
|
|
8
|
+
uniform mat4 u_matrix;
|
|
9
|
+
uniform int u_offset;
|
|
10
|
+
uniform highp float u_map_width;
|
|
11
|
+
uniform highp float u_map_height;
|
|
12
|
+
uniform highp float u_map_zoom;
|
|
13
|
+
uniform highp float u_font_size;
|
|
14
|
+
|
|
15
|
+
attribute vec3 a_pos;
|
|
16
|
+
attribute vec2 a_offset;
|
|
17
|
+
attribute vec2 a_tex_coord;
|
|
18
|
+
|
|
19
|
+
varying highp vec2 v_tex_coord;
|
|
20
|
+
|
|
21
|
+
mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
|
|
22
|
+
return mat4(x_scale, 0.0, 0.0, 0.0,
|
|
23
|
+
0.0, y_scale, 0.0, 0.0,
|
|
24
|
+
0.0, 0.0, z_scale, 0.0,
|
|
25
|
+
0.0, 0.0, 0.0, 1.0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void main() {
|
|
29
|
+
float min_zoom = a_pos.z;
|
|
30
|
+
|
|
31
|
+
vec2 offset = a_offset;
|
|
32
|
+
if (u_map_zoom < min_zoom)
|
|
33
|
+
offset = vec2(0., 0.);
|
|
34
|
+
|
|
35
|
+
float globe_width = 1.;
|
|
36
|
+
vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
|
|
37
|
+
|
|
38
|
+
mat4 map_stretch_matrix = scalingMatrix(u_map_height / u_map_width, 1., 1.);
|
|
39
|
+
|
|
40
|
+
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.);
|
|
41
|
+
v_tex_coord = a_tex_coord;
|
|
42
|
+
}`
|
|
43
|
+
const text_fragment_shader_src = `
|
|
44
|
+
varying highp vec2 v_tex_coord;
|
|
45
|
+
uniform sampler2D u_sdf_sampler;
|
|
46
|
+
uniform int u_is_halo;
|
|
47
|
+
|
|
48
|
+
uniform lowp vec3 u_text_color;
|
|
49
|
+
uniform lowp vec3 u_halo_color;
|
|
50
|
+
|
|
51
|
+
#define SDF_FILL 0.75
|
|
52
|
+
#define SDF_HALO 0.45
|
|
53
|
+
|
|
54
|
+
void main() {
|
|
55
|
+
highp float sdf_val = texture2D(u_sdf_sampler, v_tex_coord).r;
|
|
56
|
+
|
|
57
|
+
lowp float step_width = 0.08;
|
|
58
|
+
lowp float alpha = smoothstep(SDF_FILL - step_width, SDF_FILL + step_width, sdf_val);
|
|
59
|
+
|
|
60
|
+
lowp vec3 color = u_is_halo == 1 ? u_halo_color : u_text_color;
|
|
61
|
+
|
|
62
|
+
if (u_is_halo == 1) {
|
|
63
|
+
alpha = min(smoothstep(SDF_HALO - step_width, SDF_HALO + step_width, sdf_val), 1.0 - alpha);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
gl_FragColor = vec4(color, alpha);
|
|
67
|
+
}`
|
|
68
|
+
const program_cache = new Cache((gl) => new WGLProgram(gl, text_vertex_shader_src, text_fragment_shader_src));
|
|
69
|
+
const PADDING = 3;
|
|
70
|
+
function parseFontPBF(data) {
|
|
71
|
+
const readGlyph = (tag, glyph, pbf) => {
|
|
72
|
+
switch (tag) {
|
|
73
|
+
case 1:
|
|
74
|
+
glyph.id = pbf.readVarint();
|
|
75
|
+
break;
|
|
76
|
+
case 2:
|
|
77
|
+
glyph.data = pbf.readBytes();
|
|
78
|
+
break;
|
|
79
|
+
case 3:
|
|
80
|
+
glyph.width = pbf.readVarint() + 2 * PADDING;
|
|
81
|
+
break;
|
|
82
|
+
case 4:
|
|
83
|
+
glyph.height = pbf.readVarint() + 2 * PADDING;
|
|
84
|
+
break;
|
|
85
|
+
case 5:
|
|
86
|
+
glyph.left = pbf.readSVarint();
|
|
87
|
+
break;
|
|
88
|
+
case 6:
|
|
89
|
+
glyph.top = pbf.readSVarint();
|
|
90
|
+
break;
|
|
91
|
+
case 7:
|
|
92
|
+
glyph.advance = pbf.readVarint();
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const readFontStack = (tag, glyphs, pbf) => {
|
|
97
|
+
if (tag == 3) {
|
|
98
|
+
const glyph = pbf.readMessage(readGlyph, {});
|
|
99
|
+
glyphs.push(glyph);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const readFontStacks = (tag, glyphs, pbf) => {
|
|
103
|
+
if (tag == 1) {
|
|
104
|
+
pbf.readMessage(readFontStack, glyphs);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
return new Protobuf(data).readFields(readFontStacks, []);
|
|
108
|
+
}
|
|
109
|
+
function createAtlas(pbf_glyphs) {
|
|
110
|
+
const pbf_glyphs_filtered = pbf_glyphs.filter(glyph => glyph.data !== undefined);
|
|
111
|
+
const glyph_bins = [];
|
|
112
|
+
const bins = pbf_glyphs_filtered.map(glyph => {
|
|
113
|
+
const bin = { x: 0, y: 0, w: glyph.width, h: glyph.height };
|
|
114
|
+
glyph_bins.push({ glyph: glyph, bin: bin });
|
|
115
|
+
return bin;
|
|
116
|
+
});
|
|
117
|
+
const { w: img_width, h: img_height } = potpack(bins);
|
|
118
|
+
const atlas_data = new Uint8Array(img_width * img_height);
|
|
119
|
+
const glyphs = {};
|
|
120
|
+
glyph_bins.forEach(glyph_bin => {
|
|
121
|
+
const { bin, glyph } = glyph_bin;
|
|
122
|
+
glyphs[glyph.id] = {
|
|
123
|
+
id: glyph.id, width: glyph.width, height: glyph.height, left: glyph.left, top: glyph.top,
|
|
124
|
+
atlas_i: bin.x, atlas_j: bin.y, advance: glyph.advance
|
|
125
|
+
};
|
|
126
|
+
for (let i = 0; i < glyph.width; i++) {
|
|
127
|
+
for (let j = 0; j < glyph.height; j++) {
|
|
128
|
+
const glyph_idx = i + glyph.width * j;
|
|
129
|
+
const atlas_idx = (i + bin.x) + img_width * (j + bin.y);
|
|
130
|
+
atlas_data[atlas_idx] = glyph.data[glyph_idx];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
const glyph_M = glyphs['M'.charCodeAt(0)];
|
|
135
|
+
const baseline = glyph_M.height - glyph_M.top;
|
|
136
|
+
const top = -glyph_M.top;
|
|
137
|
+
return { atlas: atlas_data, atlas_width: img_width, atlas_height: img_height, baseline: baseline, top: top, glyph_info: glyphs };
|
|
138
|
+
}
|
|
139
|
+
async function getFontAtlas(url) {
|
|
140
|
+
const resp = await fetch(url);
|
|
141
|
+
const blob = await resp.blob();
|
|
142
|
+
const data_buffer = await blob.arrayBuffer();
|
|
143
|
+
// Parse the PBF and get the glyph data
|
|
144
|
+
const glyphs = parseFontPBF(new Uint8Array(data_buffer));
|
|
145
|
+
// Create an atlas for the glyphs
|
|
146
|
+
return createAtlas(glyphs);
|
|
147
|
+
}
|
|
148
|
+
const text_collection_opt_defaults = {
|
|
149
|
+
horizontal_align: 'left',
|
|
150
|
+
vertical_align: 'baseline',
|
|
151
|
+
font_size: 12,
|
|
152
|
+
text_color: [0, 0, 0],
|
|
153
|
+
halo_color: [0, 0, 0],
|
|
154
|
+
halo: false
|
|
155
|
+
};
|
|
156
|
+
class TextCollection {
|
|
157
|
+
constructor(gl, text_locs, font_atlas, opts) {
|
|
158
|
+
this.program = program_cache.getValue(gl);
|
|
159
|
+
this.opts = normalizeOptions(opts, text_collection_opt_defaults);
|
|
160
|
+
const is_webgl2 = isWebGL2Ctx(gl);
|
|
161
|
+
const format = is_webgl2 ? gl.R8 : gl.LUMINANCE;
|
|
162
|
+
const type = gl.UNSIGNED_BYTE;
|
|
163
|
+
const row_alignment = 1;
|
|
164
|
+
const image = {
|
|
165
|
+
'format': format, 'type': type, 'width': font_atlas.atlas_width, 'height': font_atlas.atlas_height,
|
|
166
|
+
'image': font_atlas.atlas, 'row_alignment': row_alignment, 'mag_filter': gl.LINEAR
|
|
167
|
+
};
|
|
168
|
+
this.texture = new WGLTexture(gl, image);
|
|
169
|
+
const n_verts = text_locs.map(tl => tl.text.length).reduce((a, b) => a + b, 0) * 6;
|
|
170
|
+
const anchor_data = new Float32Array(n_verts * 3);
|
|
171
|
+
const offset_data = new Float32Array(n_verts * 2);
|
|
172
|
+
const tc_data = new Float32Array(n_verts * 2);
|
|
173
|
+
let i_anch = 0, i_off = 0, i_tc = 0;
|
|
174
|
+
text_locs.forEach(loc => {
|
|
175
|
+
const { lat, lon, text } = loc;
|
|
176
|
+
const min_zoom = loc.min_zoom === undefined ? 0 : loc.min_zoom;
|
|
177
|
+
const { x: anchor_x, y: anchor_y } = new LngLat(lon, lat).toMercatorCoord();
|
|
178
|
+
let x_offset = 0;
|
|
179
|
+
const init_i_off = i_off;
|
|
180
|
+
for (let i = 0; i < text.length; i++) {
|
|
181
|
+
const glyph_code = text.charCodeAt(i);
|
|
182
|
+
const glyph_info = font_atlas.glyph_info[glyph_code];
|
|
183
|
+
if (glyph_info === undefined) {
|
|
184
|
+
x_offset += 7;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
x_offset += glyph_info.left;
|
|
188
|
+
anchor_data[i_anch++] = anchor_x;
|
|
189
|
+
anchor_data[i_anch++] = anchor_y;
|
|
190
|
+
anchor_data[i_anch++] = min_zoom;
|
|
191
|
+
anchor_data[i_anch++] = anchor_x;
|
|
192
|
+
anchor_data[i_anch++] = anchor_y;
|
|
193
|
+
anchor_data[i_anch++] = min_zoom;
|
|
194
|
+
anchor_data[i_anch++] = anchor_x;
|
|
195
|
+
anchor_data[i_anch++] = anchor_y;
|
|
196
|
+
anchor_data[i_anch++] = min_zoom;
|
|
197
|
+
anchor_data[i_anch++] = anchor_x;
|
|
198
|
+
anchor_data[i_anch++] = anchor_y;
|
|
199
|
+
anchor_data[i_anch++] = min_zoom;
|
|
200
|
+
anchor_data[i_anch++] = anchor_x;
|
|
201
|
+
anchor_data[i_anch++] = anchor_y;
|
|
202
|
+
anchor_data[i_anch++] = min_zoom;
|
|
203
|
+
anchor_data[i_anch++] = anchor_x;
|
|
204
|
+
anchor_data[i_anch++] = anchor_y;
|
|
205
|
+
anchor_data[i_anch++] = min_zoom;
|
|
206
|
+
offset_data[i_off++] = x_offset;
|
|
207
|
+
offset_data[i_off++] = font_atlas.baseline + glyph_info.top - glyph_info.height;
|
|
208
|
+
offset_data[i_off++] = x_offset;
|
|
209
|
+
offset_data[i_off++] = font_atlas.baseline + glyph_info.top - glyph_info.height;
|
|
210
|
+
offset_data[i_off++] = x_offset + glyph_info.width;
|
|
211
|
+
offset_data[i_off++] = font_atlas.baseline + glyph_info.top - glyph_info.height;
|
|
212
|
+
offset_data[i_off++] = x_offset;
|
|
213
|
+
offset_data[i_off++] = font_atlas.baseline + glyph_info.top;
|
|
214
|
+
offset_data[i_off++] = x_offset + glyph_info.width;
|
|
215
|
+
offset_data[i_off++] = font_atlas.baseline + glyph_info.top;
|
|
216
|
+
offset_data[i_off++] = x_offset + glyph_info.width;
|
|
217
|
+
offset_data[i_off++] = font_atlas.baseline + glyph_info.top;
|
|
218
|
+
tc_data[i_tc++] = glyph_info.atlas_i / font_atlas.atlas_width;
|
|
219
|
+
tc_data[i_tc++] = (glyph_info.atlas_j + glyph_info.height) / font_atlas.atlas_height;
|
|
220
|
+
tc_data[i_tc++] = glyph_info.atlas_i / font_atlas.atlas_width;
|
|
221
|
+
tc_data[i_tc++] = (glyph_info.atlas_j + glyph_info.height) / font_atlas.atlas_height;
|
|
222
|
+
tc_data[i_tc++] = (glyph_info.atlas_i + glyph_info.width) / font_atlas.atlas_width;
|
|
223
|
+
tc_data[i_tc++] = (glyph_info.atlas_j + glyph_info.height) / font_atlas.atlas_height;
|
|
224
|
+
tc_data[i_tc++] = glyph_info.atlas_i / font_atlas.atlas_width;
|
|
225
|
+
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
226
|
+
tc_data[i_tc++] = (glyph_info.atlas_i + glyph_info.width) / font_atlas.atlas_width;
|
|
227
|
+
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
228
|
+
tc_data[i_tc++] = (glyph_info.atlas_i + glyph_info.width) / font_atlas.atlas_width;
|
|
229
|
+
tc_data[i_tc++] = glyph_info.atlas_j / font_atlas.atlas_height;
|
|
230
|
+
x_offset += glyph_info.advance - glyph_info.left;
|
|
231
|
+
}
|
|
232
|
+
if (opts.horizontal_align == 'center') {
|
|
233
|
+
for (let i = init_i_off; i < init_i_off + text.length * 12; i += 2) {
|
|
234
|
+
offset_data[i] -= x_offset / 2;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (opts.horizontal_align == 'right') {
|
|
238
|
+
for (let i = init_i_off; i < init_i_off + text.length * 12; i += 2) {
|
|
239
|
+
offset_data[i] -= x_offset;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (opts.vertical_align == 'top') {
|
|
243
|
+
for (let i = init_i_off + 1; i < init_i_off + text.length * 12; i += 2) {
|
|
244
|
+
offset_data[i] -= (font_atlas.baseline - font_atlas.top);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else if (opts.vertical_align == 'middle') {
|
|
248
|
+
for (let i = init_i_off + 1; i < init_i_off + text.length * 12; i += 2) {
|
|
249
|
+
offset_data[i] -= (font_atlas.baseline - font_atlas.top) / 2;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
this.anchors = new WGLBuffer(gl, anchor_data, 3, gl.TRIANGLE_STRIP);
|
|
254
|
+
this.offsets = new WGLBuffer(gl, offset_data, 2, gl.TRIANGLE_STRIP);
|
|
255
|
+
this.texcoords = new WGLBuffer(gl, tc_data, 2, gl.TRIANGLE_STRIP);
|
|
256
|
+
}
|
|
257
|
+
static async make(gl, text_locs, fontstack_url, opts) {
|
|
258
|
+
const atlas = await getFontAtlas(fontstack_url);
|
|
259
|
+
return new TextCollection(gl, text_locs, atlas, opts);
|
|
260
|
+
}
|
|
261
|
+
render(gl, matrix, [map_width, map_height], map_zoom) {
|
|
262
|
+
const uniforms = {
|
|
263
|
+
'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,
|
|
264
|
+
'u_text_color': this.opts.text_color, 'u_halo_color': this.opts.halo_color, 'u_offset': 0
|
|
265
|
+
};
|
|
266
|
+
uniforms['u_is_halo'] = this.opts.halo ? 1 : 0;
|
|
267
|
+
this.program.use({ 'a_pos': this.anchors, 'a_offset': this.offsets, 'a_tex_coord': this.texcoords }, uniforms, { 'u_sdf_sampler': this.texture });
|
|
268
|
+
gl.enable(gl.BLEND);
|
|
269
|
+
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
270
|
+
this.program.draw();
|
|
271
|
+
if (this.opts.halo) {
|
|
272
|
+
this.program.setUniforms({ 'u_is_halo': 0 });
|
|
273
|
+
this.program.draw();
|
|
274
|
+
}
|
|
275
|
+
this.program.setUniforms({ 'u_offset': -2, 'u_is_halo': this.opts.halo ? 1 : 0 });
|
|
276
|
+
this.program.draw();
|
|
277
|
+
if (this.opts.halo) {
|
|
278
|
+
this.program.setUniforms({ 'u_is_halo': 0 });
|
|
279
|
+
this.program.draw();
|
|
280
|
+
}
|
|
281
|
+
this.program.setUniforms({ 'u_offset': -1, 'u_is_halo': this.opts.halo ? 1 : 0 });
|
|
282
|
+
this.program.draw();
|
|
283
|
+
if (this.opts.halo) {
|
|
284
|
+
this.program.setUniforms({ 'u_is_halo': 0 });
|
|
285
|
+
this.program.draw();
|
|
286
|
+
}
|
|
287
|
+
this.program.setUniforms({ 'u_offset': 1, 'u_is_halo': this.opts.halo ? 1 : 0 });
|
|
288
|
+
this.program.draw();
|
|
289
|
+
if (this.opts.halo) {
|
|
290
|
+
this.program.setUniforms({ 'u_is_halo': 0 });
|
|
291
|
+
this.program.draw();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
export { TextCollection };
|