@guinetik/gcanvas 1.0.4 → 1.0.5

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.
Files changed (193) hide show
  1. package/dist/CNAME +1 -0
  2. package/dist/animations.html +31 -0
  3. package/dist/basic.html +38 -0
  4. package/dist/baskara.html +31 -0
  5. package/dist/bezier.html +35 -0
  6. package/dist/beziersignature.html +29 -0
  7. package/dist/blackhole.html +28 -0
  8. package/dist/blob.html +35 -0
  9. package/dist/coordinates.html +698 -0
  10. package/dist/cube3d.html +23 -0
  11. package/dist/demos.css +303 -0
  12. package/dist/dino.html +42 -0
  13. package/dist/easing.html +28 -0
  14. package/dist/events.html +195 -0
  15. package/dist/fluent.html +647 -0
  16. package/dist/fluid-simple.html +22 -0
  17. package/dist/fluid.html +37 -0
  18. package/dist/fractals.html +36 -0
  19. package/dist/gameobjects.html +626 -0
  20. package/dist/gcanvas.es.js +517 -0
  21. package/dist/gcanvas.es.min.js +1 -1
  22. package/dist/gcanvas.umd.js +1 -1
  23. package/dist/gcanvas.umd.min.js +1 -1
  24. package/dist/genart.html +26 -0
  25. package/dist/gendream.html +26 -0
  26. package/dist/group.html +36 -0
  27. package/dist/home.html +587 -0
  28. package/dist/hyperbolic001.html +23 -0
  29. package/dist/hyperbolic002.html +23 -0
  30. package/dist/hyperbolic003.html +23 -0
  31. package/dist/hyperbolic004.html +23 -0
  32. package/dist/hyperbolic005.html +22 -0
  33. package/dist/index.html +398 -0
  34. package/dist/isometric.html +34 -0
  35. package/dist/js/animations.js +452 -0
  36. package/dist/js/basic.js +204 -0
  37. package/dist/js/baskara.js +751 -0
  38. package/dist/js/bezier.js +692 -0
  39. package/dist/js/beziersignature.js +241 -0
  40. package/dist/js/blackhole/accretiondisk.obj.js +379 -0
  41. package/dist/js/blackhole/blackhole.obj.js +318 -0
  42. package/dist/js/blackhole/index.js +409 -0
  43. package/dist/js/blackhole/particle.js +56 -0
  44. package/dist/js/blackhole/starfield.obj.js +218 -0
  45. package/dist/js/blob.js +2276 -0
  46. package/dist/js/coordinates.js +840 -0
  47. package/dist/js/cube3d.js +789 -0
  48. package/dist/js/dino.js +1420 -0
  49. package/dist/js/easing.js +477 -0
  50. package/dist/js/fluent.js +183 -0
  51. package/dist/js/fluid-simple.js +253 -0
  52. package/dist/js/fluid.js +527 -0
  53. package/dist/js/fractals.js +932 -0
  54. package/dist/js/fractalworker.js +93 -0
  55. package/dist/js/gameobjects.js +176 -0
  56. package/dist/js/genart.js +268 -0
  57. package/dist/js/gendream.js +209 -0
  58. package/dist/js/group.js +140 -0
  59. package/dist/js/hyperbolic001.js +310 -0
  60. package/dist/js/hyperbolic002.js +388 -0
  61. package/dist/js/hyperbolic003.js +319 -0
  62. package/dist/js/hyperbolic004.js +345 -0
  63. package/dist/js/hyperbolic005.js +340 -0
  64. package/dist/js/info-toggle.js +25 -0
  65. package/dist/js/isometric.js +863 -0
  66. package/dist/js/kerr.js +1547 -0
  67. package/dist/js/lavalamp.js +590 -0
  68. package/dist/js/layout.js +354 -0
  69. package/dist/js/mondrian.js +285 -0
  70. package/dist/js/opacity.js +275 -0
  71. package/dist/js/painter.js +484 -0
  72. package/dist/js/particles-showcase.js +514 -0
  73. package/dist/js/particles.js +299 -0
  74. package/dist/js/patterns.js +397 -0
  75. package/dist/js/penrose/artifact.js +69 -0
  76. package/dist/js/penrose/blackhole.js +121 -0
  77. package/dist/js/penrose/constants.js +73 -0
  78. package/dist/js/penrose/game.js +943 -0
  79. package/dist/js/penrose/lore.js +278 -0
  80. package/dist/js/penrose/penrosescene.js +892 -0
  81. package/dist/js/penrose/ship.js +216 -0
  82. package/dist/js/penrose/sounds.js +211 -0
  83. package/dist/js/penrose/voidparticle.js +55 -0
  84. package/dist/js/penrose/voidscene.js +258 -0
  85. package/dist/js/penrose/voidship.js +144 -0
  86. package/dist/js/penrose/wormhole.js +46 -0
  87. package/dist/js/pipeline.js +555 -0
  88. package/dist/js/plane3d.js +256 -0
  89. package/dist/js/platformer.js +1579 -0
  90. package/dist/js/scene.js +304 -0
  91. package/dist/js/scenes.js +320 -0
  92. package/dist/js/schrodinger.js +410 -0
  93. package/dist/js/schwarzschild.js +1015 -0
  94. package/dist/js/shapes.js +628 -0
  95. package/dist/js/space/alien.js +171 -0
  96. package/dist/js/space/boom.js +98 -0
  97. package/dist/js/space/boss.js +353 -0
  98. package/dist/js/space/buff.js +73 -0
  99. package/dist/js/space/bullet.js +102 -0
  100. package/dist/js/space/constants.js +85 -0
  101. package/dist/js/space/game.js +1884 -0
  102. package/dist/js/space/hud.js +112 -0
  103. package/dist/js/space/laserbeam.js +179 -0
  104. package/dist/js/space/lightning.js +277 -0
  105. package/dist/js/space/minion.js +192 -0
  106. package/dist/js/space/missile.js +212 -0
  107. package/dist/js/space/player.js +430 -0
  108. package/dist/js/space/powerup.js +90 -0
  109. package/dist/js/space/starfield.js +58 -0
  110. package/dist/js/space/starpower.js +90 -0
  111. package/dist/js/spacetime.js +559 -0
  112. package/dist/js/sphere3d.js +229 -0
  113. package/dist/js/sprite.js +473 -0
  114. package/dist/js/starfaux/config.js +118 -0
  115. package/dist/js/starfaux/enemy.js +353 -0
  116. package/dist/js/starfaux/hud.js +78 -0
  117. package/dist/js/starfaux/index.js +482 -0
  118. package/dist/js/starfaux/laser.js +182 -0
  119. package/dist/js/starfaux/player.js +468 -0
  120. package/dist/js/starfaux/terrain.js +560 -0
  121. package/dist/js/study001.js +275 -0
  122. package/dist/js/study002.js +366 -0
  123. package/dist/js/study003.js +331 -0
  124. package/dist/js/study004.js +389 -0
  125. package/dist/js/study005.js +209 -0
  126. package/dist/js/study006.js +194 -0
  127. package/dist/js/study007.js +192 -0
  128. package/dist/js/study008.js +413 -0
  129. package/dist/js/svgtween.js +204 -0
  130. package/dist/js/tde/accretiondisk.js +471 -0
  131. package/dist/js/tde/blackhole.js +219 -0
  132. package/dist/js/tde/blackholescene.js +209 -0
  133. package/dist/js/tde/config.js +59 -0
  134. package/dist/js/tde/index.js +820 -0
  135. package/dist/js/tde/jets.js +290 -0
  136. package/dist/js/tde/lensedstarfield.js +154 -0
  137. package/dist/js/tde/tdestar.js +297 -0
  138. package/dist/js/tde/tidalstream.js +372 -0
  139. package/dist/js/tde_old/blackhole.obj.js +354 -0
  140. package/dist/js/tde_old/debris.obj.js +791 -0
  141. package/dist/js/tde_old/flare.obj.js +239 -0
  142. package/dist/js/tde_old/index.js +448 -0
  143. package/dist/js/tde_old/star.obj.js +812 -0
  144. package/dist/js/tetris/config.js +157 -0
  145. package/dist/js/tetris/grid.js +286 -0
  146. package/dist/js/tetris/index.js +1195 -0
  147. package/dist/js/tetris/renderer.js +634 -0
  148. package/dist/js/tetris/tetrominos.js +280 -0
  149. package/dist/js/tiles.js +312 -0
  150. package/dist/js/tweendemo.js +79 -0
  151. package/dist/js/visibility.js +102 -0
  152. package/dist/kerr.html +28 -0
  153. package/dist/lavalamp.html +27 -0
  154. package/dist/layouts.html +37 -0
  155. package/dist/logo.svg +4 -0
  156. package/dist/loop.html +84 -0
  157. package/dist/mondrian.html +32 -0
  158. package/dist/og_image.png +0 -0
  159. package/dist/opacity.html +36 -0
  160. package/dist/painter.html +39 -0
  161. package/dist/particles-showcase.html +28 -0
  162. package/dist/particles.html +24 -0
  163. package/dist/patterns.html +33 -0
  164. package/dist/penrose-game.html +31 -0
  165. package/dist/pipeline.html +737 -0
  166. package/dist/plane3d.html +24 -0
  167. package/dist/platformer.html +43 -0
  168. package/dist/scene.html +33 -0
  169. package/dist/scenes.html +96 -0
  170. package/dist/schrodinger.html +27 -0
  171. package/dist/schwarzschild.html +27 -0
  172. package/dist/shapes.html +16 -0
  173. package/dist/space.html +85 -0
  174. package/dist/spacetime.html +27 -0
  175. package/dist/sphere3d.html +24 -0
  176. package/dist/sprite.html +18 -0
  177. package/dist/starfaux.html +22 -0
  178. package/dist/study001.html +23 -0
  179. package/dist/study002.html +23 -0
  180. package/dist/study003.html +23 -0
  181. package/dist/study004.html +23 -0
  182. package/dist/study005.html +22 -0
  183. package/dist/study006.html +24 -0
  184. package/dist/study007.html +24 -0
  185. package/dist/study008.html +22 -0
  186. package/dist/svgtween.html +29 -0
  187. package/dist/tde.html +28 -0
  188. package/dist/tetris3d.html +25 -0
  189. package/dist/tiles.html +28 -0
  190. package/dist/transforms.html +400 -0
  191. package/dist/tween.html +45 -0
  192. package/dist/visibility.html +33 -0
  193. package/package.json +1 -1
@@ -5111,6 +5111,337 @@ _name = new WeakMap();
5111
5111
  _signature = new WeakMap();
5112
5112
  _coordinates = new WeakMap();
5113
5113
  let Tensor = _Tensor;
5114
+ const CONFIG$1 = {
5115
+ tokens: {
5116
+ // Supported operator aliases. Words are case-insensitive.
5117
+ unaryNot: ["!", "~", "¬", "NOT"],
5118
+ and: ["&", "∧", "AND"],
5119
+ nand: ["NAND"],
5120
+ or: ["|", "∨", "OR"],
5121
+ nor: ["NOR"],
5122
+ xor: ["^", "⊕", "XOR"],
5123
+ xnor: ["XNOR"]
5124
+ },
5125
+ precedence: {
5126
+ // Higher number = higher precedence
5127
+ OR: 1,
5128
+ NOR: 1,
5129
+ XOR: 2,
5130
+ XNOR: 2,
5131
+ AND: 3,
5132
+ NAND: 3,
5133
+ NOT: 4
5134
+ },
5135
+ constants: {
5136
+ true: ["1", "TRUE"],
5137
+ false: ["0", "FALSE"]
5138
+ }
5139
+ };
5140
+ function toBool(v) {
5141
+ return Boolean(v);
5142
+ }
5143
+ function upper(s) {
5144
+ return String(s).toUpperCase();
5145
+ }
5146
+ function tokenize(src) {
5147
+ const out = [];
5148
+ const s = String(src ?? "");
5149
+ let i = 0;
5150
+ const isWs = (c) => c === " " || c === "\n" || c === " " || c === "\r";
5151
+ const isAlpha = (c) => c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c === "_";
5152
+ const isAlnum = (c) => isAlpha(c) || c >= "0" && c <= "9";
5153
+ while (i < s.length) {
5154
+ const c = s[i];
5155
+ if (isWs(c)) {
5156
+ i++;
5157
+ continue;
5158
+ }
5159
+ if (c === "(") {
5160
+ out.push({ type: "lp", value: c });
5161
+ i++;
5162
+ continue;
5163
+ }
5164
+ if (c === ")") {
5165
+ out.push({ type: "rp", value: c });
5166
+ i++;
5167
+ continue;
5168
+ }
5169
+ if (c === "!" || c === "~" || c === "¬" || c === "&" || c === "∧" || c === "|" || c === "∨" || c === "^" || c === "⊕") {
5170
+ out.push({ type: "op", value: c });
5171
+ i++;
5172
+ continue;
5173
+ }
5174
+ if (isAlpha(c) || c >= "0" && c <= "9") {
5175
+ let j = i + 1;
5176
+ while (j < s.length && isAlnum(s[j])) j++;
5177
+ const raw = s.slice(i, j);
5178
+ const u = upper(raw);
5179
+ if (CONFIG$1.constants.true.includes(u) || CONFIG$1.constants.false.includes(u)) {
5180
+ out.push({ type: "const", value: u });
5181
+ } else if (CONFIG$1.tokens.unaryNot.includes(u) || CONFIG$1.tokens.and.includes(u) || CONFIG$1.tokens.nand.includes(u) || CONFIG$1.tokens.or.includes(u) || CONFIG$1.tokens.nor.includes(u) || CONFIG$1.tokens.xor.includes(u) || CONFIG$1.tokens.xnor.includes(u)) {
5182
+ out.push({ type: "op", value: u });
5183
+ } else {
5184
+ out.push({ type: "ident", value: raw });
5185
+ }
5186
+ i = j;
5187
+ continue;
5188
+ }
5189
+ throw new Error(`[BooleanAlgebra] Unexpected character "${c}" at ${i}`);
5190
+ }
5191
+ return out;
5192
+ }
5193
+ function normalizeOperator(op) {
5194
+ const u = upper(op);
5195
+ if (CONFIG$1.tokens.unaryNot.map(upper).includes(u)) return "NOT";
5196
+ if (CONFIG$1.tokens.and.map(upper).includes(u)) return "AND";
5197
+ if (CONFIG$1.tokens.nand.map(upper).includes(u)) return "NAND";
5198
+ if (CONFIG$1.tokens.or.map(upper).includes(u)) return "OR";
5199
+ if (CONFIG$1.tokens.nor.map(upper).includes(u)) return "NOR";
5200
+ if (CONFIG$1.tokens.xor.map(upper).includes(u)) return "XOR";
5201
+ if (CONFIG$1.tokens.xnor.map(upper).includes(u)) return "XNOR";
5202
+ throw new Error(`[BooleanAlgebra] Unknown operator "${op}"`);
5203
+ }
5204
+ function precedence(opName) {
5205
+ return CONFIG$1.precedence[opName] ?? 0;
5206
+ }
5207
+ function parseTokens(tokens) {
5208
+ let idx = 0;
5209
+ const peek = () => tokens[idx];
5210
+ const next = () => tokens[idx++];
5211
+ function parsePrimary() {
5212
+ const t = next();
5213
+ if (!t) throw new Error("[BooleanAlgebra] Unexpected end of input");
5214
+ if (t.type === "const") {
5215
+ const u = upper(t.value);
5216
+ return { type: "const", value: CONFIG$1.constants.true.includes(u) };
5217
+ }
5218
+ if (t.type === "ident") {
5219
+ return { type: "var", name: t.value };
5220
+ }
5221
+ if (t.type === "lp") {
5222
+ const expr = parseExpr(0);
5223
+ const r = next();
5224
+ if (!r || r.type !== "rp") {
5225
+ throw new Error("[BooleanAlgebra] Missing closing ')'");
5226
+ }
5227
+ return expr;
5228
+ }
5229
+ if (t.type === "op") {
5230
+ const opName = normalizeOperator(t.value);
5231
+ if (opName !== "NOT") {
5232
+ throw new Error(`[BooleanAlgebra] Unexpected binary operator "${t.value}"`);
5233
+ }
5234
+ return { type: "not", left: parsePrimary() };
5235
+ }
5236
+ throw new Error(`[BooleanAlgebra] Unexpected token "${t.type}"`);
5237
+ }
5238
+ function parseExpr(minPrec) {
5239
+ let left = parsePrimary();
5240
+ while (true) {
5241
+ const t = peek();
5242
+ if (!t || t.type !== "op") break;
5243
+ const opName = normalizeOperator(t.value);
5244
+ if (opName === "NOT") break;
5245
+ const prec = precedence(opName);
5246
+ if (prec < minPrec) break;
5247
+ next();
5248
+ const right = parseExpr(prec + 1);
5249
+ const type = opName === "AND" ? "and" : opName === "OR" ? "or" : opName === "XOR" ? "xor" : opName === "NAND" ? "nand" : opName === "NOR" ? "nor" : "xnor";
5250
+ left = { type, left, right };
5251
+ }
5252
+ return left;
5253
+ }
5254
+ const ast = parseExpr(0);
5255
+ if (idx < tokens.length) {
5256
+ throw new Error("[BooleanAlgebra] Unexpected trailing tokens");
5257
+ }
5258
+ return ast;
5259
+ }
5260
+ function evalAst(ast, env) {
5261
+ switch (ast.type) {
5262
+ case "const":
5263
+ return Boolean(ast.value);
5264
+ case "var": {
5265
+ const v = (env == null ? void 0 : env[ast.name]) ?? false;
5266
+ return toBool(v);
5267
+ }
5268
+ case "not":
5269
+ return !evalAst(ast.left, env);
5270
+ case "and":
5271
+ return evalAst(ast.left, env) && evalAst(ast.right, env);
5272
+ case "nand":
5273
+ return !(evalAst(ast.left, env) && evalAst(ast.right, env));
5274
+ case "or":
5275
+ return evalAst(ast.left, env) || evalAst(ast.right, env);
5276
+ case "nor":
5277
+ return !(evalAst(ast.left, env) || evalAst(ast.right, env));
5278
+ case "xor": {
5279
+ const a = evalAst(ast.left, env);
5280
+ const b = evalAst(ast.right, env);
5281
+ return a && !b || !a && b;
5282
+ }
5283
+ case "xnor": {
5284
+ const a = evalAst(ast.left, env);
5285
+ const b = evalAst(ast.right, env);
5286
+ return a === b;
5287
+ }
5288
+ default:
5289
+ throw new Error(`[BooleanAlgebra] Unknown AST node type "${ast.type}"`);
5290
+ }
5291
+ }
5292
+ function collectVars(ast, out) {
5293
+ if (!ast) return;
5294
+ if (ast.type === "var" && ast.name) out.add(ast.name);
5295
+ if (ast.left) collectVars(ast.left, out);
5296
+ if (ast.right) collectVars(ast.right, out);
5297
+ }
5298
+ function grayOrder(n) {
5299
+ const size = 1 << n;
5300
+ const out = new Array(size);
5301
+ for (let i = 0; i < size; i++) {
5302
+ out[i] = i ^ i >> 1;
5303
+ }
5304
+ return out;
5305
+ }
5306
+ class BooleanAlgebra {
5307
+ /**
5308
+ * Basic gates.
5309
+ * @param {boolean|number} a
5310
+ * @param {boolean|number} b
5311
+ * @returns {boolean}
5312
+ */
5313
+ static and(a, b) {
5314
+ return toBool(a) && toBool(b);
5315
+ }
5316
+ /**
5317
+ * @param {boolean|number} a
5318
+ * @param {boolean|number} b
5319
+ * @returns {boolean}
5320
+ */
5321
+ static nand(a, b) {
5322
+ return !(toBool(a) && toBool(b));
5323
+ }
5324
+ /**
5325
+ * @param {boolean|number} a
5326
+ * @param {boolean|number} b
5327
+ * @returns {boolean}
5328
+ */
5329
+ static or(a, b) {
5330
+ return toBool(a) || toBool(b);
5331
+ }
5332
+ /**
5333
+ * @param {boolean|number} a
5334
+ * @param {boolean|number} b
5335
+ * @returns {boolean}
5336
+ */
5337
+ static nor(a, b) {
5338
+ return !(toBool(a) || toBool(b));
5339
+ }
5340
+ /**
5341
+ * @param {boolean|number} a
5342
+ * @param {boolean|number} b
5343
+ * @returns {boolean}
5344
+ */
5345
+ static xor(a, b) {
5346
+ const aa = toBool(a);
5347
+ const bb = toBool(b);
5348
+ return aa && !bb || !aa && bb;
5349
+ }
5350
+ /**
5351
+ * @param {boolean|number} a
5352
+ * @param {boolean|number} b
5353
+ * @returns {boolean}
5354
+ */
5355
+ static xnor(a, b) {
5356
+ return toBool(a) === toBool(b);
5357
+ }
5358
+ /**
5359
+ * @param {boolean|number} a
5360
+ * @returns {boolean}
5361
+ */
5362
+ static not(a) {
5363
+ return !toBool(a);
5364
+ }
5365
+ /**
5366
+ * Parse an expression string into an AST.
5367
+ *
5368
+ * Supported operators:
5369
+ * - NOT: `!`, `~`, `¬`, `NOT`
5370
+ * - AND: `&`, `∧`, `AND`
5371
+ * - OR: `|`, `∨`, `OR`
5372
+ * - XOR: `^`, `⊕`, `XOR`
5373
+ * - NAND/NOR/XNOR as words (case-insensitive)
5374
+ *
5375
+ * Constants: `0/1`, `false/true` (case-insensitive)
5376
+ *
5377
+ * @param {string} expr
5378
+ * @returns {BooleanAst}
5379
+ */
5380
+ static parse(expr) {
5381
+ return parseTokens(tokenize(expr));
5382
+ }
5383
+ /**
5384
+ * Evaluate an expression string or AST against an environment.
5385
+ * @param {string|BooleanAst} expr
5386
+ * @param {BooleanEnv} env
5387
+ * @returns {boolean}
5388
+ */
5389
+ static evaluate(expr, env = {}) {
5390
+ const ast = typeof expr === "string" ? BooleanAlgebra.parse(expr) : expr;
5391
+ return evalAst(ast, env);
5392
+ }
5393
+ /**
5394
+ * Get sorted variable names used by an expression.
5395
+ * @param {string|BooleanAst} expr
5396
+ * @returns {string[]}
5397
+ */
5398
+ static variables(expr) {
5399
+ const ast = typeof expr === "string" ? BooleanAlgebra.parse(expr) : expr;
5400
+ const vars = /* @__PURE__ */ new Set();
5401
+ collectVars(ast, vars);
5402
+ return [...vars].sort((a, b) => a.localeCompare(b));
5403
+ }
5404
+ /**
5405
+ * Gray-code ordering for n bits.
5406
+ * @param {number} n
5407
+ * @returns {number[]}
5408
+ */
5409
+ static grayCode(n) {
5410
+ return grayOrder(n);
5411
+ }
5412
+ /**
5413
+ * Build a truth table for an expression.
5414
+ * @param {string|BooleanAst} expr
5415
+ * @param {string[]} [variables] - Optional variable ordering. If omitted, derived from expression.
5416
+ * @param {{ order?: "binary"|"gray" }} [options]
5417
+ * @returns {{
5418
+ * variables: string[],
5419
+ * order: "binary"|"gray",
5420
+ * rows: Array<{ index: number, inputs: Record<string, boolean>, output: boolean }>
5421
+ * }}
5422
+ */
5423
+ static truthTable(expr, variables, options = {}) {
5424
+ const ast = typeof expr === "string" ? BooleanAlgebra.parse(expr) : expr;
5425
+ const vars = variables && variables.length > 0 ? [...variables] : BooleanAlgebra.variables(ast);
5426
+ const n = vars.length;
5427
+ const size = 1 << n;
5428
+ const order = options.order === "gray" ? "gray" : "binary";
5429
+ const indices = order === "gray" ? grayOrder(n) : Array.from({ length: size }, (_, i) => i);
5430
+ const rows = indices.map((idx) => {
5431
+ const inputs = {};
5432
+ for (let bit = 0; bit < n; bit++) {
5433
+ const mask = 1 << n - 1 - bit;
5434
+ inputs[vars[bit]] = Boolean(idx & mask);
5435
+ }
5436
+ return {
5437
+ index: idx,
5438
+ inputs,
5439
+ output: evalAst(ast, inputs)
5440
+ };
5441
+ });
5442
+ return { variables: vars, order, rows };
5443
+ }
5444
+ }
5114
5445
  function flammEmbedding(r, rHorizon, M) {
5115
5446
  if (r <= rHorizon) return 0;
5116
5447
  return Math.sqrt(8 * M * (r - rHorizon));
@@ -16579,6 +16910,190 @@ class Sprite extends GameObject {
16579
16910
  return `[Sprite frames=${this.totalFrames} current=${this.currentFrame} playing=${this.isPlaying}]`;
16580
16911
  }
16581
16912
  }
16913
+ class SpriteSheet extends Sprite {
16914
+ /**
16915
+ * Creates a new SpriteSheet
16916
+ * @param {Game} game - The game instance
16917
+ * @param {Object} options - Configuration options
16918
+ * @param {string} options.src - URL/path to the spritesheet image
16919
+ * @param {number} options.frameWidth - Width of each frame in pixels
16920
+ * @param {number} options.frameHeight - Height of each frame in pixels
16921
+ * @param {number} options.columns - Number of columns in the spritesheet
16922
+ * @param {number} options.rows - Number of rows in the spritesheet
16923
+ * @param {number} [options.frameCount] - Total frames (defaults to columns * rows)
16924
+ * @param {number} [options.startFrame=0] - First frame index to use
16925
+ * @param {number} [options.frameRate=12] - Frames per second
16926
+ * @param {boolean} [options.loop=true] - Whether to loop the animation
16927
+ * @param {boolean} [options.autoPlay=false] - Whether to start playing automatically
16928
+ * @param {boolean} [options.smoothing=false] - Image smoothing (false for pixel art)
16929
+ */
16930
+ constructor(game, options = {}) {
16931
+ super(game, {
16932
+ frameRate: options.frameRate || 12,
16933
+ loop: options.loop !== void 0 ? options.loop : true,
16934
+ autoPlay: false,
16935
+ // Don't autoplay until loaded
16936
+ ...options
16937
+ });
16938
+ this._src = options.src;
16939
+ this._frameWidth = options.frameWidth;
16940
+ this._frameHeight = options.frameHeight;
16941
+ this._columns = options.columns;
16942
+ this._rows = options.rows;
16943
+ this._frameCount = options.frameCount || options.columns * options.rows;
16944
+ this._startFrame = options.startFrame || 0;
16945
+ this._smoothing = options.smoothing !== void 0 ? options.smoothing : false;
16946
+ this._autoPlayAfterLoad = options.autoPlay || false;
16947
+ this._loaded = false;
16948
+ this._loading = false;
16949
+ this._image = null;
16950
+ this._frameCanvases = [];
16951
+ this.width = this._frameWidth;
16952
+ this.height = this._frameHeight;
16953
+ }
16954
+ /**
16955
+ * Static factory method that creates and loads a SpriteSheet
16956
+ * @param {Game} game - The game instance
16957
+ * @param {Object} options - Configuration options (same as constructor)
16958
+ * @returns {Promise<SpriteSheet>} Loaded SpriteSheet instance
16959
+ */
16960
+ static async create(game, options) {
16961
+ const sheet = new SpriteSheet(game, options);
16962
+ await sheet.load();
16963
+ return sheet;
16964
+ }
16965
+ /**
16966
+ * Loads the spritesheet image and creates frame shapes
16967
+ * @returns {Promise<SpriteSheet>} This instance for chaining
16968
+ */
16969
+ async load() {
16970
+ if (this._loaded || this._loading) {
16971
+ return this;
16972
+ }
16973
+ this._loading = true;
16974
+ try {
16975
+ this._image = await this._loadImage(this._src);
16976
+ await this._sliceFrames();
16977
+ this._loaded = true;
16978
+ this._loading = false;
16979
+ if (this._autoPlayAfterLoad) {
16980
+ this.play();
16981
+ }
16982
+ return this;
16983
+ } catch (error) {
16984
+ this._loading = false;
16985
+ console.error("SpriteSheet.load failed:", error);
16986
+ throw error;
16987
+ }
16988
+ }
16989
+ /**
16990
+ * Loads an image from a URL
16991
+ * @private
16992
+ * @param {string} src - Image URL
16993
+ * @returns {Promise<HTMLImageElement>}
16994
+ */
16995
+ _loadImage(src) {
16996
+ return new Promise((resolve, reject) => {
16997
+ const img = new Image();
16998
+ img.onload = () => resolve(img);
16999
+ img.onerror = (e) => reject(new Error(`Failed to load image: ${src}`));
17000
+ img.src = src;
17001
+ });
17002
+ }
17003
+ /**
17004
+ * Slices the spritesheet into individual frame canvases
17005
+ * @private
17006
+ */
17007
+ async _sliceFrames() {
17008
+ const { _image: img, _frameWidth: fw, _frameHeight: fh, _columns: cols } = this;
17009
+ this.clearFrames();
17010
+ this._frameCanvases = [];
17011
+ for (let i = this._startFrame; i < this._startFrame + this._frameCount; i++) {
17012
+ const col = i % cols;
17013
+ const row = Math.floor(i / cols);
17014
+ const sx = col * fw;
17015
+ const sy = row * fh;
17016
+ const frameCanvas = document.createElement("canvas");
17017
+ frameCanvas.width = fw;
17018
+ frameCanvas.height = fh;
17019
+ const ctx = frameCanvas.getContext("2d");
17020
+ ctx.imageSmoothingEnabled = this._smoothing;
17021
+ ctx.drawImage(img, sx, sy, fw, fh, 0, 0, fw, fh);
17022
+ this._frameCanvases.push(frameCanvas);
17023
+ const frameShape = new ImageShape(frameCanvas, {
17024
+ width: fw,
17025
+ height: fh,
17026
+ anchor: "center",
17027
+ smoothing: this._smoothing
17028
+ });
17029
+ this.addFrame(frameShape);
17030
+ }
17031
+ }
17032
+ /**
17033
+ * Gets whether the spritesheet is loaded
17034
+ * @returns {boolean}
17035
+ */
17036
+ get loaded() {
17037
+ return this._loaded;
17038
+ }
17039
+ /**
17040
+ * Gets whether the spritesheet is currently loading
17041
+ * @returns {boolean}
17042
+ */
17043
+ get loading() {
17044
+ return this._loading;
17045
+ }
17046
+ /**
17047
+ * Gets the frame width
17048
+ * @returns {number}
17049
+ */
17050
+ get frameWidth() {
17051
+ return this._frameWidth;
17052
+ }
17053
+ /**
17054
+ * Gets the frame height
17055
+ * @returns {number}
17056
+ */
17057
+ get frameHeight() {
17058
+ return this._frameHeight;
17059
+ }
17060
+ /**
17061
+ * Gets the number of columns in the spritesheet
17062
+ * @returns {number}
17063
+ */
17064
+ get columns() {
17065
+ return this._columns;
17066
+ }
17067
+ /**
17068
+ * Gets the number of rows in the spritesheet
17069
+ * @returns {number}
17070
+ */
17071
+ get rows() {
17072
+ return this._rows;
17073
+ }
17074
+ /**
17075
+ * Override update to skip if not loaded
17076
+ * @param {number} dt - Delta time
17077
+ */
17078
+ update(dt) {
17079
+ if (!this._loaded) return;
17080
+ super.update(dt);
17081
+ }
17082
+ /**
17083
+ * Override draw to skip if not loaded
17084
+ */
17085
+ draw() {
17086
+ if (!this._loaded) return;
17087
+ super.draw();
17088
+ }
17089
+ /**
17090
+ * Creates a string representation
17091
+ * @returns {string}
17092
+ */
17093
+ toString() {
17094
+ return `[SpriteSheet src="${this._src}" frames=${this._frameCount} loaded=${this._loaded}]`;
17095
+ }
17096
+ }
16582
17097
  class Text extends GameObjectShapeWrapper {
16583
17098
  /**
16584
17099
  * Create a Text component
@@ -25473,6 +25988,7 @@ export {
25473
25988
  Arc,
25474
25989
  Arrow,
25475
25990
  BezierShape,
25991
+ BooleanAlgebra,
25476
25992
  Button,
25477
25993
  Camera2D,
25478
25994
  Camera3D,
@@ -25559,6 +26075,7 @@ export {
25559
26075
  Sphere,
25560
26076
  Sphere3D,
25561
26077
  Sprite,
26078
+ SpriteSheet,
25562
26079
  Square,
25563
26080
  Star,
25564
26081
  StateMachine,