UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

76 lines (75 loc) 3 kB
import { Vec2, Path, PathCommandType, Color4 } from '@js-draw/math'; import { pathToRenderable } from '../../rendering/RenderablePathSpec.mjs'; import Viewport from '../../Viewport.mjs'; import Stroke from '../Stroke.mjs'; import makeSnapToGridAutocorrect from './autocorrect/makeSnapToGridAutocorrect.mjs'; /** * Creates a stroke builder that generates outlined circles. * * Example: * [[include:doc-pages/inline-examples/changing-pen-types.md]] */ export const makeOutlinedCircleBuilder = makeSnapToGridAutocorrect((initialPoint, viewport) => { return new CircleBuilder(initialPoint, viewport); }); class CircleBuilder { constructor(startPoint, viewport) { this.startPoint = startPoint; this.viewport = viewport; // Initially, the start and end points are the same. this.endPoint = startPoint; } getBBox() { const preview = this.buildPreview(); return preview.getBBox(); } buildPreview() { const pathCommands = []; const numDivisions = 6; const stepSize = (Math.PI * 2) / numDivisions; // Round the stroke width so that when exported it doesn't have unnecessary trailing decimals. const strokeWidth = Viewport.roundPoint(this.endPoint.width, 5 / this.viewport.getScaleFactor()); const center = this.startPoint.pos.lerp(this.endPoint.pos, 0.5); const startEndDelta = this.endPoint.pos.minus(center); const radius = startEndDelta.length() - strokeWidth / 2; const startPoint = center.plus(Vec2.of(radius, 0)); for (let t = stepSize; t <= Math.PI * 2; t += stepSize) { const endPoint = Vec2.of(radius * Math.cos(t), -radius * Math.sin(t)).plus(center); // controlPointRadiusScale is selected to make the circles appear circular and // **does** depend on stepSize. const controlPointRadiusScale = 1.141; const controlPoint = Vec2.of(Math.cos(t - stepSize / 2), -Math.sin(t - stepSize / 2)) .times(radius * controlPointRadiusScale) .plus(center); pathCommands.push({ kind: PathCommandType.QuadraticBezierTo, controlPoint, endPoint, }); } pathCommands.push({ kind: PathCommandType.LineTo, point: startPoint, }); const path = new Path(startPoint, pathCommands).mapPoints((point) => this.viewport.roundPoint(point)); const preview = new Stroke([ pathToRenderable(path, { fill: Color4.transparent, stroke: { width: strokeWidth, color: this.endPoint.color, }, }), ]); return preview; } build() { return this.buildPreview(); } preview(renderer) { this.buildPreview().render(renderer); } addPoint(point) { this.endPoint = point; } }