@dopaminefx/effect-solarbloom 0.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/dist/check-fonts.d.ts +22 -0
- package/dist/check-fonts.d.ts.map +1 -0
- package/dist/check-fonts.js +19 -0
- package/dist/check-fonts.js.map +1 -0
- package/dist/check-renderer.d.ts +31 -0
- package/dist/check-renderer.d.ts.map +1 -0
- package/dist/check-renderer.js +102 -0
- package/dist/check-renderer.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/solarbloom-params.d.ts +48 -0
- package/dist/solarbloom-params.d.ts.map +1 -0
- package/dist/solarbloom-params.js +7 -0
- package/dist/solarbloom-params.js.map +1 -0
- package/dist/solarbloom-renderer.d.ts +40 -0
- package/dist/solarbloom-renderer.d.ts.map +1 -0
- package/dist/solarbloom-renderer.js +110 -0
- package/dist/solarbloom-renderer.js.map +1 -0
- package/dist/solarbloom-shader.d.ts +28 -0
- package/dist/solarbloom-shader.d.ts.map +1 -0
- package/dist/solarbloom-shader.js +447 -0
- package/dist/solarbloom-shader.js.map +1 -0
- package/dist/solarbloom-tempo.d.ts +13 -0
- package/dist/solarbloom-tempo.d.ts.map +1 -0
- package/dist/solarbloom-tempo.js +16 -0
- package/dist/solarbloom-tempo.js.map +1 -0
- package/dist/solarbloom.dope.json +552 -0
- package/package.json +46 -0
- package/src/check-fonts.ts +26 -0
- package/src/check-renderer.ts +109 -0
- package/src/index.ts +96 -0
- package/src/solarbloom-params.ts +50 -0
- package/src/solarbloom-renderer.ts +135 -0
- package/src/solarbloom-shader.ts +461 -0
- package/src/solarbloom-tempo.ts +18 -0
- package/src/solarbloom.dope.json +552 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated module: CHECK-GLYPH woff2 subsets bundled as base64 so the Solarbloom
|
|
3
|
+
* success effect renders its checkmark from a REAL typeface glyph (✓ U+2713 /
|
|
4
|
+
* ✔ U+2714) and never silently depends on a host font being installed.
|
|
5
|
+
* Regenerate with scripts/embed-check-fonts.mjs.
|
|
6
|
+
*
|
|
7
|
+
* Faces (SIL Open Font License 1.1 — see assets/fonts/OFL.txt), subset to the two
|
|
8
|
+
* check codepoints only:
|
|
9
|
+
* Dopamine Check Sans — Source Sans 3 (humanist, refined ✓ / clean ✔)
|
|
10
|
+
* Dopamine Check Symbols — Noto Sans Symbols 2 (calligraphic ✓ / fat playful ✔)
|
|
11
|
+
*
|
|
12
|
+
* Solarbloom selects (face, codepoint) by WHIMSY (see CHECK_GLYPHS in
|
|
13
|
+
* engine/mood.ts): low whimsy = a refined/elegant check, high = a bold/playful one.
|
|
14
|
+
*/
|
|
15
|
+
export interface CheckFace {
|
|
16
|
+
/** CSS font-family name to register + use. */
|
|
17
|
+
readonly family: string;
|
|
18
|
+
/** base64-encoded woff2 payload (subset to U+2713 / U+2714 only). */
|
|
19
|
+
readonly base64: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const CHECK_FACES: readonly CheckFace[];
|
|
22
|
+
//# sourceMappingURL=check-fonts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-fonts.d.ts","sourceRoot":"","sources":["../src/check-fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAG3C,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated module: CHECK-GLYPH woff2 subsets bundled as base64 so the Solarbloom
|
|
3
|
+
* success effect renders its checkmark from a REAL typeface glyph (✓ U+2713 /
|
|
4
|
+
* ✔ U+2714) and never silently depends on a host font being installed.
|
|
5
|
+
* Regenerate with scripts/embed-check-fonts.mjs.
|
|
6
|
+
*
|
|
7
|
+
* Faces (SIL Open Font License 1.1 — see assets/fonts/OFL.txt), subset to the two
|
|
8
|
+
* check codepoints only:
|
|
9
|
+
* Dopamine Check Sans — Source Sans 3 (humanist, refined ✓ / clean ✔)
|
|
10
|
+
* Dopamine Check Symbols — Noto Sans Symbols 2 (calligraphic ✓ / fat playful ✔)
|
|
11
|
+
*
|
|
12
|
+
* Solarbloom selects (face, codepoint) by WHIMSY (see CHECK_GLYPHS in
|
|
13
|
+
* engine/mood.ts): low whimsy = a refined/elegant check, high = a bold/playful one.
|
|
14
|
+
*/
|
|
15
|
+
export const CHECK_FACES = [
|
|
16
|
+
{ family: "Dopamine Check Sans", base64: "d09GMgABAAAAAAKMABAAAAAABKwAAAIxAAMNUAAAAAAAAAAAAAAAAAAAAAAAAAAAGUYbIBw2BmA/U1RBVEQANBEICoFkgVoBNgIkAwwLCAAEIAWBAgcgDAcbhANgjtFLn/JCEEKZZ6ZUBNF+/Gb37ot6UqyJNVGPFjqhW6JSSZFIEm1U3tWabpIiOCS8a/nmc2FWJXA/lf2ML6FrhCmgIkvjq/pmO93fbj6RYSwBFlCkCQZWRlkyswwTDECTQCfDCeJreMOwoUA7ql75WEy1Bq1aA15H2npGIOqu3aCCqIcGqoSoZ8SkgSitIKR23aIZI0Cg07RZYV37qdDwsNDvsOs0p3BKK8UCfs2FxOBNvk4M4GgyixtQFGlkVyGqlszq2w4eaeqH6/TIteEK/3WgfIgEBMSvZDGvRQjtEsMSy/KVVGYSJPD4OVEJBELVLyC2EgMoQsYuECBRMKwIDKvmUVXB2nDTsDbUNJ//QoAQDa8PFNCKNuzFIwAqaBGitraWMjzU5d1T7wSOjQWMoECEAkYxGqOikdHjl5c65HkeDXyVDo8+5SaaqDAF7h45wv6xtOuhXJrLeOXdzXurudfzsacA2+WaG83dEOdW38GbofL1O2XToV0Frvr5sQavqMuduxMc3aDt9R0tK1f6r7QNH4dwLqmYZqy9ykqrwiv+1eSTdu/DyJwbOclnxjYQKD2nwtwrGTdDa/61l/IdvnTtXQK+dlVfqc1v7uQJWiUIHlby/Zj+BwKHlHzvXFrAK9/rFihGtYAgr8UhIlE7pF6KIEK3XszLpG3pt3phu3Ta4wxKykyFMOhkQqjIMRBKTkrGZBilJtdES0VsiJYaoSECmhs7UH8KfunQawsAAA==" },
|
|
17
|
+
{ family: "Dopamine Check Symbols", base64: "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" },
|
|
18
|
+
];
|
|
19
|
+
//# sourceMappingURL=check-fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-fonts.js","sourceRoot":"","sources":["../src/check-fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,MAAM,CAAC,MAAM,WAAW,GAAyB;IAC/C,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,EAAE,02BAA02B,EAAE;IACr5B,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,EAAE,k/GAAk/G,EAAE;CACjiH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom checkmark GLYPH rasterizer + bundled-font loader.
|
|
3
|
+
*
|
|
4
|
+
* Solarbloom's checkmark used to be an analytic two-segment SDF "drawn in light".
|
|
5
|
+
* It is now a REAL typeface glyph (✓ U+2713 / ✔ U+2714) whose FACE + codepoint
|
|
6
|
+
* are chosen by whimsy (engine/mood.ts `pickCheckGlyph`): low whimsy = a refined,
|
|
7
|
+
* calligraphic check; high whimsy = a fat, playful heavy check. We mirror Comic's
|
|
8
|
+
* hybrid pattern — rasterize the chosen glyph into an OFFSCREEN Canvas2D and
|
|
9
|
+
* upload it as a small ALPHA texture the Solarbloom shader samples for the
|
|
10
|
+
* checkmark layer (the bloom + motes stay procedural). The faces ship
|
|
11
|
+
* base64-embedded (check-fonts.ts) and register via the FontFace API, so the
|
|
12
|
+
* effect carries its own glyphs and NEVER fetches an asset at runtime.
|
|
13
|
+
*
|
|
14
|
+
* The texture is a centred square (the glyph fills it); the shader maps a square
|
|
15
|
+
* box around the bloom origin to it and reveals it with a diagonal draw-in wipe.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Register + load the embedded check faces once. Resolves even if loading fails
|
|
19
|
+
* (the shader then falls back to its analytic SDF checkmark, so the effect still
|
|
20
|
+
* confirms the win). Safe to await before every paint — it's cached.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ensureCheckFonts(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Rasterize the chosen check glyph, centred, into a square offscreen canvas. The
|
|
25
|
+
* glyph is drawn WHITE on transparent so the shader can read coverage from the
|
|
26
|
+
* alpha channel. Returns true if a glyph was drawn (face present); false if the
|
|
27
|
+
* face wasn't ready (the caller should then disable the glyph texture so the
|
|
28
|
+
* shader uses its analytic fallback). Pure given (family, char, size) — no time.
|
|
29
|
+
*/
|
|
30
|
+
export declare function drawCheckGlyph(ctx: CanvasRenderingContext2D, size: number, family: string, char: string): boolean;
|
|
31
|
+
//# sourceMappingURL=check-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-renderer.d.ts","sourceRoot":"","sources":["../src/check-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAcH;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6BhD;AAmBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAcT"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom checkmark GLYPH rasterizer + bundled-font loader.
|
|
3
|
+
*
|
|
4
|
+
* Solarbloom's checkmark used to be an analytic two-segment SDF "drawn in light".
|
|
5
|
+
* It is now a REAL typeface glyph (✓ U+2713 / ✔ U+2714) whose FACE + codepoint
|
|
6
|
+
* are chosen by whimsy (engine/mood.ts `pickCheckGlyph`): low whimsy = a refined,
|
|
7
|
+
* calligraphic check; high whimsy = a fat, playful heavy check. We mirror Comic's
|
|
8
|
+
* hybrid pattern — rasterize the chosen glyph into an OFFSCREEN Canvas2D and
|
|
9
|
+
* upload it as a small ALPHA texture the Solarbloom shader samples for the
|
|
10
|
+
* checkmark layer (the bloom + motes stay procedural). The faces ship
|
|
11
|
+
* base64-embedded (check-fonts.ts) and register via the FontFace API, so the
|
|
12
|
+
* effect carries its own glyphs and NEVER fetches an asset at runtime.
|
|
13
|
+
*
|
|
14
|
+
* The texture is a centred square (the glyph fills it); the shader maps a square
|
|
15
|
+
* box around the bloom origin to it and reveals it with a diagonal draw-in wipe.
|
|
16
|
+
*/
|
|
17
|
+
import { CHECK_FACES } from "./check-fonts.js";
|
|
18
|
+
let fontsReady = null;
|
|
19
|
+
function base64ToArrayBuffer(b64) {
|
|
20
|
+
const bin = atob(b64);
|
|
21
|
+
const len = bin.length;
|
|
22
|
+
const bytes = new Uint8Array(len);
|
|
23
|
+
for (let i = 0; i < len; i++)
|
|
24
|
+
bytes[i] = bin.charCodeAt(i);
|
|
25
|
+
return bytes.buffer;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Register + load the embedded check faces once. Resolves even if loading fails
|
|
29
|
+
* (the shader then falls back to its analytic SDF checkmark, so the effect still
|
|
30
|
+
* confirms the win). Safe to await before every paint — it's cached.
|
|
31
|
+
*/
|
|
32
|
+
export function ensureCheckFonts() {
|
|
33
|
+
if (fontsReady)
|
|
34
|
+
return fontsReady;
|
|
35
|
+
if (typeof document === "undefined" ||
|
|
36
|
+
typeof FontFace === "undefined" ||
|
|
37
|
+
!document.fonts) {
|
|
38
|
+
fontsReady = Promise.resolve();
|
|
39
|
+
return fontsReady;
|
|
40
|
+
}
|
|
41
|
+
fontsReady = (async () => {
|
|
42
|
+
await Promise.all(CHECK_FACES.map(async (f) => {
|
|
43
|
+
try {
|
|
44
|
+
const face = new FontFace(f.family, base64ToArrayBuffer(f.base64));
|
|
45
|
+
await face.load();
|
|
46
|
+
document.fonts.add(face);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* fall back to the analytic SDF checkmark for this fire */
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
try {
|
|
53
|
+
await document.fonts.ready;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* ignore */
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
return fontsReady;
|
|
60
|
+
}
|
|
61
|
+
// Kick font loading off at import so faces are usually ready by the first fire.
|
|
62
|
+
if (typeof document !== "undefined")
|
|
63
|
+
void ensureCheckFonts();
|
|
64
|
+
/**
|
|
65
|
+
* Has the named face actually loaded (so the glyph will render rather than a
|
|
66
|
+
* fallback box)? Used by the renderer to decide whether to upload a glyph
|
|
67
|
+
* texture or let the shader use its analytic checkmark.
|
|
68
|
+
*/
|
|
69
|
+
function checkFaceReady(family) {
|
|
70
|
+
if (typeof document === "undefined" || !document.fonts)
|
|
71
|
+
return false;
|
|
72
|
+
try {
|
|
73
|
+
return document.fonts.check(`64px "${family}"`);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Rasterize the chosen check glyph, centred, into a square offscreen canvas. The
|
|
81
|
+
* glyph is drawn WHITE on transparent so the shader can read coverage from the
|
|
82
|
+
* alpha channel. Returns true if a glyph was drawn (face present); false if the
|
|
83
|
+
* face wasn't ready (the caller should then disable the glyph texture so the
|
|
84
|
+
* shader uses its analytic fallback). Pure given (family, char, size) — no time.
|
|
85
|
+
*/
|
|
86
|
+
export function drawCheckGlyph(ctx, size, family, char) {
|
|
87
|
+
ctx.clearRect(0, 0, size, size);
|
|
88
|
+
if (!checkFaceReady(family))
|
|
89
|
+
return false;
|
|
90
|
+
ctx.save();
|
|
91
|
+
ctx.textAlign = "center";
|
|
92
|
+
ctx.textBaseline = "middle";
|
|
93
|
+
// Fill most of the box; leave a small margin so the wipe + glow have room and
|
|
94
|
+
// CLAMP_TO_EDGE sampling never smears an inked edge.
|
|
95
|
+
ctx.font = `${Math.round(size * 0.78)}px "${family}"`;
|
|
96
|
+
ctx.fillStyle = "#fff";
|
|
97
|
+
// Nudge up a hair: many check glyphs sit slightly low on the em box.
|
|
98
|
+
ctx.fillText(char, size * 0.5, size * 0.52);
|
|
99
|
+
ctx.restore();
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=check-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-renderer.js","sourceRoot":"","sources":["../src/check-renderer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom (the radial volumetric-bloom success effect) — the DATA-DRIVEN
|
|
3
|
+
* factory shim.
|
|
4
|
+
*
|
|
5
|
+
* Solarbloom is a PASS HYBRID: a procedural full-screen volumetric bloom +
|
|
6
|
+
* a checkmark drawn in light, plus a Canvas2D SPRITE PANEL for the drifting
|
|
7
|
+
* motes. Everything that isn't the shader or the two code-shaped draws is DATA
|
|
8
|
+
* in solarbloom.dope.json, interpreted by the shared backbone via
|
|
9
|
+
* `registerDopeEffect`:
|
|
10
|
+
* - the mood→params mapping + the OKLCH golden-angle palette (the loader),
|
|
11
|
+
* - the per-frame logic (`tempo.frame` — amp = the held-breath envelope, the
|
|
12
|
+
* `check` draw-in progress, delta-0 with the old factory frame() hook),
|
|
13
|
+
* - the shadow height (`render.shadowHeightFrac` = bloomRadius), the per-pass
|
|
14
|
+
* checkmark-box / SDF-stroke / SDF-range uniforms (`render.pass`), the
|
|
15
|
+
* MAX_MOTES clamp const (`render.consts`), `render.config.usesOrigin`, and
|
|
16
|
+
* `tempo.reducedMotion`,
|
|
17
|
+
* - the DECLARATIVE baked-SDF checkmark: `binding.samplers[].outline`/`on`
|
|
18
|
+
* binds the geometry.outlines.checkmark SDF at texture(1) and flips uSdfOn
|
|
19
|
+
* (the fail precedent) — no hand auxTextures code,
|
|
20
|
+
* - the uniform `binding` contract (the `u<Name>` list + exceptions).
|
|
21
|
+
*
|
|
22
|
+
* The genuinely code-shaped parts that stay JS are the GLSL (solarbloom-shader.ts)
|
|
23
|
+
* and the mote SPRITE-PANEL draw (solarbloom-renderer.ts — the per-mote poses are
|
|
24
|
+
* panel GEOMETRY, code by design). The whimsy-picked check GLYPH band is composed
|
|
25
|
+
* onto the resolved bag (metadata for a host's optional glyph-fallback rasterize;
|
|
26
|
+
* the canonical effect always carries the baked SDF, so the SDF path renders).
|
|
27
|
+
*/
|
|
28
|
+
import type { RenderParams } from "./solarbloom-params.js";
|
|
29
|
+
import { type EffectFactory } from "@dopaminefx/core";
|
|
30
|
+
export type { RenderParams, CheckGlyph } from "./solarbloom-params.js";
|
|
31
|
+
export { ensureCheckFonts } from "./check-renderer.js";
|
|
32
|
+
export { MAX_MOTES } from "./solarbloom-shader.js";
|
|
33
|
+
export declare const solarbloom: EffectFactory<RenderParams>;
|
|
34
|
+
export default solarbloom;
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEvE,OAAO,EAIL,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAKvD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAyBnD,eAAO,MAAM,UAAU,EAmBN,aAAa,CAAC,YAAY,CAAC,CAAC;AAE7C,eAAe,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom (the radial volumetric-bloom success effect) — the DATA-DRIVEN
|
|
3
|
+
* factory shim.
|
|
4
|
+
*
|
|
5
|
+
* Solarbloom is a PASS HYBRID: a procedural full-screen volumetric bloom +
|
|
6
|
+
* a checkmark drawn in light, plus a Canvas2D SPRITE PANEL for the drifting
|
|
7
|
+
* motes. Everything that isn't the shader or the two code-shaped draws is DATA
|
|
8
|
+
* in solarbloom.dope.json, interpreted by the shared backbone via
|
|
9
|
+
* `registerDopeEffect`:
|
|
10
|
+
* - the mood→params mapping + the OKLCH golden-angle palette (the loader),
|
|
11
|
+
* - the per-frame logic (`tempo.frame` — amp = the held-breath envelope, the
|
|
12
|
+
* `check` draw-in progress, delta-0 with the old factory frame() hook),
|
|
13
|
+
* - the shadow height (`render.shadowHeightFrac` = bloomRadius), the per-pass
|
|
14
|
+
* checkmark-box / SDF-stroke / SDF-range uniforms (`render.pass`), the
|
|
15
|
+
* MAX_MOTES clamp const (`render.consts`), `render.config.usesOrigin`, and
|
|
16
|
+
* `tempo.reducedMotion`,
|
|
17
|
+
* - the DECLARATIVE baked-SDF checkmark: `binding.samplers[].outline`/`on`
|
|
18
|
+
* binds the geometry.outlines.checkmark SDF at texture(1) and flips uSdfOn
|
|
19
|
+
* (the fail precedent) — no hand auxTextures code,
|
|
20
|
+
* - the uniform `binding` contract (the `u<Name>` list + exceptions).
|
|
21
|
+
*
|
|
22
|
+
* The genuinely code-shaped parts that stay JS are the GLSL (solarbloom-shader.ts)
|
|
23
|
+
* and the mote SPRITE-PANEL draw (solarbloom-renderer.ts — the per-mote poses are
|
|
24
|
+
* panel GEOMETRY, code by design). The whimsy-picked check GLYPH band is composed
|
|
25
|
+
* onto the resolved bag (metadata for a host's optional glyph-fallback rasterize;
|
|
26
|
+
* the canonical effect always carries the baked SDF, so the SDF path renders).
|
|
27
|
+
*/
|
|
28
|
+
import { VERTEX_SRC, FRAGMENT_SRC } from "./solarbloom-shader.js";
|
|
29
|
+
import { drawMotePanel } from "./solarbloom-renderer.js";
|
|
30
|
+
import { parseDope, pickBand, registerDopeEffect, } from "@dopaminefx/core";
|
|
31
|
+
import doc from "./solarbloom.dope.json";
|
|
32
|
+
// Re-export the bundled check-glyph face preloader from the effect's own chunk
|
|
33
|
+
// (a host that wires the glyph-fallback aux can await the bundled faces).
|
|
34
|
+
export { ensureCheckFonts } from "./check-renderer.js";
|
|
35
|
+
// MAX_MOTES is the single source of truth for the mote cap: BOTH the shader
|
|
36
|
+
// `#define` (the per-pixel native loop bound) AND the integer clamp the `.dope`
|
|
37
|
+
// mapping references (`render.params.moteCount.clampMax`, from `render.consts`).
|
|
38
|
+
export { MAX_MOTES } from "./solarbloom-shader.js";
|
|
39
|
+
const DOPE = parseDope(doc);
|
|
40
|
+
// The whimsy→check-glyph fallback BANDS live in the `.dope` (content.glyphBands).
|
|
41
|
+
const GLYPH_BANDS = (DOPE.content?.glyphBands ?? [
|
|
42
|
+
{ family: "Dopamine Check Symbols", char: "✓" },
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* Compose the whimsy-picked CHECK GLYPH band onto the numeric/palette bag (the
|
|
46
|
+
* only non-numeric, code-shaped param). The baked SDF is the canonical icon
|
|
47
|
+
* source, so this is metadata for a host's optional glyph-fallback rasterize.
|
|
48
|
+
*/
|
|
49
|
+
function composeSolarbloom(numeric, feeling) {
|
|
50
|
+
return { ...numeric, checkGlyph: pickBand(GLYPH_BANDS, feeling.whimsy) };
|
|
51
|
+
}
|
|
52
|
+
// Registers the EffectFactory AND the bundled "solarbloom" program. The whole
|
|
53
|
+
// factory is data + the two code-shaped hooks: the GLSL shader and the mote
|
|
54
|
+
// SPRITE-PANEL draw (`hooks.panelDraw`, wired at the `render.panel` unit/sampler);
|
|
55
|
+
// the baked-SDF checkmark binds declaratively from `binding.samplers`.
|
|
56
|
+
export const solarbloom = registerDopeEffect(DOPE, { vertex: VERTEX_SRC, fragment: FRAGMENT_SRC }, {
|
|
57
|
+
composeParams: composeSolarbloom,
|
|
58
|
+
hooks: {
|
|
59
|
+
panelDraw: (pctx, w, h, params, info) => {
|
|
60
|
+
const p = params;
|
|
61
|
+
drawMotePanel(pctx, w, h, {
|
|
62
|
+
palette: p.palette,
|
|
63
|
+
bloomRadius: p.bloomRadius,
|
|
64
|
+
turbulence: p.turbulence,
|
|
65
|
+
moteSpeed: p.moteSpeed,
|
|
66
|
+
moteCount: p.moteCount,
|
|
67
|
+
moteSeed: p.moteSeed,
|
|
68
|
+
}, info.life, info.animMs / 1000, info.centerPx);
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
export default solarbloom;
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EACL,SAAS,EACT,QAAQ,EACR,kBAAkB,GAGnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,GAAG,MAAM,wBAAwB,CAAC;AAIzC,+EAA+E;AAC/E,0EAA0E;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,4EAA4E;AAC5E,gFAAgF;AAChF,iFAAiF;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAa,CAAC,CAAC;AAEtC,kFAAkF;AAClF,MAAM,WAAW,GAAG,CAAE,IAAI,CAAC,OAAyC,EAAE,UAAU,IAAI;IAClF,EAAE,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,GAAG,EAAE;CAChD,CAAiB,CAAC;AAEnB;;;;GAIG;AACH,SAAS,iBAAiB,CACxB,OAAgC,EAChC,OAA0E;IAE1E,OAAO,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,8EAA8E;AAC9E,4EAA4E;AAC5E,mFAAmF;AACnF,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE;IACjG,aAAa,EAAE,iBAAiB;IAChC,KAAK,EAAE;QACL,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;YACtC,MAAM,CAAC,GAAG,MAAiC,CAAC;YAC5C,aAAa,CACX,IAAI,EAAE,CAAC,EAAE,CAAC,EACV;gBACE,OAAO,EAAE,CAAC,CAAC,OAAgB;gBAC3B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;aACrB,EACD,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,QAAQ,CAC7C,CAAC;QACJ,CAAC;KACF;CACF,CAA2C,CAAC;AAE7C,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom param-SHAPE types — the resolved render params the shader + check
|
|
3
|
+
* layer consume. The values are produced data-driven by the `.dope` loader (see
|
|
4
|
+
* `index.ts`); these pure interfaces just describe the bag's shape.
|
|
5
|
+
*/
|
|
6
|
+
import type { RGB } from "@dopaminefx/core";
|
|
7
|
+
export interface RenderParams {
|
|
8
|
+
seed: number;
|
|
9
|
+
/** Total afterglow length in milliseconds. */
|
|
10
|
+
durationMs: number;
|
|
11
|
+
/** Three linear-RGB palette stops. */
|
|
12
|
+
palette: [RGB, RGB, RGB];
|
|
13
|
+
/** Overall brightness multiplier for the bloom + motes. */
|
|
14
|
+
exposure: number;
|
|
15
|
+
/** Bloom radius as a fraction of the smaller viewport dimension. */
|
|
16
|
+
bloomRadius: number;
|
|
17
|
+
/** Number of drifting light motes (integer). */
|
|
18
|
+
moteCount: number;
|
|
19
|
+
/** How fast motes travel outward. */
|
|
20
|
+
moteSpeed: number;
|
|
21
|
+
/** Curl/buoyancy turbulence applied to mote paths. */
|
|
22
|
+
turbulence: number;
|
|
23
|
+
/** Held-breath overshoot magnitude for the envelope. */
|
|
24
|
+
overshoot: number;
|
|
25
|
+
/** A per-fire hash offset so mote layouts differ run to run. */
|
|
26
|
+
moteSeed: number;
|
|
27
|
+
/** 0..1 — strength of the iridescent thin-film shimmer on the bloom shell. */
|
|
28
|
+
iridescence: number;
|
|
29
|
+
/** 0..1 — strength of the chromatic/spectral split at the bloom edge. */
|
|
30
|
+
dispersion: number;
|
|
31
|
+
/** 0..1 — stylization (whimsy): photoreal lighting/motion → cel-shaded, hand-drawn. */
|
|
32
|
+
style: number;
|
|
33
|
+
/**
|
|
34
|
+
* Which bundled check-glyph face + codepoint the checkmark layer renders this
|
|
35
|
+
* fire, chosen by WHIMSY. Purely whimsy-derived (no rng, no effect on any
|
|
36
|
+
* numeric/palette param), so the `.dope` parity stays intact while the
|
|
37
|
+
* checkmark's SHAPE changes from a refined to a bold/playful glyph.
|
|
38
|
+
*/
|
|
39
|
+
checkGlyph: CheckGlyph;
|
|
40
|
+
}
|
|
41
|
+
/** A concrete check-glyph choice: a bundled face + the codepoint to render. */
|
|
42
|
+
export interface CheckGlyph {
|
|
43
|
+
/** CSS font-family — must match a `CHECK_FACES` entry registered at runtime. */
|
|
44
|
+
family: string;
|
|
45
|
+
/** The check character to draw (✓ U+2713 or ✔ U+2714). */
|
|
46
|
+
char: string;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=solarbloom-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solarbloom-params.d.ts","sourceRoot":"","sources":["../src/solarbloom-params.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom param-SHAPE types — the resolved render params the shader + check
|
|
3
|
+
* layer consume. The values are produced data-driven by the `.dope` loader (see
|
|
4
|
+
* `index.ts`); these pure interfaces just describe the bag's shape.
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=solarbloom-params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solarbloom-params.js","sourceRoot":"","sources":["../src/solarbloom-params.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom drifting-motes Canvas2D PANEL drawing (web).
|
|
3
|
+
*
|
|
4
|
+
* PERFORMANCE: the motes used to be an 80-iteration loop AT EVERY pixel
|
|
5
|
+
* (O(pixels × motes)) — the dominant cost of solarbloom under software/ANGLE
|
|
6
|
+
* WebGL (the full-screen volumetric bloom itself is cheap, ~28 ms). The motes are
|
|
7
|
+
* sparse glowing sprites, so they belong in a panel: each mote's pose + palette
|
|
8
|
+
* colour + streak + twinkle is computed ONCE per frame here and rasterized into
|
|
9
|
+
* an offscreen Canvas2D as a soft sprite. The fragment shader keeps the bloom,
|
|
10
|
+
* iridescence, dispersion, shafts and the checkmark fully procedural, and just
|
|
11
|
+
* SAMPLES this panel for the mote layer.
|
|
12
|
+
*
|
|
13
|
+
* Swift/Metal solarbloom is untouched; solarbloom.dope.json is unchanged across
|
|
14
|
+
* platforms — only the web mote render path moved.
|
|
15
|
+
*
|
|
16
|
+
* Panel encoding: RGB = Σ(per-mote lit colour × sprite falloff × fade × twinkle),
|
|
17
|
+
* accumulated additively (the shader multiplies by the bloom gain).
|
|
18
|
+
*/
|
|
19
|
+
import { type RGB } from "@dopaminefx/core";
|
|
20
|
+
/** Resolved params the mote panel consumes (a subset of solarbloom's RenderParams). */
|
|
21
|
+
export interface MotePanelParams {
|
|
22
|
+
palette: RGB[];
|
|
23
|
+
bloomRadius: number;
|
|
24
|
+
turbulence: number;
|
|
25
|
+
moteSpeed: number;
|
|
26
|
+
moteCount: number;
|
|
27
|
+
moteSeed: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Draw the drifting motes for this frame into the offscreen panel. Mirrors the
|
|
31
|
+
* original shader's per-mote motion (outward drift + buoyancy + curl), depth
|
|
32
|
+
* layering, motion-blur streak, twinkle and lifetime fade — computed once per
|
|
33
|
+
* mote in JS. `life` is whole-effect progress; `center` the bloom origin (device
|
|
34
|
+
* px, canvas y-down); `timeS` the elapsed seconds (for twinkle).
|
|
35
|
+
*/
|
|
36
|
+
export declare function drawMotePanel(ctx: CanvasRenderingContext2D, w: number, h: number, params: MotePanelParams, life: number, timeS: number, center: {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
}): void;
|
|
40
|
+
//# sourceMappingURL=solarbloom-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solarbloom-renderer.d.ts","sourceRoot":"","sources":["../src/solarbloom-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAc,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAqBxD,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,wBAAwB,EAC7B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,IAAI,CAqEN"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarbloom drifting-motes Canvas2D PANEL drawing (web).
|
|
3
|
+
*
|
|
4
|
+
* PERFORMANCE: the motes used to be an 80-iteration loop AT EVERY pixel
|
|
5
|
+
* (O(pixels × motes)) — the dominant cost of solarbloom under software/ANGLE
|
|
6
|
+
* WebGL (the full-screen volumetric bloom itself is cheap, ~28 ms). The motes are
|
|
7
|
+
* sparse glowing sprites, so they belong in a panel: each mote's pose + palette
|
|
8
|
+
* colour + streak + twinkle is computed ONCE per frame here and rasterized into
|
|
9
|
+
* an offscreen Canvas2D as a soft sprite. The fragment shader keeps the bloom,
|
|
10
|
+
* iridescence, dispersion, shafts and the checkmark fully procedural, and just
|
|
11
|
+
* SAMPLES this panel for the mote layer.
|
|
12
|
+
*
|
|
13
|
+
* Swift/Metal solarbloom is untouched; solarbloom.dope.json is unchanged across
|
|
14
|
+
* platforms — only the web mote render path moved.
|
|
15
|
+
*
|
|
16
|
+
* Panel encoding: RGB = Σ(per-mote lit colour × sprite falloff × fade × twinkle),
|
|
17
|
+
* accumulated additively (the shader multiplies by the bloom gain).
|
|
18
|
+
*/
|
|
19
|
+
import { mulberry32 } from "@dopaminefx/core";
|
|
20
|
+
const TAU = Math.PI * 2;
|
|
21
|
+
const clamp01 = (x) => (x < 0 ? 0 : x > 1 ? 1 : x);
|
|
22
|
+
const mix = (a, b, t) => a + (b - a) * t;
|
|
23
|
+
const smoothstep = (e0, e1, x) => {
|
|
24
|
+
const t = clamp01((x - e0) / (e1 - e0));
|
|
25
|
+
return t * t * (3 - 2 * t);
|
|
26
|
+
};
|
|
27
|
+
function paletteMix(pal, t) {
|
|
28
|
+
t = clamp01(t);
|
|
29
|
+
const [c0, c1, c2] = pal;
|
|
30
|
+
if (t < 0.5) {
|
|
31
|
+
const k = t * 2;
|
|
32
|
+
return { r: mix(c0.r, c1.r, k), g: mix(c0.g, c1.g, k), b: mix(c0.b, c1.b, k) };
|
|
33
|
+
}
|
|
34
|
+
const k = (t - 0.5) * 2;
|
|
35
|
+
return { r: mix(c1.r, c2.r, k), g: mix(c1.g, c2.g, k), b: mix(c1.b, c2.b, k) };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Draw the drifting motes for this frame into the offscreen panel. Mirrors the
|
|
39
|
+
* original shader's per-mote motion (outward drift + buoyancy + curl), depth
|
|
40
|
+
* layering, motion-blur streak, twinkle and lifetime fade — computed once per
|
|
41
|
+
* mote in JS. `life` is whole-effect progress; `center` the bloom origin (device
|
|
42
|
+
* px, canvas y-down); `timeS` the elapsed seconds (for twinkle).
|
|
43
|
+
*/
|
|
44
|
+
export function drawMotePanel(ctx, w, h, params, life, timeS, center) {
|
|
45
|
+
ctx.clearRect(0, 0, w, h);
|
|
46
|
+
const minDim = Math.min(w, h);
|
|
47
|
+
const r = params.bloomRadius * minDim;
|
|
48
|
+
const count = Math.max(0, Math.round(params.moteCount));
|
|
49
|
+
const rng = mulberry32(((params.moteSeed * 1000) >>> 0) + 7);
|
|
50
|
+
ctx.save();
|
|
51
|
+
ctx.globalCompositeOperation = "lighter";
|
|
52
|
+
for (let i = 0; i < count; i++) {
|
|
53
|
+
const hx = rng(), hy = rng(), h2x = rng(), h2y = rng(), delayR = rng();
|
|
54
|
+
const a0 = hx * TAU;
|
|
55
|
+
const spd = 0.5 + hy;
|
|
56
|
+
const delay = delayR * 0.15;
|
|
57
|
+
const ml = clamp01((life - delay) / (1 - delay));
|
|
58
|
+
if (ml <= 0)
|
|
59
|
+
continue;
|
|
60
|
+
const near = h2x >= 0.66 ? 1 : 0;
|
|
61
|
+
const depth = mix(0.7, 1.4, near);
|
|
62
|
+
const dirx = Math.cos(a0), diry = Math.sin(a0);
|
|
63
|
+
const travel = ml * spd * params.moteSpeed * r * 1.3 * depth;
|
|
64
|
+
// y-up local frame (buoyancy floats upward = +y).
|
|
65
|
+
let px = dirx * travel;
|
|
66
|
+
let py = diry * travel + ml * ml * r * 0.5;
|
|
67
|
+
const t1 = a0 * 3.0 + ml * TAU * spd;
|
|
68
|
+
px += Math.sin(t1) * params.turbulence * r * 0.3 * ml;
|
|
69
|
+
py += Math.cos(t1 * 0.8 + a0) * params.turbulence * r * 0.3 * ml;
|
|
70
|
+
// Velocity → motion-blur streak direction + amount (matches the shader).
|
|
71
|
+
const velx = dirx * spd * params.moteSpeed * 1.3 * depth + Math.cos(t1) * params.turbulence * 0.3;
|
|
72
|
+
const vely = diry * spd * params.moteSpeed * 1.3 * depth + 2.0 * ml * 0.5 - Math.sin(t1 * 0.8 + a0) * params.turbulence * 0.3;
|
|
73
|
+
const vlen = Math.hypot(velx, vely) || 1e-4;
|
|
74
|
+
const streak = clamp01(vlen * 0.12) * smoothstep(0, 0.25, ml) * 0.65;
|
|
75
|
+
const size = minDim * 0.006 * (0.6 + hx * 0.8) * depth;
|
|
76
|
+
const twinkle = 0.75 + 0.25 * Math.sin(timeS * (6.0 + h2y * 10.0) + hx * TAU);
|
|
77
|
+
const fade = (1 - Math.pow(ml, 1.3)) * smoothstep(0, 0.08, ml);
|
|
78
|
+
const amp = fade * twinkle * 1.2 * mix(0.9, 1.3, near);
|
|
79
|
+
if (amp <= 0.001)
|
|
80
|
+
continue;
|
|
81
|
+
const base = paletteMix(params.palette, hy);
|
|
82
|
+
const cr = Math.round(clamp01(base.r * amp) * 255);
|
|
83
|
+
const cg = Math.round(clamp01(base.g * amp) * 255);
|
|
84
|
+
const cb = Math.round(clamp01(base.b * amp) * 255);
|
|
85
|
+
if (cr + cg + cb <= 0)
|
|
86
|
+
continue;
|
|
87
|
+
// Canvas position (flip y-up → y-down).
|
|
88
|
+
const cx = center.x + px;
|
|
89
|
+
const cy = center.y - py;
|
|
90
|
+
// Stretch along the velocity direction to mimic the motion-blur streak.
|
|
91
|
+
const ang = Math.atan2(vely, velx);
|
|
92
|
+
const stretch = 1 / (1 - streak);
|
|
93
|
+
const rad = Math.max(size * 3, 1.5);
|
|
94
|
+
ctx.save();
|
|
95
|
+
ctx.translate(cx, cy);
|
|
96
|
+
ctx.rotate(ang);
|
|
97
|
+
ctx.scale(stretch, 1);
|
|
98
|
+
const grad = ctx.createRadialGradient(0, 0, 0, 0, 0, rad);
|
|
99
|
+
grad.addColorStop(0, `rgb(${cr},${cg},${cb})`);
|
|
100
|
+
grad.addColorStop(0.4, `rgba(${cr},${cg},${cb},0.35)`);
|
|
101
|
+
grad.addColorStop(1, "rgba(0,0,0,0)");
|
|
102
|
+
ctx.fillStyle = grad;
|
|
103
|
+
ctx.beginPath();
|
|
104
|
+
ctx.arc(0, 0, rad, 0, TAU);
|
|
105
|
+
ctx.fill();
|
|
106
|
+
ctx.restore();
|
|
107
|
+
}
|
|
108
|
+
ctx.restore();
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=solarbloom-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solarbloom-renderer.js","sourceRoot":"","sources":["../src/solarbloom-renderer.ts"],"names":[],"mappings":"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"}
|