UNPKG

toosoon-utils

Version:
138 lines (137 loc) 5.03 kB
import { EPSILON, PI, TWO_PI, TAU_EPSILON } from '../../constants'; import { toDegrees } from '../../geometry'; import PolylineCurve from './PolylineCurve'; import CatmullRomCurve from './CatmulRomCurve'; import SplineCurve from './SplineCurve'; import EllipseCurve from './EllipseCurve'; import ArcCurve from './ArcCurve'; import Path from './Path'; /** * Utility class for manipulating connected curves and generating SVG path * * It works by serializing 2D Canvas API to SVG path data * * @exports * @class PathSVG * @extends Path */ export default class PathSVG extends Path { /** * Resolution used for Catmul-Rom curve and Spline curve approximations */ curveResolution; constructor({ curveResolution = 5 }) { super(); this.curveResolution = curveResolution; } _commands = ``; moveTo(x, y) { super.moveTo(x, y); this._write(`M${x},${y}`); return this; } lineTo(x, y) { super.lineTo(x, y); this._writeLineTo(x, y); return this; } polylineTo(points) { super.polylineTo(points); points.forEach(([x, y]) => this._writeLineTo(x, y)); return this; } // public arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): this { // super.arcTo(x1, y1, x2, y2, radius); // return this; // } quadraticCurveTo(cpx, cpy, x2, y2) { super.quadraticCurveTo(cpx, cpy, x2, y2); this._write(`Q${cpx},${cpy},${x2},${y2}`); return this; } bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) { super.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2); this._write(`C${cp1x},${cp1y},${cp2x},${cp2y},${x2},${y2}`); return this; } catmulRomCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) { super.catmulRomCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2); const curve = this.curves[this.curves.length - 1]; if (curve instanceof CatmullRomCurve) { const divisions = (curve.getLength() * this.curveResolution) / 100; curve.getSpacedPoints(divisions).forEach(([x, y]) => this._writeLineTo(x, y)); } return this; } splineTo(points) { super.splineTo(points); const curve = this.curves[this.curves.length - 1]; if (curve instanceof SplineCurve) { const divisions = (curve.getLength() * this.curveResolution) / 100; curve.getSpacedPoints(divisions).forEach(([x, y]) => this._writeLineTo(x, y)); } return this; } ellipse(cx, cy, rx, ry, rotation = 0, startAngle = 0, endAngle = TWO_PI, counterclockwise) { super.ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise); const curve = this.curves[this.curves.length - 1]; if (curve instanceof EllipseCurve) { this._writeArc(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise); } return this; } arc(cx, cy, radius, startAngle, endAngle, counterclockwise) { super.arc(cx, cy, radius, startAngle, endAngle, counterclockwise); const curve = this.curves[this.curves.length - 1]; if (curve instanceof ArcCurve) { this._writeArc(cx, cy, radius, radius, 0, startAngle, endAngle, counterclockwise); } return this; } rect(x, y, width, height) { super.rect(x, y, width, height); const curve = this.curves[this.curves.length - 1]; if (curve instanceof PolylineCurve) { curve.points?.forEach(([x, y]) => this._writeLineTo(x, y)); } return this; } closePath() { super.closePath(); this._write(`Z`); return this; } _writeLineTo(x, y) { this._write(`L${x},${y}`); } _writeArc(cx, cy, rx, ry, rotation = 0, startAngle = 0, endAngle = TWO_PI, counterclockwise) { let deltaAngle = counterclockwise ? startAngle - endAngle : endAngle - startAngle; if (deltaAngle < 0) deltaAngle = (deltaAngle % TWO_PI) + TWO_PI; const xAxisRotation = toDegrees(rotation); const largeArcFlag = deltaAngle >= PI ? 1 : 0; const sweepFlag = counterclockwise ? 0 : 1; if (deltaAngle > TAU_EPSILON) { const dx = Math.cos(startAngle) * rx; const dy = Math.sin(startAngle) * ry; this._write(`A${rx},${ry},${xAxisRotation},1,${sweepFlag},${cx - dx},${cy - dy}`); this._write(`A${rx},${ry},${xAxisRotation},1,${sweepFlag},${cx + dx},${cy + dy}`); } else if (deltaAngle > EPSILON) { const dx = Math.cos(endAngle) * rx; const dy = Math.sin(endAngle) * ry; this._write(`A${rx},${ry},${xAxisRotation},${largeArcFlag},${sweepFlag},${cx + dx},${cy + dy}`); } } _write(command) { this._commands += `${command}`; } /** * Return SVG path data string * * @returns {string} */ toString() { return this._commands; } }