UNPKG

tldraw

Version:

A tiny little drawing editor.

176 lines (175 loc) • 7.53 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var getStrokeOutlinePoints_exports = {}; __export(getStrokeOutlinePoints_exports, { getStrokeOutlinePoints: () => getStrokeOutlinePoints, getStrokeOutlineTracks: () => getStrokeOutlineTracks }); module.exports = __toCommonJS(getStrokeOutlinePoints_exports); var import_editor = require("@tldraw/editor"); const { PI } = Math; const FIXED_PI = PI + 1e-4; function getStrokeOutlineTracks(strokePoints, options = {}) { const { size = 16, smoothing = 0.5 } = options; if (strokePoints.length === 0 || size <= 0) { return { left: [], right: [] }; } const firstStrokePoint = strokePoints[0]; const lastStrokePoint = strokePoints[strokePoints.length - 1]; const totalLength = lastStrokePoint.runningLength; const minDistance = Math.pow(size * smoothing, 2); const leftPts = []; const rightPts = []; let prevVector = strokePoints[0].vector; let pl = strokePoints[0].point; let pr = pl; let tl = pl; let tr = pr; let isPrevPointSharpCorner = false; let strokePoint; for (let i = 0; i < strokePoints.length; i++) { strokePoint = strokePoints[i]; const { point, vector } = strokePoints[i]; const prevDpr = strokePoint.vector.dpr(prevVector); const nextVector = (i < strokePoints.length - 1 ? strokePoints[i + 1] : strokePoints[i]).vector; const nextDpr = i < strokePoints.length - 1 ? nextVector.dpr(strokePoint.vector) : 1; const isPointSharpCorner = prevDpr < 0 && !isPrevPointSharpCorner; const isNextPointSharpCorner = nextDpr !== null && nextDpr < 0.2; if (isPointSharpCorner || isNextPointSharpCorner) { if (nextDpr > -0.62 && totalLength - strokePoint.runningLength > strokePoint.radius) { const offset2 = prevVector.clone().mul(strokePoint.radius); const cpr = prevVector.clone().cpr(nextVector); if (cpr < 0) { tl = import_editor.Vec.Add(point, offset2); tr = import_editor.Vec.Sub(point, offset2); } else { tl = import_editor.Vec.Sub(point, offset2); tr = import_editor.Vec.Add(point, offset2); } leftPts.push(tl); rightPts.push(tr); } else { const offset2 = prevVector.clone().mul(strokePoint.radius).per(); const start = import_editor.Vec.Sub(strokePoint.input, offset2); for (let step = 1 / 13, t = 0; t < 1; t += step) { tl = import_editor.Vec.RotWith(start, strokePoint.input, FIXED_PI * t); leftPts.push(tl); tr = import_editor.Vec.RotWith(start, strokePoint.input, FIXED_PI + FIXED_PI * -t); rightPts.push(tr); } } pl = tl; pr = tr; if (isNextPointSharpCorner) { isPrevPointSharpCorner = true; } continue; } isPrevPointSharpCorner = false; if (strokePoint === firstStrokePoint || strokePoint === lastStrokePoint) { const offset2 = import_editor.Vec.Per(vector).mul(strokePoint.radius); leftPts.push(import_editor.Vec.Sub(point, offset2)); rightPts.push(import_editor.Vec.Add(point, offset2)); continue; } const offset = import_editor.Vec.Lrp(nextVector, vector, nextDpr).per().mul(strokePoint.radius); tl = import_editor.Vec.Sub(point, offset); if (i <= 1 || import_editor.Vec.Dist2(pl, tl) > minDistance) { leftPts.push(tl); pl = tl; } tr = import_editor.Vec.Add(point, offset); if (i <= 1 || import_editor.Vec.Dist2(pr, tr) > minDistance) { rightPts.push(tr); pr = tr; } prevVector = vector; continue; } return { left: leftPts, right: rightPts }; } function getStrokeOutlinePoints(strokePoints, options = {}) { const { size = 16, start = {}, end = {}, last: isComplete = false } = options; const { cap: capStart = true } = start; const { cap: capEnd = true } = end; if (strokePoints.length === 0 || size <= 0) { return []; } const firstStrokePoint = strokePoints[0]; const lastStrokePoint = strokePoints[strokePoints.length - 1]; const totalLength = lastStrokePoint.runningLength; const taperStart = start.taper === false ? 0 : start.taper === true ? Math.max(size, totalLength) : start.taper; const taperEnd = end.taper === false ? 0 : end.taper === true ? Math.max(size, totalLength) : end.taper; const { left: leftPts, right: rightPts } = getStrokeOutlineTracks(strokePoints, options); const firstPoint = firstStrokePoint.point; const lastPoint = strokePoints.length > 1 ? strokePoints[strokePoints.length - 1].point : import_editor.Vec.AddXY(firstStrokePoint.point, 1, 1); if (strokePoints.length === 1) { if (!(taperStart || taperEnd) || isComplete) { const start2 = import_editor.Vec.Add( firstPoint, import_editor.Vec.Sub(firstPoint, lastPoint).uni().per().mul(-firstStrokePoint.radius) ); const dotPts = []; for (let step = 1 / 13, t = step; t <= 1; t += step) { dotPts.push(import_editor.Vec.RotWith(start2, firstPoint, FIXED_PI * 2 * t)); } return dotPts; } } const startCap = []; if (taperStart || taperEnd && strokePoints.length === 1) { } else if (capStart) { for (let step = 1 / 8, t = step; t <= 1; t += step) { const pt = import_editor.Vec.RotWith(rightPts[0], firstPoint, FIXED_PI * t); startCap.push(pt); } } else { const cornersVector = import_editor.Vec.Sub(leftPts[0], rightPts[0]); const offsetA = import_editor.Vec.Mul(cornersVector, 0.5); const offsetB = import_editor.Vec.Mul(cornersVector, 0.51); startCap.push( import_editor.Vec.Sub(firstPoint, offsetA), import_editor.Vec.Sub(firstPoint, offsetB), import_editor.Vec.Add(firstPoint, offsetB), import_editor.Vec.Add(firstPoint, offsetA) ); } const endCap = []; const direction = lastStrokePoint.vector.clone().per().neg(); if (taperEnd || taperStart && strokePoints.length === 1) { endCap.push(lastPoint); } else if (capEnd) { const start2 = import_editor.Vec.Add(lastPoint, import_editor.Vec.Mul(direction, lastStrokePoint.radius)); for (let step = 1 / 29, t = step; t < 1; t += step) { endCap.push(import_editor.Vec.RotWith(start2, lastPoint, FIXED_PI * 3 * t)); } } else { endCap.push( import_editor.Vec.Add(lastPoint, import_editor.Vec.Mul(direction, lastStrokePoint.radius)), import_editor.Vec.Add(lastPoint, import_editor.Vec.Mul(direction, lastStrokePoint.radius * 0.99)), import_editor.Vec.Sub(lastPoint, import_editor.Vec.Mul(direction, lastStrokePoint.radius * 0.99)), import_editor.Vec.Sub(lastPoint, import_editor.Vec.Mul(direction, lastStrokePoint.radius)) ); } return leftPts.concat(endCap, rightPts.reverse(), startCap); } //# sourceMappingURL=getStrokeOutlinePoints.js.map