@fxi/zartigl 0.1.2 → 0.1.4

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/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ import { CustomLayerInterface } from 'maplibre-gl';
2
2
  import { CustomRenderMethodInput } from 'maplibre-gl';
3
3
  import { Map as Map_2 } from 'maplibre-gl';
4
4
 
5
+ export declare type ColorRampInput = string | string[] | Record<number, string>;
6
+
5
7
  declare interface CoordArrays {
6
8
  time: Float64Array | Float32Array;
7
9
  vertical: Float32Array;
@@ -9,35 +11,107 @@ declare interface CoordArrays {
9
11
  longitude: Float32Array;
10
12
  }
11
13
 
14
+ export declare interface FieldMeta {
15
+ min: number;
16
+ max: number;
17
+ unit: string;
18
+ time: string;
19
+ depth?: number;
20
+ }
21
+
22
+ export declare function getPalettes(): PaletteMeta[];
23
+
24
+ declare type LayerEventMap = {
25
+ loading: () => void;
26
+ loaded: (meta: FieldMeta) => void;
27
+ error: (err: Error) => void;
28
+ /** Fired when a time step has been pre-fetched and is ready for instant swap. */
29
+ frameBuffered: (ms: number) => void;
30
+ /** Fired when the frame cache is wiped (viewport changed). */
31
+ cacheInvalidated: () => void;
32
+ };
33
+
34
+ export declare interface PaletteMeta {
35
+ id: string;
36
+ label: string;
37
+ tags: string[];
38
+ colors: string[];
39
+ }
40
+
12
41
  export declare class ParticleLayer implements CustomLayerInterface {
13
42
  readonly id: string;
14
43
  readonly type: "custom";
15
- readonly renderingMode: "2d";
44
+ readonly renderingMode: "3d";
16
45
  private map;
17
46
  private gl;
18
47
  private simulation;
19
48
  private velocityField;
20
49
  private zarrSource;
21
- private opts;
50
+ private speedFactorParam;
51
+ private fadeOpacityParam;
52
+ private zoomRange;
53
+ private variableU;
54
+ private variableV;
55
+ private time;
56
+ private depth;
57
+ private scalarMode;
58
+ private scalarUnit;
22
59
  private initialized;
23
60
  private loading;
24
61
  private velocityTexUnit;
62
+ private moveStartHandler;
25
63
  private moveEndHandler;
64
+ /** Pre-fetched frames keyed by time-ms. Cleared when the viewport changes. */
65
+ private frameCache;
66
+ /** Time-ms values currently being fetched in the background. */
67
+ private inflight;
68
+ private listeners;
26
69
  constructor(options: ParticleLayerOptions);
27
70
  onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
28
71
  private initAsync;
72
+ private timeToMs;
73
+ private computeFieldMeta;
74
+ /**
75
+ * Fetch and stitch velocity data for the given time without touching GL state.
76
+ * The map's current viewport is used to determine the lat bounds of the request.
77
+ */
78
+ private fetchVelocityData;
29
79
  private loadViewportVelocity;
30
80
  render(gl: WebGLRenderingContext, options: CustomRenderMethodInput | number[]): void;
31
81
  onRemove(): void;
82
+ /**
83
+ * Interpolate speed and fade per render frame (cheap uniform uploads).
84
+ * t=0 at low zoom (global), t=1 at high zoom (local).
85
+ * For each range [min, max]: min applies at high zoom, max at low zoom.
86
+ */
87
+ private applyZoomWeighting;
32
88
  setTime(time: string | number): void;
89
+ /**
90
+ * Pre-fetch velocity data for a future time step without swapping the active
91
+ * field. Fires `frameBuffered(ms)` when ready so playback can advance
92
+ * immediately without a loading gap.
93
+ */
94
+ prefetchTime(ms: number): Promise<void>;
95
+ /** True when the frame for the given time (ms) is already in the cache. */
96
+ isFrameCached(ms: number): boolean;
97
+ /** Abort all pending Zarr prefetch fetches and clear the inflight set. */
98
+ cancelPrefetches(): void;
33
99
  setDepth(depth: number): void;
34
- setSpeedFactor(v: number): void;
35
- setFadeOpacity(v: number): void;
100
+ setSpeedFactor(v: ZoomWeighted): void;
101
+ setFadeOpacity(v: ZoomWeighted): void;
36
102
  setDropRate(v: number): void;
37
103
  setDropRateBump(v: number): void;
38
- setPointSize(v: number): void;
39
- setParticleCount(count: number): void;
40
- setColorRamp(ramp: Record<number, string>): void;
104
+ setParticleDensity(density: number): void;
105
+ setZoomRange(range: [number, number]): void;
106
+ setColorRamp(ramp: ColorRampInput): void;
107
+ setRenderMode(mode: RenderMode): void;
108
+ setOpacity(v: number): void;
109
+ setLogScale(v: boolean): void;
110
+ setVibrance(v: number): void;
111
+ setScalarVariable(varName: string): void;
112
+ on<K extends keyof LayerEventMap>(event: K, handler: LayerEventMap[K]): this;
113
+ off<K extends keyof LayerEventMap>(event: K, handler: LayerEventMap[K]): this;
114
+ private emit;
41
115
  }
42
116
 
43
117
  export declare interface ParticleLayerOptions {
@@ -45,15 +119,27 @@ export declare interface ParticleLayerOptions {
45
119
  source: string;
46
120
  variableU?: string;
47
121
  variableV?: string;
48
- particleCount?: number;
49
- speedFactor?: number;
50
- fadeOpacity?: number;
122
+ /** Particles per screen pixel. Count = clamp(w × h × density, 1, 262144). Default 0.05. */
123
+ particleDensity?: number;
124
+ /** Fixed speed or [atHighZoom, atLowZoom]. */
125
+ speedFactor?: ZoomWeighted;
126
+ /** Fixed fade or [atHighZoom, atLowZoom]. */
127
+ fadeOpacity?: ZoomWeighted;
51
128
  dropRate?: number;
52
129
  dropRateBump?: number;
53
- pointSize?: number;
54
130
  colorRamp?: Record<number, string>;
55
131
  time?: string | number;
56
132
  depth?: number;
133
+ /** [lowZoom, highZoom] breakpoints for zoom-weighted params. Default [2, 12]. */
134
+ zoomRange?: [number, number];
135
+ /** Global layer opacity [0, 1]. Default 1.0. */
136
+ opacity?: number;
137
+ /** Logarithmic speed normalization. Default false. */
138
+ logScale?: boolean;
139
+ /** Vibrance adjustment [-1, 1]. Default 0.0. */
140
+ vibrance?: number;
141
+ scalarMode?: boolean;
142
+ scalarUnit?: string;
57
143
  }
58
144
 
59
145
  export declare class ParticleSimulation {
@@ -62,10 +148,12 @@ export declare class ParticleSimulation {
62
148
  private updateProgram;
63
149
  private drawProgram;
64
150
  private fadeProgram;
151
+ private gridProgram;
65
152
  private particleStateTextures;
66
153
  private particleFramebuffers;
67
- private particleStateRes;
68
154
  private particleIndexBuffer;
155
+ private particleIsCurrBuffer;
156
+ /** Active draw count — changes with canvas size / density, no realloc. */
69
157
  private numParticles;
70
158
  private screenTextures;
71
159
  private screenFramebuffers;
@@ -73,15 +161,18 @@ export declare class ParticleSimulation {
73
161
  private screenHeight;
74
162
  private quadBuffer;
75
163
  private colorRampTexture;
164
+ renderMode: RenderMode;
76
165
  private updateLocs;
77
166
  private drawLocs;
78
167
  private fadeLocs;
168
+ private gridLocs;
79
169
  constructor(params?: Partial<SimulationParams>);
80
170
  init(gl: WebGLRenderingContext): void;
81
171
  private getUniforms;
82
172
  private initParticleState;
83
173
  /**
84
- * Ensure screen textures match the current canvas size.
174
+ * Ensure screen textures match the current canvas size and recompute
175
+ * the active particle count from density × canvas area.
85
176
  */
86
177
  resize(width: number, height: number): void;
87
178
  /**
@@ -93,35 +184,51 @@ export declare class ParticleSimulation {
93
184
  minY: number;
94
185
  maxX: number;
95
186
  maxY: number;
96
- }, worldSize: number, geoBounds?: {
187
+ }, worldSize: number, worldCopyOffsets: number[], geoBounds?: {
97
188
  west: number;
98
189
  south: number;
99
190
  east: number;
100
191
  north: number;
101
- }): void;
192
+ }, isGlobe?: boolean): void;
102
193
  private drawQuad;
103
194
  setSpeedFactor(v: number): void;
104
195
  setFadeOpacity(v: number): void;
105
196
  setDropRate(v: number): void;
106
197
  setDropRateBump(v: number): void;
107
- setPointSize(v: number): void;
108
- setParticleCount(count: number): void;
198
+ setParticleDensity(density: number): void;
109
199
  setColorRamp(ramp: Record<number, string>): void;
200
+ setRenderMode(mode: RenderMode): void;
201
+ setOpacity(v: number): void;
202
+ setLogScale(v: boolean): void;
203
+ setVibrance(v: number): void;
204
+ setScalarMode(v: boolean): void;
110
205
  /**
111
- * Reset all particles to random positions. Call after time/depth change.
206
+ * Reset all particles to random positions. Call after time/depth change
207
+ * or map move. Randomises the full state texture and clears trail FBOs.
112
208
  */
113
209
  resetParticles(): void;
210
+ /**
211
+ * Clear screen framebuffers to transparent black.
212
+ * Eliminates ghost trails when the viewport shifts.
213
+ */
214
+ clearState(): void;
114
215
  destroy(): void;
115
216
  }
116
217
 
218
+ declare type RenderMode = 'raster' | 'particles' | 'raster+particles';
219
+
117
220
  declare interface SimulationParams {
118
- particleCount: number;
221
+ /** Particles per screen pixel. Active count = clamp(w * h * density, 1, MAX). */
222
+ particleDensity: number;
119
223
  speedFactor: number;
120
224
  fadeOpacity: number;
121
225
  dropRate: number;
122
226
  dropRateBump: number;
123
- pointSize: number;
124
227
  colorRamp?: Record<number, string>;
228
+ opacity: number;
229
+ logScale: boolean;
230
+ vibrance: number;
231
+ scalarMode: boolean;
125
232
  }
126
233
 
127
234
  /**
@@ -135,18 +242,20 @@ export declare function stitchVelocityChunks(uChunks: Array<{
135
242
  lonStart: number;
136
243
  latSize: number;
137
244
  lonSize: number;
245
+ lonChunkSize: number;
138
246
  }>, vChunks: Array<{
139
247
  data: Float32Array;
140
248
  latStart: number;
141
249
  lonStart: number;
142
250
  latSize: number;
143
251
  lonSize: number;
252
+ lonChunkSize: number;
144
253
  }>, totalLat: number, totalLon: number, geoBounds?: {
145
254
  west: number;
146
255
  south: number;
147
256
  east: number;
148
257
  north: number;
149
- }): VelocityData;
258
+ }, latDescending?: boolean, scalarMode?: boolean): VelocityData;
150
259
 
151
260
  export declare interface VelocityData {
152
261
  u: Float32Array;
@@ -163,6 +272,10 @@ export declare interface VelocityData {
163
272
  east: number;
164
273
  north: number;
165
274
  };
275
+ /** True when latitude rows are stored north-to-south (needs GL Y-flip). */
276
+ latDescending?: boolean;
277
+ /** When true: R=scalar_norm, G=128(neutral), V loop skipped. */
278
+ scalarMode?: boolean;
166
279
  }
167
280
 
168
281
  /**
@@ -184,6 +297,8 @@ export declare class VelocityField {
184
297
  east: number;
185
298
  north: number;
186
299
  };
300
+ scalarMode: boolean;
301
+ private linearFilter;
187
302
  init(gl: WebGLRenderingContext): void;
188
303
  /**
189
304
  * Upload velocity data as a 2-channel (RG) texture.
@@ -191,6 +306,8 @@ export declare class VelocityField {
191
306
  * Stored normalized to [0,1] using uMin/uMax/vMin/vMax.
192
307
  */
193
308
  update(data: VelocityData): void;
309
+ /** Switch between bilinear (particles) and nearest-neighbour (raster) sampling. */
310
+ setFilter(linear: boolean): void;
194
311
  bind(unit: number): void;
195
312
  hasData(): boolean;
196
313
  destroy(): void;
@@ -236,6 +353,7 @@ export declare class ZarrSource {
236
353
  private meta;
237
354
  private coords;
238
355
  private verticalName;
356
+ private timeUnits;
239
357
  private cache;
240
358
  private maxCacheSize;
241
359
  private abortControllers;
@@ -251,6 +369,12 @@ export declare class ZarrSource {
251
369
  getDimensions(variable: string): string[];
252
370
  findNearestIndex(array: Float32Array | Float64Array, value: number): number;
253
371
  findTimeIndex(time: string | number): number;
372
+ /**
373
+ * Convert a Unix-ms timestamp to the native time units used by this zarr
374
+ * (read from the time variable's .zattrs "units" field).
375
+ * Falls back to a magnitude heuristic when units are absent or unparseable.
376
+ */
377
+ private msToZarrTime;
254
378
  findDepthIndex(depth: number): number;
255
379
  /** Return the actual name of the vertical dimension ("depth" or "elevation"). */
256
380
  getVerticalDimName(): string;
@@ -279,4 +403,14 @@ export declare class ZarrSource {
279
403
  private loadCoordinates;
280
404
  }
281
405
 
406
+ /**
407
+ * A param that is either a fixed scalar or a zoom-weighted range.
408
+ * When a range [min, max] is given:
409
+ * - min is the value at high zoom (local / detail view)
410
+ * - max is the value at low zoom (global / overview)
411
+ * The layer interpolates between them based on the current map zoom
412
+ * and the configured zoomRange.
413
+ */
414
+ export declare type ZoomWeighted = number | [min: number, max: number];
415
+
282
416
  export { }