@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,408 @@
|
|
|
1
|
+
import { Painter } from "../painter/painter";
|
|
2
|
+
import { Shape } from "./shape";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SVGShape - A specialized Shape class that can render SVG path data
|
|
6
|
+
*/
|
|
7
|
+
export class SVGShape extends Shape {
|
|
8
|
+
/**
|
|
9
|
+
* @param {number} x - Center X position
|
|
10
|
+
* @param {number} y - Center Y position
|
|
11
|
+
* @param {string} svgPathData - SVG path data string (e.g. "M0,0 L10,10...")
|
|
12
|
+
* @param {object} options - Standard shape options plus SVG-specific options
|
|
13
|
+
* @param {number} [options.scale=1] - Scale factor for the SVG
|
|
14
|
+
* @param {boolean} [options.centerPath=true] - Automatically center the path
|
|
15
|
+
* @param {number} [options.animationProgress=1] - Animation progress (0-1)
|
|
16
|
+
*/
|
|
17
|
+
constructor(svgPathData, options = {}) {
|
|
18
|
+
console.log("SVGShape", options.x)
|
|
19
|
+
super(options);
|
|
20
|
+
// SVG specific options
|
|
21
|
+
this.scale = options.scale || 1;
|
|
22
|
+
this.centerPath =
|
|
23
|
+
options.centerPath !== undefined ? options.centerPath : true;
|
|
24
|
+
this.animationProgress =
|
|
25
|
+
options.animationProgress !== undefined ? options.animationProgress : 1;
|
|
26
|
+
|
|
27
|
+
// Parse the SVG path data
|
|
28
|
+
this.svgPathData = svgPathData;
|
|
29
|
+
this.pathCommands = this.parseSVGPath(svgPathData);
|
|
30
|
+
|
|
31
|
+
// If centering is enabled, center and scale the path
|
|
32
|
+
if (this.centerPath) {
|
|
33
|
+
this.pathCommands = this.centerAndScalePath(
|
|
34
|
+
this.pathCommands,
|
|
35
|
+
this.scale
|
|
36
|
+
);
|
|
37
|
+
} else {
|
|
38
|
+
this.pathCommands = this.scalePath(this.pathCommands, this.scale);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Drawing state tracking
|
|
42
|
+
this.prevX = 0;
|
|
43
|
+
this.prevY = 0;
|
|
44
|
+
this.currentPoint = { x: 0, y: 0 };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parse an SVG path string into a command array for rendering
|
|
49
|
+
* @param {string} svgPath - SVG path data string
|
|
50
|
+
* @returns {Array} Array of path commands
|
|
51
|
+
*/
|
|
52
|
+
parseSVGPath(svgPath) {
|
|
53
|
+
// Regular expressions to match SVG path commands
|
|
54
|
+
const moveRegex = /M\s*([-\d.]+)[,\s]*([-\d.]+)/g;
|
|
55
|
+
const lineRegex = /L\s*([-\d.]+)[,\s]*([-\d.]+)/g;
|
|
56
|
+
const curveRegex =
|
|
57
|
+
/C\s*([-\d.]+)[,\s]*([-\d.]+)\s*([-\d.]+)[,\s]*([-\d.]+)\s*([-\d.]+)[,\s]*([-\d.]+)/g;
|
|
58
|
+
const zRegex = /Z/g;
|
|
59
|
+
|
|
60
|
+
// Arrays to store the parsed commands
|
|
61
|
+
const commands = [];
|
|
62
|
+
|
|
63
|
+
// Match and process Move commands (M)
|
|
64
|
+
let match;
|
|
65
|
+
while ((match = moveRegex.exec(svgPath)) !== null) {
|
|
66
|
+
commands.push(["M", parseFloat(match[1]), parseFloat(match[2])]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Match and process Line commands (L)
|
|
70
|
+
while ((match = lineRegex.exec(svgPath)) !== null) {
|
|
71
|
+
// Convert line to bezier curve for animation
|
|
72
|
+
const x = parseFloat(match[1]);
|
|
73
|
+
const y = parseFloat(match[2]);
|
|
74
|
+
|
|
75
|
+
// Find the previous point to calculate the control points
|
|
76
|
+
let prevX = 0;
|
|
77
|
+
let prevY = 0;
|
|
78
|
+
for (let i = commands.length - 1; i >= 0; i--) {
|
|
79
|
+
const cmd = commands[i];
|
|
80
|
+
if (cmd[0] === "M") {
|
|
81
|
+
prevX = cmd[1];
|
|
82
|
+
prevY = cmd[2];
|
|
83
|
+
break;
|
|
84
|
+
} else if (cmd[0] === "C") {
|
|
85
|
+
prevX = cmd[5];
|
|
86
|
+
prevY = cmd[6];
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Calculate control points for an approximated curve
|
|
92
|
+
// For a line, we can use control points that are 1/3 and 2/3 along the line
|
|
93
|
+
const cp1x = prevX + (x - prevX) / 3;
|
|
94
|
+
const cp1y = prevY + (y - prevY) / 3;
|
|
95
|
+
const cp2x = prevX + (2 * (x - prevX)) / 3;
|
|
96
|
+
const cp2y = prevY + (2 * (y - prevY)) / 3;
|
|
97
|
+
|
|
98
|
+
commands.push(["C", cp1x, cp1y, cp2x, cp2y, x, y]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Match and process Curve commands (C)
|
|
102
|
+
while ((match = curveRegex.exec(svgPath)) !== null) {
|
|
103
|
+
commands.push([
|
|
104
|
+
"C",
|
|
105
|
+
parseFloat(match[1]),
|
|
106
|
+
parseFloat(match[2]),
|
|
107
|
+
parseFloat(match[3]),
|
|
108
|
+
parseFloat(match[4]),
|
|
109
|
+
parseFloat(match[5]),
|
|
110
|
+
parseFloat(match[6]),
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Match and process Close commands (Z)
|
|
115
|
+
if (zRegex.test(svgPath)) {
|
|
116
|
+
commands.push(["Z"]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return commands;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Center and scale the path commands
|
|
124
|
+
* @param {Array} path - Array of path commands
|
|
125
|
+
* @param {number} scale - Scale factor
|
|
126
|
+
* @returns {Array} Centered and scaled path commands
|
|
127
|
+
*/
|
|
128
|
+
centerAndScalePath(path, scale) {
|
|
129
|
+
// Find the bounds of the path
|
|
130
|
+
let minX = Infinity,
|
|
131
|
+
minY = Infinity;
|
|
132
|
+
let maxX = -Infinity,
|
|
133
|
+
maxY = -Infinity;
|
|
134
|
+
|
|
135
|
+
for (const cmd of path) {
|
|
136
|
+
if (cmd[0] === "M") {
|
|
137
|
+
minX = Math.min(minX, cmd[1]);
|
|
138
|
+
minY = Math.min(minY, cmd[2]);
|
|
139
|
+
maxX = Math.max(maxX, cmd[1]);
|
|
140
|
+
maxY = Math.max(maxY, cmd[2]);
|
|
141
|
+
} else if (cmd[0] === "C") {
|
|
142
|
+
minX = Math.min(minX, cmd[1], cmd[3], cmd[5]);
|
|
143
|
+
minY = Math.min(minY, cmd[2], cmd[4], cmd[6]);
|
|
144
|
+
maxX = Math.max(maxX, cmd[1], cmd[3], cmd[5]);
|
|
145
|
+
maxY = Math.max(maxY, cmd[2], cmd[4], cmd[6]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Calculate center of the original path
|
|
150
|
+
const pathCenterX = (minX + maxX) / 2;
|
|
151
|
+
const pathCenterY = (minY + maxY) / 2;
|
|
152
|
+
|
|
153
|
+
// Save the original dimensions for bounds calculation
|
|
154
|
+
this.originalWidth = (maxX - minX) * scale;
|
|
155
|
+
this.originalHeight = (maxY - minY) * scale;
|
|
156
|
+
|
|
157
|
+
// Translate the center to the origin and scale
|
|
158
|
+
return path.map((cmd) => {
|
|
159
|
+
if (cmd[0] === "M") {
|
|
160
|
+
return [
|
|
161
|
+
"M",
|
|
162
|
+
(cmd[1] - pathCenterX) * scale,
|
|
163
|
+
(cmd[2] - pathCenterY) * scale,
|
|
164
|
+
];
|
|
165
|
+
} else if (cmd[0] === "C") {
|
|
166
|
+
return [
|
|
167
|
+
"C",
|
|
168
|
+
(cmd[1] - pathCenterX) * scale,
|
|
169
|
+
(cmd[2] - pathCenterY) * scale,
|
|
170
|
+
(cmd[3] - pathCenterX) * scale,
|
|
171
|
+
(cmd[4] - pathCenterY) * scale,
|
|
172
|
+
(cmd[5] - pathCenterX) * scale,
|
|
173
|
+
(cmd[6] - pathCenterY) * scale,
|
|
174
|
+
];
|
|
175
|
+
} else {
|
|
176
|
+
return [...cmd]; // Z command
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Scale the path commands without centering
|
|
183
|
+
* @param {Array} path - Array of path commands
|
|
184
|
+
* @param {number} scale - Scale factor
|
|
185
|
+
* @returns {Array} Scaled path commands
|
|
186
|
+
*/
|
|
187
|
+
scalePath(path, scale) {
|
|
188
|
+
// Find the bounds for dimension calculation
|
|
189
|
+
let minX = Infinity,
|
|
190
|
+
minY = Infinity;
|
|
191
|
+
let maxX = -Infinity,
|
|
192
|
+
maxY = -Infinity;
|
|
193
|
+
|
|
194
|
+
for (const cmd of path) {
|
|
195
|
+
if (cmd[0] === "M") {
|
|
196
|
+
minX = Math.min(minX, cmd[1]);
|
|
197
|
+
minY = Math.min(minY, cmd[2]);
|
|
198
|
+
maxX = Math.max(maxX, cmd[1]);
|
|
199
|
+
maxY = Math.max(maxY, cmd[2]);
|
|
200
|
+
} else if (cmd[0] === "C") {
|
|
201
|
+
minX = Math.min(minX, cmd[1], cmd[3], cmd[5]);
|
|
202
|
+
minY = Math.min(minY, cmd[2], cmd[4], cmd[6]);
|
|
203
|
+
maxX = Math.max(maxX, cmd[1], cmd[3], cmd[5]);
|
|
204
|
+
maxY = Math.max(maxY, cmd[2], cmd[4], cmd[6]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Save the original dimensions for bounds calculation
|
|
209
|
+
this.originalWidth = (maxX - minX) * scale;
|
|
210
|
+
this.originalHeight = (maxY - minY) * scale;
|
|
211
|
+
|
|
212
|
+
// Scale without centering
|
|
213
|
+
return path.map((cmd) => {
|
|
214
|
+
if (cmd[0] === "M") {
|
|
215
|
+
return ["M", cmd[1] * scale, cmd[2] * scale];
|
|
216
|
+
} else if (cmd[0] === "C") {
|
|
217
|
+
return [
|
|
218
|
+
"C",
|
|
219
|
+
cmd[1] * scale,
|
|
220
|
+
cmd[2] * scale,
|
|
221
|
+
cmd[3] * scale,
|
|
222
|
+
cmd[4] * scale,
|
|
223
|
+
cmd[5] * scale,
|
|
224
|
+
cmd[6] * scale,
|
|
225
|
+
];
|
|
226
|
+
} else {
|
|
227
|
+
return [...cmd]; // Z command
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Calculate a point along a bezier curve at time t
|
|
234
|
+
* @param {Array} segment - Path segment command
|
|
235
|
+
* @param {number} t - Time parameter (0-1)
|
|
236
|
+
* @returns {Object} Point coordinates {x, y}
|
|
237
|
+
*/
|
|
238
|
+
getBezierPoint(segment, t) {
|
|
239
|
+
if (segment[0] === "M") {
|
|
240
|
+
// For move commands, just return the point
|
|
241
|
+
return { x: segment[1], y: segment[2] };
|
|
242
|
+
} else if (segment[0] === "C") {
|
|
243
|
+
// For Cubic Bezier curves, calculate the point at t
|
|
244
|
+
const startX = this.prevX;
|
|
245
|
+
const startY = this.prevY;
|
|
246
|
+
const cp1x = segment[1];
|
|
247
|
+
const cp1y = segment[2];
|
|
248
|
+
const cp2x = segment[3];
|
|
249
|
+
const cp2y = segment[4];
|
|
250
|
+
const endX = segment[5];
|
|
251
|
+
const endY = segment[6];
|
|
252
|
+
|
|
253
|
+
// Cubic Bezier formula
|
|
254
|
+
const x =
|
|
255
|
+
Math.pow(1 - t, 3) * startX +
|
|
256
|
+
3 * Math.pow(1 - t, 2) * t * cp1x +
|
|
257
|
+
3 * (1 - t) * Math.pow(t, 2) * cp2x +
|
|
258
|
+
Math.pow(t, 3) * endX;
|
|
259
|
+
|
|
260
|
+
const y =
|
|
261
|
+
Math.pow(1 - t, 3) * startY +
|
|
262
|
+
3 * Math.pow(1 - t, 2) * t * cp1y +
|
|
263
|
+
3 * (1 - t) * Math.pow(t, 2) * cp2y +
|
|
264
|
+
Math.pow(t, 3) * endY;
|
|
265
|
+
|
|
266
|
+
return { x, y };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return { x: 0, y: 0 };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get a subset of the path up to the current animation progress
|
|
274
|
+
* @returns {Array} Array of path commands representing the partial path
|
|
275
|
+
*/
|
|
276
|
+
getPartialPath() {
|
|
277
|
+
const result = [];
|
|
278
|
+
let totalSegments = this.pathCommands.length;
|
|
279
|
+
let segmentIndex = Math.floor(this.animationProgress * totalSegments);
|
|
280
|
+
let segmentProgress = (this.animationProgress * totalSegments) % 1;
|
|
281
|
+
|
|
282
|
+
// Initialize with a null previous point to indicate no previous point exists
|
|
283
|
+
let hasPrevPoint = false;
|
|
284
|
+
this.prevX = 0;
|
|
285
|
+
this.prevY = 0;
|
|
286
|
+
|
|
287
|
+
// Add all completed segments
|
|
288
|
+
for (let i = 0; i < segmentIndex; i++) {
|
|
289
|
+
const cmd = this.pathCommands[i];
|
|
290
|
+
|
|
291
|
+
// Add to result
|
|
292
|
+
result.push([...cmd]);
|
|
293
|
+
|
|
294
|
+
// Track points for bezier calculations
|
|
295
|
+
if (cmd[0] === "M") {
|
|
296
|
+
// For Move commands, just update position
|
|
297
|
+
this.prevX = cmd[1];
|
|
298
|
+
this.prevY = cmd[2];
|
|
299
|
+
hasPrevPoint = true; // Now we have a previous point
|
|
300
|
+
} else if (cmd[0] === "C") {
|
|
301
|
+
// For Bezier commands, update to end point
|
|
302
|
+
this.prevX = cmd[5];
|
|
303
|
+
this.prevY = cmd[6];
|
|
304
|
+
hasPrevPoint = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Add the current segment with partial progress
|
|
309
|
+
if (segmentIndex < totalSegments) {
|
|
310
|
+
const currentSegment = this.pathCommands[segmentIndex];
|
|
311
|
+
|
|
312
|
+
if (currentSegment[0] === "M") {
|
|
313
|
+
// For Move commands, add the full move
|
|
314
|
+
result.push([...currentSegment]);
|
|
315
|
+
this.prevX = currentSegment[1];
|
|
316
|
+
this.prevY = currentSegment[2];
|
|
317
|
+
this.currentPoint = { x: currentSegment[1], y: currentSegment[2] };
|
|
318
|
+
hasPrevPoint = true;
|
|
319
|
+
} else if (currentSegment[0] === "C") {
|
|
320
|
+
if (!hasPrevPoint) {
|
|
321
|
+
// If there's no previous point but we're trying to draw a curve,
|
|
322
|
+
// we need to find the closest previous Move command
|
|
323
|
+
for (let i = segmentIndex - 1; i >= 0; i--) {
|
|
324
|
+
if (this.pathCommands[i][0] === "M") {
|
|
325
|
+
this.prevX = this.pathCommands[i][1];
|
|
326
|
+
this.prevY = this.pathCommands[i][2];
|
|
327
|
+
hasPrevPoint = true;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// If still no previous point, use (0,0)
|
|
333
|
+
if (!hasPrevPoint) {
|
|
334
|
+
this.prevX = 0;
|
|
335
|
+
this.prevY = 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Calculate the partial curve point
|
|
340
|
+
const point = this.getBezierPoint(currentSegment, segmentProgress);
|
|
341
|
+
|
|
342
|
+
// Add partial curve to result
|
|
343
|
+
result.push([
|
|
344
|
+
"C",
|
|
345
|
+
currentSegment[1],
|
|
346
|
+
currentSegment[2],
|
|
347
|
+
currentSegment[3],
|
|
348
|
+
currentSegment[4],
|
|
349
|
+
point.x,
|
|
350
|
+
point.y,
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
this.currentPoint = point;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Draw the SVG path
|
|
362
|
+
*/
|
|
363
|
+
draw() {
|
|
364
|
+
super.draw();
|
|
365
|
+
// Get the path to draw based on animation progress
|
|
366
|
+
const pathToDraw = this.getPartialPath();
|
|
367
|
+
//console.log(pathToDraw);
|
|
368
|
+
// Use Painter to render the path
|
|
369
|
+
Painter.lines.path(
|
|
370
|
+
pathToDraw,
|
|
371
|
+
this.color,
|
|
372
|
+
this.stroke,
|
|
373
|
+
this.lineWidth
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get the current position of the "pen" for animations
|
|
379
|
+
* @returns {Object} Current point {x, y} in world coordinates
|
|
380
|
+
*/
|
|
381
|
+
getCurrentPoint() {
|
|
382
|
+
return {
|
|
383
|
+
x: this.currentPoint.x,
|
|
384
|
+
y: this.currentPoint.y,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Set the animation progress
|
|
390
|
+
* @param {number} progress - Animation progress (0-1)
|
|
391
|
+
*/
|
|
392
|
+
setAnimationProgress(progress) {
|
|
393
|
+
this.animationProgress = Math.max(0, Math.min(1, progress));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get bounds of the shape for hit detection and layout
|
|
398
|
+
* @returns {Object} Bounds {x, y, width, height}
|
|
399
|
+
*/
|
|
400
|
+
calculateBounds() {
|
|
401
|
+
return {
|
|
402
|
+
x: this.x,
|
|
403
|
+
y: this.y,
|
|
404
|
+
width: this.originalWidth || 100,
|
|
405
|
+
height: this.originalHeight || 100,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|