Source code of plot #054 back to plot

Download full working sketch as 054.tar.gz.
Unzip, then start a local web server and load the page in a browser.

///<reference path="../pub/lib/paper.d.ts" />
import {info, init, loadLib, setSketch} from "./utils/boilerplate.js";
import {mulberry32, setRandomGenerator, rand, rand_range} from "./utils/random.js"
import {SimplexNoise} from "./utils/simplex-noise.js";
import {CanvasMasker} from "./utils/canvas-masker.js";

const pw = 2100;    // Paper width
const ph = 1480;    // Paper height
const w = 1480;     // Drawing width
const h = 1050;     // Drawing height
const margin = 50;  // Margin (within drawing)

const rf = 1;
const segLen = 2;
let simplex1, simplex2;
let seed = Math.round(Math.random() * 65535);
// seed = 57788;

setSketch(function () {
  setRandomGenerator(mulberry32(seed));
  simplex1 = new SimplexNoise(rand() * 100);
  simplex2 = new SimplexNoise(rand() * 100);
  info("Seed: " + seed);
  init(w, h, pw, ph);
  draw();
});

function draw() {
  paper.project.currentStyle.strokeColor = "black";
  paper.project.currentStyle.strokeWidth = 2;

  const cm = new CanvasMasker(w, h, rf/*, elmCanvasHost*/);

  const circles = [];
  const nCircles = 20;
  const minCircleDist = 100;
  const circleSizes = [20, 100];
  const curves = [];
  const nCurves = 1000;
  const curveGap = 10;
  const shapeGap = 15;

  cm.includeRect(margin, margin, w-2*margin, h-2*margin);
  cm.takeSnapshot();

  while (circles.length < nCircles) {
    const rad = Math.round(rand_range(...circleSizes));
    const center = new Point(
      Math.round(margin + (w-2*margin) * rand()),
      Math.round(margin + (h-2*margin) * rand())
    );
    let isects = false;
    for (const [c2, r2] of circles) {
      if (c2.subtract(center).length < r2 + rad + minCircleDist) isects = true;
    }
    if (isects) continue;
    circles.push([center, rad]);
  }

  const shapePaths = [];
  for (const [center, rad] of circles) {
    const turnGap = 8;
    const pts = makeSpiralPoints(center, rad, turnGap, segLen);
    shapePaths.push(pts);
    const visiblePaths = cm.getMaskedPoly(pts, true);
    for (const pathPts of visiblePaths) {
      const path = new paper.Path(pathPts);
      paper.project.activeLayer.addChild(path);
    }
  }
  for (const pts of shapePaths)
    cm.blockPath(pts, curveGap);
  cm.takeSnapshot();

  while (curves.length < nCurves) {
    const midPt = new Point(
      Math.round(margin + (w-2*margin) * rand()),
      Math.round(margin + (h-2*margin) * rand())
    );

    let next = rand() < 0.5
      ? (pt, dir) => curveGen(pt, dir, simplex1)
      : (pt, dir) => curveGen(pt, dir, simplex2);

    let pts = cm.genSeq(midPt, next, 100, true);
    if (!pts) continue;
    curves.push(pts);
    cm.blockPath(pts, curveGap);
    cm.takeSnapshot();
  }

  for (const pts of curves) {
    const path = new Path({segments: pts});
    project.activeLayer.addChild(path);
  }
}

function curveGen(pt, dir, simplex) {
  const sampleX = ((pt.x / w) - 0.5) * 1;
  const sampleY = ((pt.y / h) - 0.5) * 1;
  const stepLen = 2;
  let angle = Math.PI * simplex.noise2D(sampleX, sampleY);
  if (dir < 0) angle += Math.PI;
  pt.x += stepLen * Math.sin(angle);
  pt.y += stepLen * Math.cos(angle);
  return true;
}

function makeSpiralPoints(center, rad, turnGap, segLen) {

  const pts = [];

  const vec = new Point(0, 0);
  const setVec = (angle, r) => {
    vec.x = r * Math.sin(angle);
    vec.y = r * Math.cos(angle);
  }
  const startAngle = 2 * Math.PI * rand();
  let angle = 0;
  let r = turnGap * 0.5;
  while (r < rad) {
    setVec(angle + startAngle, r);
    pts.push(vec.add(center));
    const cfer = 2 * r * Math.PI;
    const nSteps = Math.max(8, Math.round(cfer / segLen));
    angle += 2 * Math.PI / nSteps;
    r += turnGap / nSteps;
  }
  return pts;
}