{"_id":"@amphore-dev/kinogrida","_rev":"4-7d77383ccb8264736dd9a4d014ec1b92","name":"@amphore-dev/kinogrida","dist-tags":{"latest":"0.5.0"},"versions":{"0.3.0":{"name":"@amphore-dev/kinogrida","version":"0.3.0","keywords":["canvas","animation","visualization","shapes","graphics","javascript","web","art","typescript","library"],"author":{"name":"Amphore Development Team"},"license":"MIT","_id":"@amphore-dev/kinogrida@0.3.0","maintainers":[{"name":"shimon42","email":"contact@amphore.dev"}],"homepage":"https://amphore-dev.github.io/kinogrida/","bugs":{"url":"https://github.com/Amphore-Dev/kinogrida/issues"},"dist":{"shasum":"2d8057dd68d1ee281b4459da3a7e01272c0eca59","tarball":"https://registry.npmjs.org/@amphore-dev/kinogrida/-/kinogrida-0.3.0.tgz","fileCount":67,"integrity":"sha512-gC+lFDxn2WysRpFUSNkFisrh2XbSuT791DmY0DI0Flr8ZoiobZeNcjNfvqI17whOslMAmV0GKiXZ4r5oAmcGAA==","signatures":[{"sig":"MEYCIQDw3m2C91aRpWyvWwB9bqdse4you0EDr//4mG1rXUJGWAIhAK3mA+bYMptwmnlaoo01tiYrk/grPduD0AX5a/B/fHQy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":98322},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2e34c1a0487b48e5474c472d754937ee2835825e","scripts":{"dev":"concurrently --names \"Build,Example\" --colors \"blue,green\" \"npm run build:watch\" \"npm run example\"","lint":"eslint src","test":"jest","build":"tsc && tsc-alias","clean":"rm -rf dist","format":"prettier --write .","example":"cd example && npm run dev","lint:fix":"eslint src --fix","test:watch":"jest --watch","build:watch":"concurrently \"tsc --watch\" \"tsc-alias --watch\"","format:check":"prettier --check .","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"shimon42","email":"contact@amphore.dev"},"repository":{"url":"git+https://github.com/Amphore-Dev/kinogrida.git","type":"git"},"_npmVersion":"10.9.3","description":"Library for creating animated shape visualizations on canvas.","directories":{},"_nodeVersion":"22.19.0","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^10.1.0","ts-jest":"^29.1.1","ts-node":"^10.9.1","prettier":"^3.8.1","tsc-alias":"^1.8.16","@eslint/js":"^10.0.1","typescript":"^6.0.2","@types/jest":"^29.5.5","@types/node":"^25.5.0","concurrently":"^9.2.1","typescript-eslint":"^8.58.0","eslint-config-prettier":"^10.1.8","jest-environment-jsdom":"^29.7.0","@typescript-eslint/parser":"^8.58.0","@typescript-eslint/eslint-plugin":"^8.58.0"},"_npmOperationalInternal":{"tmp":"tmp/kinogrida_0.3.0_1775352432113_0.10623263105389613","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@amphore-dev/kinogrida","version":"0.3.1","keywords":["canvas","animation","visualization","shapes","graphics","javascript","web","art","typescript","library"],"author":{"name":"Amphore Development Team"},"license":"MIT","_id":"@amphore-dev/kinogrida@0.3.1","maintainers":[{"name":"shimon42","email":"contact@amphore.dev"}],"homepage":"https://amphore-dev.github.io/Kinogrida/","bugs":{"url":"https://github.com/Amphore-Dev/kinogrida/issues"},"dist":{"shasum":"d4124a609d3f2939ee1d3b2ad42e891460f6ade1","tarball":"https://registry.npmjs.org/@amphore-dev/kinogrida/-/kinogrida-0.3.1.tgz","fileCount":67,"integrity":"sha512-uV+Uo1TTzDgGD6Oxj69W5yK4EPk4eNlOurIFRAoWMwO1v49ZcEB5UEtvNuJT68QN0aLgvBm4468nHFYBg1+tcA==","signatures":[{"sig":"MEYCIQDNEc534RuUaVwDMvapNmE1vPUwhB9MsPxNg1M26R3UHAIhAKv80EcVIVlIcgvB2mdI8o0ASQloKc/VT6rZnGwP9bTP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":98322},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"3f9c48c86ec53fcea67030aa619936106934f69a","scripts":{"dev":"concurrently --names \"Build,Example\" --colors \"blue,green\" \"npm run build:watch\" \"npm run example\"","lint":"eslint src","test":"jest","build":"tsc && tsc-alias","clean":"rm -rf dist","format":"prettier --write .","example":"cd example && npm run dev","lint:fix":"eslint src --fix","test:watch":"jest --watch","build:watch":"concurrently \"tsc --watch\" \"tsc-alias --watch\"","format:check":"prettier --check .","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"shimon42","email":"contact@amphore.dev"},"repository":{"url":"git+https://github.com/Amphore-Dev/kinogrida.git","type":"git"},"_npmVersion":"10.9.3","description":"Library for creating animated shape visualizations on canvas.","directories":{},"_nodeVersion":"22.19.0","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^10.1.0","ts-jest":"^29.1.1","ts-node":"^10.9.1","prettier":"^3.8.1","tsc-alias":"^1.8.16","@eslint/js":"^10.0.1","typescript":"^6.0.2","@types/jest":"^29.5.5","@types/node":"^25.5.0","concurrently":"^9.2.1","typescript-eslint":"^8.58.0","eslint-config-prettier":"^10.1.8","jest-environment-jsdom":"^29.7.0","@typescript-eslint/parser":"^8.58.0","@typescript-eslint/eslint-plugin":"^8.58.0"},"_npmOperationalInternal":{"tmp":"tmp/kinogrida_0.3.1_1775352964557_0.6190285018678714","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@amphore-dev/kinogrida","version":"0.4.0","keywords":["canvas","animation","visualization","shapes","graphics","javascript","web","art","typescript","library"],"author":{"name":"Amphore Development Team"},"license":"MIT","_id":"@amphore-dev/kinogrida@0.4.0","maintainers":[{"name":"shimon42","email":"contact@amphore.dev"}],"homepage":"https://amphore-dev.github.io/Kinogrida/","bugs":{"url":"https://github.com/Amphore-Dev/kinogrida/issues"},"dist":{"shasum":"c06041e3ba12587f1532c00845533a24823e57e8","tarball":"https://registry.npmjs.org/@amphore-dev/kinogrida/-/kinogrida-0.4.0.tgz","fileCount":71,"integrity":"sha512-qhYIFh5sre8thOIUK18P0UNU1N+ayNpnqJQ3CVpTm8WRcrRtFqT/diz7ux5KpyXLWWWy5BZCfv7osymUAzE/ug==","signatures":[{"sig":"MEUCIQDTH93nmahpeFB/fbW+iAL0VBaJ5ov5i6yi26JRtRAK7QIgPFlYdCUefGpPMoS/QYD+yGbeJa5ZqyE7OoOa4NlRlu8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":119798},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"16fb33266bcee1fed7609659d55b7368acf936d4","scripts":{"dev":"concurrently --names \"Build,Example\" --colors \"blue,green\" \"npm run build:watch\" \"npm run example\"","lint":"eslint src","test":"jest","build":"tsc && tsc-alias","clean":"rm -rf dist","format":"prettier --write .","example":"cd example && npm run dev","lint:fix":"eslint src --fix","test:watch":"jest --watch","build:watch":"concurrently \"tsc --watch\" \"tsc-alias --watch\"","format:check":"prettier --check .","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"shimon42","email":"contact@amphore.dev"},"repository":{"url":"git+https://github.com/Amphore-Dev/kinogrida.git","type":"git"},"_npmVersion":"10.9.3","description":"Library for creating animated shape visualizations on canvas.","directories":{},"_nodeVersion":"22.19.0","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^10.1.0","ts-jest":"^29.1.1","ts-node":"^10.9.1","prettier":"^3.8.1","tsc-alias":"^1.8.16","@eslint/js":"^10.0.1","typescript":"^6.0.2","@types/jest":"^29.5.5","@types/node":"^25.5.0","concurrently":"^9.2.1","typescript-eslint":"^8.58.0","eslint-config-prettier":"^10.1.8","jest-environment-jsdom":"^29.7.0","@typescript-eslint/parser":"^8.58.0","@typescript-eslint/eslint-plugin":"^8.58.0"},"_npmOperationalInternal":{"tmp":"tmp/kinogrida_0.4.0_1775417172254_0.7154886952219055","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@amphore-dev/kinogrida","version":"0.5.0","description":"Library for creating animated shape visualizations on canvas.","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"keywords":["canvas","animation","visualization","shapes","graphics","javascript","web","art","typescript","library"],"bugs":{"url":"https://github.com/Amphore-Dev/kinogrida/issues"},"homepage":"https://amphore-dev.github.io/Kinogrida/","author":{"name":"Amphore Development Team"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/Amphore-Dev/kinogrida.git"},"scripts":{"build":"tsc && tsc-alias","build:watch":"concurrently \"tsc --watch\" \"tsc-alias --watch\"","dev":"concurrently --names \"Build,Example\" --colors \"blue,green\" \"npm run build:watch\" \"npm run example\"","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","lint":"eslint src","lint:fix":"eslint src --fix","format":"prettier --write .","format:check":"prettier --check .","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build","example":"cd example && npm run dev"},"devDependencies":{"@eslint/js":"^10.0.1","@types/jest":"^29.5.5","@types/node":"^25.5.0","@typescript-eslint/eslint-plugin":"^8.58.0","@typescript-eslint/parser":"^8.58.0","concurrently":"^9.2.1","eslint":"^10.1.0","eslint-config-prettier":"^10.1.8","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","prettier":"^3.8.1","ts-jest":"^29.1.1","ts-node":"^10.9.1","tsc-alias":"^1.8.16","typescript":"^6.0.2","typescript-eslint":"^8.58.0"},"engines":{"node":">=16.0.0"},"_id":"@amphore-dev/kinogrida@0.5.0","gitHead":"4c806fc4e5f7a524ccd8ed14fd5de07426e6ff64","_nodeVersion":"22.19.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-HFrOHDRLe6+TubReleqRo3o1idrDEbFCkUCcHqnJG1fzR357oIOeU6AXQD0lHGbqxVbvh8DHs/7/dPG9vbMA3A==","shasum":"636e6740e9afb0870f097aafb3f96f5b04fa88e6","tarball":"https://registry.npmjs.org/@amphore-dev/kinogrida/-/kinogrida-0.5.0.tgz","fileCount":71,"unpackedSize":128363,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIERiB8mXbigQ2if+xZQ58cDDy1lMs7NeNwhAKsVk4Tb5AiEAkJqGZATJM7FqHJ2y+78InHIb2pUE2hjZ2JdSf24oPpU="}]},"_npmUser":{"name":"shimon42","email":"contact@amphore.dev"},"directories":{},"maintainers":[{"name":"shimon42","email":"contact@amphore.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/kinogrida_0.5.0_1775443621304_0.39241466600444763"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-05T01:27:12.005Z","modified":"2026-04-06T02:47:01.586Z","0.3.0":"2026-04-05T01:27:12.279Z","0.3.1":"2026-04-05T01:36:04.700Z","0.4.0":"2026-04-05T19:26:12.403Z","0.5.0":"2026-04-06T02:47:01.456Z"},"bugs":{"url":"https://github.com/Amphore-Dev/kinogrida/issues"},"author":{"name":"Amphore Development Team"},"license":"MIT","homepage":"https://amphore-dev.github.io/Kinogrida/","keywords":["canvas","animation","visualization","shapes","graphics","javascript","web","art","typescript","library"],"repository":{"type":"git","url":"git+https://github.com/Amphore-Dev/kinogrida.git"},"description":"Library for creating animated shape visualizations on canvas.","maintainers":[{"name":"shimon42","email":"contact@amphore.dev"}],"readme":"# 🎨 Kinogrida\n\n**A TypeScript library for creating animated shape visualizations on HTML5 Canvas**\n\nKinogrida provides a powerful and flexible system for creating animated grid-based visualizations with dynamic shape movements, collision detection, and customizable visual effects.\n\n## ✨ Features\n\n- 🎯 **Grid-based Animation System** - Smooth movement between grid cells with pathfinding\n- 🔒 **Collision Detection** - Smart path locking prevents overlapping movements\n- 🎨 **Multiple Shape Types** - SquareShape (rounded or not), ArcShape, and extensible architecture for custom shapes\n- 🌈 **Rich Color Palettes** - 8 pre-built themes (Rainbow, Pastel, Neon, Ocean, Sunset, Dark, Earth, Cyberpunk)\n- 🔄 **Advanced Arc Animation** - Clockwise/counterclockwise rotation with configurable amplitude\n- ⚡ **High Performance** - Frame-independent animation with requestAnimationFrame\n- 📱 **Responsive Design** - Auto-adapts to canvas resizing\n- 🎛️ **Highly Customizable** - Extensive configuration options\n- 📡 **Event System** - Typed event emitter (`on`/`off`) for `cellClick`, `moveStart`, `move`, `moveEnd`\n- 🐛 **Granular Debug** - Individual toggles for grid, stats, locked cells, paths, and positions\n\n## 🚀 Installation\n\n```bash\nnpm install @amphore-dev/kinogrida\n```\n\n## 📖 Quick Start\n\n```typescript\nimport { Kinogrida } from \"@amphore-dev/kinogrida\";\n\n// Get your canvas element\nconst canvas = document.getElementById(\"myCanvas\") as HTMLCanvasElement;\n\n// Create and start the visualization\nconst kinogrida = new Kinogrida(canvas, {\n    speed: 2000, // Animation duration in ms (grow + shrink)\n});\nkinogrida.play();\n\n// Stop when needed\n// kinogrida.pause();\n// kinogrida.destroy();\n```\n\n## 🎛️ Configuration\n\n### Engine Options\n\n```typescript\nconst kinogrida = new Kinogrida(canvas, {\n    nbrColumns: 20, // Number of columns\n    nbrRows: 15, // Number of rows\n    gridMargin: 50, // Margin around grid (px)\n    lineWidth: 2, // Stroke width for shapes\n    speed: 4000, // Total animation duration in ms (grow + shrink)\n    showMouseHighlight: true, // Highlight cell on hover\n    debug: false, // Debug mode (boolean or TDebugOptions)\n});\n```\n\n### Speed\n\n`speed` is the total duration of a single move animation in milliseconds, covering both the grow phase (head moves to target) and the shrink phase (tail catches up). Lower values = faster movement.\n\n```typescript\nkinogrida.setSpeed(1000); // Fast (1s per move)\nkinogrida.setSpeed(4000); // Default (4s per move)\nkinogrida.setSpeed(10000); // Slow (10s per move)\n\nkinogrida.getSpeed(); // Current speed in ms\n```\n\n### Debug Options\n\nDebug can be a simple boolean (`true` enables all) or a granular `TDebugOptions` object:\n\n```typescript\nimport { TDebugOptions } from \"@amphore-dev/kinogrida\";\n\n// Enable all debug overlays\nkinogrida.toggleDebugMode(true);\n\n// Granular control\nkinogrida.toggleDebugMode({\n    showGrid: true, // Draw grid lines\n    showStats: true, // FPS, cell count, grid size overlay\n    showLockedCells: true, // Highlight locked cells during movement\n    showPath: true, // Draw arc debug paths\n    showPosition: false, // Show (x,y) coordinates on cells\n});\n\n// Toggle all on/off\nkinogrida.toggleDebugMode();\n```\n\n## 📡 Events\n\nKinogrida exposes a typed event emitter:\n\n```typescript\n// Cell click\nkinogrida.on(\"cellClick\", (x, y, isValid) => {\n    if (isValid) {\n        kinogrida.addCell(\"arc\", x, y, { color: \"#ff0000\" });\n    }\n});\n\n// Movement lifecycle\nkinogrida.on(\"moveStart\", (shape, fromX, fromY, toX, toY) => {\n    /* ... */\n});\nkinogrida.on(\"move\", (shape, x, y) => {\n    /* ... */\n});\nkinogrida.on(\"moveEnd\", (shape, x, y) => {\n    /* ... */\n});\n\n// Unsubscribe\nkinogrida.off(\"cellClick\", myHandler);\n```\n\n## 🎨 Color Palettes\n\n```typescript\nimport {\n    CCOLORS_OCEAN_PALETTE,\n    CCOLORS_SUNSET_PALETTE,\n    CCOLORS_DARK_PALETTE,\n    CCOLORS_CYBERPUNK_PALETTE,\n    CCOLORS_MONOCHROME_PALETTE,\n    CWATERMELON_PALETTE,\n} from \"@amphore-dev/kinogrida\";\n\n// Use any palette for your shapes\nconst colors = CWATERMELON_PALETTE;\n```\n\n## 🔧 API Reference\n\n### Kinogrida\n\n| Method                         | Description                                                   |\n| ------------------------------ | ------------------------------------------------------------- |\n| `play()`                       | Start the animation loop                                      |\n| `pause()`                      | Pause the animation loop                                      |\n| `destroy()`                    | Stop and clean up all resources                               |\n| `addCell(type, x, y, config?)` | Add a shape (`\"square\"`, `\"arc\"`, or custom) at grid position |\n| `clearGrid()`                  | Remove all cells                                              |\n| `initGrid(config?)`            | Reinitialize the grid                                         |\n| `fillGridRandomly()`           | Fill the grid with random shapes                              |\n| `setConfig(config, reset?)`    | Update grid config; pass `reset: true` to reinitialize        |\n| `getConfig()`                  | Get current grid config                                       |\n| `setSpeed(ms)`                 | Set animation duration in ms                                  |\n| `getSpeed()`                   | Get current animation duration                                |\n| `setFillPercentage(pct)`       | Set fill density (0–1) and reinitialize                       |\n| `getFillPercentage()`          | Get current fill density                                      |\n| `toggleDebugMode(value?)`      | Toggle or set debug options                                   |\n| `getCurrentFPS()`              | Current measured FPS                                          |\n| `getDeltaTime()`               | Last frame delta in ms                                        |\n| `on(event, handler)`           | Subscribe to an event                                         |\n| `off(event, handler)`          | Unsubscribe from an event                                     |\n\n## 🎭 Shape Types\n\n### SquareShape\n\nSquare with optional rounded corners, moves in straight lines (horizontal/vertical).\n\n```typescript\nkinogrida.addCell(\"square\", x, y, {\n    color: \"#3498db\",\n    radiusPercent: 0.5, // 0 = sharp, 1 = fully rounded\n});\n```\n\n### ArcShape\n\nCurved arc with rotation control, moves along circular paths.\n\n```typescript\nkinogrida.addCell(\"arc\", x, y, {\n    color: \"#e74c3c\",\n    clockwise: true, // Rotation direction\n    rotationAmount: 0.25, // 0.25=90°, 0.5=180°, 0.75=270°, 1.0=360°\n});\n```\n\n## 🧩 Custom Shapes\n\nYou can create your own shape classes by extending `BaseShape` and passing them to the engine via the `customShapes` option.\n\n### Creating a Custom Shape\n\nExtend `BaseShape` and implement the three required abstract methods:\n\n```typescript\nimport {\n    BaseShape,\n    TGrid,\n    TGridConfig,\n    TShapeConfig,\n} from \"@amphore-dev/kinogrida\";\n\nexport class DiamondShape extends BaseShape {\n    protected angle: number = 0;\n    protected moveDistance: number = 0;\n\n    constructor(grid: TGrid, x: number, y: number, options: TShapeConfig) {\n        super(grid, x, y, options);\n        this.angle = Math.PI / 4; // 45° diagonal\n        this.moveDistance = 3;\n    }\n\n    // Required: render the shape on canvas\n    draw(context: CanvasRenderingContext2D, gridConfig: TGridConfig): void {\n        const { cellSize, offsetX, offsetY, lineWidth } = gridConfig;\n        const centerX = this.x * cellSize + cellSize / 2 + offsetX;\n        const centerY = this.y * cellSize + cellSize / 2 + offsetY;\n        const half = cellSize * 0.4;\n\n        context.lineWidth = lineWidth;\n        context.strokeStyle = this.color;\n        context.beginPath();\n        context.moveTo(centerX, centerY - half);\n        context.lineTo(centerX + half, centerY);\n        context.lineTo(centerX, centerY + half);\n        context.lineTo(centerX - half, centerY);\n        context.closePath();\n        context.stroke();\n    }\n\n    // Required: decide where to move next\n    protected calculateNewTarget(grid: TGrid, gridConfig: TGridConfig): void {\n        const targetX =\n            this.x + Math.round(this.moveDistance * Math.cos(this.angle));\n        const targetY =\n            this.y + Math.round(this.moveDistance * Math.sin(this.angle));\n        this.moveTo(grid, gridConfig, targetX, targetY);\n    }\n\n    // Required: animate position using this.progress / this.tailProgress\n    protected updatePosition(grid: TGrid): void {\n        const step = this.speed * this.moveDistance;\n\n        if (this.hasReachedTarget) {\n            this.tailProgress = Math.min(\n                this.tailProgress + step,\n                this.moveDistance,\n            );\n            if (this.tailProgress === this.moveDistance) {\n                this.onMoveComplete(grid, this.targetX, this.targetY);\n            }\n        } else {\n            this.progress = Math.min(this.progress + step, this.moveDistance);\n            if (this.progress === this.moveDistance) {\n                this.hasReachedTarget = true;\n            }\n        }\n    }\n}\n```\n\n### Registering Custom Shapes\n\nPass your custom shape classes in the `customShapes` option when creating the engine:\n\n```typescript\nimport { Kinogrida } from \"@amphore-dev/kinogrida\";\nimport { DiamondShape } from \"./shapes/DiamondShape\";\n\nconst kinogrida = new Kinogrida(canvas, {\n    speed: 2000,\n    customShapes: {\n        diamond: DiamondShape,\n    },\n});\nkinogrida.play();\n\n// Now you can use your custom shape type\nkinogrida.addCell(\"diamond\", 5, 5, { color: \"#9b59b6\" });\n```\n\nCustom shapes are merged with built-in shapes (`\"square\"`, `\"arc\"`) and can be used anywhere a shape type is expected, including `fillGridRandomly()`.\n\n### Abstract Methods Reference\n\n| Method                                 | Description                                                                                                               |\n| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `draw(context, gridConfig)`            | Render the shape. Use `this.x`, `this.y`, `this.progress`, and `this.tailProgress` for animated positioning.              |\n| `calculateNewTarget(grid, gridConfig)` | Choose a target cell and call `this.moveTo(grid, gridConfig, targetX, targetY)`.                                          |\n| `updatePosition(grid)`                 | Advance `this.progress` (head) and `this.tailProgress` (tail) using `this.speed`. Call `this.onMoveComplete()` when done. |\n\n### Useful Inherited Properties\n\n| Property                        | Type      | Description                                            |\n| ------------------------------- | --------- | ------------------------------------------------------ |\n| `this.x` / `this.y`             | `number`  | Current grid position                                  |\n| `this.targetX` / `this.targetY` | `number`  | Movement target position                               |\n| `this.progress`                 | `number`  | Head progress (0 → moveDistance)                       |\n| `this.tailProgress`             | `number`  | Tail progress (0 → moveDistance, starts after head)    |\n| `this.speed`                    | `number`  | Per-frame increment (computed from `gridConfig.speed`) |\n| `this.color`                    | `string`  | Shape color from config                                |\n| `this.isMoving`                 | `boolean` | Whether the shape is currently animating               |\n| `this.hasReachedTarget`         | `boolean` | Whether the head has reached the target                |\n\n## 🏗️ Architecture\n\n### Core Classes\n\n- **`Kinogrida`** - Main orchestrator class managing canvas, animation loop, and events\n- **`BaseShape`** - Abstract base class for all animated shapes\n- **`SquareShape`** - Square implementation with optional rounded corners\n- **`ArcShape`** - Curved arc implementation with rotation and directional control\n\n### Movement Lifecycle\n\n1. **Path Generation** - Calculate cells between start and target\n2. **Collision Check** - Verify path is clear of obstacles\n3. **Cell Locking** - Reserve path cells to prevent conflicts\n4. **Grow Phase** - Move shape head to target (duration: `speed / 2`)\n5. **Shrink Phase** - Move shape tail to target (duration: `speed / 2`)\n6. **Cleanup** - Release locked cells and update grid state\n\n## 📚 Development\n\n### Build & Test\n\n```bash\n# Development build with watch\nnpm run build:watch\n\n# Run tests\nnpm test\n\n# Run example\ncd example && npm run dev\n```\n\n### Project Structure\n\n```\nsrc/\n├── classes/          # Core shape classes (Kinogrida, BaseShape, SquareShape, ArcShape)\n├── constants/        # Color palettes and default config\n├── types/            # TypeScript type definitions\n└── utils/            # Utility functions (math, colors, shapes)\n```\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","readmeFilename":"README.md"}