Files
snk/packages/draw/drawCircleStack.ts
2025-02-20 19:58:59 +07:00

87 lines
1.9 KiB
TypeScript

import { pathRoundedRect } from "./pathRoundedRect";
import type { Color } from "@snk/types/grid";
import type { Point } from "@snk/types/point";
type Options = {
colorDots: Record<Color, string>;
colorBorder: string;
sizeCell: number;
sizeDot: number;
sizeBorderRadius: number;
};
const isInsideCircle = (x: number, y: number, r: number) => {
const l = 6;
let k = 0;
for (let dx = 0; dx < l; dx++)
for (let dy = 0; dy < l; dy++) {
const ux = x + (dx + 0.5) / l;
const uy = y + (dy + 0.5) / l;
if (ux * ux + uy * uy < r * r) k++;
}
return k > l * l * 0.6;
};
export const getCellPath = (n: number): Point[] => {
const l = Math.ceil(Math.sqrt(n));
const cells = [];
for (let x = -l; x <= l; x++)
for (let y = -l; y <= l; y++) {
const a = (Math.atan2(y, x) + (5 * Math.PI) / 2) % (Math.PI * 2);
let r = 0;
while (!isInsideCircle(x, y, r + 0.5)) r++;
cells.push({ x, y, f: r * 100 + a });
}
return cells.sort((a, b) => a.f - b.f).slice(0, n);
};
export const cellPath = getCellPath(52 * 7 + 5);
export const getCircleSize = (n: number) => {
const c = cellPath.slice(0, n);
const xs = c.map((p) => p.x);
const ys = c.map((p) => p.y);
return {
max: { x: Math.max(0, ...xs), y: Math.max(0, ...ys) },
min: { x: Math.min(0, ...xs), y: Math.min(0, ...ys) },
};
};
export const drawCircleStack = (
ctx: CanvasRenderingContext2D,
stack: Color[],
o: Options,
) => {
for (let i = stack.length; i--; ) {
const { x, y } = cellPath[i];
ctx.save();
ctx.translate(
x * o.sizeCell + (o.sizeCell - o.sizeDot) / 2,
y * o.sizeCell + (o.sizeCell - o.sizeDot) / 2,
);
//@ts-ignore
ctx.fillStyle = o.colorDots[stack[i]];
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();
}
};