🚀 draw world
This commit is contained in:
26
packages/compute/__tests__/grid.spec.ts
Normal file
26
packages/compute/__tests__/grid.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { generateEmptyGrid } from "../generateGrid";
|
||||||
|
import { setColor, getColor, isInside } from "../grid";
|
||||||
|
|
||||||
|
it("should set / get cell", () => {
|
||||||
|
const grid = generateEmptyGrid(2, 3);
|
||||||
|
|
||||||
|
expect(getColor(grid, 0, 1)).toBe(null);
|
||||||
|
|
||||||
|
setColor(grid, 0, 1, 1);
|
||||||
|
|
||||||
|
expect(getColor(grid, 0, 1)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
[0, 1, true],
|
||||||
|
[1, 2, true],
|
||||||
|
|
||||||
|
[-1, 1, false],
|
||||||
|
[0, -1, false],
|
||||||
|
[2, 1, false],
|
||||||
|
[0, 3, false],
|
||||||
|
])("isInside", (x, y, output) => {
|
||||||
|
const grid = generateEmptyGrid(2, 3);
|
||||||
|
|
||||||
|
expect(isInside(grid, x, y)).toBe(output);
|
||||||
|
});
|
||||||
24
packages/compute/__tests__/snake.spec.ts
Normal file
24
packages/compute/__tests__/snake.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { snakeSelfCollide } from "../snake";
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
[[{ x: 0, y: 0 }], false],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{ x: 0, y: 0 },
|
||||||
|
{ x: 0, y: 0 },
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{ x: 1, y: 7 },
|
||||||
|
{ x: 0, y: 6 },
|
||||||
|
{ x: 2, y: 8 },
|
||||||
|
{ x: 1, y: 7 },
|
||||||
|
{ x: 3, y: 9 },
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
])("should report snake collision", (snake, collide) => {
|
||||||
|
expect(snakeSelfCollide(snake)).toBe(collide);
|
||||||
|
});
|
||||||
94
packages/compute/__tests__/step.spec.ts
Normal file
94
packages/compute/__tests__/step.spec.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { step } from "../step";
|
||||||
|
import { generateEmptyGrid } from "../generateGrid";
|
||||||
|
import { around4 } from "../point";
|
||||||
|
import { setColor, getColor } from "../grid";
|
||||||
|
|
||||||
|
it("should move snake", () => {
|
||||||
|
const grid = generateEmptyGrid(4, 3);
|
||||||
|
const snake = [{ x: 1, y: 1 }];
|
||||||
|
const direction = around4[0];
|
||||||
|
const stack: number[] = [];
|
||||||
|
const options = { maxSnakeLength: 5 };
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 2, y: 1 },
|
||||||
|
{ x: 1, y: 1 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 3, y: 1 },
|
||||||
|
{ x: 2, y: 1 },
|
||||||
|
{ x: 1, y: 1 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 4, y: 1 },
|
||||||
|
{ x: 3, y: 1 },
|
||||||
|
{ x: 2, y: 1 },
|
||||||
|
{ x: 1, y: 1 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should move short snake", () => {
|
||||||
|
const grid = generateEmptyGrid(8, 3);
|
||||||
|
const snake = [{ x: 1, y: 1 }];
|
||||||
|
const direction = around4[0];
|
||||||
|
const stack: number[] = [];
|
||||||
|
const options = { maxSnakeLength: 3 };
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 2, y: 1 },
|
||||||
|
{ x: 1, y: 1 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 3, y: 1 },
|
||||||
|
{ x: 2, y: 1 },
|
||||||
|
{ x: 1, y: 1 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 4, y: 1 },
|
||||||
|
{ x: 3, y: 1 },
|
||||||
|
{ x: 2, y: 1 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(snake).toEqual([
|
||||||
|
{ x: 5, y: 1 },
|
||||||
|
{ x: 4, y: 1 },
|
||||||
|
{ x: 3, y: 1 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should pick up fruit", () => {
|
||||||
|
const grid = generateEmptyGrid(4, 3);
|
||||||
|
const snake = [{ x: 1, y: 1 }];
|
||||||
|
const direction = around4[0];
|
||||||
|
const stack: number[] = [];
|
||||||
|
const options = { maxSnakeLength: 2 };
|
||||||
|
setColor(grid, 3, 1, 9);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(getColor(grid, 3, 1)).toBe(9);
|
||||||
|
expect(stack).toEqual([]);
|
||||||
|
|
||||||
|
step(grid, snake, stack, direction, options);
|
||||||
|
|
||||||
|
expect(getColor(grid, 3, 1)).toBe(null);
|
||||||
|
expect(stack).toEqual([9]);
|
||||||
|
});
|
||||||
@@ -2,6 +2,9 @@ import { Grid, Color } from "./grid";
|
|||||||
|
|
||||||
const rand = (a: number, b: number) => Math.floor(Math.random() * (b - a)) + a;
|
const rand = (a: number, b: number) => Math.floor(Math.random() * (b - a)) + a;
|
||||||
|
|
||||||
|
export const generateEmptyGrid = (width: number, height: number) =>
|
||||||
|
generateGrid(width, height, { colors: [], emptyP: 1 });
|
||||||
|
|
||||||
export const generateGrid = (
|
export const generateGrid = (
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
|
|||||||
@@ -12,9 +12,14 @@ export const getIndex = (grid: Grid, x: number, y: number) =>
|
|||||||
export const isInside = (grid: Grid, x: number, y: number) =>
|
export const isInside = (grid: Grid, x: number, y: number) =>
|
||||||
x >= 0 && y >= 0 && x < grid.width && y < grid.height;
|
x >= 0 && y >= 0 && x < grid.width && y < grid.height;
|
||||||
|
|
||||||
|
export const isInsideLarge = (grid: Grid, m: number, x: number, y: number) =>
|
||||||
|
x >= -m && y >= -m && x < grid.width + m && y < grid.height + m;
|
||||||
|
|
||||||
export const getColor = (grid: Grid, x: number, y: number) =>
|
export const getColor = (grid: Grid, x: number, y: number) =>
|
||||||
grid.data[getIndex(grid, x, y)];
|
grid.data[getIndex(grid, x, y)];
|
||||||
|
|
||||||
|
export const copyGrid = (grid: Grid) => ({ ...grid, data: grid.data.slice() });
|
||||||
|
|
||||||
export const setColor = (
|
export const setColor = (
|
||||||
grid: Grid,
|
grid: Grid,
|
||||||
x: number,
|
x: number,
|
||||||
|
|||||||
@@ -1,13 +1,44 @@
|
|||||||
import { Grid, Color } from "./grid";
|
import { Grid, Color, copyGrid, isInsideLarge } from "./grid";
|
||||||
|
import { Point, around4 } from "./point";
|
||||||
|
import { stepSnake, step } from "./step";
|
||||||
|
import { copySnake, snakeSelfCollide } from "./snake";
|
||||||
|
|
||||||
type Point = { x: number; y: number };
|
const isGridEmpty = (grid: Grid) => grid.data.every((x) => x === null);
|
||||||
|
|
||||||
export const computeBestRun = (
|
export const computeBestRun = (
|
||||||
pattern: Color[],
|
|
||||||
grid: Grid,
|
grid: Grid,
|
||||||
originalPosition: Point
|
snake: Point[],
|
||||||
|
options: { maxSnakeLength: number }
|
||||||
) => {
|
) => {
|
||||||
console.log(pattern, grid, originalPosition);
|
const g = copyGrid(grid);
|
||||||
|
const s = copySnake(snake);
|
||||||
|
const q: Color[] = [];
|
||||||
|
|
||||||
return [];
|
const commands: Point[] = [];
|
||||||
|
|
||||||
|
let u = 100;
|
||||||
|
|
||||||
|
while (!isGridEmpty(g) && u-- > 0) {
|
||||||
|
let direction;
|
||||||
|
|
||||||
|
for (let k = 10; k--; ) {
|
||||||
|
direction = around4[Math.floor(Math.random() * around4.length)];
|
||||||
|
|
||||||
|
const sn = copySnake(s);
|
||||||
|
stepSnake(sn, direction, options);
|
||||||
|
|
||||||
|
if (isInsideLarge(g, 1, sn[0].x, sn[0].y) && !snakeSelfCollide(sn)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
direction = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction !== undefined) {
|
||||||
|
step(g, s, q, direction, options);
|
||||||
|
commands.push(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands;
|
||||||
};
|
};
|
||||||
|
|||||||
8
packages/compute/point.ts
Normal file
8
packages/compute/point.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type Point = { x: number; y: number };
|
||||||
|
|
||||||
|
export const around4 = [
|
||||||
|
{ x: 1, y: 0 },
|
||||||
|
{ x: 0, y: -1 },
|
||||||
|
{ x: -1, y: 0 },
|
||||||
|
{ x: 0, y: 1 },
|
||||||
|
];
|
||||||
24
packages/compute/snake.ts
Normal file
24
packages/compute/snake.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { Point } from "./point";
|
||||||
|
|
||||||
|
export const snakeSelfCollideNext = (
|
||||||
|
snake: Point[],
|
||||||
|
direction: Point,
|
||||||
|
options: { maxSnakeLength: number }
|
||||||
|
) => {
|
||||||
|
const hx = snake[0].x + direction.x;
|
||||||
|
const hy = snake[0].y + direction.y;
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(options.maxSnakeLength, snake.length); i++)
|
||||||
|
if (snake[i].x === hx && snake[i].y === hy) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const snakeSelfCollide = (snake: Point[]) => {
|
||||||
|
for (let i = 1; i < snake.length; i++)
|
||||||
|
if (snake[i].x === snake[0].x && snake[i].y === snake[0].y) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const copySnake = (x: Point[]) => x.map((p) => ({ ...p }));
|
||||||
48
packages/compute/step.ts
Normal file
48
packages/compute/step.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Grid, Color, getColor, isInside, setColor } from "./grid";
|
||||||
|
import { Point } from "./point";
|
||||||
|
|
||||||
|
const moveSnake = (snake: Point[], headx: number, heady: number) => {
|
||||||
|
for (let k = snake.length - 1; k > 0; k--) {
|
||||||
|
snake[k].x = snake[k - 1].x;
|
||||||
|
snake[k].y = snake[k - 1].y;
|
||||||
|
}
|
||||||
|
snake[0].x = headx;
|
||||||
|
snake[0].y = heady;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stepSnake = (
|
||||||
|
snake: Point[],
|
||||||
|
direction: Point,
|
||||||
|
options: { maxSnakeLength: number }
|
||||||
|
) => {
|
||||||
|
const headx = snake[0].x + direction.x;
|
||||||
|
const heady = snake[0].y + direction.y;
|
||||||
|
|
||||||
|
if (snake.length === options.maxSnakeLength) {
|
||||||
|
moveSnake(snake, headx, heady);
|
||||||
|
} else {
|
||||||
|
snake.unshift({ x: headx, y: heady });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stepPicking = (grid: Grid, snake: Point[], stack: Color[]) => {
|
||||||
|
if (isInside(grid, snake[0].x, snake[0].y)) {
|
||||||
|
const c = getColor(grid, snake[0].x, snake[0].y);
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
setColor(grid, snake[0].x, snake[0].y, null);
|
||||||
|
stack.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const step = (
|
||||||
|
grid: Grid,
|
||||||
|
snake: Point[],
|
||||||
|
stack: Color[],
|
||||||
|
direction: Point,
|
||||||
|
options: { maxSnakeLength: number }
|
||||||
|
) => {
|
||||||
|
stepSnake(snake, direction, options);
|
||||||
|
stepPicking(grid, snake, stack);
|
||||||
|
};
|
||||||
@@ -1,13 +1,82 @@
|
|||||||
// import { generateGrid } from "@snk/compute/generateGrid";
|
// import { generateGrid } from "@snk/compute/generateGrid";
|
||||||
|
|
||||||
import { generateGrid } from "@snk/compute/generateGrid";
|
import { generateGrid } from "@snk/compute/generateGrid";
|
||||||
|
import { Color, copyGrid } from "@snk/compute/grid";
|
||||||
|
import { computeBestRun } from "@snk/compute";
|
||||||
|
import { step } from "@snk/compute/step";
|
||||||
|
import { drawWorld } from "@snk/draw/drawWorld";
|
||||||
|
import { Point } from "@snk/compute/point";
|
||||||
|
|
||||||
console.log("hello world");
|
const copySnake = (x: Point[]) => x.map((p) => ({ ...p }));
|
||||||
|
|
||||||
export const run = async (a: number) => {
|
export const run = async () => {
|
||||||
generateGrid(100, 100);
|
const options = {
|
||||||
|
sizeBorderRadius: 2,
|
||||||
|
sizeCell: 16,
|
||||||
|
sizeDot: 12,
|
||||||
|
colorBorder: "#1b1f230a",
|
||||||
|
colorDots: { 1: "#9be9a8", 2: "#40c463", 3: "#30a14e", 4: "#216e39" },
|
||||||
|
colorEmpty: "#ebedf0",
|
||||||
|
colorSnake: "purple",
|
||||||
|
};
|
||||||
|
|
||||||
console.log(a, generateGrid(100, 100));
|
const gameOptions = { maxSnakeLength: 5 };
|
||||||
|
|
||||||
|
const grid = generateGrid(14, 7, { colors: [1, 2, 3, 4], emptyP: 3 });
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = options.sizeCell * (grid.width + 4);
|
||||||
|
canvas.height = options.sizeCell * (grid.height + 4) + 100;
|
||||||
|
const ctx = canvas.getContext("2d")!;
|
||||||
|
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
|
||||||
|
const snake = [
|
||||||
|
{ x: 4, y: -1 },
|
||||||
|
{ x: 3, y: -1 },
|
||||||
|
{ x: 2, y: -1 },
|
||||||
|
{ x: 1, y: -1 },
|
||||||
|
{ x: 0, y: -1 },
|
||||||
|
];
|
||||||
|
const stack: Color[] = [];
|
||||||
|
|
||||||
|
const chain = computeBestRun(grid, snake, gameOptions);
|
||||||
|
|
||||||
|
const update = (x: number) => {
|
||||||
|
const s = copySnake(snake);
|
||||||
|
const q = stack.slice();
|
||||||
|
const g = copyGrid(grid);
|
||||||
|
|
||||||
|
for (let i = 0; i < x; i++) step(g, s, q, chain[i], gameOptions);
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, 9999, 9999);
|
||||||
|
drawWorld(ctx, g, s, q, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const input: any = document.createElement("input");
|
||||||
|
input.type = "range";
|
||||||
|
input.style.width = "100%";
|
||||||
|
input.min = 0;
|
||||||
|
input.max = chain.length;
|
||||||
|
input.step = 1;
|
||||||
|
input.value = 0;
|
||||||
|
input.addEventListener("input", () => update(+input.value));
|
||||||
|
document.addEventListener("click", () => input.focus());
|
||||||
|
|
||||||
|
document.body.appendChild(input);
|
||||||
|
|
||||||
|
update(+input.value);
|
||||||
|
|
||||||
|
// while (chain.length) {
|
||||||
|
// await wait(100);
|
||||||
|
|
||||||
|
// step(grid, snake, stack, chain.shift()!, gameOptions);
|
||||||
|
|
||||||
|
// ctx.clearRect(0, 0, 9999, 9999);
|
||||||
|
// drawWorld(ctx, grid, snake, stack, options);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const wait = (delay = 0) => new Promise((r) => setTimeout(r, delay));
|
||||||
};
|
};
|
||||||
|
|
||||||
run(1);
|
run();
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ const config: Configuration = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
devtool: false,
|
devtool: false,
|
||||||
|
stats: "errors-only",
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
devServer: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
42
packages/draw/drawGrid.ts
Normal file
42
packages/draw/drawGrid.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Grid, getColor, Color } from "@snk/compute/grid";
|
||||||
|
import { pathRoundedRect } from "./pathRoundedRect";
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
colorDots: Record<Color, string>;
|
||||||
|
colorEmpty: string;
|
||||||
|
colorBorder: string;
|
||||||
|
sizeCell: number;
|
||||||
|
sizeDot: number;
|
||||||
|
sizeBorderRadius: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawGrid = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
grid: Grid,
|
||||||
|
o: Options
|
||||||
|
) => {
|
||||||
|
for (let x = grid.width; x--; )
|
||||||
|
for (let y = grid.height; y--; ) {
|
||||||
|
const c = getColor(grid, x, y);
|
||||||
|
// @ts-ignore
|
||||||
|
const color = c === null ? o.colorEmpty : o.colorDots[c];
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(
|
||||||
|
x * o.sizeCell + (o.sizeCell - o.sizeDot) / 2,
|
||||||
|
y * o.sizeCell + (o.sizeCell - o.sizeDot) / 2
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.strokeStyle = o.colorBorder;
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
pathRoundedRect(ctx, o.sizeDot, o.sizeDot, o.sizeBorderRadius);
|
||||||
|
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
};
|
||||||
63
packages/draw/drawWorld.ts
Normal file
63
packages/draw/drawWorld.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Grid, Color } from "@snk/compute/grid";
|
||||||
|
import { pathRoundedRect } from "./pathRoundedRect";
|
||||||
|
import { Point } from "@snk/compute/point";
|
||||||
|
import { drawGrid } from "./drawGrid";
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
colorDots: Record<Color, string>;
|
||||||
|
colorEmpty: string;
|
||||||
|
colorBorder: string;
|
||||||
|
colorSnake: string;
|
||||||
|
sizeCell: number;
|
||||||
|
sizeDot: number;
|
||||||
|
sizeBorderRadius: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawSnake = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
snake: Point[],
|
||||||
|
o: Options
|
||||||
|
) => {
|
||||||
|
for (let i = 0; i < snake.length; i++) {
|
||||||
|
const u = (i + 1) * 0.6;
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = o.colorSnake;
|
||||||
|
ctx.translate(snake[i].x * o.sizeCell + u, snake[i].y * o.sizeCell + u);
|
||||||
|
ctx.beginPath();
|
||||||
|
pathRoundedRect(
|
||||||
|
ctx,
|
||||||
|
o.sizeCell - u * 2,
|
||||||
|
o.sizeCell - u * 2,
|
||||||
|
(o.sizeCell - u * 2) * 0.25
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawWorld = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
grid: Grid,
|
||||||
|
snake: Point[],
|
||||||
|
stack: Color[],
|
||||||
|
o: Options
|
||||||
|
) => {
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
ctx.translate(2 * o.sizeCell, 2 * o.sizeCell);
|
||||||
|
drawGrid(ctx, grid, o);
|
||||||
|
drawSnake(ctx, snake, o);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
const m = 5;
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(o.sizeCell, (grid.height + 4) * o.sizeCell);
|
||||||
|
for (let i = 0; i < stack.length; i++) {
|
||||||
|
ctx.fillStyle = o.colorDots[stack[i]];
|
||||||
|
ctx.fillRect(i * m, 0, m, 10);
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
7
packages/draw/package.json
Normal file
7
packages/draw/package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@snk/draw",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@snk/compute": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
packages/draw/pathRoundedRect.ts
Normal file
12
packages/draw/pathRoundedRect.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const pathRoundedRect = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
borderRadius: number
|
||||||
|
) => {
|
||||||
|
ctx.moveTo(borderRadius, 0);
|
||||||
|
ctx.arcTo(width, 0, width, height, borderRadius);
|
||||||
|
ctx.arcTo(width, height, 0, height, borderRadius);
|
||||||
|
ctx.arcTo(0, height, 0, 0, borderRadius);
|
||||||
|
ctx.arcTo(0, 0, width, 0, borderRadius);
|
||||||
|
};
|
||||||
@@ -6,8 +6,7 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true
|
||||||
"isolatedModules": true
|
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user