@guinetik/gcanvas 1.0.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/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- package/vitest.config.js +13 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { Complex } from "./complex";
|
|
2
|
+
|
|
3
|
+
// Helper function to convert hex color to RGB array
|
|
4
|
+
function hexToRgb(hex) {
|
|
5
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
6
|
+
return result
|
|
7
|
+
? [
|
|
8
|
+
parseInt(result[1], 16) / 255,
|
|
9
|
+
parseInt(result[2], 16) / 255,
|
|
10
|
+
parseInt(result[3], 16) / 255,
|
|
11
|
+
1, // Add alpha
|
|
12
|
+
]
|
|
13
|
+
: [0, 0, 0, 1];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function generatePenroseTilingPixels(width = 800, height = 800, options) {
|
|
17
|
+
// Get parameters from options or use defaults
|
|
18
|
+
const {
|
|
19
|
+
divisions = 5,
|
|
20
|
+
zoomType = "in",
|
|
21
|
+
color1 = [255, 0, 0, 255], // Red for thin rhombi (0-255 range)
|
|
22
|
+
color2 = [0, 0, 255, 255], // Blue for thick rhombi (0-255 range)
|
|
23
|
+
color3 = [0, 0, 0, 255], // Black for outlines (0-255 range)
|
|
24
|
+
backgroundColor = [255, 255, 255, 255], // White background (0-255 range)
|
|
25
|
+
} = options || {};
|
|
26
|
+
|
|
27
|
+
// Create pixel data array (RGBA - 4 bytes per pixel)
|
|
28
|
+
const pixels = new Uint8ClampedArray(width * height * 4);
|
|
29
|
+
|
|
30
|
+
// Fill with background color initially
|
|
31
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
32
|
+
pixels[i] = backgroundColor[0]; // R
|
|
33
|
+
pixels[i + 1] = backgroundColor[1]; // G
|
|
34
|
+
pixels[i + 2] = backgroundColor[2]; // B
|
|
35
|
+
pixels[i + 3] = backgroundColor[3] || 255; // A (default to 255 if not provided)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Set up scale
|
|
39
|
+
const scale = zoomType === "in" ? 1 : 2;
|
|
40
|
+
const maxDim = Math.max(width, height);
|
|
41
|
+
const scaleX = maxDim / scale;
|
|
42
|
+
const scaleY = maxDim / scale;
|
|
43
|
+
const translateX = 0.5 * scale;
|
|
44
|
+
const translateY = 0.5 * scale;
|
|
45
|
+
|
|
46
|
+
// Golden ratio
|
|
47
|
+
const phi = (Math.sqrt(5) + 1) / 2;
|
|
48
|
+
|
|
49
|
+
// Base for the tiling pattern
|
|
50
|
+
const base = 5;
|
|
51
|
+
|
|
52
|
+
// Create initial triangles
|
|
53
|
+
let triangles = [];
|
|
54
|
+
for (let i = 0; i < base * 2; i++) {
|
|
55
|
+
const v2 = Complex.fromPolar(1, ((2 * i - 1) * Math.PI) / (base * 2));
|
|
56
|
+
const v3 = Complex.fromPolar(1, ((2 * i + 1) * Math.PI) / (base * 2));
|
|
57
|
+
|
|
58
|
+
if (i % 2 === 0) {
|
|
59
|
+
// Mirror every other triangle
|
|
60
|
+
triangles.push(["thin", new Complex(0), v3, v2]);
|
|
61
|
+
} else {
|
|
62
|
+
triangles.push(["thin", new Complex(0), v2, v3]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Subdivide triangles
|
|
67
|
+
for (let i = 0; i < divisions; i++) {
|
|
68
|
+
const newTriangles = [];
|
|
69
|
+
|
|
70
|
+
for (const [shape, v1, v2, v3] of triangles) {
|
|
71
|
+
if (shape === "thin") {
|
|
72
|
+
// Divide thin rhombus
|
|
73
|
+
const p1 = v1.add(v2.subtract(v1).scale(1 / phi));
|
|
74
|
+
newTriangles.push(["thin", v3, p1, v2]);
|
|
75
|
+
newTriangles.push(["thicc", p1, v3, v1]);
|
|
76
|
+
} else {
|
|
77
|
+
// Divide thicc rhombus
|
|
78
|
+
const p2 = v2.add(v1.subtract(v2).scale(1 / phi));
|
|
79
|
+
const p3 = v2.add(v3.subtract(v2).scale(1 / phi));
|
|
80
|
+
newTriangles.push(["thicc", p3, v3, v1]);
|
|
81
|
+
newTriangles.push(["thicc", p2, p3, v2]);
|
|
82
|
+
newTriangles.push(["thin", p3, p2, v1]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
triangles = newTriangles;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Convert triangle coordinates to screen coordinates
|
|
90
|
+
function worldToScreen(point) {
|
|
91
|
+
const x = Math.floor(
|
|
92
|
+
((point.real * scaleX + translateX * scaleX) * width) / maxDim
|
|
93
|
+
);
|
|
94
|
+
const y = Math.floor(
|
|
95
|
+
((point.imag * scaleY + translateY * scaleY) * height) / maxDim
|
|
96
|
+
);
|
|
97
|
+
return { x, y };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Draw triangles to pixel array using rasterization
|
|
101
|
+
for (const [shape, v1, v2, v3] of triangles) {
|
|
102
|
+
const p1 = worldToScreen(v1);
|
|
103
|
+
const p2 = worldToScreen(v2);
|
|
104
|
+
const p3 = worldToScreen(v3);
|
|
105
|
+
|
|
106
|
+
// Get color based on shape
|
|
107
|
+
const color = shape === "thin" ? color1 : color2;
|
|
108
|
+
|
|
109
|
+
// Rasterize triangle
|
|
110
|
+
fillTriangle(pixels, p1, p2, p3, color, width, height);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Optional: Draw outlines (simplified)
|
|
114
|
+
if (color3 && color3[3] > 0) {
|
|
115
|
+
for (const [shape, v1, v2, v3] of triangles) {
|
|
116
|
+
const p1 = worldToScreen(v1);
|
|
117
|
+
const p2 = worldToScreen(v2);
|
|
118
|
+
const p3 = worldToScreen(v3);
|
|
119
|
+
|
|
120
|
+
// Draw outline with 1px width
|
|
121
|
+
drawLine(pixels, p1, p2, color3, width, height);
|
|
122
|
+
drawLine(pixels, p2, p3, color3, width, height);
|
|
123
|
+
drawLine(pixels, p3, p1, color3, width, height);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return pixels;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Helper function to fill a triangle
|
|
131
|
+
function fillTriangle(pixels, p1, p2, p3, color, width, height) {
|
|
132
|
+
// Sort vertices by y-coordinate
|
|
133
|
+
if (p1.y > p2.y) [p1, p2] = [p2, p1];
|
|
134
|
+
if (p1.y > p3.y) [p1, p3] = [p3, p1];
|
|
135
|
+
if (p2.y > p3.y) [p2, p3] = [p3, p2];
|
|
136
|
+
|
|
137
|
+
// Get RGB values (now directly in 0-255 range)
|
|
138
|
+
const r = color[0];
|
|
139
|
+
const g = color[1];
|
|
140
|
+
const b = color[2];
|
|
141
|
+
const a = color[3] || 255; // Default alpha to 255 if not provided
|
|
142
|
+
|
|
143
|
+
// Flat bottom triangle
|
|
144
|
+
if (p2.y === p3.y) {
|
|
145
|
+
fillFlatBottomTriangle(pixels, p1, p2, p3, r, g, b, a, width, height);
|
|
146
|
+
}
|
|
147
|
+
// Flat top triangle
|
|
148
|
+
else if (p1.y === p2.y) {
|
|
149
|
+
fillFlatTopTriangle(pixels, p1, p2, p3, r, g, b, a, width, height);
|
|
150
|
+
}
|
|
151
|
+
// General triangle - split into flat-bottom and flat-top triangles
|
|
152
|
+
else {
|
|
153
|
+
// Calculate the new vertex
|
|
154
|
+
const p4 = {
|
|
155
|
+
x: Math.floor(p1.x + ((p2.y - p1.y) / (p3.y - p1.y)) * (p3.x - p1.x)),
|
|
156
|
+
y: p2.y
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
fillFlatBottomTriangle(pixels, p1, p2, p4, r, g, b, a, width, height);
|
|
160
|
+
fillFlatTopTriangle(pixels, p2, p4, p3, r, g, b, a, width, height);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Helper to fill a flat-bottom triangle
|
|
165
|
+
function fillFlatBottomTriangle(pixels, p1, p2, p3, r, g, b, a, width, height) {
|
|
166
|
+
const invSlope1 = (p2.x - p1.x) / (p2.y - p1.y || 1); // Avoid division by zero
|
|
167
|
+
const invSlope2 = (p3.x - p1.x) / (p3.y - p1.y || 1);
|
|
168
|
+
|
|
169
|
+
let curx1 = p1.x;
|
|
170
|
+
let curx2 = p1.x;
|
|
171
|
+
|
|
172
|
+
for (let scanlineY = p1.y; scanlineY <= p2.y; scanlineY++) {
|
|
173
|
+
if (scanlineY >= 0 && scanlineY < height) {
|
|
174
|
+
const startX = Math.max(0, Math.min(Math.floor(curx1), width - 1));
|
|
175
|
+
const endX = Math.max(0, Math.min(Math.floor(curx2), width - 1));
|
|
176
|
+
|
|
177
|
+
for (let x = Math.min(startX, endX); x <= Math.max(startX, endX); x++) {
|
|
178
|
+
const index = (scanlineY * width + x) * 4;
|
|
179
|
+
if (index >= 0 && index < pixels.length - 3) {
|
|
180
|
+
pixels[index] = r;
|
|
181
|
+
pixels[index + 1] = g;
|
|
182
|
+
pixels[index + 2] = b;
|
|
183
|
+
pixels[index + 3] = a;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
curx1 += invSlope1;
|
|
189
|
+
curx2 += invSlope2;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Helper to fill a flat-top triangle
|
|
194
|
+
function fillFlatTopTriangle(pixels, p1, p2, p3, r, g, b, a, width, height) {
|
|
195
|
+
const invSlope1 = (p3.x - p1.x) / (p3.y - p1.y || 1);
|
|
196
|
+
const invSlope2 = (p3.x - p2.x) / (p3.y - p2.y || 1);
|
|
197
|
+
|
|
198
|
+
let curx1 = p3.x;
|
|
199
|
+
let curx2 = p3.x;
|
|
200
|
+
|
|
201
|
+
for (let scanlineY = p3.y; scanlineY > p1.y; scanlineY--) {
|
|
202
|
+
if (scanlineY >= 0 && scanlineY < height) {
|
|
203
|
+
curx1 -= invSlope1;
|
|
204
|
+
curx2 -= invSlope2;
|
|
205
|
+
|
|
206
|
+
const startX = Math.max(0, Math.min(Math.floor(curx1), width - 1));
|
|
207
|
+
const endX = Math.max(0, Math.min(Math.floor(curx2), width - 1));
|
|
208
|
+
|
|
209
|
+
for (let x = Math.min(startX, endX); x <= Math.max(startX, endX); x++) {
|
|
210
|
+
const index = (scanlineY * width + x) * 4;
|
|
211
|
+
if (index >= 0 && index < pixels.length - 3) {
|
|
212
|
+
pixels[index] = r;
|
|
213
|
+
pixels[index + 1] = g;
|
|
214
|
+
pixels[index + 2] = b;
|
|
215
|
+
pixels[index + 3] = a;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Helper to draw a line using Bresenham's algorithm
|
|
223
|
+
function drawLine(pixels, p1, p2, color, width, height) {
|
|
224
|
+
// Get RGB values (now directly in 0-255 range)
|
|
225
|
+
const r = color[0];
|
|
226
|
+
const g = color[1];
|
|
227
|
+
const b = color[2];
|
|
228
|
+
const a = color[3] || 255;
|
|
229
|
+
|
|
230
|
+
let x0 = p1.x;
|
|
231
|
+
let y0 = p1.y;
|
|
232
|
+
let x1 = p2.x;
|
|
233
|
+
let y1 = p2.y;
|
|
234
|
+
|
|
235
|
+
const dx = Math.abs(x1 - x0);
|
|
236
|
+
const dy = Math.abs(y1 - y0);
|
|
237
|
+
const sx = x0 < x1 ? 1 : -1;
|
|
238
|
+
const sy = y0 < y1 ? 1 : -1;
|
|
239
|
+
let err = dx - dy;
|
|
240
|
+
|
|
241
|
+
while (true) {
|
|
242
|
+
if (x0 >= 0 && x0 < width && y0 >= 0 && y0 < height) {
|
|
243
|
+
const index = (y0 * width + x0) * 4;
|
|
244
|
+
if (index >= 0 && index < pixels.length - 3) {
|
|
245
|
+
// Simple alpha blending
|
|
246
|
+
const alpha = a / 255;
|
|
247
|
+
pixels[index] = Math.round(pixels[index] * (1 - alpha) + r * alpha);
|
|
248
|
+
pixels[index + 1] = Math.round(pixels[index + 1] * (1 - alpha) + g * alpha);
|
|
249
|
+
pixels[index + 2] = Math.round(pixels[index + 2] * (1 - alpha) + b * alpha);
|
|
250
|
+
pixels[index + 3] = 255; // Full opacity for the line
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (x0 === x1 && y0 === y1) break;
|
|
255
|
+
const e2 = 2 * err;
|
|
256
|
+
if (e2 > -dy) { err -= dy; x0 += sx; }
|
|
257
|
+
if (e2 < dx) { err += dx; y0 += sy; }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quantum mechanics utilities for wave function visualization.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* import { gaussianWavePacket } from './quantum.js';
|
|
6
|
+
*
|
|
7
|
+
* const params = { amplitude: 1, sigma: 0.8, k: 8, omega: 4, velocity: 0.5 };
|
|
8
|
+
* const { psi, envelope } = gaussianWavePacket(x, t, params);
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Complex } from "./complex.js";
|
|
12
|
+
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
// WAVE PACKETS
|
|
15
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Gaussian wave packet: Ψ(x,t) = A * e^(-(x-vt)²/4σ²) * e^(i(kx-ωt))
|
|
19
|
+
*
|
|
20
|
+
* This is the standard form of a localized quantum mechanical wave packet.
|
|
21
|
+
* The Gaussian envelope moves with group velocity v, while the complex
|
|
22
|
+
* phase oscillates with wave number k and frequency ω.
|
|
23
|
+
*
|
|
24
|
+
* @param {number} x - Position
|
|
25
|
+
* @param {number} t - Time
|
|
26
|
+
* @param {Object} params - Wave packet parameters
|
|
27
|
+
* @param {number} params.amplitude - Amplitude A
|
|
28
|
+
* @param {number} params.sigma - Width of Gaussian envelope σ
|
|
29
|
+
* @param {number} params.k - Wave number (controls oscillation frequency)
|
|
30
|
+
* @param {number} params.omega - Angular frequency ω
|
|
31
|
+
* @param {number} params.velocity - Group velocity v
|
|
32
|
+
* @returns {{ psi: Complex, envelope: number }} Wave function and envelope
|
|
33
|
+
*/
|
|
34
|
+
export function gaussianWavePacket(x, t, { amplitude, sigma, k, omega, velocity }) {
|
|
35
|
+
// Center of wave packet moves with group velocity
|
|
36
|
+
const x0 = velocity * t;
|
|
37
|
+
|
|
38
|
+
// Gaussian envelope: A * e^(-(x-x0)²/4σ²)
|
|
39
|
+
const dx = x - x0;
|
|
40
|
+
const envelope = amplitude * Math.exp(-(dx * dx) / (4 * sigma * sigma));
|
|
41
|
+
|
|
42
|
+
// Complex phase: e^(i(kx - ωt))
|
|
43
|
+
const phase = k * x - omega * t;
|
|
44
|
+
const psi = Complex.fromPolar(envelope, phase);
|
|
45
|
+
|
|
46
|
+
return { psi, envelope };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compute just the phase for a plane wave: e^(i(kx - ωt))
|
|
51
|
+
*
|
|
52
|
+
* @param {number} x - Position
|
|
53
|
+
* @param {number} t - Time
|
|
54
|
+
* @param {number} k - Wave number
|
|
55
|
+
* @param {number} omega - Angular frequency
|
|
56
|
+
* @returns {number} Phase angle in radians
|
|
57
|
+
*/
|
|
58
|
+
export function planeWavePhase(x, t, k, omega) {
|
|
59
|
+
return k * x - omega * t;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gaussian envelope only (probability amplitude).
|
|
64
|
+
*
|
|
65
|
+
* |Ψ|² ∝ e^(-(x-x0)²/2σ²)
|
|
66
|
+
*
|
|
67
|
+
* @param {number} x - Position
|
|
68
|
+
* @param {number} x0 - Center of packet
|
|
69
|
+
* @param {number} sigma - Width parameter
|
|
70
|
+
* @param {number} [amplitude=1] - Amplitude
|
|
71
|
+
* @returns {number} Envelope value
|
|
72
|
+
*/
|
|
73
|
+
export function gaussianEnvelope(x, x0, sigma, amplitude = 1) {
|
|
74
|
+
const dx = x - x0;
|
|
75
|
+
return amplitude * Math.exp(-(dx * dx) / (4 * sigma * sigma));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
79
|
+
// WAVE FUNCTION UTILITIES
|
|
80
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Probability density |Ψ|².
|
|
84
|
+
*
|
|
85
|
+
* @param {Complex} psi - Wave function value
|
|
86
|
+
* @returns {number} Probability density
|
|
87
|
+
*/
|
|
88
|
+
export function probabilityDensity(psi) {
|
|
89
|
+
return psi.real * psi.real + psi.imag * psi.imag;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* De Broglie wavelength: λ = 2π/k
|
|
94
|
+
*
|
|
95
|
+
* @param {number} k - Wave number
|
|
96
|
+
* @returns {number} Wavelength
|
|
97
|
+
*/
|
|
98
|
+
export function deBroglieWavelength(k) {
|
|
99
|
+
return (2 * Math.PI) / k;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Group velocity from dispersion relation.
|
|
104
|
+
* For a free particle: v_g = dω/dk = ℏk/m
|
|
105
|
+
*
|
|
106
|
+
* @param {number} k - Wave number
|
|
107
|
+
* @param {number} omega - Angular frequency
|
|
108
|
+
* @param {number} [dk=0.01] - Step for numerical derivative
|
|
109
|
+
* @returns {number} Group velocity
|
|
110
|
+
*/
|
|
111
|
+
export function groupVelocity(k, omega, dk = 0.01) {
|
|
112
|
+
// For visualization, we typically use v_g = ω/k (phase velocity)
|
|
113
|
+
// or a custom dispersion relation
|
|
114
|
+
return omega / k;
|
|
115
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎲 Random - A utility class for game-friendly pseudorandom operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides a variety of methods for positioning, noise, variance, and procedural randomness.
|
|
5
|
+
* Designed to simplify natural placement, movement, and behaviors across the GCanvas engine.
|
|
6
|
+
*/
|
|
7
|
+
export class Random {
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {'centered'|'topleft'} CoordinateSystem
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Random point centered in the area with symmetric spread.
|
|
14
|
+
* Spawns across the whole area, not just positive quadrant.
|
|
15
|
+
* @param {number} x - X coordinate (center or top-left depending on coordSystem)
|
|
16
|
+
* @param {number} y - Y coordinate (center or top-left depending on coordSystem)
|
|
17
|
+
* @param {number} width - Area width
|
|
18
|
+
* @param {number} height - Area height
|
|
19
|
+
* @param {number} spread - Controls how far from center (default: 1 = full area)
|
|
20
|
+
* @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
|
|
21
|
+
* @returns {{x: number, y: number}}
|
|
22
|
+
*/
|
|
23
|
+
static symmetric(x, y, width, height, spread = 1, coordSystem = 'topleft') {
|
|
24
|
+
const cx = coordSystem === 'centered' ? x : x + width / 2;
|
|
25
|
+
const cy = coordSystem === 'centered' ? y : y + height / 2;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
x: cx + (Math.random() - 0.5) * width * spread,
|
|
29
|
+
y: cy + (Math.random() - 0.5) * height * spread,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Uniformly random point within a given area
|
|
35
|
+
* @param {number} x - X coordinate (center or top-left depending on coordSystem)
|
|
36
|
+
* @param {number} y - Y coordinate (center or top-left depending on coordSystem)
|
|
37
|
+
* @param {number} width - Width of the area
|
|
38
|
+
* @param {number} height - Height of the area
|
|
39
|
+
* @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
|
|
40
|
+
* @returns {{x: number, y: number}}
|
|
41
|
+
*/
|
|
42
|
+
static pointInBox(x, y, width, height, coordSystem = 'topleft') {
|
|
43
|
+
if (coordSystem === 'centered') {
|
|
44
|
+
return {
|
|
45
|
+
x: x + (Math.random() - 0.5) * width,
|
|
46
|
+
y: y + (Math.random() - 0.5) * height,
|
|
47
|
+
};
|
|
48
|
+
} else {
|
|
49
|
+
return {
|
|
50
|
+
x: x + Math.random() * width,
|
|
51
|
+
y: y + Math.random() * height,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Random point centered around specified point, with uniform variance
|
|
58
|
+
* @param {number} x - X coordinate (center or top-left depending on coordSystem)
|
|
59
|
+
* @param {number} y - Y coordinate (center or top-left depending on coordSystem)
|
|
60
|
+
* @param {number} width - Width of the area
|
|
61
|
+
* @param {number} height - Height of the area
|
|
62
|
+
* @param {number} variance - Max deviation from center
|
|
63
|
+
* @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
|
|
64
|
+
* @returns {{x: number, y: number}}
|
|
65
|
+
*/
|
|
66
|
+
static centered(x, y, width, height, variance = 50, coordSystem = 'topleft') {
|
|
67
|
+
const cx = coordSystem === 'centered' ? x : x + width / 2;
|
|
68
|
+
const cy = coordSystem === 'centered' ? y : y + height / 2;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
x: cx + (Math.random() - 0.5) * 2 * variance,
|
|
72
|
+
y: cy + (Math.random() - 0.5) * 2 * variance,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gaussian (bell-curve) distributed point around center
|
|
78
|
+
* @param {number} x - X coordinate (center or top-left depending on coordSystem)
|
|
79
|
+
* @param {number} y - Y coordinate (center or top-left depending on coordSystem)
|
|
80
|
+
* @param {number} width - Width of the area
|
|
81
|
+
* @param {number} height - Height of the area
|
|
82
|
+
* @param {number} stdDev - Standard deviation (spread)
|
|
83
|
+
* @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
|
|
84
|
+
* @returns {{x: number, y: number}}
|
|
85
|
+
*/
|
|
86
|
+
static gaussian(x, y, width, height, stdDev = 40, coordSystem = 'topleft') {
|
|
87
|
+
const cx = coordSystem === 'centered' ? x : x + width / 2;
|
|
88
|
+
const cy = coordSystem === 'centered' ? y : y + height / 2;
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
x: cx + Random._gaussian(0, stdDev),
|
|
92
|
+
y: cy + Random._gaussian(0, stdDev),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Polar random point around center, within radius
|
|
98
|
+
* @param {number} x - X coordinate (center or top-left depending on coordSystem)
|
|
99
|
+
* @param {number} y - Y coordinate (center or top-left depending on coordSystem)
|
|
100
|
+
* @param {number} width - Width of area
|
|
101
|
+
* @param {number} height - Height of area
|
|
102
|
+
* @param {number} radius - Maximum radial distance
|
|
103
|
+
* @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
|
|
104
|
+
* @returns {{x: number, y: number}}
|
|
105
|
+
*/
|
|
106
|
+
static radial(x, y, width, height, radius = 100, coordSystem = 'topleft') {
|
|
107
|
+
const cx = coordSystem === 'centered' ? x : x + width / 2;
|
|
108
|
+
const cy = coordSystem === 'centered' ? y : y + height / 2;
|
|
109
|
+
|
|
110
|
+
const angle = Math.random() * Math.PI * 2;
|
|
111
|
+
const r = Math.random() * radius;
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
x: cx + Math.cos(angle) * r,
|
|
115
|
+
y: cy + Math.sin(angle) * r,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Choose a random item from an array
|
|
121
|
+
* @template T
|
|
122
|
+
* @param {T[]} array
|
|
123
|
+
* @returns {T}
|
|
124
|
+
*/
|
|
125
|
+
static pick(array) {
|
|
126
|
+
return array[Math.floor(Math.random() * array.length)];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Picks a random item from the array that is NOT equal to the excluded item.
|
|
131
|
+
* Returns `undefined` if no valid alternative exists.
|
|
132
|
+
* @template T
|
|
133
|
+
* @param {T[]} array - Source array
|
|
134
|
+
* @param {T} exclude - Value to exclude from selection
|
|
135
|
+
* @returns {T|undefined}
|
|
136
|
+
*/
|
|
137
|
+
static pickOther(array, exclude) {
|
|
138
|
+
const filtered = array.filter((item) => item !== exclude);
|
|
139
|
+
if (filtered.length === 0) return undefined;
|
|
140
|
+
return Random.pick(filtered);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Random float between min and max
|
|
145
|
+
* @param {number} min
|
|
146
|
+
* @param {number} max
|
|
147
|
+
* @returns {number}
|
|
148
|
+
*/
|
|
149
|
+
static float(min, max) {
|
|
150
|
+
return min + Math.random() * (max - min);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Random integer between min and max (inclusive)
|
|
155
|
+
* @param {number} min
|
|
156
|
+
* @param {number} max
|
|
157
|
+
* @returns {number}
|
|
158
|
+
*/
|
|
159
|
+
static int(min, max) {
|
|
160
|
+
return Math.floor(Random.float(min, max + 1));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Chance roll: returns true with given probability (0 to 1)
|
|
165
|
+
* @param {number} probability
|
|
166
|
+
* @returns {boolean}
|
|
167
|
+
*/
|
|
168
|
+
static chance(probability = 0.5) {
|
|
169
|
+
return Math.random() < probability;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Flip a coin (true or false, 50/50)
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
static coin() {
|
|
177
|
+
return Math.random() < 0.5;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Internal Gaussian random generator (Box-Muller transform)
|
|
182
|
+
* @param {number} mean
|
|
183
|
+
* @param {number} stdDev
|
|
184
|
+
* @returns {number}
|
|
185
|
+
* @private
|
|
186
|
+
*/
|
|
187
|
+
static _gaussian(mean = 0, stdDev = 1) {
|
|
188
|
+
let u = 1 - Math.random();
|
|
189
|
+
let v = 1 - Math.random();
|
|
190
|
+
return (
|
|
191
|
+
mean +
|
|
192
|
+
stdDev * Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|