packages/core/src/lib/geometry/star.ts
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import type { LayoutBox } from '../layout/types.js';
2 import { createPolygonPath } from './polygon.js';
3 import {
4 resolveNumericCornerRadii,
5 type RoundedCornerPathOptions,
6 } from './rounded-corners.js';
7 import {
8 finiteNumber,
9 integerAtLeast,
10 nonnegativeNumber,
11 pathRangeError,
12 type GeometryErrorFactory,
13 } from './validation.js';
14
15 /**
16 * Geometry and construction options for a star path.
17 *
18 * @see {@link RoundedCornerPathOptions}
19 * @see {@link createStarPath}
20 */
21 export interface StarPathOptions extends RoundedCornerPathOptions {
22 /** Points defining the geometry or reported by the contact query. See {@link StarPathOptions}. */
23 points: number;
24 /** Horizontal coordinate of the center. See {@link StarPathOptions}. */
25 cx?: number;
26 /** Vertical coordinate of the center. See {@link StarPathOptions}. */
27 cy?: number;
28 /** Radius of the outer boundary. See {@link StarPathOptions}. */
29 outerRadius: number;
30 /** Radius of the inner boundary. See {@link StarPathOptions}. */
31 innerRadius: number;
32 /** Rotation applied to the containing geometry. See {@link StarPathOptions}. */
33 rotation?: number;
34 }
35
36 export interface ResolvedStarGeometry {
37 path: Path2D;
38 vertices: readonly (readonly [number, number])[];
39 bounds?: LayoutBox;
40 }
41
42 export function resolveStarGeometry(
43 options: Readonly<StarPathOptions>,
44 errorFactory: GeometryErrorFactory = pathRangeError,
45 ): ResolvedStarGeometry {
46 const points = integerAtLeast(options.points, 3, 'points', errorFactory);
47 const outerRadius = nonnegativeNumber(
48 options.outerRadius,
49 'outerRadius',
50 errorFactory,
51 );
52 const innerRadius = nonnegativeNumber(
53 options.innerRadius,
54 'innerRadius',
55 errorFactory,
56 );
57 if (innerRadius > outerRadius) {
58 throw errorFactory(
59 'innerRadius',
60 innerRadius,
61 `must not exceed outerRadius ${outerRadius}`,
62 );
63 }
64 const cx = finiteNumber(options.cx ?? 0, 'cx', errorFactory);
65 const cy = finiteNumber(options.cy ?? 0, 'cy', errorFactory);
66 const rotation = finiteNumber(
67 options.rotation ?? -Math.PI / 2,
68 'rotation',
69 errorFactory,
70 );
71 const cornerCount = points * 2;
72 const radii = resolveNumericCornerRadii(
73 cornerCount,
74 options,
75 errorFactory,
76 );
77
78 let left = Number.POSITIVE_INFINITY;
79 let right = Number.NEGATIVE_INFINITY;
80 let top = Number.POSITIVE_INFINITY;
81 let bottom = Number.NEGATIVE_INFINITY;
82 const vertices = Array.from({ length: cornerCount }, (_, index) => {
83 const angle = rotation + (index * Math.PI) / points;
84 const radius = index % 2 === 0 ? outerRadius : innerRadius;
85 const vertex = [
86 cx + radius * Math.cos(angle),
87 cy + radius * Math.sin(angle),
88 ] as const;
89 left = Math.min(left, vertex[0]);
90 right = Math.max(right, vertex[0]);
91 top = Math.min(top, vertex[1]);
92 bottom = Math.max(bottom, vertex[1]);
93 return vertex;
94 });
95
96 if (outerRadius === 0 || innerRadius === 0) {
97 return {
98 path: new Path2D(),
99 vertices,
100 bounds: {
101 x: left,
102 y: top,
103 width: right - left,
104 height: bottom - top,
105 },
106 };
107 }
108
109 return {
110 path: createPolygonPath({ coords: vertices, cornerRadii: radii }),
111 vertices,
112 bounds: {
113 x: left,
114 y: top,
115 width: right - left,
116 height: bottom - top,
117 },
118 };
119 }
120
121 /**
122 * Creates a Canvas Path2D for star geometry from validated options.
123 *
124 * @param options - Star center, inner and outer radii, point count, and rotation. See
125 * {@link StarPathOptions} .
126 * @returns A native Canvas path containing the star.
127 *
128 * @see {@link StarPathOptions}
129 */
130 export function createStarPath(
131 options: Readonly<StarPathOptions>,
132 ): Path2D {
133 return resolveStarGeometry(options).path;
134 }
135
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.