UNPKG

js-draw

Version:

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

94 lines (93 loc) 3.38 kB
import { Path, PathCommandType } from '@js-draw/math'; import Stroke from '../Stroke.mjs'; import makeSnapToGridAutocorrect from './autocorrect/makeSnapToGridAutocorrect.mjs'; /** * Creates a stroke builder that generates arrows. * * Example: * [[include:doc-pages/inline-examples/changing-pen-types.md]] */ export const makeArrowBuilder = makeSnapToGridAutocorrect((initialPoint, viewport) => { return new ArrowBuilder(initialPoint, viewport); }); export default class ArrowBuilder { constructor(startPoint, viewport) { this.startPoint = startPoint; this.viewport = viewport; this.endPoint = startPoint; } getLineWidth() { return Math.max(this.endPoint.width, this.startPoint.width); } getBBox() { const preview = this.buildPreview(); return preview.getBBox(); } buildPreview() { const lineStartPoint = this.startPoint.pos; const endPoint = this.endPoint.pos; const toEnd = endPoint.minus(lineStartPoint).normalized(); const arrowLength = endPoint.distanceTo(lineStartPoint); // Ensure that the arrow tip is smaller than the arrow. const arrowTipSize = Math.min(this.getLineWidth(), arrowLength / 2); const startSize = this.startPoint.width / 2; const endSize = this.endPoint.width / 2; const arrowTipBase = endPoint.minus(toEnd.times(arrowTipSize)); // Scaled normal vectors. const lineNormal = toEnd.orthog(); const scaledStartNormal = lineNormal.times(startSize); const scaledBaseNormal = lineNormal.times(endSize); const path = new Path(arrowTipBase.minus(scaledBaseNormal), [ // Stem { kind: PathCommandType.LineTo, point: lineStartPoint.minus(scaledStartNormal), }, { kind: PathCommandType.LineTo, point: lineStartPoint.plus(scaledStartNormal), }, { kind: PathCommandType.LineTo, point: arrowTipBase.plus(scaledBaseNormal), }, // Head { kind: PathCommandType.LineTo, point: arrowTipBase.plus(lineNormal.times(arrowTipSize).plus(scaledBaseNormal)), }, { kind: PathCommandType.LineTo, point: endPoint.plus(toEnd.times(endSize)), }, { kind: PathCommandType.LineTo, point: arrowTipBase.plus(lineNormal.times(-arrowTipSize).minus(scaledBaseNormal)), }, { kind: PathCommandType.LineTo, point: arrowTipBase.minus(scaledBaseNormal), }, // Round all points in the arrow (to remove unnecessary decimal places) ]).mapPoints((point) => this.viewport.roundPoint(point)); const preview = new Stroke([ { startPoint: path.startPoint, commands: path.parts, style: { fill: this.startPoint.color, }, }, ]); return preview; } build() { return this.buildPreview(); } preview(renderer) { this.buildPreview().render(renderer); } addPoint(point) { this.endPoint = point; } }