summaryrefslogtreecommitdiff
path: root/apps/web/src/graphics/definitions/star.ts
blob: 3aa40fee0ecc8276d228375ac03a4e53ddd81f2f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import type { ParamDefinition } from "@/params";
import { applyAlignedStroke } from "../stroke";
import { STROKE_ALIGN_PARAM, type GraphicStrokeAlign } from "./shared";
import type { GraphicDefinition } from "../types";

interface StarParams {
	fill: string;
	stroke: string;
	strokeWidth: number;
	strokeAlign: GraphicStrokeAlign;
	points: number;
	depth: number;
}

const STAR_PARAMS: ParamDefinition<keyof StarParams & string>[] = [
	{
		key: "fill",
		label: "Fill",
		type: "color",
		default: "#ffffff",
	},
	{
		key: "stroke",
		label: "Color",
		type: "color",
		default: "#000000",
		group: "stroke",
	},
	{
		key: "strokeWidth",
		label: "Width",
		type: "number",
		default: 0,
		min: 0,
		max: 64,
		step: 1,
		shortLabel: "W",
		group: "stroke",
	},
	STROKE_ALIGN_PARAM,
	{
		key: "points",
		label: "Points",
		type: "number",
		default: 5,
		min: 3,
		max: 12,
		step: 1,
		shortLabel: "P",
	},
	{
		key: "depth",
		label: "Depth",
		type: "number",
		default: 45,
		min: 1,
		max: 99,
		step: 1,
		shortLabel: "D",
	},
];

export const starGraphicDefinition: GraphicDefinition = {
	id: "star",
	name: "Star",
	keywords: ["star", "sparkle", "burst"],
	params: STAR_PARAMS,
	render({ ctx, params, width, height }) {
		const fill = String(params.fill ?? "#ffffff");
		const stroke = String(params.stroke ?? "#000000");
		const strokeWidth = Math.max(0, Number(params.strokeWidth ?? 0));
		const strokeAlign = (params.strokeAlign ?? "center") as GraphicStrokeAlign;
		const points = Math.max(3, Math.min(12, Math.round(Number(params.points ?? 5))));
		const depth = Math.max(1, Math.min(99, Number(params.depth ?? 45))) / 100;
		const inset = strokeAlign === "center" ? strokeWidth / 2 : 0;
		const outerRadius = Math.max(1, Math.min(width, height) / 2 - inset);
		const innerRadius = outerRadius * depth;
		const centerX = width / 2;
		const centerY = height / 2;

		ctx.clearRect(0, 0, width, height);
		const path = new Path2D();
		for (let index = 0; index < points * 2; index++) {
			const radius = index % 2 === 0 ? outerRadius : innerRadius;
			const angle = -Math.PI / 2 + (index * Math.PI) / points;
			const x = centerX + Math.cos(angle) * radius;
			const y = centerY + Math.sin(angle) * radius;

			if (index === 0) {
				path.moveTo(x, y);
			} else {
				path.lineTo(x, y);
			}
		}
		path.closePath();
		ctx.fillStyle = fill;
		ctx.fill(path);

		if (strokeWidth > 0) {
			applyAlignedStroke({
				ctx,
				path,
				strokeWidth,
				strokeAlign,
				strokeColor: stroke,
			});
		}
	},
};