Spaces:
Sleeping
Sleeping
| import type { ElementKind } from "@/lib/tatting"; | |
| type Props = { | |
| kind: ElementKind; | |
| picots: number; | |
| radius: number; | |
| color: string; | |
| }; | |
| const r3 = (n: number) => Math.round(n * 1000) / 1000; | |
| /** Renders a tatting motif centered at (0,0) in local coordinates. */ | |
| export function Shape({ kind, picots, radius, color }: Props) { | |
| const picotR = Math.max(3, radius * 0.11); | |
| if (kind === "ring") { | |
| const dots = Array.from({ length: picots }, (_, i) => { | |
| const a = (i / picots) * Math.PI * 2 - Math.PI / 2; | |
| return { | |
| cx: r3(Math.cos(a) * (radius + picotR * 0.7)), | |
| cy: r3(Math.sin(a) * (radius + picotR * 0.7)), | |
| }; | |
| }); | |
| return ( | |
| <g fill="none" stroke={color} strokeWidth={2.5} strokeLinecap="round"> | |
| <circle cx={0} cy={0} r={radius} /> | |
| <circle cx={0} cy={0} r={radius - 5} strokeWidth={1} opacity={0.45} /> | |
| {dots.map((d, i) => ( | |
| <circle key={i} cx={d.cx} cy={d.cy} r={picotR} /> | |
| ))} | |
| </g> | |
| ); | |
| } | |
| // Arc: half-circle bow with picots along the outer edge | |
| const start = { x: -radius, y: 0 }; | |
| const end = { x: radius, y: 0 }; | |
| const dots = Array.from({ length: picots }, (_, i) => { | |
| const t = (i + 1) / (picots + 1); | |
| const a = Math.PI - t * Math.PI; | |
| const rr = radius + picotR * 0.7; | |
| return { cx: r3(Math.cos(a) * rr), cy: r3(-Math.sin(a) * rr) }; | |
| }); | |
| return ( | |
| <g fill="none" stroke={color} strokeWidth={2.5} strokeLinecap="round"> | |
| <path d={`M ${start.x} ${start.y} A ${radius} ${radius} 0 0 1 ${end.x} ${end.y}`} /> | |
| <path | |
| d={`M ${start.x + 5} 0 A ${radius - 5} ${radius - 5} 0 0 1 ${end.x - 5} 0`} | |
| strokeWidth={1} | |
| opacity={0.45} | |
| /> | |
| {dots.map((d, i) => ( | |
| <circle key={i} cx={d.cx} cy={d.cy} r={picotR} /> | |
| ))} | |
| </g> | |
| ); | |
| } | |