Source code of plot #030 back to plot
Download full working sketch as 030.tar.gz.
Unzip, then start a local web server and load the page in a browser.
Unless otherwise noted, code published here is © Gábor L Ugray, shared under the Creative Commons
BY-NC-SA license (Attribution, Non-Commercial, Share-Alike). Files in lib/thirdparty
, and additional
libraries in the downloadable archive, are shared under their respective open-source licenses, attributed
to their authors.
class Vec2 {
constructor(x, y) {
this.x = x;
this.y = y;
}
clone() {
return new Vec2(this.x, this.y);
}
equals(other) {
return this.x == other.x && this.y == other.y;
}
}
class Vec3 {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
rotx(angle) {
const sin = Math.sin(angle);
const cos = Math.cos(angle);
return new Vec3(this.x, cos * this.y - sin * this.z, sin * this.y + cos * this.z);
}
roty(angle) {
const sin = Math.sin(angle);
const cos = Math.cos(angle);
return new Vec3(this.x * cos + this.z * sin, this.y, this.x * (-sin) + this.z * cos);
}
}
function makeHatch(x, y, angle, length, interval, count, startHalf) {
let res = [];
const sin = Math.sin(angle);
const cos = Math.cos(angle);
const rsin = Math.sin(angle - Math.PI / 2);
const rcos = Math.cos(angle - Math.PI / 2);
for (let i = 0; i < count; ++i) {
const midOfs = startHalf ? interval * (i + 0.5) : interval * i;
const midX = x + midOfs * rcos;
const midY = y - midOfs * rsin;
const x1 = midX + length / 2 * cos;
const y1 = midY - length / 2 * sin;
const x2 = midX - length / 2 * cos;
const y2 = midY + length / 2 * sin;
res.push([new Vec2(x1, y1), new Vec2(x2, y2)]);
}
return res;
}
function makePaperHatchLines(angle, length, interval, count, startHalf, midPt) {
let ox = 0, oy = 0;
if (midPt) { ox = midPt.x; oy = midPt.y; }
let res = [];
const sin = Math.sin(angle / 180 * Math.PI);
const cos = Math.cos(angle / 180 * Math.PI);
const rsin = Math.sin(angle / 180 * Math.PI - Math.PI / 2);
const rcos = Math.cos(angle / 180 * Math.PI - Math.PI / 2);
for (let i = -count; i < count; ++i) {
const midOfs = startHalf ? interval * (i + 0.5) : interval * i;
const midX = ox + midOfs * rcos;
const midY = oy - midOfs * rsin;
const x1 = midX + length / 2 * cos;
const y1 = midY - length / 2 * sin;
const x2 = midX - length / 2 * cos;
const y2 = midY + length / 2 * sin;
const line = paper.Path.Line(new paper.Point(x1, y1), new paper.Point(x2, y2));
res.push(line);
// DBG
//line.strokeColor = "red";
//paper.project.activeLayer.addChild(line);
//paper.view.draw();
}
return res;
}
function shortenLine(line, val) {
const a = line[0], b = line[1];
const mid = new Vec2((a.x + b.x) / 2, (a.y + b.y) / 2);
const len = Math.sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
if (len - val < 2) return null;
const prop = (len - val) / len;
const newA = new Vec2(mid.x + (a.x - mid.x) * prop, mid.y + (a.y - mid.y) * prop);
const newB = new Vec2(mid.x + (b.x - mid.x) * prop, mid.y + (b.y - mid.y) * prop);
return [newA, newB];
}
function shortenPaperLine(line, val) {
if (val == 0) return line;
const a = line.segments[0].point, b = line.segments[1].point;
const mid = new Vec2((a.x + b.x) / 2, (a.y + b.y) / 2);
const len = Math.sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
if (len - val < 2) return null;
const prop = (len - val) / len;
const newA = new Point(mid.x + (a.x - mid.x) * prop, mid.y + (a.y - mid.y) * prop);
const newB = new Point(mid.x + (b.x - mid.x) * prop, mid.y + (b.y - mid.y) * prop);
return Path.Line(newA, newB);
}
function lerpPt(pta, ptb, f) {
return new paper.Point(pta.x + (ptb.x - pta.x) * f, pta.y + (ptb.y - pta.y) * f);
}
function lnPtDist(ln1, ln2, pt) {
let B = pt.y - ln1.y;
let A = pt.x - ln1.x;
let C = ln2.x - ln1.x;
let D = ln2.y - ln1.y;
let dot = A * C + B * D;
let len_sq = C * C + D * D;
let param = -1;
if (len_sq != 0) //in case of 0 length line
param = dot / len_sq;
let xx, yy;
if (param < 0) {
xx = ln1.x;
yy = ln1.y;
} else if (param > 1) {
xx = ln2.x;
yy = ln2.y;
} else {
xx = ln1.x + param * C;
yy = ln1.y + param * D;
}
let dx = pt.x - xx;
let dy = pt.y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
function getMaskedPoly(pts, negMasks, posMasks) {
// Result: array of array of points.
const polys = [];
// Bounding rectangle: for quick test before invoking expensive "contains"
const negRects = [];
negMasks.forEach(x => negRects.push(x.bounds));
const posRects = [];
posMasks.forEach(x => posRects.push(x.bounds));
let currPts = [];
for (const pt of pts) {
let visible = true;
for (let i = 0; i < posRects.length && visible; ++i)
visible &= posRects[i].contains(pt);
for (let i = 0; i < negRects.length && visible; ++i) {
if (negRects[i].contains(pt) && negMasks[i].contains(pt))
visible = false;
}
for (let i = 0; i < posMasks.length && visible; ++i)
visible &= posMasks[i].contains(pt);
if (!visible)
addCurrentPoints();
else
currPts.push(pt);
}
addCurrentPoints();
return polys;
function addCurrentPoints() {
if (currPts.length == 0) return;
if (currPts.length >= 2) {
polys.push(currPts);
}
currPts = [];
}
}
function getMaskedLine(pt1, pt2, negMasks, posMasks, segLength = 2) {
// Build points: short segments of the requested length
const lineVect = pt2.subtract(pt1);
const lineLength = lineVect.length;
const nSegs = Math.max(2, Math.round(lineLength / segLength));
const segVect = lineVect.divide(nSegs);
const pts = [];
for (let i = 0; i <= nSegs; ++i) {
pts.push(pt1.add(segVect.multiply(i)));
}
// Get polylines
const polys = getMaskedPoly(pts, negMasks, posMasks);
const res = [];
// Simplify: just keep first and last point of each polyline.
// These are all straight lines.
polys.forEach(poly => {
const pta = poly[0];
const ptb = poly[poly.length - 1];
res.push([pta, ptb]);
});
return res;
}
export {
Vec2, Vec3, shortenLine, shortenPaperLine, lerpPt, lnPtDist,
makeHatch, makePaperHatchLines,
getMaskedLine, getMaskedPoly
};