Source code of plot #055 back to plot
Download full working sketch as 055.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.
///<reference path="../pub/lib/paper.d.ts" />
import {info, init, loadLib, setSketch, spin} from "./utils/boilerplate.js";
import {mulberry32, setRandomGenerator, rand, rand_range} from "./utils/random.js"
import {kdTree} from "./utils/kdTree.js";
const pw = 2100; // Paper width
const ph = 1480; // Paper height
const w = 1480; // Drawing width
const h = 1050; // Drawing height
const rf = 1;
const segLen = 2;
let seed = Math.round(Math.random() * 65535);
// seed = 57788;
setSketch(async function () {
setRandomGenerator(mulberry32(seed));
info("Seed: " + seed);
init(w, h, pw, ph);
const startTime = performance.now();
await draw();
const elapsed = performance.now() - startTime;
console.log(`Drawn in ${elapsed} msec`);
});
async function draw() {
paper.project.currentStyle.strokeWidth = 2;
paper.project.currentStyle.strokeColor = "black";
const kdt = new kdTree([], (a, b) => Math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2), ["x", "y"]);
const nCurves = 150;
const xfact = 0.475
const yfact = 0.475;
for (let i = 0; i < nCurves; ++i) {
const prms = [];
const nHarm = Math.floor(7 * rand()) + 1;
while (prms.length < nHarm) {
const prm = {
h: (prms.length + 2) * 0.5,
a: (rand() - 0.5) ** 3,
};
prms.push(prm);
}
const paths = makeCurvePaths(prms);
const canvasPaths = [];
for (const pts of paths) {
let canvasPts = pts.map(p => new Point(w * (0.5 + p.x * xfact), h * (0.5 + p.y * yfact)));
canvasPts = sparser(canvasPts, segLen);
if ((canvasPaths.length%2) == 0) canvasPts.reverse();
canvasPaths.push(sparser(canvasPts, segLen));
}
for (const pts of canvasPaths) {
const visiblePaths = getVisiblePaths(pts, kdt, 3);
for (const pathPts of visiblePaths) {
const shortenedPts = shorten(pathPts, 5);
if (shortenedPts.length < 5) continue;
const path = new paper.Path(shortenedPts);
paper.project.activeLayer.addChild(path);
}
}
for (const pts of canvasPaths) {
for (const pt of pts) kdt.insert(pt);
}
await spin();
}
}
function makeCurvePaths(prms) {
const eps = 1e-6;
const nSegs = 1e4;
let pts = [];
let max = Number.MIN_VALUE;
for (let i = 0; i <= nSegs; ++i) {
const x = 2 * i / nSegs - 1;
let y = 0;
for (const prm of prms) {
let phase = 0;
if (Math.abs((prm.h%1) - 0.5) < eps)
phase = Math.PI * 0.5;
y += prm.a * Math.sin(prm.h * x * Math.PI + phase);
}
pts.push(new Point(x, y));
if (Math.abs(y) > max) max = Math.abs(y);
}
for (const pt of pts) {
pt.y /= max;
pt.y *= Math.exp(-4*(pt.x)**2);
}
return [pts];
}
function sparser(pts, minLen) {
const res = [pts[0]];
let travel = 0;
for (let i = 1; i < pts.length; ++i) {
travel += pts[i].subtract(pts[i-1]).length;
if (travel >= minLen || i == pts.length - 1) {
res.push(pts[i]);
travel = 0;
}
}
return res;
}
function shorten(pts, byLen) {
let firstIx = -1, lastIx = -1;
for (let travel = 0, i = 1; travel < byLen && i < pts.length; ++i) {
travel += pts[i].subtract(pts[i-1]).length;
if (travel >= byLen) firstIx = i;
}
for (let travel = 0, i = pts.length - 2; travel < byLen && i >= 0; --i) {
travel += pts[i].subtract(pts[i+1]).length;
if (travel >= byLen) lastIx = i;
}
if (firstIx == -1 || lastIx == -1 || firstIx >= lastIx)
return [];
return pts.slice(firstIx, lastIx + 1);
}
function getVisiblePaths(pts, kdt, dist) {
// Result: array of array of points.
const polys = [];
let currPts = [];
for (const pt of pts) {
const neighbors = kdt.nearest(pt, 1, dist);
const visible = neighbors.length == 0;
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 = [];
}
}