js-draw
Version:
Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.
129 lines (128 loc) • 4.71 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.makePolylineBuilder = void 0;
const math_1 = require("@js-draw/math");
const Stroke_1 = __importDefault(require("../Stroke"));
const Viewport_1 = __importDefault(require("../../Viewport"));
const makeShapeFitAutocorrect_1 = __importDefault(require("./autocorrect/makeShapeFitAutocorrect"));
/**
* Creates a freehand line builder that creates strokes from line segments
* rather than Bézier curves.
*
* Example:
* [[include:doc-pages/inline-examples/changing-pen-types.md]]
*/
exports.makePolylineBuilder = (0, makeShapeFitAutocorrect_1.default)((initialPoint, viewport) => {
// Fit to a value slightly smaller than the pixel size. A larger value can
// cause the stroke to appear jagged at some zoom levels.
const minFit = viewport.getSizeOfPixelOnCanvas() * 0.65;
return new PolylineBuilder(initialPoint, minFit, viewport);
});
class PolylineBuilder {
constructor(startPoint, minFitAllowed, viewport) {
this.minFitAllowed = minFitAllowed;
this.viewport = viewport;
this.parts = [];
this.widthAverageNumSamples = 1;
this.lastLineSegment = null;
this.averageWidth = startPoint.width;
this.startPoint = {
...startPoint,
pos: this.roundPoint(startPoint.pos),
};
this.lastPoint = this.startPoint.pos;
this.bbox = new math_1.Rect2(this.startPoint.pos.x, this.startPoint.pos.y, 0, 0);
this.parts = [
{
kind: math_1.PathCommandType.MoveTo,
point: this.startPoint.pos,
},
];
}
getBBox() {
return this.bbox.grownBy(this.averageWidth);
}
getRenderingStyle() {
return {
fill: math_1.Color4.transparent,
stroke: {
color: this.startPoint.color,
width: this.roundDistance(this.averageWidth),
},
};
}
previewCurrentPath() {
const startPoint = this.startPoint.pos;
const commands = [...this.parts];
// TODO: For now, this is necesary for the path to be visible.
if (commands.length <= 1) {
commands.push({
kind: math_1.PathCommandType.LineTo,
point: startPoint.plus(math_1.Vec2.of(this.averageWidth / 4, 0)),
});
}
return {
startPoint,
commands,
style: this.getRenderingStyle(),
};
}
previewFullPath() {
return [this.previewCurrentPath()];
}
preview(renderer) {
const paths = this.previewFullPath();
if (paths) {
const approxBBox = this.viewport.visibleRect;
renderer.startObject(approxBBox);
for (const path of paths) {
renderer.drawPath(path);
}
renderer.endObject();
}
}
build() {
return new Stroke_1.default(this.previewFullPath());
}
getMinFit() {
let minFit = Math.min(this.minFitAllowed, this.averageWidth / 4);
if (minFit < 1e-10) {
minFit = this.minFitAllowed;
}
return minFit;
}
roundPoint(point) {
const minFit = this.getMinFit();
return Viewport_1.default.roundPoint(point, minFit);
}
roundDistance(dist) {
const minFit = this.getMinFit();
return Viewport_1.default.roundPoint(dist, minFit);
}
addPoint(newPoint) {
this.widthAverageNumSamples++;
this.averageWidth =
(this.averageWidth * (this.widthAverageNumSamples - 1)) / this.widthAverageNumSamples +
newPoint.width / this.widthAverageNumSamples;
const roundedPoint = this.roundPoint(newPoint.pos);
if (!roundedPoint.eq(this.lastPoint)) {
// If almost exactly in the same line as the previous
if (this.lastLineSegment &&
this.lastLineSegment.direction.dot(roundedPoint.minus(this.lastPoint).normalized()) > 0.997) {
this.parts.pop();
this.lastPoint = this.lastLineSegment.p1;
}
this.parts.push({
kind: math_1.PathCommandType.LineTo,
point: this.roundPoint(newPoint.pos),
});
this.bbox = this.bbox.grownToPoint(roundedPoint);
this.lastLineSegment = new math_1.LineSegment2(this.lastPoint, roundedPoint);
this.lastPoint = roundedPoint;
}
}
}
exports.default = PolylineBuilder;