@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,49 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Orbit motion animation (circular or elliptical)
|
|
5
|
+
*
|
|
6
|
+
* @param {number} centerX - X coordinate of orbit center
|
|
7
|
+
* @param {number} centerY - Y coordinate of orbit center
|
|
8
|
+
* @param {number} radiusX - X radius of the orbit (horizontal)
|
|
9
|
+
* @param {number} radiusY - Y radius of the orbit (vertical)
|
|
10
|
+
* @param {number} startAngle - Starting angle in radians
|
|
11
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
12
|
+
* @param {number} duration - Duration of one full orbit in seconds
|
|
13
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
14
|
+
* @param {boolean} [clockwise=true] - Direction of orbit
|
|
15
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
16
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
17
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
18
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
19
|
+
*/
|
|
20
|
+
export function orbitV1(
|
|
21
|
+
centerX,
|
|
22
|
+
centerY,
|
|
23
|
+
radiusX,
|
|
24
|
+
radiusY,
|
|
25
|
+
startAngle,
|
|
26
|
+
elapsedTime,
|
|
27
|
+
duration,
|
|
28
|
+
loop = true,
|
|
29
|
+
clockwise = true,
|
|
30
|
+
easingFn = null,
|
|
31
|
+
callbacks = {},
|
|
32
|
+
state = null
|
|
33
|
+
) {
|
|
34
|
+
// Update animation time and apply easing
|
|
35
|
+
const {
|
|
36
|
+
t,
|
|
37
|
+
easedT,
|
|
38
|
+
completed,
|
|
39
|
+
state: newState,
|
|
40
|
+
} = Motion._frame(elapsedTime, duration, loop, easingFn, callbacks, state);
|
|
41
|
+
// Calculate current angle
|
|
42
|
+
const direction = clockwise ? 1 : -1;
|
|
43
|
+
const angle = startAngle + direction * easedT * Math.PI * 2;
|
|
44
|
+
// Convert polar coordinates to Cartesian
|
|
45
|
+
const x = centerX + radiusX * Math.cos(angle);
|
|
46
|
+
const y = centerY + radiusY * Math.sin(angle);
|
|
47
|
+
// Return standardized result
|
|
48
|
+
return Motion.animationResult({ x, y, angle }, t, loop, completed, newState);
|
|
49
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Oscillate between min and max value using sine
|
|
5
|
+
*
|
|
6
|
+
* @param {number} min - Minimum value
|
|
7
|
+
* @param {number} max - Maximum value
|
|
8
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
9
|
+
* @param {number} duration - Duration of one full oscillation in seconds
|
|
10
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
11
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
12
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
13
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
14
|
+
* @returns {Object} Animation result with value and metadata
|
|
15
|
+
*/
|
|
16
|
+
export function oscillateV1(
|
|
17
|
+
min,
|
|
18
|
+
max,
|
|
19
|
+
elapsedTime,
|
|
20
|
+
duration,
|
|
21
|
+
loop = true,
|
|
22
|
+
easingFn = null,
|
|
23
|
+
callbacks = {},
|
|
24
|
+
state = null
|
|
25
|
+
) {
|
|
26
|
+
// Update animation time and apply easing
|
|
27
|
+
const {
|
|
28
|
+
t,
|
|
29
|
+
easedT,
|
|
30
|
+
completed,
|
|
31
|
+
state: newState,
|
|
32
|
+
} = Motion._frame(elapsedTime, duration, loop, easingFn, callbacks, state);
|
|
33
|
+
// Calculate oscillation using sine
|
|
34
|
+
const amplitude = (max - min) / 2;
|
|
35
|
+
const center = min + amplitude;
|
|
36
|
+
const value = center + amplitude * Math.sin(easedT * Math.PI * 2);
|
|
37
|
+
// Return standardized result
|
|
38
|
+
return Motion.animationResult({ value }, t, loop, completed, newState);
|
|
39
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parabolic arc interpolation
|
|
5
|
+
*
|
|
6
|
+
* @param {number} start - Start value
|
|
7
|
+
* @param {number} peak - Peak value
|
|
8
|
+
* @param {number} end - End value
|
|
9
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
10
|
+
* @param {number} duration - Duration of animation in seconds
|
|
11
|
+
* @param {boolean} [loop=false] - Whether animation should loop (restart from beginning)
|
|
12
|
+
* @param {boolean} [yoyo=false] - Whether animation should reverse direction at the end
|
|
13
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
14
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
15
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
16
|
+
* @returns {Object} Animation result with value and metadata
|
|
17
|
+
*/
|
|
18
|
+
export function parabolicV1(
|
|
19
|
+
start,
|
|
20
|
+
peak,
|
|
21
|
+
end,
|
|
22
|
+
elapsedTime,
|
|
23
|
+
duration,
|
|
24
|
+
loop = false,
|
|
25
|
+
yoyo = false,
|
|
26
|
+
easingFn = null,
|
|
27
|
+
callbacks = {},
|
|
28
|
+
state = null
|
|
29
|
+
) {
|
|
30
|
+
// Initialize state if needed
|
|
31
|
+
if (!state) {
|
|
32
|
+
state = {
|
|
33
|
+
started: false,
|
|
34
|
+
loopCount: 0,
|
|
35
|
+
direction: 1, // 1 = forward, -1 = backward (for yoyo)
|
|
36
|
+
lastDirection: 1,
|
|
37
|
+
completed: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Calculate normalized time (0-1)
|
|
41
|
+
let t = duration > 0 ? elapsedTime / duration : 1;
|
|
42
|
+
let completed = false;
|
|
43
|
+
let activeCallbacks = { ...callbacks };
|
|
44
|
+
// Handle yoyo and loop logic
|
|
45
|
+
if (yoyo || loop) {
|
|
46
|
+
// For yoyo + loop, or just loop: calculate cycle
|
|
47
|
+
if (loop) {
|
|
48
|
+
if (yoyo) {
|
|
49
|
+
// For yoyo + loop: full cycle is 2x duration (forward + backward)
|
|
50
|
+
const fullCycle = duration * 2;
|
|
51
|
+
const cycleTime = elapsedTime % fullCycle;
|
|
52
|
+
const cycleCount = Math.floor(elapsedTime / fullCycle);
|
|
53
|
+
|
|
54
|
+
// Determine direction and adjusted t
|
|
55
|
+
const newDirection = cycleTime < duration ? 1 : -1;
|
|
56
|
+
t =
|
|
57
|
+
newDirection === 1 ? cycleTime / duration : 2 - cycleTime / duration;
|
|
58
|
+
|
|
59
|
+
// Check for direction change for callbacks
|
|
60
|
+
if (newDirection !== state.direction) {
|
|
61
|
+
state.direction = newDirection;
|
|
62
|
+
if (state.direction === 1 && activeCallbacks.onLoop) {
|
|
63
|
+
activeCallbacks.onLoop(cycleCount);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Track loop count
|
|
68
|
+
if (cycleCount > state.loopCount) {
|
|
69
|
+
state.loopCount = cycleCount;
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
// Just loop, no yoyo: reset t at each cycle
|
|
73
|
+
t = t % 1;
|
|
74
|
+
|
|
75
|
+
// Track loop count for callbacks
|
|
76
|
+
const newLoopCount = Math.floor(elapsedTime / duration);
|
|
77
|
+
if (newLoopCount > state.loopCount && activeCallbacks.onLoop) {
|
|
78
|
+
activeCallbacks.onLoop(newLoopCount);
|
|
79
|
+
state.loopCount = newLoopCount;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else if (yoyo && !loop) {
|
|
83
|
+
// Yoyo without loop: complete one cycle then stop
|
|
84
|
+
if (t <= 1) {
|
|
85
|
+
// Forward part of the cycle
|
|
86
|
+
state.direction = 1;
|
|
87
|
+
} else if (t <= 2) {
|
|
88
|
+
// Backward part of the cycle
|
|
89
|
+
t = 2 - t;
|
|
90
|
+
state.direction = -1;
|
|
91
|
+
} else {
|
|
92
|
+
// Complete
|
|
93
|
+
t = 0;
|
|
94
|
+
completed = true;
|
|
95
|
+
state.direction = 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// No loop or yoyo: standard behavior
|
|
100
|
+
if (t >= 1) {
|
|
101
|
+
t = 1;
|
|
102
|
+
completed = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Call onStart callback once
|
|
106
|
+
if (!state.started && activeCallbacks.onStart) {
|
|
107
|
+
activeCallbacks.onStart();
|
|
108
|
+
state.started = true;
|
|
109
|
+
}
|
|
110
|
+
// Call onComplete callback once when non-looping animation completes
|
|
111
|
+
if (completed && !state.completed && activeCallbacks.onComplete) {
|
|
112
|
+
activeCallbacks.onComplete();
|
|
113
|
+
state.completed = true;
|
|
114
|
+
}
|
|
115
|
+
// Apply easing if provided
|
|
116
|
+
const easedT = easingFn ? easingFn(t) : t;
|
|
117
|
+
// Calculate quadratic coefficients
|
|
118
|
+
// For a parabola that passes through three points: (0,start), (0.5,peak), (1,end)
|
|
119
|
+
const a = start + end - 2 * peak;
|
|
120
|
+
const b = 2 * (peak - start);
|
|
121
|
+
const c = start;
|
|
122
|
+
// Apply the quadratic formula: a*t^2 + b*t + c
|
|
123
|
+
const value = a * easedT * easedT + b * easedT + c;
|
|
124
|
+
// Update state for next frame
|
|
125
|
+
const newState = {
|
|
126
|
+
...state,
|
|
127
|
+
lastDirection: state.direction,
|
|
128
|
+
completed: completed || state.completed,
|
|
129
|
+
};
|
|
130
|
+
// Return standardized result
|
|
131
|
+
return Motion.animationResult(
|
|
132
|
+
{
|
|
133
|
+
value,
|
|
134
|
+
direction: state.direction, // Include current direction (1 = forward, -1 = backward)
|
|
135
|
+
},
|
|
136
|
+
t,
|
|
137
|
+
loop || (yoyo && !completed),
|
|
138
|
+
completed,
|
|
139
|
+
newState
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
/**
|
|
3
|
+
* Simple patrol animation that moves randomly within a radius
|
|
4
|
+
* Character moves along cardinal directions with waiting periods
|
|
5
|
+
* @function patrolV1
|
|
6
|
+
* @param {number} initialX - Initial X position (center point)
|
|
7
|
+
* @param {number} initialY - Initial Y position (center point)
|
|
8
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
9
|
+
* @param {number} moveTime - Time to spend moving between points
|
|
10
|
+
* @param {number} waitTime - Time to wait at each point
|
|
11
|
+
* @param {number} radius - Maximum distance from center point
|
|
12
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
13
|
+
* @param {Object} [state] - Internal state tracking
|
|
14
|
+
* @returns {Object} Animation result with position and direction
|
|
15
|
+
*
|
|
16
|
+
* @section Metadata
|
|
17
|
+
* @property {boolean} requiresState - Indicates this animation must be called with a persistent state object across frames.
|
|
18
|
+
* This ensures consistent behavior (e.g. waypoint progress, direction, internal timers).
|
|
19
|
+
* You should initialize and reuse a `state` object outside the update loop.
|
|
20
|
+
*/
|
|
21
|
+
export function patrolV1(
|
|
22
|
+
initialX,
|
|
23
|
+
initialY,
|
|
24
|
+
elapsedTime,
|
|
25
|
+
moveTime,
|
|
26
|
+
waitTime,
|
|
27
|
+
radius,
|
|
28
|
+
loop = true,
|
|
29
|
+
state = null
|
|
30
|
+
) {
|
|
31
|
+
// Initialize state if not provided
|
|
32
|
+
if (!state) {
|
|
33
|
+
state = {
|
|
34
|
+
currentX: initialX,
|
|
35
|
+
currentY: initialY,
|
|
36
|
+
targetX: initialX,
|
|
37
|
+
targetY: initialY,
|
|
38
|
+
isWaiting: true,
|
|
39
|
+
waitStartTime: 0,
|
|
40
|
+
moveStartTime: 0,
|
|
41
|
+
moveCount: 0,
|
|
42
|
+
direction: "idle",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Create a simple random function
|
|
46
|
+
const rand = () => Math.random();
|
|
47
|
+
// Calculate time and movement state
|
|
48
|
+
let isWaiting = state.isWaiting;
|
|
49
|
+
let x = state.currentX;
|
|
50
|
+
let y = state.currentY;
|
|
51
|
+
let direction = state.direction;
|
|
52
|
+
// Check if we need to transition between waiting and moving
|
|
53
|
+
if (isWaiting) {
|
|
54
|
+
// Currently waiting
|
|
55
|
+
if (elapsedTime - state.waitStartTime >= waitTime) {
|
|
56
|
+
// Wait period is over, pick a new target point
|
|
57
|
+
isWaiting = false;
|
|
58
|
+
state.moveStartTime = elapsedTime;
|
|
59
|
+
// Choose a new cardinal direction (up, down, left, right)
|
|
60
|
+
const directions = ["up", "down", "left", "right"];
|
|
61
|
+
direction = directions[Math.floor(rand() * 4)];
|
|
62
|
+
// Calculate potential new target based on direction
|
|
63
|
+
let newTargetX = state.currentX;
|
|
64
|
+
let newTargetY = state.currentY;
|
|
65
|
+
// Random move distance (20-80% of radius)
|
|
66
|
+
const moveDistance = radius * (0.2 + rand() * 0.6);
|
|
67
|
+
// handle direction by manipulating corresponding position
|
|
68
|
+
switch (direction) {
|
|
69
|
+
case "up":
|
|
70
|
+
newTargetY = state.currentY - moveDistance;
|
|
71
|
+
break;
|
|
72
|
+
case "down":
|
|
73
|
+
newTargetY = state.currentY + moveDistance;
|
|
74
|
+
break;
|
|
75
|
+
case "left":
|
|
76
|
+
newTargetX = state.currentX - moveDistance;
|
|
77
|
+
break;
|
|
78
|
+
case "right":
|
|
79
|
+
newTargetX = state.currentX + moveDistance;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
// If new target would be outside radius, move toward center instead
|
|
83
|
+
const newDistSq =
|
|
84
|
+
Math.pow(newTargetX - initialX, 2) + Math.pow(newTargetY - initialY, 2);
|
|
85
|
+
if (newDistSq > radius * radius) {
|
|
86
|
+
// Move back toward center
|
|
87
|
+
if (direction === "up" || direction === "down") {
|
|
88
|
+
// Moving vertically, adjust Y
|
|
89
|
+
newTargetY = initialY;
|
|
90
|
+
direction = state.currentY > initialY ? "up" : "down";
|
|
91
|
+
} else {
|
|
92
|
+
// Moving horizontally, adjust X
|
|
93
|
+
newTargetX = initialX;
|
|
94
|
+
direction = state.currentX > initialX ? "left" : "right";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Update State
|
|
98
|
+
state.targetX = newTargetX;
|
|
99
|
+
state.targetY = newTargetY;
|
|
100
|
+
state.direction = direction;
|
|
101
|
+
state.moveCount++;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
// Currently moving
|
|
105
|
+
const moveProgress = (elapsedTime - state.moveStartTime) / moveTime;
|
|
106
|
+
if (moveProgress >= 1) {
|
|
107
|
+
// Reached target, start waiting
|
|
108
|
+
isWaiting = true;
|
|
109
|
+
state.waitStartTime = elapsedTime;
|
|
110
|
+
state.currentX = state.targetX;
|
|
111
|
+
state.currentY = state.targetY;
|
|
112
|
+
direction = "idle";
|
|
113
|
+
} else {
|
|
114
|
+
// Interpolate position
|
|
115
|
+
x = state.currentX + (state.targetX - state.currentX) * moveProgress;
|
|
116
|
+
y = state.currentY + (state.targetY - state.currentY) * moveProgress;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Update state
|
|
120
|
+
state.isWaiting = isWaiting;
|
|
121
|
+
state.direction = direction;
|
|
122
|
+
if (!isWaiting) {
|
|
123
|
+
state.currentX = x;
|
|
124
|
+
state.currentY = y;
|
|
125
|
+
}
|
|
126
|
+
// Calculate t value (0-1)
|
|
127
|
+
const cycleTime = moveTime + waitTime;
|
|
128
|
+
const t = (elapsedTime % cycleTime) / cycleTime;
|
|
129
|
+
// Calculate distance from center
|
|
130
|
+
const distanceFromCenter = Math.sqrt(
|
|
131
|
+
Math.pow(x - initialX, 2) + Math.pow(y - initialY, 2)
|
|
132
|
+
);
|
|
133
|
+
// Return result
|
|
134
|
+
return Motion.animationResult(
|
|
135
|
+
{
|
|
136
|
+
x,
|
|
137
|
+
y,
|
|
138
|
+
moving: !isWaiting,
|
|
139
|
+
direction,
|
|
140
|
+
distanceFromCenter,
|
|
141
|
+
},
|
|
142
|
+
t,
|
|
143
|
+
loop,
|
|
144
|
+
false,
|
|
145
|
+
state
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pendulum animation - swings around a center angle with optional damping
|
|
5
|
+
*
|
|
6
|
+
* @param {number} originAngle - Resting angle in radians (typically 0)
|
|
7
|
+
* @param {number} amplitude - Maximum swing from center (in radians)
|
|
8
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
9
|
+
* @param {number} duration - Time for one full swing (left-right-left)
|
|
10
|
+
* @param {boolean} [loop=true] - Whether the animation loops
|
|
11
|
+
* @param {boolean} [damped=false] - If true, swing slows down over time
|
|
12
|
+
* @param {Function} [easingFn=null] - Optional easing function
|
|
13
|
+
* @param {Object} [callbacks={}] - Optional callback functions
|
|
14
|
+
* @param {Object} [state=null] - Internal state tracking
|
|
15
|
+
* @returns {Object} Animation result with angle and metadata
|
|
16
|
+
*/
|
|
17
|
+
export function pendulumV1(
|
|
18
|
+
originAngle,
|
|
19
|
+
amplitude,
|
|
20
|
+
elapsedTime,
|
|
21
|
+
duration,
|
|
22
|
+
loop = true,
|
|
23
|
+
damped = false,
|
|
24
|
+
easingFn = null,
|
|
25
|
+
callbacks = {},
|
|
26
|
+
state = null
|
|
27
|
+
) {
|
|
28
|
+
const {
|
|
29
|
+
t,
|
|
30
|
+
easedT,
|
|
31
|
+
completed,
|
|
32
|
+
state: newState,
|
|
33
|
+
} = Motion._frame(elapsedTime, duration, loop, null, callbacks, state); // <- remove easing here
|
|
34
|
+
|
|
35
|
+
// Optional decay multiplier (energy loss)
|
|
36
|
+
const decay = damped && !loop ? Math.exp(-4 * t) : 1;
|
|
37
|
+
|
|
38
|
+
// Oscillate with cosine (max at start, zero at center)
|
|
39
|
+
let angle = originAngle + amplitude * Math.cos(easedT * 2 * Math.PI) * decay;
|
|
40
|
+
|
|
41
|
+
// Apply optional easing to the *angle*, not the time
|
|
42
|
+
if (easingFn) {
|
|
43
|
+
const normalized = (angle - originAngle) / (amplitude * decay);
|
|
44
|
+
angle = originAngle + easingFn((normalized + 1) / 2) * amplitude * decay * 2 - amplitude * decay;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return Motion.animationResult({ angle }, t, loop, completed, newState);
|
|
48
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Easing } from "./easing";
|
|
2
|
+
import { Motion } from "./motion";
|
|
3
|
+
import { Tween } from "./tween";
|
|
4
|
+
/**
|
|
5
|
+
* Pulse between min and max value and back
|
|
6
|
+
*
|
|
7
|
+
* @param {number} min - Minimum value
|
|
8
|
+
* @param {number} max - Maximum value
|
|
9
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
10
|
+
* @param {number} duration - Duration of one full pulse in seconds
|
|
11
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
12
|
+
* @param {boolean} [yoyo=false] - Whether to use separate easing for return journey
|
|
13
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
14
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
15
|
+
* @returns {Object} Animation result with value and metadata
|
|
16
|
+
*/
|
|
17
|
+
export function pulseV1(
|
|
18
|
+
min,
|
|
19
|
+
max,
|
|
20
|
+
elapsedTime,
|
|
21
|
+
duration,
|
|
22
|
+
loop = true,
|
|
23
|
+
yoyo = false,
|
|
24
|
+
easingFn = null,
|
|
25
|
+
callbacks = {}
|
|
26
|
+
) {
|
|
27
|
+
// Normalize time (0-1) within current cycle
|
|
28
|
+
let t = elapsedTime / duration;
|
|
29
|
+
let phase = "forward";
|
|
30
|
+
// Handle looping vs. non-looping
|
|
31
|
+
if (loop) {
|
|
32
|
+
// Calculate loop count (for callbacks)
|
|
33
|
+
const loopCount = Math.floor(t);
|
|
34
|
+
// Use only the fractional part for looping (0-1 repeating)
|
|
35
|
+
t = t % 1;
|
|
36
|
+
// Call onLoop callback if provided and we crossed a loop boundary
|
|
37
|
+
if (loopCount > 0 && callbacks.onLoop) {
|
|
38
|
+
callbacks.onLoop(loopCount);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
// Clamp to 1 for non-looping animations
|
|
42
|
+
if (t > 1) t = 1;
|
|
43
|
+
}
|
|
44
|
+
// Call onStart callback if needed
|
|
45
|
+
if (t > 0 && elapsedTime <= duration && callbacks.onStart) {
|
|
46
|
+
callbacks.onStart();
|
|
47
|
+
}
|
|
48
|
+
let value;
|
|
49
|
+
if (yoyo) {
|
|
50
|
+
// Yoyo approach - separate forward and return journeys
|
|
51
|
+
if (t < 0.5) {
|
|
52
|
+
// Forward journey (0 to 0.5 becomes 0 to 1)
|
|
53
|
+
const adjustedT = t * 2;
|
|
54
|
+
// Apply easing if provided
|
|
55
|
+
const easedT = easingFn ? easingFn(adjustedT) : adjustedT;
|
|
56
|
+
// Interpolate from min to max
|
|
57
|
+
value = min + (max - min) * easedT;
|
|
58
|
+
phase = "forward";
|
|
59
|
+
} else {
|
|
60
|
+
// Return journey (0.5 to 1 becomes 0 to 1)
|
|
61
|
+
const adjustedT = (t - 0.5) * 2;
|
|
62
|
+
// Apply easing if provided
|
|
63
|
+
const easedT = easingFn ? easingFn(adjustedT) : adjustedT;
|
|
64
|
+
// Interpolate from max to min
|
|
65
|
+
value = max - (max - min) * easedT;
|
|
66
|
+
phase = "return";
|
|
67
|
+
// Call onYoyoTurn callback at the turning point
|
|
68
|
+
if (t >= 0.5 && t < 0.51 && callbacks.onYoyoTurn) {
|
|
69
|
+
callbacks.onYoyoTurn();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// Standard pulse approach (triangle wave)
|
|
74
|
+
// Apply easing to normalized time if provided
|
|
75
|
+
const easedT = easingFn ? easingFn(t) : t;
|
|
76
|
+
// Convert to 0-1-0 pattern
|
|
77
|
+
const adjusted = easedT < 0.5 ? easedT * 2 : 2 - easedT * 2;
|
|
78
|
+
value = min + (max - min) * adjusted;
|
|
79
|
+
}
|
|
80
|
+
// Check if non-looping animation is complete
|
|
81
|
+
const isDone = !loop && t >= 1;
|
|
82
|
+
// Call onComplete if animation has completed
|
|
83
|
+
if (isDone && callbacks.onComplete) {
|
|
84
|
+
callbacks.onComplete();
|
|
85
|
+
}
|
|
86
|
+
// Return standardized result
|
|
87
|
+
return Motion.animationResult({ value, phase }, t, loop, isDone);
|
|
88
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shake animation with decreasing intensity
|
|
5
|
+
*
|
|
6
|
+
* @param {number} centerX - Center X position
|
|
7
|
+
* @param {number} centerY - Center Y position
|
|
8
|
+
* @param {number} maxOffsetX - Maximum X offset
|
|
9
|
+
* @param {number} maxOffsetY - Maximum Y offset
|
|
10
|
+
* @param {number} frequency - Frequency of shakes
|
|
11
|
+
* @param {number} decay - How quickly the shake decreases (0-1)
|
|
12
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
13
|
+
* @param {number} duration - Duration of animation in seconds
|
|
14
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
15
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
16
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
17
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
18
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
19
|
+
*/
|
|
20
|
+
export function shakeV1(
|
|
21
|
+
centerX,
|
|
22
|
+
centerY,
|
|
23
|
+
maxOffsetX,
|
|
24
|
+
maxOffsetY,
|
|
25
|
+
frequency,
|
|
26
|
+
decay,
|
|
27
|
+
elapsedTime,
|
|
28
|
+
duration,
|
|
29
|
+
loop = false,
|
|
30
|
+
easingFn = null,
|
|
31
|
+
callbacks = {},
|
|
32
|
+
state = null
|
|
33
|
+
) {
|
|
34
|
+
// Update animation time and apply easing
|
|
35
|
+
const {
|
|
36
|
+
t,
|
|
37
|
+
easedT,
|
|
38
|
+
completed,
|
|
39
|
+
state: newState,
|
|
40
|
+
} = Motion._frame(elapsedTime, duration, loop, easingFn, callbacks, state);
|
|
41
|
+
|
|
42
|
+
// Apply decay to reduce shake over time
|
|
43
|
+
const intensity = Math.pow(1 - easedT, decay);
|
|
44
|
+
|
|
45
|
+
// Create randomized but deterministic shake using sine/cosine at different frequencies
|
|
46
|
+
const angleX = easedT * Math.PI * 2 * frequency;
|
|
47
|
+
const angleY = easedT * Math.PI * 2 * frequency * 1.3; // Different frequency for Y
|
|
48
|
+
|
|
49
|
+
// Add multiple sine waves at different frequencies for more organic motion
|
|
50
|
+
const xOffset =
|
|
51
|
+
intensity *
|
|
52
|
+
maxOffsetX *
|
|
53
|
+
(Math.sin(angleX) * 0.6 +
|
|
54
|
+
Math.sin(angleX * 2.5) * 0.3 +
|
|
55
|
+
Math.sin(angleX * 5.6) * 0.1);
|
|
56
|
+
|
|
57
|
+
const yOffset =
|
|
58
|
+
intensity *
|
|
59
|
+
maxOffsetY *
|
|
60
|
+
(Math.cos(angleY) * 0.6 +
|
|
61
|
+
Math.cos(angleY * 2.7) * 0.3 +
|
|
62
|
+
Math.cos(angleY * 6.3) * 0.1);
|
|
63
|
+
|
|
64
|
+
// Make sure we end at the center
|
|
65
|
+
let x = centerX + xOffset;
|
|
66
|
+
let y = centerY + yOffset;
|
|
67
|
+
|
|
68
|
+
// Gradually return to center in the last 10% of the animation
|
|
69
|
+
if (easedT > 0.9) {
|
|
70
|
+
const returnT = (easedT - 0.9) / 0.1;
|
|
71
|
+
x = centerX + xOffset * (1 - returnT);
|
|
72
|
+
y = centerY + yOffset * (1 - returnT);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Return standardized result
|
|
76
|
+
return Motion.animationResult(
|
|
77
|
+
{ x, y, intensity },
|
|
78
|
+
t,
|
|
79
|
+
loop,
|
|
80
|
+
completed,
|
|
81
|
+
newState
|
|
82
|
+
);
|
|
83
|
+
}
|