@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,210 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Waypoint is a patrol animation that follows a path of waypoints with proper waiting periods
|
|
5
|
+
* Moves characters along cardinal directions (horizontal and vertical movement)
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} target - Object with x,y properties (not used for position calculation)
|
|
8
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
9
|
+
* @param {Array<Array<number>>} waypoints - Array of waypoints [[x1,y1], [x2,y2], ...]
|
|
10
|
+
* @param {number} speed - Movement speed in units per second
|
|
11
|
+
* @param {number} waitTime - Time to wait at each waypoint in seconds
|
|
12
|
+
* @param {boolean} [loop=true] - Whether patrol should loop back to start
|
|
13
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
14
|
+
* @param {Function} [callbacks.onWaypointReached] - Called when reaching a waypoint
|
|
15
|
+
* @param {Function} [callbacks.onWaitStart] - Called when starting to wait at a waypoint
|
|
16
|
+
* @param {Function} [callbacks.onWaitEnd] - Called when done waiting at a waypoint
|
|
17
|
+
* @param {Function} [callbacks.onPatrolComplete] - Called when patrol is complete (non-looping only)
|
|
18
|
+
* @param {Object} [state] - Internal state tracking
|
|
19
|
+
* @returns {Object} Animation result with position and patrol metadata
|
|
20
|
+
*/
|
|
21
|
+
export function waypointV1(
|
|
22
|
+
target,
|
|
23
|
+
elapsedTime,
|
|
24
|
+
waypoints,
|
|
25
|
+
speed,
|
|
26
|
+
waitTime,
|
|
27
|
+
loop = true,
|
|
28
|
+
callbacks = {},
|
|
29
|
+
state = null
|
|
30
|
+
) {
|
|
31
|
+
// Validate waypoints
|
|
32
|
+
if (!waypoints || !Array.isArray(waypoints) || waypoints.length < 2) {
|
|
33
|
+
console.warn("Patrol animation requires at least 2 waypoints");
|
|
34
|
+
return Motion._createResult(
|
|
35
|
+
{ x: 0, y: 0, moving: false, direction: "idle", waypoint: 0 },
|
|
36
|
+
0,
|
|
37
|
+
false,
|
|
38
|
+
true
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
// Initialize state if not provided
|
|
42
|
+
if (!state) {
|
|
43
|
+
state = {
|
|
44
|
+
currentWaypoint: 0,
|
|
45
|
+
nextWaypoint: 1,
|
|
46
|
+
isWaiting: true,
|
|
47
|
+
waitStartTime: 0,
|
|
48
|
+
lastWaypointTime: 0,
|
|
49
|
+
lastWaypointReached: -1,
|
|
50
|
+
completed: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Calculate total patrol path length
|
|
54
|
+
let totalPathLength = 0;
|
|
55
|
+
for (let i = 0; i < waypoints.length; i++) {
|
|
56
|
+
const nextIndex = (i + 1) % waypoints.length;
|
|
57
|
+
if (!loop && i === waypoints.length - 1) break;
|
|
58
|
+
|
|
59
|
+
const dx = waypoints[nextIndex][0] - waypoints[i][0];
|
|
60
|
+
const dy = waypoints[nextIndex][1] - waypoints[i][1];
|
|
61
|
+
totalPathLength += Math.abs(dx) + Math.abs(dy); // Manhattan distance for cardinal movement
|
|
62
|
+
}
|
|
63
|
+
// Calculate total cycle time
|
|
64
|
+
const moveTime = totalPathLength / speed;
|
|
65
|
+
const totalWaitTime = waitTime * waypoints.length;
|
|
66
|
+
const cycleTime = moveTime + totalWaitTime;
|
|
67
|
+
// Normalize time for looping
|
|
68
|
+
let normalizedTime = elapsedTime;
|
|
69
|
+
if (loop) {
|
|
70
|
+
normalizedTime = elapsedTime % cycleTime;
|
|
71
|
+
} else {
|
|
72
|
+
normalizedTime = Math.min(elapsedTime, cycleTime);
|
|
73
|
+
}
|
|
74
|
+
// Calculate current t value (0-1)
|
|
75
|
+
const t = normalizedTime / cycleTime;
|
|
76
|
+
// Process patrol logic to find current position
|
|
77
|
+
let timeRemaining = normalizedTime;
|
|
78
|
+
let currentWaypoint = 0;
|
|
79
|
+
let nextWaypoint = 1;
|
|
80
|
+
let isWaiting = true;
|
|
81
|
+
let waitProgress = 0;
|
|
82
|
+
let segmentProgress = 0;
|
|
83
|
+
let completed = false;
|
|
84
|
+
// Start at the first waypoint and wait
|
|
85
|
+
if (timeRemaining < waitTime) {
|
|
86
|
+
// Still waiting at the first waypoint
|
|
87
|
+
waitProgress = timeRemaining / waitTime;
|
|
88
|
+
currentWaypoint = 0;
|
|
89
|
+
nextWaypoint = 1;
|
|
90
|
+
isWaiting = true;
|
|
91
|
+
} else {
|
|
92
|
+
// Move through the waypoints
|
|
93
|
+
timeRemaining -= waitTime;
|
|
94
|
+
for (let i = 0; i < waypoints.length; i++) {
|
|
95
|
+
// Check if we've reached the last waypoint in non-looping mode
|
|
96
|
+
if (!loop && i === waypoints.length - 1) {
|
|
97
|
+
currentWaypoint = i;
|
|
98
|
+
nextWaypoint = i;
|
|
99
|
+
isWaiting = true;
|
|
100
|
+
waitProgress = 1;
|
|
101
|
+
completed = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
//
|
|
105
|
+
const nextIndex = (i + 1) % waypoints.length;
|
|
106
|
+
// Calculate segment distance (using Manhattan distance for cardinal movement)
|
|
107
|
+
const dx = waypoints[nextIndex][0] - waypoints[i][0];
|
|
108
|
+
const dy = waypoints[nextIndex][1] - waypoints[i][1];
|
|
109
|
+
const segmentLength = Math.abs(dx) + Math.abs(dy);
|
|
110
|
+
// Time to move through this segment
|
|
111
|
+
const segmentTime = segmentLength / speed;
|
|
112
|
+
// Check if we're on this segment
|
|
113
|
+
if (timeRemaining < segmentTime) {
|
|
114
|
+
currentWaypoint = i;
|
|
115
|
+
nextWaypoint = nextIndex;
|
|
116
|
+
isWaiting = false;
|
|
117
|
+
segmentProgress = timeRemaining / segmentTime;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
// Move to next segment
|
|
121
|
+
timeRemaining -= segmentTime;
|
|
122
|
+
// Check if we're waiting at the next waypoint
|
|
123
|
+
if (timeRemaining < waitTime) {
|
|
124
|
+
currentWaypoint = nextIndex;
|
|
125
|
+
nextWaypoint = (nextIndex + 1) % waypoints.length;
|
|
126
|
+
isWaiting = true;
|
|
127
|
+
waitProgress = timeRemaining / waitTime;
|
|
128
|
+
// If this is a new waypoint reached, trigger callback
|
|
129
|
+
if (state.lastWaypointReached !== currentWaypoint) {
|
|
130
|
+
if (callbacks.onWaypointReached) {
|
|
131
|
+
callbacks.onWaypointReached(currentWaypoint);
|
|
132
|
+
}
|
|
133
|
+
// callbacks
|
|
134
|
+
if (callbacks.onWaitStart) {
|
|
135
|
+
callbacks.onWaitStart(currentWaypoint);
|
|
136
|
+
}
|
|
137
|
+
state.lastWaypointReached = currentWaypoint;
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
timeRemaining -= waitTime;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Calculate position and direction
|
|
146
|
+
let x, y, direction;
|
|
147
|
+
if (isWaiting || completed) {
|
|
148
|
+
// Use the current waypoint position
|
|
149
|
+
x = waypoints[currentWaypoint][0];
|
|
150
|
+
y = waypoints[currentWaypoint][1];
|
|
151
|
+
direction = "idle";
|
|
152
|
+
// If we just finished waiting, trigger callback
|
|
153
|
+
if (!state.isWaiting && isWaiting && callbacks.onWaitEnd) {
|
|
154
|
+
callbacks.onWaitEnd(currentWaypoint);
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
// We're moving between waypoints
|
|
158
|
+
const current = waypoints[currentWaypoint];
|
|
159
|
+
const next = waypoints[nextWaypoint];
|
|
160
|
+
// Calculate movement along cardinal directions
|
|
161
|
+
// First move horizontally, then vertically
|
|
162
|
+
const dx = next[0] - current[0];
|
|
163
|
+
const dy = next[1] - current[1];
|
|
164
|
+
// Determine if we're moving horizontally or vertically first
|
|
165
|
+
// Here we prioritize horizontal movement, but you could make this configurable
|
|
166
|
+
const totalDistance = Math.abs(dx) + Math.abs(dy);
|
|
167
|
+
const horizontalRatio = Math.abs(dx) / totalDistance;
|
|
168
|
+
// Calculate position
|
|
169
|
+
if (segmentProgress <= horizontalRatio && dx !== 0) {
|
|
170
|
+
// Moving horizontally
|
|
171
|
+
const horizontalProgress = segmentProgress / horizontalRatio;
|
|
172
|
+
x = current[0] + dx * horizontalProgress;
|
|
173
|
+
y = current[1];
|
|
174
|
+
direction = dx > 0 ? "right" : "left";
|
|
175
|
+
} else {
|
|
176
|
+
// Moving vertically
|
|
177
|
+
const verticalProgress =
|
|
178
|
+
(segmentProgress - horizontalRatio) / (1 - horizontalRatio);
|
|
179
|
+
x = next[0]; // Horizontal movement is complete
|
|
180
|
+
y = current[1] + dy * verticalProgress;
|
|
181
|
+
direction = dy > 0 ? "down" : "up";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Update state for next call
|
|
185
|
+
state.currentWaypoint = currentWaypoint;
|
|
186
|
+
state.nextWaypoint = nextWaypoint;
|
|
187
|
+
state.isWaiting = isWaiting;
|
|
188
|
+
// Call completion callback if needed
|
|
189
|
+
if (!state.completed && completed && callbacks.onPatrolComplete) {
|
|
190
|
+
callbacks.onPatrolComplete();
|
|
191
|
+
state.completed = true;
|
|
192
|
+
}
|
|
193
|
+
// Return result with patrol-specific metadata
|
|
194
|
+
return Motion.animationResult(
|
|
195
|
+
{
|
|
196
|
+
x,
|
|
197
|
+
y,
|
|
198
|
+
moving: !isWaiting,
|
|
199
|
+
waiting: isWaiting,
|
|
200
|
+
waitProgress: isWaiting ? waitProgress : 0,
|
|
201
|
+
direction,
|
|
202
|
+
waypoint: currentWaypoint,
|
|
203
|
+
nextWaypoint,
|
|
204
|
+
},
|
|
205
|
+
t,
|
|
206
|
+
loop,
|
|
207
|
+
completed,
|
|
208
|
+
state
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { PainterColors } from "./painter.colors";
|
|
2
|
+
export { PainterEffects } from "./painter.effects";
|
|
3
|
+
export { PainterImages } from "./painter.img";
|
|
4
|
+
export { PainterLines } from "./painter.lines";
|
|
5
|
+
export { PainterOpacity } from "./painter.opacity";
|
|
6
|
+
export { PainterShapes } from "./painter.shapes";
|
|
7
|
+
export { PainterText } from "./painter.text";
|
|
8
|
+
export { Painter } from "./painter";
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { Painter } from "./painter";
|
|
2
|
+
|
|
3
|
+
export class PainterColors {
|
|
4
|
+
/**
|
|
5
|
+
* Fill the current path
|
|
6
|
+
* @param {string|CanvasGradient} [color] - Fill color
|
|
7
|
+
* @returns {void}
|
|
8
|
+
*/
|
|
9
|
+
static fill(color) {
|
|
10
|
+
Painter.logger.log("PainterColors.fill - before:",
|
|
11
|
+
Painter.ctx.fillStyle, "setting to:", color);
|
|
12
|
+
|
|
13
|
+
// Store original
|
|
14
|
+
const originalFill = Painter.ctx.fillStyle;
|
|
15
|
+
|
|
16
|
+
// Set new color
|
|
17
|
+
Painter.ctx.fillStyle = color;
|
|
18
|
+
|
|
19
|
+
// Fill
|
|
20
|
+
Painter.ctx.fill();
|
|
21
|
+
|
|
22
|
+
// Log after
|
|
23
|
+
Painter.logger.log("PainterColors.fill - after:", Painter.ctx.fillStyle);
|
|
24
|
+
|
|
25
|
+
// Maybe the issue is the fill() call itself?
|
|
26
|
+
// Try uncommenting this to restore the original fillStyle
|
|
27
|
+
// Painter.ctx.fillStyle = originalFill;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Set stroke options
|
|
31
|
+
* @param {*} options The options for the stroke
|
|
32
|
+
*/
|
|
33
|
+
static strokeOptions(options) {
|
|
34
|
+
if (options.color) Painter.ctx.strokeStyle = options.color;
|
|
35
|
+
if (options.lineWidth !== undefined)
|
|
36
|
+
Painter.ctx.lineWidth = options.lineWidth;
|
|
37
|
+
if (options.lineCap) Painter.ctx.lineCap = options.lineCap;
|
|
38
|
+
if (options.lineJoin) Painter.ctx.lineJoin = options.lineJoin;
|
|
39
|
+
if (options.strokeStyle) Painter.ctx.strokeStyle = options.strokeStyle;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Stroke the current path
|
|
43
|
+
* @param {string|CanvasGradient} [color] - Stroke color
|
|
44
|
+
* @param {number} [lineWidth] - Line width
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
static stroke(color, lineWidth) {
|
|
48
|
+
if (color) Painter.ctx.strokeStyle = color;
|
|
49
|
+
if (lineWidth !== undefined) Painter.ctx.lineWidth = lineWidth;
|
|
50
|
+
Painter.ctx.stroke();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Set fill color
|
|
55
|
+
* @param {string|CanvasGradient} color - Fill color
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
static setFillColor(color) {
|
|
59
|
+
Painter.ctx.fillStyle = color;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Set stroke color
|
|
64
|
+
* @param {string|CanvasGradient} color - Stroke color
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
static setStrokeColor(color) {
|
|
68
|
+
Painter.ctx.strokeStyle = color;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generate a random pleasing color in RGB format
|
|
73
|
+
* @returns {Array<number>} RGB color array [r, g, b]
|
|
74
|
+
*/
|
|
75
|
+
static randomColorRGB() {
|
|
76
|
+
// Generate vibrant, pleasing colors by using HSL first
|
|
77
|
+
// then converting to RGB
|
|
78
|
+
// Random hue (0-360)
|
|
79
|
+
const hue = Math.floor(Math.random() * 360);
|
|
80
|
+
// High saturation for vibrant colors (70-100%)
|
|
81
|
+
const saturation = 70 + Math.floor(Math.random() * 30);
|
|
82
|
+
// Medium-high lightness for visibility (50-70%)
|
|
83
|
+
const lightness = 50 + Math.floor(Math.random() * 20);
|
|
84
|
+
// Convert HSL to RGB
|
|
85
|
+
return Painter.colors.hslToRgb(hue, saturation, lightness);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static randomColorRGBA(alpha = 255) {
|
|
89
|
+
const [r, g, b] = this.randomColorRGB();
|
|
90
|
+
return [r, g, b, alpha];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static randomColorHSL() {
|
|
94
|
+
return `hsl(${Math.random() * 360}, 100%, 50%)`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static randomColorHSL_RGBA(alpha = 255) {
|
|
98
|
+
const h = Math.random() * 360;
|
|
99
|
+
const s = 60 + Math.random() * 40; // 60–100%
|
|
100
|
+
const l = 40 + Math.random() * 40; // 40–80%
|
|
101
|
+
const [r, g, b] = Painter.colors.hslToRgb(h, s, l);
|
|
102
|
+
return [r, g, b, alpha];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static randomColorHEX() {
|
|
106
|
+
let n = (Math.random() * 0xfffff * 1000000).toString(16);
|
|
107
|
+
return "#" + n.slice(0, 6);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static parseColorString(str) {
|
|
111
|
+
str = str.trim().toLowerCase();
|
|
112
|
+
|
|
113
|
+
// 1) Check if it's hsl(...) form
|
|
114
|
+
if (str.startsWith("hsl")) {
|
|
115
|
+
// e.g. "hsl(130, 100%, 50%)"
|
|
116
|
+
// Remove "hsl(" and ")" => "130, 100%, 50%"
|
|
117
|
+
const inner = str.replace(/hsla?\(|\)/g, "");
|
|
118
|
+
const [hue, satPercent, lightPercent] = inner
|
|
119
|
+
.split(",")
|
|
120
|
+
.map((c) => c.trim());
|
|
121
|
+
const h = parseFloat(hue);
|
|
122
|
+
const s = parseFloat(satPercent) / 100;
|
|
123
|
+
const l = parseFloat(lightPercent) / 100;
|
|
124
|
+
return Painter.colors.hslToRgb(h, s, l); // Convert HSL->RGB
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 2) Check if it's #RRGGBB
|
|
128
|
+
if (str.startsWith("#")) {
|
|
129
|
+
// e.g. "#ff00ff" => r=255,g=0,b=255
|
|
130
|
+
return hexToRgb(str);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 3) If it's rgb(...) form, parse that
|
|
134
|
+
if (str.startsWith("rgb")) {
|
|
135
|
+
// e.g. "rgb(255, 128, 50)"
|
|
136
|
+
// Remove "rgb(" + ")"
|
|
137
|
+
const inner = str.replace(/rgba?\(|\)/g, "");
|
|
138
|
+
const [r, g, b] = inner.split(",").map((x) => parseInt(x.trim()));
|
|
139
|
+
return [r, g, b];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Fallback: assume black
|
|
143
|
+
return [0, 0, 0];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Convert [r,g,b] => "rgb(r, g, b)" string
|
|
148
|
+
*/
|
|
149
|
+
static rgbArrayToCSS([r, g, b]) {
|
|
150
|
+
return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Convert HSL => [r,g,b] (0..255).
|
|
155
|
+
* Formulas from standard color conversion references.
|
|
156
|
+
*/
|
|
157
|
+
static hslToRgb(h, s, l) {
|
|
158
|
+
s /= 100;
|
|
159
|
+
l /= 100;
|
|
160
|
+
const k = n => (n + h / 30) % 12;
|
|
161
|
+
const a = s * Math.min(l, 1 - l);
|
|
162
|
+
const f = n =>
|
|
163
|
+
l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
|
164
|
+
return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static rgbToHsl(r, g, b) {
|
|
168
|
+
r /= 255;
|
|
169
|
+
g /= 255;
|
|
170
|
+
b /= 255;
|
|
171
|
+
|
|
172
|
+
const max = Math.max(r, g, b);
|
|
173
|
+
const min = Math.min(r, g, b);
|
|
174
|
+
const delta = max - min;
|
|
175
|
+
|
|
176
|
+
let h = 0,
|
|
177
|
+
s = 0,
|
|
178
|
+
l = (max + min) / 2;
|
|
179
|
+
|
|
180
|
+
if (delta !== 0) {
|
|
181
|
+
s = delta / (1 - Math.abs(2 * l - 1));
|
|
182
|
+
switch (max) {
|
|
183
|
+
case r:
|
|
184
|
+
h = 60 * (((g - b) / delta + 6) % 6);
|
|
185
|
+
break;
|
|
186
|
+
case g:
|
|
187
|
+
h = 60 * ((b - r) / delta + 2);
|
|
188
|
+
break;
|
|
189
|
+
case b:
|
|
190
|
+
h = 60 * ((r - g) / delta + 4);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return [h % 360, s, l];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Convert a hex color like "#ff00ff" => [255, 0, 255].
|
|
200
|
+
*/
|
|
201
|
+
static hexToRgb(hex) {
|
|
202
|
+
const clean = hex.replace("#", "");
|
|
203
|
+
const r = parseInt(clean.substring(0, 2), 16);
|
|
204
|
+
const g = parseInt(clean.substring(2, 4), 16);
|
|
205
|
+
const b = parseInt(clean.substring(4, 6), 16);
|
|
206
|
+
return [r, g, b];
|
|
207
|
+
}
|
|
208
|
+
// GRADIENT METHODS
|
|
209
|
+
// =========================================================================
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Create a linear gradient
|
|
213
|
+
* @param {number} x0 - Start X
|
|
214
|
+
* @param {number} y0 - Start Y
|
|
215
|
+
* @param {number} x1 - End X
|
|
216
|
+
* @param {number} y1 - End Y
|
|
217
|
+
* @param {Array<{offset: number, color: string}>} colorStops - Array of color stops
|
|
218
|
+
* @returns {CanvasGradient} The created gradient
|
|
219
|
+
*/
|
|
220
|
+
static linearGradient(x0, y0, x1, y1, colorStops) {
|
|
221
|
+
const gradient = Painter.ctx.createLinearGradient(x0, y0, x1, y1);
|
|
222
|
+
|
|
223
|
+
for (const stop of colorStops) {
|
|
224
|
+
gradient.addColorStop(stop.offset, stop.color);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return gradient;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Create a radial gradient
|
|
232
|
+
* @param {number} x0 - Inner circle center X
|
|
233
|
+
* @param {number} y0 - Inner circle center Y
|
|
234
|
+
* @param {number} r0 - Inner circle radius
|
|
235
|
+
* @param {number} x1 - Outer circle center X
|
|
236
|
+
* @param {number} y1 - Outer circle center Y
|
|
237
|
+
* @param {number} r1 - Outer circle radius
|
|
238
|
+
* @param {Array<{offset: number, color: string}>} colorStops - Array of color stops
|
|
239
|
+
* @returns {CanvasGradient} The created gradient
|
|
240
|
+
*/
|
|
241
|
+
static radialGradient(x0, y0, r0, x1, y1, r1, colorStops) {
|
|
242
|
+
const gradient = Painter.ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
|
|
243
|
+
|
|
244
|
+
for (const stop of colorStops) {
|
|
245
|
+
gradient.addColorStop(stop.offset, stop.color);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return gradient;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Create a vertical gradient (convenience method)
|
|
253
|
+
* @param {number} x - X position
|
|
254
|
+
* @param {number} y - Top Y position
|
|
255
|
+
* @param {number} width - Width
|
|
256
|
+
* @param {number} height - Height
|
|
257
|
+
* @param {Array<{offset: number, color: string}>} colorStops - Array of color stops
|
|
258
|
+
* @returns {CanvasGradient} The created gradient
|
|
259
|
+
*/
|
|
260
|
+
static verticalGradient(x, y, width, height, colorStops) {
|
|
261
|
+
return Painter.colors.linearGradient(x, y, x, y + height, colorStops);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Create a horizontal gradient (convenience method)
|
|
266
|
+
* @param {number} x - Left X position
|
|
267
|
+
* @param {number} y - Y position
|
|
268
|
+
* @param {number} width - Width
|
|
269
|
+
* @param {number} height - Height
|
|
270
|
+
* @param {Array<{offset: number, color: string}>} colorStops - Array of color stops
|
|
271
|
+
* @returns {CanvasGradient} The created gradient
|
|
272
|
+
*/
|
|
273
|
+
static horizontalGradient(x, y, width, height, colorStops) {
|
|
274
|
+
return Painter.colors.linearGradient(x, y, x + width, y, colorStops);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Create a conic gradient
|
|
279
|
+
* @param {number} x - Center X
|
|
280
|
+
* @param {number} y - Center Y
|
|
281
|
+
* @param {number} startAngle - Start angle in radians
|
|
282
|
+
* @param {Array<{offset: number, color: string}>} colorStops - Array of color stops
|
|
283
|
+
* @returns {CanvasGradient} The created gradient
|
|
284
|
+
*/
|
|
285
|
+
static conicGradient(x, y, startAngle, colorStops) {
|
|
286
|
+
// For browsers that support it
|
|
287
|
+
if (typeof Painter.ctx.createConicGradient === "function") {
|
|
288
|
+
const gradient = Painter.ctx.createConicGradient(startAngle, x, y);
|
|
289
|
+
for (const stop of colorStops) {
|
|
290
|
+
gradient.addColorStop(stop.offset, stop.color);
|
|
291
|
+
}
|
|
292
|
+
return gradient;
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Create an RGBA color string
|
|
299
|
+
* @param {number} r - Red (0-255)
|
|
300
|
+
* @param {number} g - Green (0-255)
|
|
301
|
+
* @param {number} b - Blue (0-255)
|
|
302
|
+
* @param {number} [a=1] - Alpha (0-1)
|
|
303
|
+
* @returns {string} RGBA color string
|
|
304
|
+
*/
|
|
305
|
+
static rgba(r, g, b, a = 1) {
|
|
306
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Create an HSL color string
|
|
311
|
+
* @param {number} h - Hue (0-360)
|
|
312
|
+
* @param {number} s - Saturation (0-100)
|
|
313
|
+
* @param {number} l - Lightness (0-100)
|
|
314
|
+
* @returns {string} HSL color string
|
|
315
|
+
*/
|
|
316
|
+
static hsl(h, s, l) {
|
|
317
|
+
return `hsl(${h}, ${s}%, ${l}%)`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Create an HSLA color string
|
|
322
|
+
* @param {number} h - Hue (0-360)
|
|
323
|
+
* @param {number} s - Saturation (0-100)
|
|
324
|
+
* @param {number} l - Lightness (0-100)
|
|
325
|
+
* @param {number} a - Alpha (0-1)
|
|
326
|
+
* @returns {string} HSLA color string
|
|
327
|
+
*/
|
|
328
|
+
static hsla(h, s, l, a) {
|
|
329
|
+
return `hsla(${h}, ${s}%, ${l}%, ${a})`;
|
|
330
|
+
}
|
|
331
|
+
}
|