Skip to content

packages/core/src/lib/geometry/star.ts

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

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 built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.