@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.
Files changed (349) hide show
  1. package/.github/workflows/release.yaml +70 -0
  2. package/.jshintrc +4 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CLAUDE.md +310 -0
  5. package/blackhole.jpg +0 -0
  6. package/demo.png +0 -0
  7. package/demos/CNAME +1 -0
  8. package/demos/animations.html +31 -0
  9. package/demos/basic.html +38 -0
  10. package/demos/baskara.html +31 -0
  11. package/demos/bezier.html +35 -0
  12. package/demos/beziersignature.html +29 -0
  13. package/demos/blackhole.html +28 -0
  14. package/demos/blob.html +35 -0
  15. package/demos/demos.css +289 -0
  16. package/demos/easing.html +28 -0
  17. package/demos/events.html +195 -0
  18. package/demos/fluent.html +647 -0
  19. package/demos/fractals.html +36 -0
  20. package/demos/genart.html +26 -0
  21. package/demos/gendream.html +26 -0
  22. package/demos/group.html +36 -0
  23. package/demos/home.html +587 -0
  24. package/demos/index.html +364 -0
  25. package/demos/isometric.html +34 -0
  26. package/demos/js/animations.js +452 -0
  27. package/demos/js/basic.js +204 -0
  28. package/demos/js/baskara.js +751 -0
  29. package/demos/js/bezier.js +692 -0
  30. package/demos/js/beziersignature.js +241 -0
  31. package/demos/js/blackhole/accretiondisk.obj.js +379 -0
  32. package/demos/js/blackhole/blackhole.obj.js +318 -0
  33. package/demos/js/blackhole/index.js +409 -0
  34. package/demos/js/blackhole/particle.js +56 -0
  35. package/demos/js/blackhole/starfield.obj.js +218 -0
  36. package/demos/js/blob.js +2263 -0
  37. package/demos/js/easing.js +477 -0
  38. package/demos/js/fluent.js +183 -0
  39. package/demos/js/fractals.js +931 -0
  40. package/demos/js/fractalworker.js +93 -0
  41. package/demos/js/genart.js +268 -0
  42. package/demos/js/gendream.js +209 -0
  43. package/demos/js/group.js +140 -0
  44. package/demos/js/info-toggle.js +25 -0
  45. package/demos/js/isometric.js +863 -0
  46. package/demos/js/kerr.js +1556 -0
  47. package/demos/js/lavalamp.js +590 -0
  48. package/demos/js/layout.js +354 -0
  49. package/demos/js/mondrian.js +285 -0
  50. package/demos/js/opacity.js +275 -0
  51. package/demos/js/painter.js +484 -0
  52. package/demos/js/particles-showcase.js +514 -0
  53. package/demos/js/particles.js +299 -0
  54. package/demos/js/patterns.js +397 -0
  55. package/demos/js/penrose/artifact.js +69 -0
  56. package/demos/js/penrose/blackhole.js +121 -0
  57. package/demos/js/penrose/constants.js +73 -0
  58. package/demos/js/penrose/game.js +943 -0
  59. package/demos/js/penrose/lore.js +278 -0
  60. package/demos/js/penrose/penrosescene.js +892 -0
  61. package/demos/js/penrose/ship.js +216 -0
  62. package/demos/js/penrose/sounds.js +211 -0
  63. package/demos/js/penrose/voidparticle.js +55 -0
  64. package/demos/js/penrose/voidscene.js +258 -0
  65. package/demos/js/penrose/voidship.js +144 -0
  66. package/demos/js/penrose/wormhole.js +46 -0
  67. package/demos/js/pipeline.js +555 -0
  68. package/demos/js/scene.js +304 -0
  69. package/demos/js/scenes.js +320 -0
  70. package/demos/js/schrodinger.js +410 -0
  71. package/demos/js/schwarzschild.js +1023 -0
  72. package/demos/js/shapes.js +628 -0
  73. package/demos/js/space/alien.js +171 -0
  74. package/demos/js/space/boom.js +98 -0
  75. package/demos/js/space/boss.js +353 -0
  76. package/demos/js/space/buff.js +73 -0
  77. package/demos/js/space/bullet.js +102 -0
  78. package/demos/js/space/constants.js +85 -0
  79. package/demos/js/space/game.js +1884 -0
  80. package/demos/js/space/hud.js +112 -0
  81. package/demos/js/space/laserbeam.js +179 -0
  82. package/demos/js/space/lightning.js +277 -0
  83. package/demos/js/space/minion.js +192 -0
  84. package/demos/js/space/missile.js +212 -0
  85. package/demos/js/space/player.js +430 -0
  86. package/demos/js/space/powerup.js +90 -0
  87. package/demos/js/space/starfield.js +58 -0
  88. package/demos/js/space/starpower.js +90 -0
  89. package/demos/js/spacetime.js +559 -0
  90. package/demos/js/svgtween.js +204 -0
  91. package/demos/js/tde/accretiondisk.js +418 -0
  92. package/demos/js/tde/blackhole.js +219 -0
  93. package/demos/js/tde/blackholescene.js +209 -0
  94. package/demos/js/tde/config.js +59 -0
  95. package/demos/js/tde/index.js +695 -0
  96. package/demos/js/tde/jets.js +290 -0
  97. package/demos/js/tde/lensedstarfield.js +147 -0
  98. package/demos/js/tde/tdestar.js +317 -0
  99. package/demos/js/tde/tidalstream.js +356 -0
  100. package/demos/js/tde_old/blackhole.obj.js +354 -0
  101. package/demos/js/tde_old/debris.obj.js +791 -0
  102. package/demos/js/tde_old/flare.obj.js +239 -0
  103. package/demos/js/tde_old/index.js +448 -0
  104. package/demos/js/tde_old/star.obj.js +812 -0
  105. package/demos/js/tiles.js +312 -0
  106. package/demos/js/tweendemo.js +79 -0
  107. package/demos/js/visibility.js +102 -0
  108. package/demos/kerr.html +28 -0
  109. package/demos/lavalamp.html +27 -0
  110. package/demos/layouts.html +37 -0
  111. package/demos/logo.svg +4 -0
  112. package/demos/loop.html +84 -0
  113. package/demos/mondrian.html +32 -0
  114. package/demos/og_image.png +0 -0
  115. package/demos/opacity.html +36 -0
  116. package/demos/painter.html +39 -0
  117. package/demos/particles-showcase.html +28 -0
  118. package/demos/particles.html +24 -0
  119. package/demos/patterns.html +33 -0
  120. package/demos/penrose-game.html +31 -0
  121. package/demos/pipeline.html +737 -0
  122. package/demos/scene.html +33 -0
  123. package/demos/scenes.html +96 -0
  124. package/demos/schrodinger.html +27 -0
  125. package/demos/schwarzschild.html +27 -0
  126. package/demos/shapes.html +16 -0
  127. package/demos/space.html +85 -0
  128. package/demos/spacetime.html +27 -0
  129. package/demos/svgtween.html +29 -0
  130. package/demos/tde.html +28 -0
  131. package/demos/tiles.html +28 -0
  132. package/demos/transforms.html +400 -0
  133. package/demos/tween.html +45 -0
  134. package/demos/visibility.html +33 -0
  135. package/disk_example.png +0 -0
  136. package/docs/README.md +222 -0
  137. package/docs/concepts/architecture-overview.md +204 -0
  138. package/docs/concepts/lifecycle.md +255 -0
  139. package/docs/concepts/rendering-pipeline.md +279 -0
  140. package/docs/concepts/tde-zorder.md +106 -0
  141. package/docs/concepts/two-layer-architecture.md +229 -0
  142. package/docs/getting-started/first-game.md +354 -0
  143. package/docs/getting-started/hello-world.md +269 -0
  144. package/docs/getting-started/installation.md +157 -0
  145. package/docs/modules/collision/README.md +453 -0
  146. package/docs/modules/fluent/README.md +1075 -0
  147. package/docs/modules/game/README.md +303 -0
  148. package/docs/modules/isometric-camera.md +210 -0
  149. package/docs/modules/isometric.md +275 -0
  150. package/docs/modules/painter/README.md +328 -0
  151. package/docs/modules/particle/README.md +559 -0
  152. package/docs/modules/shapes/README.md +221 -0
  153. package/docs/modules/shapes/base/euclidian.md +123 -0
  154. package/docs/modules/shapes/base/geometry2d.md +204 -0
  155. package/docs/modules/shapes/base/renderable.md +215 -0
  156. package/docs/modules/shapes/base/shape.md +262 -0
  157. package/docs/modules/shapes/base/transformable.md +243 -0
  158. package/docs/modules/shapes/hierarchy.md +218 -0
  159. package/docs/modules/state/README.md +577 -0
  160. package/docs/modules/util/README.md +99 -0
  161. package/docs/modules/util/camera3d.md +412 -0
  162. package/docs/modules/util/scene3d.md +395 -0
  163. package/index.html +17 -0
  164. package/jsdoc.json +50 -0
  165. package/package.json +55 -0
  166. package/readme.md +599 -0
  167. package/scripts/build-demo.js +69 -0
  168. package/scripts/bundle4llm.js +276 -0
  169. package/scripts/clearconsole.js +48 -0
  170. package/src/collision/collision-system.js +332 -0
  171. package/src/collision/collision.js +303 -0
  172. package/src/collision/index.js +10 -0
  173. package/src/fluent/fluent-game.js +430 -0
  174. package/src/fluent/fluent-go.js +1060 -0
  175. package/src/fluent/fluent-layer.js +152 -0
  176. package/src/fluent/fluent-scene.js +291 -0
  177. package/src/fluent/index.js +98 -0
  178. package/src/fluent/sketch.js +380 -0
  179. package/src/game/game.js +467 -0
  180. package/src/game/index.js +49 -0
  181. package/src/game/objects/go.js +220 -0
  182. package/src/game/objects/imagego.js +30 -0
  183. package/src/game/objects/index.js +54 -0
  184. package/src/game/objects/isometric-scene.js +260 -0
  185. package/src/game/objects/layoutscene.js +549 -0
  186. package/src/game/objects/scene.js +175 -0
  187. package/src/game/objects/scene3d.js +118 -0
  188. package/src/game/objects/text.js +221 -0
  189. package/src/game/objects/wrapper.js +232 -0
  190. package/src/game/pipeline.js +243 -0
  191. package/src/game/ui/button.js +396 -0
  192. package/src/game/ui/cursor.js +93 -0
  193. package/src/game/ui/fps.js +91 -0
  194. package/src/game/ui/index.js +5 -0
  195. package/src/game/ui/togglebutton.js +93 -0
  196. package/src/game/ui/tooltip.js +249 -0
  197. package/src/index.js +25 -0
  198. package/src/io/events.js +20 -0
  199. package/src/io/index.js +86 -0
  200. package/src/io/input.js +70 -0
  201. package/src/io/keys.js +152 -0
  202. package/src/io/mouse.js +61 -0
  203. package/src/io/touch.js +39 -0
  204. package/src/logger/debugtab.js +138 -0
  205. package/src/logger/index.js +3 -0
  206. package/src/logger/loggable.js +47 -0
  207. package/src/logger/logger.js +113 -0
  208. package/src/math/complex.js +37 -0
  209. package/src/math/constants.js +1 -0
  210. package/src/math/fractal.js +1271 -0
  211. package/src/math/gr.js +201 -0
  212. package/src/math/heat.js +202 -0
  213. package/src/math/index.js +12 -0
  214. package/src/math/noise.js +433 -0
  215. package/src/math/orbital.js +191 -0
  216. package/src/math/patterns.js +1339 -0
  217. package/src/math/penrose.js +259 -0
  218. package/src/math/quantum.js +115 -0
  219. package/src/math/random.js +195 -0
  220. package/src/math/tensor.js +1009 -0
  221. package/src/mixins/anchor.js +131 -0
  222. package/src/mixins/draggable.js +72 -0
  223. package/src/mixins/index.js +2 -0
  224. package/src/motion/bezier.js +132 -0
  225. package/src/motion/bounce.js +58 -0
  226. package/src/motion/easing.js +349 -0
  227. package/src/motion/float.js +130 -0
  228. package/src/motion/follow.js +125 -0
  229. package/src/motion/hop.js +52 -0
  230. package/src/motion/index.js +82 -0
  231. package/src/motion/motion.js +1124 -0
  232. package/src/motion/orbit.js +49 -0
  233. package/src/motion/oscillate.js +39 -0
  234. package/src/motion/parabolic.js +141 -0
  235. package/src/motion/patrol.js +147 -0
  236. package/src/motion/pendulum.js +48 -0
  237. package/src/motion/pulse.js +88 -0
  238. package/src/motion/shake.js +83 -0
  239. package/src/motion/spiral.js +144 -0
  240. package/src/motion/spring.js +150 -0
  241. package/src/motion/swing.js +47 -0
  242. package/src/motion/tween.js +92 -0
  243. package/src/motion/tweenetik.js +139 -0
  244. package/src/motion/waypoint.js +210 -0
  245. package/src/painter/index.js +8 -0
  246. package/src/painter/painter.colors.js +331 -0
  247. package/src/painter/painter.effects.js +230 -0
  248. package/src/painter/painter.img.js +229 -0
  249. package/src/painter/painter.js +295 -0
  250. package/src/painter/painter.lines.js +189 -0
  251. package/src/painter/painter.opacity.js +41 -0
  252. package/src/painter/painter.shapes.js +277 -0
  253. package/src/painter/painter.text.js +273 -0
  254. package/src/particle/emitter.js +124 -0
  255. package/src/particle/index.js +11 -0
  256. package/src/particle/particle-system.js +322 -0
  257. package/src/particle/particle.js +71 -0
  258. package/src/particle/updaters.js +170 -0
  259. package/src/shapes/arc.js +43 -0
  260. package/src/shapes/arrow.js +33 -0
  261. package/src/shapes/bezier.js +42 -0
  262. package/src/shapes/circle.js +62 -0
  263. package/src/shapes/clouds.js +56 -0
  264. package/src/shapes/cone.js +219 -0
  265. package/src/shapes/cross.js +70 -0
  266. package/src/shapes/cube.js +244 -0
  267. package/src/shapes/cylinder.js +254 -0
  268. package/src/shapes/diamond.js +48 -0
  269. package/src/shapes/euclidian.js +111 -0
  270. package/src/shapes/figure.js +115 -0
  271. package/src/shapes/geometry.js +220 -0
  272. package/src/shapes/group.js +375 -0
  273. package/src/shapes/heart.js +42 -0
  274. package/src/shapes/hexagon.js +26 -0
  275. package/src/shapes/image.js +192 -0
  276. package/src/shapes/index.js +111 -0
  277. package/src/shapes/line.js +29 -0
  278. package/src/shapes/pattern.js +90 -0
  279. package/src/shapes/pin.js +44 -0
  280. package/src/shapes/poly.js +31 -0
  281. package/src/shapes/prism.js +226 -0
  282. package/src/shapes/rect.js +35 -0
  283. package/src/shapes/renderable.js +333 -0
  284. package/src/shapes/ring.js +26 -0
  285. package/src/shapes/roundrect.js +95 -0
  286. package/src/shapes/shape.js +117 -0
  287. package/src/shapes/slice.js +26 -0
  288. package/src/shapes/sphere.js +314 -0
  289. package/src/shapes/sphere3d.js +537 -0
  290. package/src/shapes/square.js +15 -0
  291. package/src/shapes/star.js +99 -0
  292. package/src/shapes/svg.js +408 -0
  293. package/src/shapes/text.js +553 -0
  294. package/src/shapes/traceable.js +83 -0
  295. package/src/shapes/transform.js +357 -0
  296. package/src/shapes/transformable.js +172 -0
  297. package/src/shapes/triangle.js +26 -0
  298. package/src/sound/index.js +17 -0
  299. package/src/sound/sound.js +473 -0
  300. package/src/sound/synth.analyzer.js +149 -0
  301. package/src/sound/synth.effects.js +207 -0
  302. package/src/sound/synth.envelope.js +59 -0
  303. package/src/sound/synth.js +229 -0
  304. package/src/sound/synth.musical.js +160 -0
  305. package/src/sound/synth.noise.js +85 -0
  306. package/src/sound/synth.oscillators.js +293 -0
  307. package/src/state/index.js +10 -0
  308. package/src/state/state-machine.js +371 -0
  309. package/src/util/camera3d.js +438 -0
  310. package/src/util/index.js +6 -0
  311. package/src/util/isometric-camera.js +235 -0
  312. package/src/util/layout.js +317 -0
  313. package/src/util/position.js +147 -0
  314. package/src/util/tasks.js +47 -0
  315. package/src/util/zindex.js +287 -0
  316. package/src/webgl/index.js +9 -0
  317. package/src/webgl/shaders/sphere-shaders.js +994 -0
  318. package/src/webgl/webgl-renderer.js +388 -0
  319. package/tde.png +0 -0
  320. package/test/math/orbital.test.js +61 -0
  321. package/test/math/tensor.test.js +114 -0
  322. package/test/particle/emitter.test.js +204 -0
  323. package/test/particle/particle-system.test.js +310 -0
  324. package/test/particle/particle.test.js +116 -0
  325. package/test/particle/updaters.test.js +386 -0
  326. package/test/setup.js +120 -0
  327. package/test/shapes/euclidian.test.js +44 -0
  328. package/test/shapes/geometry.test.js +86 -0
  329. package/test/shapes/group.test.js +86 -0
  330. package/test/shapes/rectangle.test.js +64 -0
  331. package/test/shapes/transform.test.js +379 -0
  332. package/test/util/camera3d.test.js +428 -0
  333. package/test/util/scene3d.test.js +352 -0
  334. package/types/collision.d.ts +249 -0
  335. package/types/common.d.ts +155 -0
  336. package/types/game.d.ts +497 -0
  337. package/types/index.d.ts +309 -0
  338. package/types/io.d.ts +188 -0
  339. package/types/logger.d.ts +127 -0
  340. package/types/math.d.ts +268 -0
  341. package/types/mixins.d.ts +92 -0
  342. package/types/motion.d.ts +678 -0
  343. package/types/painter.d.ts +378 -0
  344. package/types/shapes.d.ts +864 -0
  345. package/types/sound.d.ts +672 -0
  346. package/types/state.d.ts +251 -0
  347. package/types/util.d.ts +253 -0
  348. package/vite.config.js +50 -0
  349. 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
+ }