91 lines
2.7 KiB
JavaScript
91 lines
2.7 KiB
JavaScript
|
import {Polygon as SVGPolygon} from '@svgdotjs/svg.js';
|
||
|
import {Geo} from './Geo.js';
|
||
|
import {TAU} from './helpers.js';
|
||
|
/**
|
||
|
* Creates an SVG star shape from Tldraw v2 JSON data.
|
||
|
*
|
||
|
* @class Star
|
||
|
* @extends {Geo}
|
||
|
*/
|
||
|
export class Star extends Geo {
|
||
|
/**
|
||
|
* Calculates the vertices of an n-point star.
|
||
|
*
|
||
|
* @param {number} w - The width of the bounding box.
|
||
|
* @param {number} h - The height of the bounding box.
|
||
|
* @param {number} n - The number of points on the star.
|
||
|
* @return {Array} - An array of {x, y} objects representing star vertices.
|
||
|
* @see {@link https://github.com/tldraw/tldraw/blob/main/packages/editor/src/lib/primitives/utils.ts} Adapted from Tldraw.
|
||
|
*/
|
||
|
getStarVertices(w, h, n) {
|
||
|
const sides = n;
|
||
|
const step = TAU / sides / 2;
|
||
|
|
||
|
const rightMostIndex = Math.floor(sides / 4) * 2;
|
||
|
const leftMostIndex = sides * 2 - rightMostIndex;
|
||
|
const topMostIndex = 0;
|
||
|
const bottomMostIndex = Math.floor(sides / 2) * 2;
|
||
|
const maxX = (Math.cos(-(TAU/4) + rightMostIndex * step) * w) / 2;
|
||
|
const minX = (Math.cos(-(TAU/4) + leftMostIndex * step) * w) / 2;
|
||
|
const minY = (Math.sin(-(TAU/4) + topMostIndex * step) * h) / 2;
|
||
|
const maxY = (Math.sin(-(TAU/4) + bottomMostIndex * step) * h) / 2;
|
||
|
|
||
|
const diffX = w - Math.abs(maxX - minX);
|
||
|
const diffY = h - Math.abs(maxY - minY);
|
||
|
|
||
|
const offsetX = w / 2 + minX - (w / 2 - maxX);
|
||
|
const offsetY = h / 2 + minY - (h / 2 - maxY);
|
||
|
|
||
|
const ratio = 1;
|
||
|
const cx = (w - offsetX) / 2;
|
||
|
const cy = (h - offsetY) / 2;
|
||
|
|
||
|
const ox = (w + diffX) / 2;
|
||
|
const oy = (h + diffY) / 2;
|
||
|
const ix = (ox * ratio) / 2;
|
||
|
const iy = (oy * ratio) / 2;
|
||
|
|
||
|
const points = Array.from(Array(sides * 2)).map((_, i) => {
|
||
|
const theta = -(TAU/4) + i * step;
|
||
|
return {
|
||
|
x: cx + (i % 2 ? ix : ox) * Math.cos(theta),
|
||
|
y: cy + (i % 2 ? iy : oy) * Math.sin(theta),
|
||
|
};
|
||
|
});
|
||
|
|
||
|
return points;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draws a star shape on the SVG canvas.
|
||
|
* @return {G} Returns the SVG group element containing the star.
|
||
|
*/
|
||
|
draw() {
|
||
|
const width = this.w;
|
||
|
const height = this.h + this.growY;
|
||
|
|
||
|
// Get the vertices of the star
|
||
|
const pointsOnPerimeter = this.getStarVertices(width, height, 5);
|
||
|
|
||
|
// Convert the vertices to SVG polygon points format
|
||
|
const points = pointsOnPerimeter.map((p) => `${p.x},${p.y}`).join(' ');
|
||
|
|
||
|
// Create the SVG polygon
|
||
|
const starGroup = this.shapeGroup;
|
||
|
const star = new SVGPolygon({
|
||
|
points,
|
||
|
'stroke': this.shapeColor,
|
||
|
'stroke-width': this.thickness,
|
||
|
'style': this.dasharray,
|
||
|
});
|
||
|
|
||
|
// Fill the polygon if required
|
||
|
this.setFill(star);
|
||
|
starGroup.add(star);
|
||
|
|
||
|
this.drawLabel(starGroup);
|
||
|
|
||
|
return starGroup;
|
||
|
}
|
||
|
}
|