Source code of plot #053 back to plot

Download full working sketch as 053.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 seed = Math.round(Math.random() * 65535);
//seed = 11454;

setSketch(function () {
setRandomGenerator(mulberry32(seed));
  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 = 17;
  const minCircleDist = 100;
  const circleSizes = [20, 100];
  const lines = [];
  const nLines = 1500;
  const lineAngleQuants = 6;
  const lineAnglePlayDeg = 5;
  const minLineLength = 10;
  const gap = 5;

  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]);
  }

  for (const [center, rad] of circles) {
    const pts = makeSpiralPoints(center, rad, 5 + 5 * rand(), segLen);
    const visiblePaths = cm.getMaskedPoly(pts, true);
    for (const pathPts of visiblePaths) {
      const path = new paper.Path(pathPts);
      paper.project.activeLayer.addChild(path);
    }
  }
  for (const [center, rad] of circles)
    cm.blockCircle(center, rad + gap);
  cm.takeSnapshot();

  while (lines.length < nLines) {
    const midPt = new Point(
      Math.round(margin + (w-2*margin) * rand()),
      Math.round(margin + (h-2*margin) * rand())
    );
    let angleDeg = Math.floor(lineAngleQuants * rand()) / lineAngleQuants * 180;
    angleDeg += 2 * lineAnglePlayDeg * (rand() - 0.5);
    let ab = cm.extendLine(midPt, angleDeg, true);
    if (!ab) continue;
    ab = shorten(...ab, gap, minLineLength);
    if (!ab) continue;
    lines.push(ab);
    maskLine(cm, ...ab, angleDeg);
    cm.takeSnapshot();
  }

  for (const [a, b] of lines) {
    const ln = Path.Line(a, b);
    project.activeLayer.addChild(ln);
  }
}

function shorten(a, b, gap, minLength) {
  const d = b.subtract(a);
  const lenAfter = d.length - 2 * gap;
  if (lenAfter < minLength) return null;
  d.length = gap;
  return [a.add(d), b.subtract(d)];
}

function maskLine(cm, a, b, angleDeg) {
  const maskThickness = 2;
  const up = new Point(-maskThickness, 0).rotate(angleDeg);
  const left = up.rotate(-90);
  const pts = [
    a.add(up).add(left),
    a.subtract(up).add(left),
    b.subtract(up).subtract(left),
    b.add(up).subtract(left),
  ];
  cm.blockPoly(pts);
}

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;
}