@johnhenry/math 0.1.0 → 0.2.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/Shader.js ADDED
@@ -0,0 +1,758 @@
1
+ export class ShaderUnsupportedError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "ShaderUnsupportedError";
5
+ }
6
+ }
7
+ const RESERVED = new Set([
8
+ // GLSL ES 3.0 keywords (incl. reserved-for-future) and builtin functions, WGSL keywords, reserved words and builtins
9
+ "NULL",
10
+ "Self",
11
+ "abs",
12
+ "abstract",
13
+ "acos",
14
+ "acosh",
15
+ "active",
16
+ "alias",
17
+ "alignas",
18
+ "alignof",
19
+ "all",
20
+ "any",
21
+ "array",
22
+ "arrayLength",
23
+ "as",
24
+ "asin",
25
+ "asinh",
26
+ "asm",
27
+ "asm_fragment",
28
+ "async",
29
+ "atan",
30
+ "atan2",
31
+ "atanh",
32
+ "atomic",
33
+ "attribute",
34
+ "auto",
35
+ "await",
36
+ "become",
37
+ "binding_array",
38
+ "bitcast",
39
+ "bool",
40
+ "break",
41
+ "buffer",
42
+ "bvec2",
43
+ "bvec3",
44
+ "bvec4",
45
+ "case",
46
+ "cast",
47
+ "catch",
48
+ "ceil",
49
+ "centroid",
50
+ "clamp",
51
+ "class",
52
+ "co_await",
53
+ "co_return",
54
+ "co_yield",
55
+ "coherent",
56
+ "column_major",
57
+ "common",
58
+ "compile",
59
+ "compile_fragment",
60
+ "concept",
61
+ "const",
62
+ "const_assert",
63
+ "const_cast",
64
+ "consteval",
65
+ "constexpr",
66
+ "constinit",
67
+ "continue",
68
+ "continuing",
69
+ "cos",
70
+ "cosh",
71
+ "countOneBits",
72
+ "crate",
73
+ "cross",
74
+ "dFdx",
75
+ "dFdy",
76
+ "debugger",
77
+ "decltype",
78
+ "default",
79
+ "degrees",
80
+ "delete",
81
+ "demote",
82
+ "demote_to_helper",
83
+ "determinant",
84
+ "diagnostic",
85
+ "discard",
86
+ "distance",
87
+ "do",
88
+ "dot",
89
+ "dvec2",
90
+ "dvec3",
91
+ "dvec4",
92
+ "dynamic_cast",
93
+ "else",
94
+ "enable",
95
+ "enum",
96
+ "equal",
97
+ "exp",
98
+ "exp2",
99
+ "explicit",
100
+ "export",
101
+ "extends",
102
+ "extern",
103
+ "external",
104
+ "f16",
105
+ "f32",
106
+ "faceforward",
107
+ "fallthrough",
108
+ "false",
109
+ "filter",
110
+ "final",
111
+ "finally",
112
+ "fixed",
113
+ "flat",
114
+ "float",
115
+ "floatBitsToInt",
116
+ "floatBitsToUint",
117
+ "floor",
118
+ "fma",
119
+ "fn",
120
+ "for",
121
+ "fract",
122
+ "friend",
123
+ "from",
124
+ "fvec2",
125
+ "fvec3",
126
+ "fvec4",
127
+ "fwidth",
128
+ "fxgroup",
129
+ "get",
130
+ "goto",
131
+ "greaterThan",
132
+ "greaterThanEqual",
133
+ "groupshared",
134
+ "half",
135
+ "highp",
136
+ "hvec2",
137
+ "hvec3",
138
+ "hvec4",
139
+ "i32",
140
+ "if",
141
+ "impl",
142
+ "implements",
143
+ "import",
144
+ "in",
145
+ "inline",
146
+ "inout",
147
+ "input",
148
+ "instanceof",
149
+ "int",
150
+ "intBitsToFloat",
151
+ "interface",
152
+ "invariant",
153
+ "inverse",
154
+ "inverseSqrt",
155
+ "inversesqrt",
156
+ "isampler1D",
157
+ "isampler1DArray",
158
+ "isampler2D",
159
+ "isampler2DArray",
160
+ "isampler2DMS",
161
+ "isampler2DMSArray",
162
+ "isampler2DRect",
163
+ "isampler3D",
164
+ "isamplerBuffer",
165
+ "isamplerCube",
166
+ "isinf",
167
+ "isnan",
168
+ "ivec2",
169
+ "ivec3",
170
+ "ivec4",
171
+ "layout",
172
+ "length",
173
+ "lessThan",
174
+ "lessThanEqual",
175
+ "let",
176
+ "log",
177
+ "log2",
178
+ "long",
179
+ "loop",
180
+ "lowp",
181
+ "macro",
182
+ "macro_rules",
183
+ "main",
184
+ "mat2",
185
+ "mat2x2",
186
+ "mat2x2f",
187
+ "mat2x3",
188
+ "mat2x4",
189
+ "mat3",
190
+ "mat3x2",
191
+ "mat3x3",
192
+ "mat3x3f",
193
+ "mat3x4",
194
+ "mat4",
195
+ "mat4x2",
196
+ "mat4x3",
197
+ "mat4x4",
198
+ "mat4x4f",
199
+ "match",
200
+ "matrixCompMult",
201
+ "max",
202
+ "mediump",
203
+ "meta",
204
+ "min",
205
+ "mix",
206
+ "mod",
207
+ "modf",
208
+ "module",
209
+ "move",
210
+ "mut",
211
+ "mutable",
212
+ "namespace",
213
+ "new",
214
+ "nil",
215
+ "noexcept",
216
+ "noinline",
217
+ "nointerpolation",
218
+ "noperspective",
219
+ "normalize",
220
+ "not",
221
+ "notEqual",
222
+ "null",
223
+ "nullptr",
224
+ "of",
225
+ "operator",
226
+ "out",
227
+ "outerProduct",
228
+ "output",
229
+ "override",
230
+ "packHalf2x16",
231
+ "packSnorm2x16",
232
+ "packUnorm2x16",
233
+ "package",
234
+ "packoffset",
235
+ "partition",
236
+ "pass",
237
+ "patch",
238
+ "pixelfragment",
239
+ "pow",
240
+ "precise",
241
+ "precision",
242
+ "premerge",
243
+ "priv",
244
+ "protected",
245
+ "ptr",
246
+ "pub",
247
+ "public",
248
+ "radians",
249
+ "readonly",
250
+ "ref",
251
+ "reflect",
252
+ "refract",
253
+ "regardless",
254
+ "register",
255
+ "reinterpret_cast",
256
+ "require",
257
+ "requires",
258
+ "resource",
259
+ "restrict",
260
+ "return",
261
+ "reverseBits",
262
+ "round",
263
+ "roundEven",
264
+ "sample",
265
+ "sampler",
266
+ "sampler1D",
267
+ "sampler1DArray",
268
+ "sampler1DArrayShadow",
269
+ "sampler1DShadow",
270
+ "sampler2D",
271
+ "sampler2DArray",
272
+ "sampler2DArrayShadow",
273
+ "sampler2DMS",
274
+ "sampler2DMSArray",
275
+ "sampler2DRect",
276
+ "sampler2DRectShadow",
277
+ "sampler2DShadow",
278
+ "sampler3D",
279
+ "samplerBuffer",
280
+ "samplerCube",
281
+ "samplerCubeShadow",
282
+ "sampler_comparison",
283
+ "saturate",
284
+ "select",
285
+ "self",
286
+ "set",
287
+ "shared",
288
+ "short",
289
+ "sign",
290
+ "sin",
291
+ "sinh",
292
+ "sizeof",
293
+ "smooth",
294
+ "smoothstep",
295
+ "snorm",
296
+ "sqrt",
297
+ "static",
298
+ "static_assert",
299
+ "static_cast",
300
+ "std",
301
+ "step",
302
+ "struct",
303
+ "subroutine",
304
+ "super",
305
+ "superp",
306
+ "switch",
307
+ "tan",
308
+ "tanh",
309
+ "target",
310
+ "template",
311
+ "texelFetch",
312
+ "texelFetchOffset",
313
+ "texture",
314
+ "textureGrad",
315
+ "textureGradOffset",
316
+ "textureLod",
317
+ "textureLodOffset",
318
+ "textureOffset",
319
+ "textureProj",
320
+ "textureProjGrad",
321
+ "textureProjGradOffset",
322
+ "textureProjLod",
323
+ "textureProjLodOffset",
324
+ "textureProjOffset",
325
+ "textureSize",
326
+ "texture_1d",
327
+ "texture_2d",
328
+ "texture_2d_array",
329
+ "texture_3d",
330
+ "texture_cube",
331
+ "texture_cube_array",
332
+ "texture_depth_2d",
333
+ "texture_multisampled_2d",
334
+ "texture_storage_1d",
335
+ "texture_storage_2d",
336
+ "texture_storage_2d_array",
337
+ "texture_storage_3d",
338
+ "this",
339
+ "thread_local",
340
+ "throw",
341
+ "trait",
342
+ "transpose",
343
+ "true",
344
+ "trunc",
345
+ "try",
346
+ "type",
347
+ "typedef",
348
+ "typeid",
349
+ "typename",
350
+ "typeof",
351
+ "u32",
352
+ "uint",
353
+ "uintBitsToFloat",
354
+ "uniform",
355
+ "union",
356
+ "unless",
357
+ "unorm",
358
+ "unpackHalf2x16",
359
+ "unpackSnorm2x16",
360
+ "unpackUnorm2x16",
361
+ "unsafe",
362
+ "unsigned",
363
+ "unsized",
364
+ "usampler1D",
365
+ "usampler1DArray",
366
+ "usampler2D",
367
+ "usampler2DArray",
368
+ "usampler2DMS",
369
+ "usampler2DMSArray",
370
+ "usampler2DRect",
371
+ "usampler3D",
372
+ "usamplerBuffer",
373
+ "usamplerCube",
374
+ "use",
375
+ "using",
376
+ "uvec2",
377
+ "uvec3",
378
+ "uvec4",
379
+ "var",
380
+ "varying",
381
+ "vec2",
382
+ "vec2f",
383
+ "vec2h",
384
+ "vec2i",
385
+ "vec2u",
386
+ "vec3",
387
+ "vec3f",
388
+ "vec3h",
389
+ "vec3i",
390
+ "vec3u",
391
+ "vec4",
392
+ "vec4f",
393
+ "vec4h",
394
+ "vec4i",
395
+ "vec4u",
396
+ "virtual",
397
+ "void",
398
+ "volatile",
399
+ "wgsl",
400
+ "where",
401
+ "while",
402
+ "with",
403
+ "writeonly",
404
+ "yield",
405
+ ]);
406
+ const LN10 = "2.302585092994046";
407
+ function lit(v, _c) {
408
+ if (!Number.isFinite(v))
409
+ throw new ShaderUnsupportedError(`constant ${v} has no shader literal`);
410
+ let s = String(v);
411
+ if (!/[.e]/i.test(s))
412
+ s += ".0";
413
+ else if (/^-?\d+e/i.test(s))
414
+ s = s.replace(/e/i, ".0e"); // 1e-7 -> 1.0e-7
415
+ // same float literal grammar in both dialects; negatives are parenthesised so "-" + "-2.0" never becomes "--2.0"
416
+ return v < 0 || Object.is(v, -0) ? `(${s})` : s;
417
+ }
418
+ function vec2(a, b, c) {
419
+ return c.d === "glsl" ? `vec2(${a}, ${b})` : `vec2<f32>(${a}, ${b})`;
420
+ }
421
+ function helper(c, name) {
422
+ c.helpers.add(name);
423
+ return `mx_${name}`;
424
+ }
425
+ function constValue(n) {
426
+ if (n.type === "const")
427
+ return n.value;
428
+ if (n.type === "neg" && n.arg.type === "const")
429
+ return -n.arg.value;
430
+ return undefined;
431
+ }
432
+ const CMP_OPS = { lt: "<", le: "<=", gt: ">", ge: ">=", eq: "==", ne: "!=" };
433
+ function cmpOp(op) {
434
+ if (!Object.hasOwn(CMP_OPS, op))
435
+ throw new ShaderUnsupportedError(`unknown comparison operator "${op}"`);
436
+ return CMP_OPS[op];
437
+ }
438
+ function boolean(n, c) {
439
+ if (n.type === "cmp") {
440
+ const op = cmpOp(n.op);
441
+ return `${emit(n.left, c)} ${op} ${emit(n.right, c)}`;
442
+ }
443
+ return `${emit(n, c)} != 0.0`;
444
+ }
445
+ function unsupported(what) {
446
+ throw new ShaderUnsupportedError(`${what} is not supported by the shader compiler`);
447
+ }
448
+ function emit(n, c) {
449
+ switch (n.type) {
450
+ case "const":
451
+ return c.complex ? vec2(lit(n.value, c), "0.0", c) : lit(n.value, c);
452
+ case "var": {
453
+ if (c.complex && n.name === "i" && !c.vars.has("i"))
454
+ return vec2("0.0", "1.0", c);
455
+ if (!c.vars.has(n.name))
456
+ throw new ShaderUnsupportedError(`variable "${n.name}" is not in the variables list`);
457
+ c.used.add(n.name);
458
+ return n.name;
459
+ }
460
+ case "neg":
461
+ return `(-${emit(n.arg, c)})`;
462
+ case "add":
463
+ return `(${emit(n.left, c)} + ${emit(n.right, c)})`;
464
+ case "sub":
465
+ return `(${emit(n.left, c)} - ${emit(n.right, c)})`;
466
+ case "mul":
467
+ return c.complex
468
+ ? `${helper(c, "cmul")}(${emit(n.left, c)}, ${emit(n.right, c)})`
469
+ : `(${emit(n.left, c)} * ${emit(n.right, c)})`;
470
+ case "div":
471
+ return c.complex
472
+ ? `${helper(c, "cdiv")}(${emit(n.left, c)}, ${emit(n.right, c)})`
473
+ : `(${emit(n.left, c)} / ${emit(n.right, c)})`;
474
+ case "pow":
475
+ return c.complex ? powComplex(n.base, n.exp, c) : powReal(n.base, n.exp, c);
476
+ case "func":
477
+ return c.complex ? funcComplex(n.name, emit(n.arg, c), c) : funcReal(n.name, emit(n.arg, c), c);
478
+ case "call2": {
479
+ if (c.complex)
480
+ unsupported(`${n.name} over complex numbers`);
481
+ const a = emit(n.left, c);
482
+ const b = emit(n.right, c);
483
+ switch (n.name) {
484
+ case "atan2":
485
+ return c.d === "glsl" ? `atan(${a}, ${b})` : `atan2(${a}, ${b})`;
486
+ case "hypot":
487
+ return c.d === "glsl" ? `length(vec2(${a}, ${b}))` : `length(vec2<f32>(${a}, ${b}))`;
488
+ case "min":
489
+ case "max":
490
+ return `${n.name}(${a}, ${b})`;
491
+ default:
492
+ return unsupported(`${n.name}`);
493
+ }
494
+ }
495
+ case "cmp": {
496
+ if (c.complex)
497
+ unsupported("comparison over complex numbers");
498
+ const t = boolean(n, c);
499
+ return c.d === "glsl" ? `(${t} ? 1.0 : 0.0)` : `select(0.0, 1.0, ${t})`;
500
+ }
501
+ case "piecewise": {
502
+ if (c.complex)
503
+ unsupported("piecewise over complex numbers");
504
+ let out = emit(n.otherwise, c);
505
+ for (let k = n.branches.length - 1; k >= 0; k--) {
506
+ const br = n.branches[k];
507
+ const cond = boolean(br.cond, c);
508
+ const e = emit(br.expr, c);
509
+ out = c.d === "glsl" ? `(${cond} ? ${e} : ${out})` : `select(${out}, ${e}, ${cond})`;
510
+ }
511
+ return out;
512
+ }
513
+ case "sum":
514
+ return unsupported("sum");
515
+ case "product":
516
+ return unsupported("product");
517
+ }
518
+ }
519
+ function powReal(base, exp, c) {
520
+ const b = emit(base, c);
521
+ const k0 = constValue(exp);
522
+ if (k0 !== undefined && Number.isInteger(k0) && Math.abs(k0) <= 4) {
523
+ const k = k0;
524
+ if (k === 0)
525
+ return "1.0";
526
+ if (k === 1)
527
+ return b;
528
+ const prod = Array.from({ length: Math.abs(k) }, () => b).join(" * ");
529
+ return k > 0 ? `(${prod})` : `(1.0 / (${prod}))`;
530
+ }
531
+ if (k0 !== undefined && Number.isInteger(k0)) {
532
+ // pow() of a negative base is undefined in GLSL: split sign and magnitude for integer powers.
533
+ const k = k0;
534
+ const odd = Math.abs(k) % 2 === 1;
535
+ const mag = `pow(abs(${b}), ${lit(k, c)})`;
536
+ return odd ? `(sign(${b}) * ${mag})` : mag;
537
+ }
538
+ return `pow(${b}, ${emit(exp, c)})`;
539
+ }
540
+ function funcReal(name, a, c) {
541
+ const g = c.d === "glsl";
542
+ switch (name) {
543
+ case "sin":
544
+ case "cos":
545
+ case "tan":
546
+ case "exp":
547
+ case "sqrt":
548
+ case "asin":
549
+ case "acos":
550
+ case "atan":
551
+ case "sinh":
552
+ case "cosh":
553
+ case "tanh":
554
+ case "asinh":
555
+ case "acosh":
556
+ case "atanh":
557
+ case "abs":
558
+ case "sign":
559
+ case "floor":
560
+ case "ceil":
561
+ case "round":
562
+ case "trunc":
563
+ case "log2":
564
+ return `${name}(${a})`;
565
+ case "ln":
566
+ return `log(${a})`;
567
+ case "log10":
568
+ return `(log(${a}) / ${LN10})`;
569
+ case "cot":
570
+ return `(1.0 / tan(${a}))`;
571
+ case "sec":
572
+ return `(1.0 / cos(${a}))`;
573
+ case "csc":
574
+ return `(1.0 / sin(${a}))`;
575
+ case "coth":
576
+ return `(1.0 / tanh(${a}))`;
577
+ case "sech":
578
+ return `(1.0 / cosh(${a}))`;
579
+ case "csch":
580
+ return `(1.0 / sinh(${a}))`;
581
+ case "acot":
582
+ return `atan(1.0 / ${a})`;
583
+ case "asec":
584
+ return `acos(1.0 / ${a})`;
585
+ case "acsc":
586
+ return `asin(1.0 / ${a})`;
587
+ case "acoth":
588
+ return `atanh(1.0 / ${a})`;
589
+ case "asech":
590
+ return `acosh(1.0 / ${a})`;
591
+ case "acsch":
592
+ return `asinh(1.0 / ${a})`;
593
+ case "cbrt":
594
+ return `(sign(${a}) * pow(abs(${a}), 0.3333333333333333))`;
595
+ case "expm1":
596
+ return `(exp(${a}) - 1.0)`;
597
+ case "log1p":
598
+ return `log(1.0 + ${a})`;
599
+ case "sigmoid":
600
+ return `(1.0 / (1.0 + exp(-${a})))`;
601
+ case "relu":
602
+ return g ? `max(${a}, 0.0)` : `max(${a}, 0.0)`;
603
+ default:
604
+ return unsupported(`function ${name}`);
605
+ }
606
+ }
607
+ function powComplex(base, exp, c) {
608
+ const b = emit(base, c);
609
+ const k = constValue(exp);
610
+ if (k !== undefined && Number.isInteger(k) && Math.abs(k) <= 64) {
611
+ return `${helper(c, "cpowi")}(${b}, ${k})`;
612
+ }
613
+ return `${helper(c, "cpow")}(${b}, ${emit(exp, c)})`;
614
+ }
615
+ function funcComplex(name, a, c) {
616
+ switch (name) {
617
+ case "exp":
618
+ case "ln":
619
+ case "sqrt":
620
+ case "sin":
621
+ case "cos":
622
+ case "tan":
623
+ case "sinh":
624
+ case "cosh":
625
+ case "tanh":
626
+ case "abs":
627
+ return `${helper(c, `c${name}`)}(${a})`;
628
+ default:
629
+ return unsupported(`function ${name} over complex numbers`);
630
+ }
631
+ }
632
+ /** Helper bodies per dialect. Dependencies listed in HELPER_DEPS; emitted in HELPER_ORDER. */
633
+ const HELPER_ORDER = [
634
+ "cmul",
635
+ "cdiv",
636
+ "cexp",
637
+ "cln",
638
+ "cpow",
639
+ "cpowi",
640
+ "csqrt",
641
+ "csin",
642
+ "ccos",
643
+ "ctan",
644
+ "csinh",
645
+ "ccosh",
646
+ "ctanh",
647
+ "cabs",
648
+ ];
649
+ const HELPER_DEPS = {
650
+ cpow: ["cmul", "cexp", "cln"],
651
+ cpowi: ["cmul", "cdiv"],
652
+ ctan: ["cdiv", "csin", "ccos"],
653
+ ctanh: ["cdiv", "csinh", "ccosh"],
654
+ };
655
+ function helperSource(name, d) {
656
+ const V = d === "glsl" ? "vec2" : "vec2<f32>";
657
+ const fn = (n, params, body) => d === "glsl"
658
+ ? `${V} mx_${n}(${params.replace(/(\w+): vec2|(\w+): int/g, (_m, a, b) => (a ? `vec2 ${a}` : `int ${b}`))}) {\n${body}\n}`
659
+ : `fn mx_${n}(${params.replace(/: vec2/g, ": vec2<f32>")}) -> ${V} {\n${body}\n}`;
660
+ const dec = (t, n, e) => d === "glsl" ? ` ${t} ${n} = ${e};` : ` let ${n}: ${t === "float" ? "f32" : t} = ${e};`;
661
+ const ret = (e) => ` return ${e};`;
662
+ const v = (a, b) => `${V}(${a}, ${b})`;
663
+ switch (name) {
664
+ case "cmul":
665
+ return fn("cmul", "a: vec2, b: vec2", ret(v("a.x * b.x - a.y * b.y", "a.x * b.y + a.y * b.x")));
666
+ case "cdiv":
667
+ return fn("cdiv", "a: vec2, b: vec2", [
668
+ dec("float", "m", "b.x * b.x + b.y * b.y"),
669
+ ret(v("(a.x * b.x + a.y * b.y) / m", "(a.y * b.x - a.x * b.y) / m")),
670
+ ].join("\n"));
671
+ case "cexp":
672
+ return fn("cexp", "z: vec2", ret(`exp(z.x) * ${v("cos(z.y)", "sin(z.y)")}`));
673
+ case "cln":
674
+ return fn("cln", "z: vec2", ret(v(d === "glsl" ? "log(length(z))" : "log(length(z))", d === "glsl" ? "atan(z.y, z.x)" : "atan2(z.y, z.x)")));
675
+ case "cpow":
676
+ return fn("cpow", "a: vec2, b: vec2", ret("mx_cexp(mx_cmul(b, mx_cln(a)))"));
677
+ case "cpowi": {
678
+ // exponentiation by squaring, bounded loop (|n| <= 64)
679
+ if (d === "glsl")
680
+ return `vec2 mx_cpowi(vec2 z, int n) {\n int k = n < 0 ? -n : n;\n vec2 r = vec2(1.0, 0.0);\n vec2 s = z;\n for (int j = 0; j < 7; j++) {\n if ((k & 1) == 1) r = mx_cmul(r, s);\n s = mx_cmul(s, s);\n k = k >> 1;\n }\n return n < 0 ? mx_cdiv(vec2(1.0, 0.0), r) : r;\n}`;
681
+ return `fn mx_cpowi(z: vec2<f32>, n: i32) -> vec2<f32> {\n var k: i32 = abs(n);\n var r: vec2<f32> = vec2<f32>(1.0, 0.0);\n var s: vec2<f32> = z;\n for (var j: i32 = 0; j < 7; j = j + 1) {\n if ((k & 1) == 1) { r = mx_cmul(r, s); }\n s = mx_cmul(s, s);\n k = k >> 1u;\n }\n if (n < 0) { return mx_cdiv(vec2<f32>(1.0, 0.0), r); }\n return r;\n}`;
682
+ }
683
+ case "csqrt":
684
+ return fn("csqrt", "z: vec2", [
685
+ dec("float", "r", "length(z)"),
686
+ ret(v("sqrt(max(0.5 * (r + z.x), 0.0))", d === "glsl"
687
+ ? "(z.y < 0.0 ? -1.0 : 1.0) * sqrt(max(0.5 * (r - z.x), 0.0))"
688
+ : "select(1.0, -1.0, z.y < 0.0) * sqrt(max(0.5 * (r - z.x), 0.0))")),
689
+ ].join("\n"));
690
+ case "csin":
691
+ return fn("csin", "z: vec2", ret(v("sin(z.x) * cosh(z.y)", "cos(z.x) * sinh(z.y)")));
692
+ case "ccos":
693
+ return fn("ccos", "z: vec2", ret(v("cos(z.x) * cosh(z.y)", "-sin(z.x) * sinh(z.y)")));
694
+ case "ctan":
695
+ return fn("ctan", "z: vec2", ret("mx_cdiv(mx_csin(z), mx_ccos(z))"));
696
+ case "csinh":
697
+ return fn("csinh", "z: vec2", ret(v("sinh(z.x) * cos(z.y)", "cosh(z.x) * sin(z.y)")));
698
+ case "ccosh":
699
+ return fn("ccosh", "z: vec2", ret(v("cosh(z.x) * cos(z.y)", "sinh(z.x) * sin(z.y)")));
700
+ case "ctanh":
701
+ return fn("ctanh", "z: vec2", ret("mx_cdiv(mx_csinh(z), mx_ccosh(z))"));
702
+ case "cabs":
703
+ return fn("cabs", "z: vec2", ret(v("length(z)", "0.0")));
704
+ default:
705
+ throw new Error(`unknown helper ${name}`);
706
+ }
707
+ }
708
+ function safeIdentifier(v) {
709
+ return (/^[A-Za-z][A-Za-z0-9_]*$/.test(v) &&
710
+ !RESERVED.has(v) &&
711
+ !v.includes("__") &&
712
+ !v.startsWith("mx_") &&
713
+ !v.startsWith("gl_"));
714
+ }
715
+ export function compileShader(expr, dialect, options = {}) {
716
+ const complex = options.complex === true;
717
+ const variables = options.variables ?? [complex ? "z" : "x"];
718
+ for (const v of variables) {
719
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(v) ||
720
+ RESERVED.has(v) ||
721
+ v.includes("__") ||
722
+ v.startsWith("mx_") ||
723
+ v.startsWith("gl_"))
724
+ throw new ShaderUnsupportedError(`variable name "${v}" is not a safe shader identifier`);
725
+ }
726
+ const fnName = options.functionName;
727
+ if (fnName !== undefined && (!safeIdentifier(fnName) || fnName === "main"))
728
+ throw new ShaderUnsupportedError(`function name "${fnName}" is not a safe shader identifier`);
729
+ const ctx = { d: dialect, complex, vars: new Set(variables), used: new Set(), helpers: new Set() };
730
+ const expression = emit(expr, ctx);
731
+ // close over dependencies
732
+ const need = new Set(ctx.helpers);
733
+ for (let grew = true; grew;) {
734
+ grew = false;
735
+ for (const h of [...need]) {
736
+ for (const dep of HELPER_DEPS[h] ?? []) {
737
+ if (!need.has(dep)) {
738
+ need.add(dep);
739
+ grew = true;
740
+ }
741
+ }
742
+ }
743
+ }
744
+ const helpers = HELPER_ORDER.filter((h) => need.has(h))
745
+ .map((h) => helperSource(h, dialect))
746
+ .join("\n\n");
747
+ let source = helpers;
748
+ if (options.functionName !== undefined) {
749
+ const T = complex ? (dialect === "glsl" ? "vec2" : "vec2<f32>") : dialect === "glsl" ? "float" : "f32";
750
+ const params = variables.map((v) => (dialect === "glsl" ? `${T} ${v}` : `${v}: ${T}`)).join(", ");
751
+ const fnText = dialect === "glsl"
752
+ ? `${T} ${options.functionName}(${params}) {\n return ${expression};\n}`
753
+ : `fn ${options.functionName}(${params}) -> ${T} {\n return ${expression};\n}`;
754
+ source = helpers ? `${helpers}\n\n${fnText}` : fnText;
755
+ }
756
+ return { expression, helpers, source, variables: [...ctx.used].sort() };
757
+ }
758
+ //# sourceMappingURL=Shader.js.map