@aeroswarm/weather-layer 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,471 @@
1
+ // src/react/index.ts
2
+ import { useEffect, useRef } from "react";
3
+
4
+ // src/math.ts
5
+ var RAD = Math.PI / 180;
6
+ function screenVector(toDeg, screenOffsetDeg = 0) {
7
+ const angle = (toDeg + screenOffsetDeg) * RAD;
8
+ return { x: Math.sin(angle), y: -Math.cos(angle) };
9
+ }
10
+ var clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
11
+ function windParticleCount(width, height, max = 520) {
12
+ return Math.min(max, Math.round(width * height / 2600));
13
+ }
14
+ function rainCount(pool, intensity) {
15
+ return Math.round(pool * clamp(intensity));
16
+ }
17
+ function particleSpeed(speedKt, gustKt = 0) {
18
+ return (speedKt + gustKt) * 7;
19
+ }
20
+ function gustPulse(gustiness, timeMs) {
21
+ if (gustiness <= 0) return 0;
22
+ const wave = 0.5 + 0.5 * Math.sin(timeMs / 700) * Math.sin(timeMs / 1900 + 1);
23
+ return wave * gustiness * 20;
24
+ }
25
+ function lightningInterval(perMinute, random) {
26
+ if (perMinute <= 0) return Number.POSITIVE_INFINITY;
27
+ return 6e4 / perMinute * (0.4 + random * 1.2);
28
+ }
29
+
30
+ // src/renderer.ts
31
+ var CALM = {
32
+ wind: { fromDeg: null, toDeg: null, speedKt: 0, gustiness: 0, turbulent: false },
33
+ clouds: { layers: [], total: 0 },
34
+ visibility: { meters: null, haze: 0 },
35
+ obscuration: null,
36
+ precipitation: null,
37
+ thunderstorm: null
38
+ };
39
+ var DROP_POOL = 700;
40
+ function makePuffSprite() {
41
+ const sprite = document.createElement("canvas");
42
+ sprite.width = sprite.height = 128;
43
+ const ctx = sprite.getContext("2d");
44
+ if (ctx) {
45
+ const gradient = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);
46
+ gradient.addColorStop(0, "rgba(255,255,255,0.95)");
47
+ gradient.addColorStop(0.55, "rgba(255,255,255,0.45)");
48
+ gradient.addColorStop(1, "rgba(255,255,255,0)");
49
+ ctx.fillStyle = gradient;
50
+ ctx.fillRect(0, 0, 128, 128);
51
+ }
52
+ return sprite;
53
+ }
54
+ var WeatherRenderer = class {
55
+ constructor(options) {
56
+ this.options = options;
57
+ this.theme = options.theme ?? "light";
58
+ this.frameInterval = 1e3 / (options.maxFps ?? 30);
59
+ this.reduceMotion = options.reducedMotion ?? (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches);
60
+ this.windCanvas = this.createCanvas("wind");
61
+ this.fxCanvas = this.createCanvas("fx");
62
+ const wind = this.windCanvas.getContext("2d");
63
+ const fx = this.fxCanvas.getContext("2d");
64
+ if (!wind || !fx) throw new Error("Canvas 2D tidak tersedia");
65
+ this.wind = wind;
66
+ this.fx = fx;
67
+ this.resize();
68
+ }
69
+ options;
70
+ windCanvas;
71
+ fxCanvas;
72
+ wind;
73
+ fx;
74
+ puff = makePuffSprite();
75
+ drops = Array.from({ length: DROP_POOL }, () => ({
76
+ x: Math.random(),
77
+ y: Math.random(),
78
+ len: 0.6 + Math.random() * 0.8,
79
+ speed: 0.7 + Math.random() * 0.6
80
+ }));
81
+ state = CALM;
82
+ theme;
83
+ screenOffset = 0;
84
+ width = 1;
85
+ height = 1;
86
+ dpr = 1;
87
+ particles = [];
88
+ clouds = [];
89
+ raf = 0;
90
+ lastFrame = 0;
91
+ accumulated = 0;
92
+ flash = 0;
93
+ bolt = null;
94
+ nextBolt = 0;
95
+ frameInterval;
96
+ reduceMotion;
97
+ running = false;
98
+ setState(state) {
99
+ this.state = state;
100
+ if (this.reduceMotion) this.renderFrame(performance.now(), 0);
101
+ }
102
+ /** Putaran peta (derajat) yang ditambahkan ke arah geografis angin. */
103
+ setScreenOffset(degrees) {
104
+ this.screenOffset = degrees;
105
+ }
106
+ setTheme(theme) {
107
+ this.theme = theme;
108
+ }
109
+ setVisible(visible) {
110
+ for (const canvas of [this.windCanvas, this.fxCanvas]) {
111
+ canvas.style.display = visible ? "block" : "none";
112
+ }
113
+ if (visible) this.start();
114
+ else this.stop();
115
+ }
116
+ setOpacity(opacity) {
117
+ for (const canvas of [this.windCanvas, this.fxCanvas]) {
118
+ canvas.style.opacity = String(clamp(opacity));
119
+ }
120
+ }
121
+ resize() {
122
+ const rect = this.options.container.getBoundingClientRect();
123
+ this.width = Math.max(1, Math.round(rect.width));
124
+ this.height = Math.max(1, Math.round(rect.height));
125
+ this.dpr = Math.min(2, window.devicePixelRatio || 1);
126
+ for (const canvas of [this.windCanvas, this.fxCanvas]) {
127
+ canvas.width = this.width * this.dpr;
128
+ canvas.height = this.height * this.dpr;
129
+ }
130
+ this.wind.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
131
+ this.fx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
132
+ this.seedParticles();
133
+ this.seedClouds();
134
+ }
135
+ start() {
136
+ if (this.running) return;
137
+ this.running = true;
138
+ this.lastFrame = performance.now();
139
+ const tick = (now) => {
140
+ if (!this.running) return;
141
+ const dt = (now - this.lastFrame) / 1e3;
142
+ this.lastFrame = now;
143
+ this.accumulated += dt * 1e3;
144
+ if (!document.hidden && this.accumulated >= this.frameInterval) {
145
+ this.renderFrame(now, Math.min(0.1, this.accumulated / 1e3));
146
+ this.accumulated = 0;
147
+ }
148
+ this.raf = requestAnimationFrame(tick);
149
+ };
150
+ this.raf = requestAnimationFrame(tick);
151
+ }
152
+ stop() {
153
+ this.running = false;
154
+ cancelAnimationFrame(this.raf);
155
+ }
156
+ destroy() {
157
+ this.stop();
158
+ this.windCanvas.remove();
159
+ this.fxCanvas.remove();
160
+ }
161
+ // ---- internal --------------------------------------------------------
162
+ createCanvas(name) {
163
+ const canvas = document.createElement("canvas");
164
+ canvas.dataset.weatherLayer = name;
165
+ Object.assign(canvas.style, {
166
+ position: "absolute",
167
+ inset: "0",
168
+ width: "100%",
169
+ height: "100%",
170
+ pointerEvents: "none",
171
+ zIndex: String(this.options.zIndex ?? 5)
172
+ });
173
+ this.options.container.appendChild(canvas);
174
+ return canvas;
175
+ }
176
+ seedParticles() {
177
+ const count = windParticleCount(this.width, this.height);
178
+ this.particles = Array.from({ length: count }, () => this.newParticle());
179
+ this.wind.clearRect(0, 0, this.width, this.height);
180
+ }
181
+ newParticle() {
182
+ return {
183
+ x: Math.random() * this.width,
184
+ y: Math.random() * this.height,
185
+ age: Math.floor(Math.random() * 80),
186
+ life: 60 + Math.random() * 90
187
+ };
188
+ }
189
+ seedClouds() {
190
+ this.clouds = Array.from({ length: 16 }, (_, i) => {
191
+ const size = 90 + Math.random() * 120;
192
+ const count = 5 + Math.floor(Math.random() * 4);
193
+ return {
194
+ x: Math.random() * (this.width + 300) - 150,
195
+ y: Math.random() * (this.height + 100) - 50,
196
+ layer: 0.6 + Math.random() * 0.8,
197
+ threshold: i / 16,
198
+ puffs: Array.from({ length: count }, () => ({
199
+ dx: (Math.random() - 0.5) * size * 1.4,
200
+ dy: (Math.random() - 0.5) * size * 0.5,
201
+ r: size * (0.5 + Math.random() * 0.5)
202
+ }))
203
+ };
204
+ });
205
+ }
206
+ renderFrame(now, dt) {
207
+ const { wind, thunderstorm } = this.state;
208
+ const motion = this.reduceMotion ? 0 : 1;
209
+ const toDeg = wind.toDeg;
210
+ const vector = toDeg === null ? null : screenVector(toDeg, this.screenOffset);
211
+ const gust = gustPulse(wind.gustiness, now);
212
+ const speed = particleSpeed(wind.speedKt, gust);
213
+ this.drawWind(now, dt, vector, wind.turbulent, speed, motion);
214
+ const fx = this.fx;
215
+ fx.clearRect(0, 0, this.width, this.height);
216
+ this.drawClouds(dt, vector, wind.speedKt, motion);
217
+ this.drawPrecipitation(dt, vector, wind.speedKt, motion);
218
+ this.drawHaze(now, motion);
219
+ this.drawLightning(now, dt, thunderstorm?.lightningPerMinute ?? 0);
220
+ }
221
+ drawWind(now, dt, vector, turbulent, speed, motion) {
222
+ const ctx = this.wind;
223
+ ctx.globalCompositeOperation = "destination-out";
224
+ ctx.fillStyle = `rgba(0,0,0,${this.reduceMotion ? 1 : 0.1})`;
225
+ ctx.fillRect(0, 0, this.width, this.height);
226
+ ctx.globalCompositeOperation = "source-over";
227
+ if (this.state.wind.speedKt <= 0) return;
228
+ const alpha = Math.min(0.75, 0.22 + this.state.wind.speedKt / 50);
229
+ ctx.lineWidth = 1.2;
230
+ ctx.lineCap = "round";
231
+ ctx.strokeStyle = this.theme === "dark" ? `rgba(120,200,220,${alpha})` : `rgba(31,122,140,${alpha})`;
232
+ ctx.beginPath();
233
+ for (const p of this.particles) {
234
+ const fromX = p.x;
235
+ const fromY = p.y;
236
+ let vx;
237
+ let vy;
238
+ if (turbulent || vector === null) {
239
+ vx = Math.cos(p.age / 12 + p.x) * 0.7;
240
+ vy = Math.sin(p.age / 14 + p.y) * 0.7;
241
+ } else {
242
+ const wobble = Math.sin(p.y / 55 + now / 1400) * 0.35 + Math.cos(p.x / 70 - now / 1700) * 0.25;
243
+ vx = vector.x - wobble * 0.25 * vector.y;
244
+ vy = vector.y + wobble * 0.25 * vector.x;
245
+ }
246
+ p.x += vx * speed * dt * motion;
247
+ p.y += vy * speed * dt * motion;
248
+ p.age++;
249
+ ctx.moveTo(fromX, fromY);
250
+ ctx.lineTo(p.x, p.y);
251
+ if (p.x < 0 || p.x > this.width || p.y < 0 || p.y > this.height || p.age > p.life) {
252
+ Object.assign(p, this.newParticle(), { age: 0 });
253
+ }
254
+ }
255
+ ctx.stroke();
256
+ }
257
+ drawClouds(dt, vector, speedKt, motion) {
258
+ const ctx = this.fx;
259
+ const cover = this.state.clouds.total;
260
+ const precipitation = this.state.precipitation;
261
+ const dark = clamp(
262
+ (precipitation?.intensity ?? 0) * 0.6 + (this.state.thunderstorm?.overhead ? 0.4 : 0)
263
+ );
264
+ if (cover <= 0) return;
265
+ for (const cloud of this.clouds) {
266
+ if (vector) {
267
+ cloud.x += vector.x * speedKt * 1.4 * cloud.layer * dt * motion;
268
+ cloud.y += vector.y * speedKt * 1.4 * cloud.layer * dt * motion;
269
+ }
270
+ if (cloud.x < -250) cloud.x = this.width + 250;
271
+ if (cloud.x > this.width + 250) cloud.x = -250;
272
+ if (cloud.y < -150) cloud.y = this.height + 150;
273
+ if (cloud.y > this.height + 150) cloud.y = -150;
274
+ const visible = clamp((cover * 1.15 - cloud.threshold) * 4);
275
+ if (visible <= 0) continue;
276
+ ctx.globalAlpha = visible * (0.38 + 0.3 * cover) * (1 - dark * 0.15);
277
+ for (const puff of cloud.puffs) {
278
+ ctx.drawImage(
279
+ this.puff,
280
+ cloud.x + puff.dx - puff.r,
281
+ cloud.y + puff.dy - puff.r,
282
+ puff.r * 2,
283
+ puff.r * 2
284
+ );
285
+ }
286
+ }
287
+ ctx.globalAlpha = 1;
288
+ if (dark > 0 || cover > 0.6) {
289
+ ctx.fillStyle = `rgba(38,48,62,${(cover * 0.2 + dark * 0.32).toFixed(3)})`;
290
+ ctx.fillRect(0, 0, this.width, this.height);
291
+ }
292
+ }
293
+ drawPrecipitation(dt, vector, speedKt, motion) {
294
+ const precipitation = this.state.precipitation;
295
+ if (!precipitation) return;
296
+ const ctx = this.fx;
297
+ const count = rainCount(this.drops.length, precipitation.intensity);
298
+ const slant = (vector?.x ?? 0) * Math.min(1, speedKt / 25) * 0.5;
299
+ const snow = precipitation.kind === "snow";
300
+ const fall = snow ? 0.25 : 1.6;
301
+ if (snow) ctx.fillStyle = "rgba(255,255,255,0.8)";
302
+ else {
303
+ ctx.strokeStyle = `rgba(150,185,225,${(0.3 + 0.3 * precipitation.intensity).toFixed(2)})`;
304
+ ctx.lineWidth = precipitation.kind === "drizzle" ? 0.7 : 1.1;
305
+ ctx.beginPath();
306
+ }
307
+ for (let i = 0; i < count; i++) {
308
+ const drop = this.drops[i];
309
+ if (!drop) continue;
310
+ drop.y += drop.speed * dt * fall * motion;
311
+ drop.x += slant * dt * fall * motion;
312
+ if (drop.y > 1) {
313
+ drop.y = -0.05;
314
+ drop.x = Math.random();
315
+ }
316
+ if (drop.x > 1) drop.x = 0;
317
+ if (drop.x < 0) drop.x = 1;
318
+ const x = drop.x * this.width;
319
+ const y = drop.y * this.height;
320
+ if (snow) ctx.fillRect(x, y, 2, 2);
321
+ else {
322
+ const length = 12 * drop.len;
323
+ ctx.moveTo(x, y);
324
+ ctx.lineTo(x - slant * length, y - length);
325
+ }
326
+ }
327
+ if (!snow) ctx.stroke();
328
+ }
329
+ drawHaze(now, motion) {
330
+ const { visibility, obscuration } = this.state;
331
+ const haze = Math.max(visibility.haze, (obscuration?.density ?? 0) * 0.7);
332
+ if (haze <= 0.01) return;
333
+ const ctx = this.fx;
334
+ const dark = this.theme === "dark";
335
+ ctx.fillStyle = `${dark ? "rgba(150,160,168," : "rgba(214,219,223,"}${(haze * 0.78).toFixed(3)})`;
336
+ ctx.fillRect(0, 0, this.width, this.height);
337
+ if (obscuration?.kind === "fog" || (visibility.meters ?? 1e4) < 1500) {
338
+ for (let i = 0; i < 3; i++) {
339
+ const cx = now / 40 * (i + 1) * 0.8 * motion % (this.width * 1.3) + i * this.width * 0.2;
340
+ const cy = this.height * (0.3 + i * 0.25);
341
+ const gradient = ctx.createRadialGradient(cx, cy, 10, cx, cy, this.width * 0.5);
342
+ gradient.addColorStop(0, dark ? "rgba(170,180,188,0.35)" : "rgba(235,238,240,0.5)");
343
+ gradient.addColorStop(1, "rgba(0,0,0,0)");
344
+ ctx.fillStyle = gradient;
345
+ ctx.fillRect(0, 0, this.width, this.height);
346
+ }
347
+ }
348
+ }
349
+ drawLightning(now, dt, perMinute) {
350
+ if (this.reduceMotion) return;
351
+ const ctx = this.fx;
352
+ if (perMinute > 0 && now > this.nextBolt) {
353
+ this.flash = 1;
354
+ this.bolt = this.makeBolt();
355
+ this.nextBolt = now + lightningInterval(perMinute, Math.random());
356
+ }
357
+ if (this.flash <= 0) return;
358
+ ctx.fillStyle = `rgba(255,255,255,${(this.flash * 0.35).toFixed(3)})`;
359
+ ctx.fillRect(0, 0, this.width, this.height);
360
+ if (this.bolt && this.flash > 0.35) {
361
+ ctx.strokeStyle = `rgba(255,255,255,${this.flash.toFixed(2)})`;
362
+ ctx.lineWidth = 2.2;
363
+ ctx.beginPath();
364
+ this.bolt.forEach(([x, y], i) => {
365
+ if (i === 0) ctx.moveTo(x, y);
366
+ else ctx.lineTo(x, y);
367
+ });
368
+ ctx.stroke();
369
+ }
370
+ this.flash -= dt * 3.2;
371
+ }
372
+ makeBolt() {
373
+ let x = this.width * (0.2 + Math.random() * 0.6);
374
+ let y = 0;
375
+ const points = [[x, y]];
376
+ const reach = this.height * (0.5 + Math.random() * 0.3);
377
+ while (y < reach) {
378
+ x += (Math.random() - 0.5) * 36;
379
+ y += 14 + Math.random() * 22;
380
+ points.push([x, y]);
381
+ }
382
+ return points;
383
+ }
384
+ };
385
+
386
+ // src/overlay.ts
387
+ function mapScreenOffset(map) {
388
+ const camera = map.camera;
389
+ if (!camera) return 0;
390
+ try {
391
+ if (map.reference?.identifier === "EPSG:4978") {
392
+ return -(camera.asLookAt?.(1e3).yaw ?? 0);
393
+ }
394
+ return -(camera.asLook2D?.().rotation ?? 0);
395
+ } catch {
396
+ return 0;
397
+ }
398
+ }
399
+ function detectTheme() {
400
+ const explicit = document.documentElement.dataset.theme;
401
+ if (explicit === "dark" || explicit === "light") return explicit;
402
+ return matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
403
+ }
404
+ function createWeatherOverlay(map, options = {}) {
405
+ const container = map.domNode;
406
+ if (getComputedStyle(container).position === "static") {
407
+ container.style.position = "relative";
408
+ }
409
+ const theme = options.theme ?? "auto";
410
+ const renderer = new WeatherRenderer({
411
+ container,
412
+ theme: theme === "auto" ? detectTheme() : theme,
413
+ zIndex: options.zIndex,
414
+ maxFps: options.maxFps
415
+ });
416
+ const offsetOf = options.getScreenOffset ?? mapScreenOffset;
417
+ let width = container.clientWidth;
418
+ let height = container.clientHeight;
419
+ const handle = map.on("MapChange", () => {
420
+ renderer.setScreenOffset(offsetOf(map));
421
+ if (container.clientWidth !== width || container.clientHeight !== height) {
422
+ width = container.clientWidth;
423
+ height = container.clientHeight;
424
+ renderer.resize();
425
+ }
426
+ });
427
+ const themeObserver = theme === "auto" ? new MutationObserver(() => renderer.setTheme(detectTheme())) : null;
428
+ themeObserver?.observe(document.documentElement, {
429
+ attributes: true,
430
+ attributeFilter: ["data-theme"]
431
+ });
432
+ renderer.setScreenOffset(offsetOf(map));
433
+ renderer.start();
434
+ return {
435
+ renderer,
436
+ setState: (state) => renderer.setState(state),
437
+ setVisible: (visible) => renderer.setVisible(visible),
438
+ setOpacity: (opacity) => renderer.setOpacity(opacity),
439
+ destroy() {
440
+ handle.remove();
441
+ themeObserver?.disconnect();
442
+ renderer.destroy();
443
+ }
444
+ };
445
+ }
446
+
447
+ // src/react/index.ts
448
+ function useWeatherOverlay(map, state, options = {}) {
449
+ const overlayRef = useRef(null);
450
+ const { visible = true, opacity = 1, theme, zIndex, maxFps, getScreenOffset } = options;
451
+ useEffect(() => {
452
+ if (!map) return;
453
+ const overlay = createWeatherOverlay(map, { theme, zIndex, maxFps, getScreenOffset });
454
+ overlayRef.current = overlay;
455
+ return () => {
456
+ overlay.destroy();
457
+ overlayRef.current = null;
458
+ };
459
+ }, [map, theme, zIndex, maxFps, getScreenOffset]);
460
+ useEffect(() => {
461
+ if (state) overlayRef.current?.setState(state);
462
+ }, [state, map]);
463
+ useEffect(() => {
464
+ overlayRef.current?.setVisible(visible);
465
+ overlayRef.current?.setOpacity(opacity);
466
+ }, [visible, opacity, map]);
467
+ }
468
+ export {
469
+ useWeatherOverlay
470
+ };
471
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/math.ts","../../src/renderer.ts","../../src/overlay.ts"],"sourcesContent":["import type { AnimationState } from \"@aeroswarm/weather\";\nimport { useEffect, useRef } from \"react\";\nimport {\n createWeatherOverlay,\n type MapLike,\n type WeatherOverlay,\n type WeatherOverlayOptions,\n} from \"../overlay.js\";\n\nexport interface UseWeatherOverlayOptions extends WeatherOverlayOptions {\n /** Tampilkan atau sembunyikan overlay. Bawaan true. */\n visible?: boolean;\n opacity?: number;\n}\n\n/**\n * Pasang overlay cuaca di atas `map` selama komponen hidup, dan perbarui kondisinya saat `state`\n * berubah. `map` boleh null sampai peta siap.\n */\nexport function useWeatherOverlay(\n map: MapLike | null | undefined,\n state: AnimationState | null | undefined,\n options: UseWeatherOverlayOptions = {},\n): void {\n const overlayRef = useRef<WeatherOverlay | null>(null);\n const { visible = true, opacity = 1, theme, zIndex, maxFps, getScreenOffset } = options;\n\n useEffect(() => {\n if (!map) return;\n const overlay = createWeatherOverlay(map, { theme, zIndex, maxFps, getScreenOffset });\n overlayRef.current = overlay;\n return () => {\n overlay.destroy();\n overlayRef.current = null;\n };\n }, [map, theme, zIndex, maxFps, getScreenOffset]);\n\n useEffect(() => {\n if (state) overlayRef.current?.setState(state);\n }, [state, map]);\n\n useEffect(() => {\n overlayRef.current?.setVisible(visible);\n overlayRef.current?.setOpacity(opacity);\n }, [visible, opacity, map]);\n}\n","/** Hitungan murni untuk renderer: tanpa DOM supaya mudah dites. */\n\nconst RAD = Math.PI / 180;\n\n/**\n * Vektor gerak di layar (x ke kanan, y ke bawah) untuk arah `toDeg` (derajat dari utara, searah jarum\n * jam). `screenOffsetDeg` = putaran peta: arah di layar = arah geografis + offset.\n */\nexport function screenVector(\n toDeg: number,\n screenOffsetDeg = 0,\n): { x: number; y: number } {\n const angle = (toDeg + screenOffsetDeg) * RAD;\n return { x: Math.sin(angle), y: -Math.cos(angle) };\n}\n\nexport const clamp = (value: number, min = 0, max = 1): number =>\n Math.min(max, Math.max(min, value));\n\n/** Jumlah partikel angin menurut luas layar, dibatasi `max`. */\nexport function windParticleCount(width: number, height: number, max = 520): number {\n return Math.min(max, Math.round((width * height) / 2600));\n}\n\n/** Jumlah garis hujan yang digambar dari `pool` tetes, menurut intensitas 0-1. */\nexport function rainCount(pool: number, intensity: number): number {\n return Math.round(pool * clamp(intensity));\n}\n\n/** Kecepatan visual partikel (px/detik) dari kecepatan angin (kt) dan hembusan sesaat. */\nexport function particleSpeed(speedKt: number, gustKt = 0): number {\n return (speedKt + gustKt) * 7;\n}\n\n/** Hembusan sesaat (kt tambahan) yang berdenyut halus sesuai `gustiness` 0-1. */\nexport function gustPulse(gustiness: number, timeMs: number): number {\n if (gustiness <= 0) return 0;\n const wave = 0.5 + 0.5 * Math.sin(timeMs / 700) * Math.sin(timeMs / 1900 + 1);\n return wave * gustiness * 20;\n}\n\n/** Selang antar kilat (ms) dari laju kilat per menit, dengan sedikit acak (`random` 0-1). */\nexport function lightningInterval(perMinute: number, random: number): number {\n if (perMinute <= 0) return Number.POSITIVE_INFINITY;\n return (60_000 / perMinute) * (0.4 + random * 1.2);\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport {\n clamp,\n gustPulse,\n lightningInterval,\n particleSpeed,\n rainCount,\n screenVector,\n windParticleCount,\n} from \"./math.js\";\n\nexport type WeatherTheme = \"light\" | \"dark\";\n\nexport interface WeatherRendererOptions {\n /** Elemen tempat canvas dipasang (mis. `map.domNode`). Harus berposisi non-static. */\n container: HTMLElement;\n theme?: WeatherTheme;\n /** Batas frame per detik. Bawaan 30. */\n maxFps?: number;\n /** Gambar diam (tanpa gerak) bila pengguna meminta mengurangi animasi. Bawaan: ikuti OS. */\n reducedMotion?: boolean;\n zIndex?: number;\n}\n\ninterface Particle {\n x: number;\n y: number;\n age: number;\n life: number;\n}\ninterface Puff {\n dx: number;\n dy: number;\n r: number;\n}\ninterface Cloud {\n x: number;\n y: number;\n puffs: Puff[];\n layer: number;\n threshold: number;\n}\n\nconst CALM: AnimationState = {\n wind: { fromDeg: null, toDeg: null, speedKt: 0, gustiness: 0, turbulent: false },\n clouds: { layers: [], total: 0 },\n visibility: { meters: null, haze: 0 },\n obscuration: null,\n precipitation: null,\n thunderstorm: null,\n};\n\nconst DROP_POOL = 700;\n\nfunction makePuffSprite(): HTMLCanvasElement {\n const sprite = document.createElement(\"canvas\");\n sprite.width = sprite.height = 128;\n const ctx = sprite.getContext(\"2d\");\n if (ctx) {\n const gradient = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);\n gradient.addColorStop(0, \"rgba(255,255,255,0.95)\");\n gradient.addColorStop(0.55, \"rgba(255,255,255,0.45)\");\n gradient.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 128, 128);\n }\n return sprite;\n}\n\n/**\n * Menggambar angin, awan, hujan, kabut, dan petir ke dua canvas transparan di atas `container`.\n * Tidak mengenal peta: arah di layar diatur lewat `setScreenOffset` (putaran peta).\n */\nexport class WeatherRenderer {\n private readonly windCanvas: HTMLCanvasElement;\n private readonly fxCanvas: HTMLCanvasElement;\n private readonly wind: CanvasRenderingContext2D;\n private readonly fx: CanvasRenderingContext2D;\n private readonly puff = makePuffSprite();\n private readonly drops = Array.from({ length: DROP_POOL }, () => ({\n x: Math.random(),\n y: Math.random(),\n len: 0.6 + Math.random() * 0.8,\n speed: 0.7 + Math.random() * 0.6,\n }));\n\n private state: AnimationState = CALM;\n private theme: WeatherTheme;\n private screenOffset = 0;\n private width = 1;\n private height = 1;\n private dpr = 1;\n private particles: Particle[] = [];\n private clouds: Cloud[] = [];\n private raf = 0;\n private lastFrame = 0;\n private accumulated = 0;\n private flash = 0;\n private bolt: [number, number][] | null = null;\n private nextBolt = 0;\n private readonly frameInterval: number;\n private readonly reduceMotion: boolean;\n private running = false;\n\n constructor(private readonly options: WeatherRendererOptions) {\n this.theme = options.theme ?? \"light\";\n this.frameInterval = 1000 / (options.maxFps ?? 30);\n this.reduceMotion =\n options.reducedMotion ??\n (typeof matchMedia === \"function\" &&\n matchMedia(\"(prefers-reduced-motion: reduce)\").matches);\n\n this.windCanvas = this.createCanvas(\"wind\");\n this.fxCanvas = this.createCanvas(\"fx\");\n const wind = this.windCanvas.getContext(\"2d\");\n const fx = this.fxCanvas.getContext(\"2d\");\n if (!wind || !fx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.wind = wind;\n this.fx = fx;\n this.resize();\n }\n\n setState(state: AnimationState): void {\n this.state = state;\n if (this.reduceMotion) this.renderFrame(performance.now(), 0);\n }\n\n /** Putaran peta (derajat) yang ditambahkan ke arah geografis angin. */\n setScreenOffset(degrees: number): void {\n this.screenOffset = degrees;\n }\n\n setTheme(theme: WeatherTheme): void {\n this.theme = theme;\n }\n\n setVisible(visible: boolean): void {\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.style.display = visible ? \"block\" : \"none\";\n }\n if (visible) this.start();\n else this.stop();\n }\n\n setOpacity(opacity: number): void {\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.style.opacity = String(clamp(opacity));\n }\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.width = this.width * this.dpr;\n canvas.height = this.height * this.dpr;\n }\n this.wind.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.fx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.seedParticles();\n this.seedClouds();\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.lastFrame = performance.now();\n const tick = (now: number): void => {\n if (!this.running) return;\n const dt = (now - this.lastFrame) / 1000;\n this.lastFrame = now;\n this.accumulated += dt * 1000;\n if (!document.hidden && this.accumulated >= this.frameInterval) {\n this.renderFrame(now, Math.min(0.1, this.accumulated / 1000));\n this.accumulated = 0;\n }\n this.raf = requestAnimationFrame(tick);\n };\n this.raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n this.running = false;\n cancelAnimationFrame(this.raf);\n }\n\n destroy(): void {\n this.stop();\n this.windCanvas.remove();\n this.fxCanvas.remove();\n }\n\n // ---- internal --------------------------------------------------------\n\n private createCanvas(name: string): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.dataset.weatherLayer = name;\n Object.assign(canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(this.options.zIndex ?? 5),\n });\n this.options.container.appendChild(canvas);\n return canvas;\n }\n\n private seedParticles(): void {\n const count = windParticleCount(this.width, this.height);\n this.particles = Array.from({ length: count }, () => this.newParticle());\n this.wind.clearRect(0, 0, this.width, this.height);\n }\n\n private newParticle(): Particle {\n return {\n x: Math.random() * this.width,\n y: Math.random() * this.height,\n age: Math.floor(Math.random() * 80),\n life: 60 + Math.random() * 90,\n };\n }\n\n private seedClouds(): void {\n this.clouds = Array.from({ length: 16 }, (_, i) => {\n const size = 90 + Math.random() * 120;\n const count = 5 + Math.floor(Math.random() * 4);\n return {\n x: Math.random() * (this.width + 300) - 150,\n y: Math.random() * (this.height + 100) - 50,\n layer: 0.6 + Math.random() * 0.8,\n threshold: i / 16,\n puffs: Array.from({ length: count }, () => ({\n dx: (Math.random() - 0.5) * size * 1.4,\n dy: (Math.random() - 0.5) * size * 0.5,\n r: size * (0.5 + Math.random() * 0.5),\n })),\n };\n });\n }\n\n private renderFrame(now: number, dt: number): void {\n const { wind, thunderstorm } = this.state;\n const motion = this.reduceMotion ? 0 : 1;\n const toDeg = wind.toDeg;\n const vector = toDeg === null ? null : screenVector(toDeg, this.screenOffset);\n const gust = gustPulse(wind.gustiness, now);\n const speed = particleSpeed(wind.speedKt, gust);\n\n this.drawWind(now, dt, vector, wind.turbulent, speed, motion);\n\n const fx = this.fx;\n fx.clearRect(0, 0, this.width, this.height);\n this.drawClouds(dt, vector, wind.speedKt, motion);\n this.drawPrecipitation(dt, vector, wind.speedKt, motion);\n this.drawHaze(now, motion);\n this.drawLightning(now, dt, thunderstorm?.lightningPerMinute ?? 0);\n }\n\n private drawWind(\n now: number,\n dt: number,\n vector: { x: number; y: number } | null,\n turbulent: boolean,\n speed: number,\n motion: number,\n ): void {\n const ctx = this.wind;\n ctx.globalCompositeOperation = \"destination-out\";\n ctx.fillStyle = `rgba(0,0,0,${this.reduceMotion ? 1 : 0.1})`;\n ctx.fillRect(0, 0, this.width, this.height);\n ctx.globalCompositeOperation = \"source-over\";\n if (this.state.wind.speedKt <= 0) return;\n\n const alpha = Math.min(0.75, 0.22 + this.state.wind.speedKt / 50);\n ctx.lineWidth = 1.2;\n ctx.lineCap = \"round\";\n ctx.strokeStyle =\n this.theme === \"dark\"\n ? `rgba(120,200,220,${alpha})`\n : `rgba(31,122,140,${alpha})`;\n ctx.beginPath();\n for (const p of this.particles) {\n const fromX = p.x;\n const fromY = p.y;\n let vx: number;\n let vy: number;\n if (turbulent || vector === null) {\n vx = Math.cos(p.age / 12 + p.x) * 0.7;\n vy = Math.sin(p.age / 14 + p.y) * 0.7;\n } else {\n const wobble =\n Math.sin(p.y / 55 + now / 1400) * 0.35 + Math.cos(p.x / 70 - now / 1700) * 0.25;\n vx = vector.x - wobble * 0.25 * vector.y;\n vy = vector.y + wobble * 0.25 * vector.x;\n }\n p.x += vx * speed * dt * motion;\n p.y += vy * speed * dt * motion;\n p.age++;\n ctx.moveTo(fromX, fromY);\n ctx.lineTo(p.x, p.y);\n if (p.x < 0 || p.x > this.width || p.y < 0 || p.y > this.height || p.age > p.life) {\n Object.assign(p, this.newParticle(), { age: 0 });\n }\n }\n ctx.stroke();\n }\n\n private drawClouds(\n dt: number,\n vector: { x: number; y: number } | null,\n speedKt: number,\n motion: number,\n ): void {\n const ctx = this.fx;\n const cover = this.state.clouds.total;\n const precipitation = this.state.precipitation;\n const dark = clamp(\n (precipitation?.intensity ?? 0) * 0.6 + (this.state.thunderstorm?.overhead ? 0.4 : 0),\n );\n if (cover <= 0) return;\n\n for (const cloud of this.clouds) {\n if (vector) {\n cloud.x += vector.x * speedKt * 1.4 * cloud.layer * dt * motion;\n cloud.y += vector.y * speedKt * 1.4 * cloud.layer * dt * motion;\n }\n if (cloud.x < -250) cloud.x = this.width + 250;\n if (cloud.x > this.width + 250) cloud.x = -250;\n if (cloud.y < -150) cloud.y = this.height + 150;\n if (cloud.y > this.height + 150) cloud.y = -150;\n const visible = clamp((cover * 1.15 - cloud.threshold) * 4);\n if (visible <= 0) continue;\n ctx.globalAlpha = visible * (0.38 + 0.3 * cover) * (1 - dark * 0.15);\n for (const puff of cloud.puffs) {\n ctx.drawImage(\n this.puff,\n cloud.x + puff.dx - puff.r,\n cloud.y + puff.dy - puff.r,\n puff.r * 2,\n puff.r * 2,\n );\n }\n }\n ctx.globalAlpha = 1;\n if (dark > 0 || cover > 0.6) {\n ctx.fillStyle = `rgba(38,48,62,${(cover * 0.2 + dark * 0.32).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n }\n }\n\n private drawPrecipitation(\n dt: number,\n vector: { x: number; y: number } | null,\n speedKt: number,\n motion: number,\n ): void {\n const precipitation = this.state.precipitation;\n if (!precipitation) return;\n const ctx = this.fx;\n const count = rainCount(this.drops.length, precipitation.intensity);\n const slant = (vector?.x ?? 0) * Math.min(1, speedKt / 25) * 0.5;\n const snow = precipitation.kind === \"snow\";\n const fall = snow ? 0.25 : 1.6;\n\n if (snow) ctx.fillStyle = \"rgba(255,255,255,0.8)\";\n else {\n ctx.strokeStyle = `rgba(150,185,225,${(0.3 + 0.3 * precipitation.intensity).toFixed(2)})`;\n ctx.lineWidth = precipitation.kind === \"drizzle\" ? 0.7 : 1.1;\n ctx.beginPath();\n }\n for (let i = 0; i < count; i++) {\n const drop = this.drops[i];\n if (!drop) continue;\n drop.y += drop.speed * dt * fall * motion;\n drop.x += slant * dt * fall * motion;\n if (drop.y > 1) {\n drop.y = -0.05;\n drop.x = Math.random();\n }\n if (drop.x > 1) drop.x = 0;\n if (drop.x < 0) drop.x = 1;\n const x = drop.x * this.width;\n const y = drop.y * this.height;\n if (snow) ctx.fillRect(x, y, 2, 2);\n else {\n const length = 12 * drop.len;\n ctx.moveTo(x, y);\n ctx.lineTo(x - slant * length, y - length);\n }\n }\n if (!snow) ctx.stroke();\n }\n\n private drawHaze(now: number, motion: number): void {\n const { visibility, obscuration } = this.state;\n const haze = Math.max(visibility.haze, (obscuration?.density ?? 0) * 0.7);\n if (haze <= 0.01) return;\n const ctx = this.fx;\n const dark = this.theme === \"dark\";\n ctx.fillStyle = `${dark ? \"rgba(150,160,168,\" : \"rgba(214,219,223,\"}${(haze * 0.78).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n if (obscuration?.kind === \"fog\" || (visibility.meters ?? 10_000) < 1500) {\n for (let i = 0; i < 3; i++) {\n const cx = (((now / 40) * (i + 1) * 0.8 * motion) % (this.width * 1.3)) + i * this.width * 0.2;\n const cy = this.height * (0.3 + i * 0.25);\n const gradient = ctx.createRadialGradient(cx, cy, 10, cx, cy, this.width * 0.5);\n gradient.addColorStop(0, dark ? \"rgba(170,180,188,0.35)\" : \"rgba(235,238,240,0.5)\");\n gradient.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, this.width, this.height);\n }\n }\n }\n\n private drawLightning(now: number, dt: number, perMinute: number): void {\n if (this.reduceMotion) return;\n const ctx = this.fx;\n if (perMinute > 0 && now > this.nextBolt) {\n this.flash = 1;\n this.bolt = this.makeBolt();\n this.nextBolt = now + lightningInterval(perMinute, Math.random());\n }\n if (this.flash <= 0) return;\n ctx.fillStyle = `rgba(255,255,255,${(this.flash * 0.35).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n if (this.bolt && this.flash > 0.35) {\n ctx.strokeStyle = `rgba(255,255,255,${this.flash.toFixed(2)})`;\n ctx.lineWidth = 2.2;\n ctx.beginPath();\n this.bolt.forEach(([x, y], i) => {\n if (i === 0) ctx.moveTo(x, y);\n else ctx.lineTo(x, y);\n });\n ctx.stroke();\n }\n this.flash -= dt * 3.2;\n }\n\n private makeBolt(): [number, number][] {\n let x = this.width * (0.2 + Math.random() * 0.6);\n let y = 0;\n const points: [number, number][] = [[x, y]];\n const reach = this.height * (0.5 + Math.random() * 0.3);\n while (y < reach) {\n x += (Math.random() - 0.5) * 36;\n y += 14 + Math.random() * 22;\n points.push([x, y]);\n }\n return points;\n }\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport { WeatherRenderer, type WeatherTheme } from \"./renderer.js\";\n\n/** Bagian peta LuciadRIA yang dipakai overlay. Sengaja struktural supaya tidak bergantung pada RIA. */\nexport interface MapLike {\n readonly domNode: HTMLElement;\n readonly reference?: { readonly identifier: string };\n readonly camera?: {\n asLook2D?: () => { rotation: number };\n asLookAt?: (distance: number) => { yaw: number };\n };\n on(event: \"MapChange\", callback: () => void): { remove(): void };\n}\n\nexport interface WeatherOverlayOptions {\n /** `auto` mengikuti `data-theme` pada <html>, lalu `prefers-color-scheme`. */\n theme?: WeatherTheme | \"auto\";\n zIndex?: number;\n maxFps?: number;\n /** Ganti cara menghitung putaran peta (derajat) bila peta Anda berbeda. */\n getScreenOffset?: (map: MapLike) => number;\n}\n\nexport interface WeatherOverlay {\n readonly renderer: WeatherRenderer;\n setState(state: AnimationState): void;\n setVisible(visible: boolean): void;\n setOpacity(opacity: number): void;\n destroy(): void;\n}\n\n/**\n * Putaran peta terhadap utara, supaya arah angin di layar tetap benar: 2D memakai rotasi kamera,\n * 3D memakai yaw kamera. Keduanya dibalik karena peta berputar berlawanan dengan kamera.\n * Tanda ini perlu dicek di peta sungguhan (putar peta, lihat arah partikel).\n */\nexport function mapScreenOffset(map: MapLike): number {\n const camera = map.camera;\n if (!camera) return 0;\n try {\n if (map.reference?.identifier === \"EPSG:4978\") {\n return -(camera.asLookAt?.(1000).yaw ?? 0);\n }\n return -(camera.asLook2D?.().rotation ?? 0);\n } catch {\n return 0;\n }\n}\n\nfunction detectTheme(): WeatherTheme {\n const explicit = document.documentElement.dataset.theme;\n if (explicit === \"dark\" || explicit === \"light\") return explicit;\n return matchMedia?.(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/** Pasang overlay cuaca (canvas transparan) di atas peta; hapus dengan `destroy()`. */\nexport function createWeatherOverlay(\n map: MapLike,\n options: WeatherOverlayOptions = {},\n): WeatherOverlay {\n const container = map.domNode;\n if (getComputedStyle(container).position === \"static\") {\n container.style.position = \"relative\";\n }\n\n const theme = options.theme ?? \"auto\";\n const renderer = new WeatherRenderer({\n container,\n theme: theme === \"auto\" ? detectTheme() : theme,\n zIndex: options.zIndex,\n maxFps: options.maxFps,\n });\n const offsetOf = options.getScreenOffset ?? mapScreenOffset;\n\n let width = container.clientWidth;\n let height = container.clientHeight;\n const handle = map.on(\"MapChange\", () => {\n renderer.setScreenOffset(offsetOf(map));\n if (container.clientWidth !== width || container.clientHeight !== height) {\n width = container.clientWidth;\n height = container.clientHeight;\n renderer.resize();\n }\n });\n\n const themeObserver =\n theme === \"auto\"\n ? new MutationObserver(() => renderer.setTheme(detectTheme()))\n : null;\n themeObserver?.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n\n renderer.setScreenOffset(offsetOf(map));\n renderer.start();\n\n return {\n renderer,\n setState: (state) => renderer.setState(state),\n setVisible: (visible) => renderer.setVisible(visible),\n setOpacity: (opacity) => renderer.setOpacity(opacity),\n destroy() {\n handle.remove();\n themeObserver?.disconnect();\n renderer.destroy();\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,55 @@
1
- {
2
- "name": "@aeroswarm/weather-layer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@aeroswarm/weather-layer",
3
+ "version": "0.1.2",
4
+ "description": "Animated weather overlay (wind, clouds, rain, fog, lightning) for LuciadRIA maps, driven by @aeroswarm/weather.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./react": {
14
+ "import": "./dist/react/index.js",
15
+ "types": "./dist/react/index.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public",
23
+ "registry": "https://registry.npmjs.org/"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup --config tsup.config.ts",
27
+ "dev": "tsup --config tsup.config.ts --watch",
28
+ "test": "vitest run --passWithNoTests",
29
+ "typecheck": "tsc --noEmit",
30
+ "clean": "rm -rf dist",
31
+ "prepublishOnly": "pnpm run typecheck && pnpm run test && pnpm run build",
32
+ "release": "release-it",
33
+ "release:dry": "release-it --dry-run",
34
+ "release:patch": "release-it patch --no-git.requireCleanWorkingDir",
35
+ "release:minor": "release-it minor --no-git.requireCleanWorkingDir",
36
+ "release:major": "release-it major --no-git.requireCleanWorkingDir"
37
+ },
38
+ "dependencies": {
39
+ "@aeroswarm/weather": "^0.1.2"
40
+ },
41
+ "peerDependencies": {
42
+ "react": ">=18"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "react": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "devDependencies": {
50
+ "@types/react": "^19.0.0",
51
+ "release-it": "^18.1.2",
52
+ "tsup": "^8.5.1",
53
+ "vitest": "^4.1.6"
54
+ }
55
+ }