Source code of plot #065 back to plot

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

import {caption} from "./lib/own/caption.js"
import * as E from "./lib/env.js"
import * as G from "./lib/own/geo.js"
import {SLGen, SLGParams} from "./slgen.js";

import {kdTree} from "../../lib/thirdparty/kdTree.js";

// Declarations below instruct build plugin to copy static files to runtime dir
// STATIC lib/texture.png

const pw = 1480;    // Paper width
const ph = 1050;    // Paper height
const w = 1480;     // Drawing width
const h = 1050;     // Drawing height
const margin = 50;

// Random composition
// const time = Math.floor(10 * Math.random() * 100) / 100;
// const a = Math.floor(5 * Math.random() * 100) / 100;
// const b = Math.floor(5 * Math.random() * 100) / 100;
// const c = Math.floor(5 * Math.random() * 100) / 100;

// Reproduce with parameters
// t: 3.67 a: 2.71 b: 2.32 c: 0.84
const time = 3.67;
const a = 2.71;
const b = 2.32;
const c = 0.84;

void setup();

async function setup() {
  E.initEnv(w, h, pw, ph);
  E.info(`t: ${time} a: ${a} b: ${b} c: ${c}`);

  const startTime = performance.now();
  await E.spin();
  await draw();
  const elapsed = performance.now() - startTime;
  console.log(`Drawn in ${elapsed} msec`);
}

async function draw() {

  const star = u => {
    let v = 4 * Math.atan2(u.x, u.y);
    v += 4 * Math.abs(Math.sin(v * c * 0.5));
    v -= c * 0.5 * Math.abs(Math.sin(v * 1.5));
    let val = Math.cos(v + a + u.length() * b);
    return val * 0.5 + 0.5;
  };

  const func = pt => {
    const uv = new G.Vec2(pt.x*2/w-1, pt.y*2/h-1);
    uv.y *= 0.7;
    uv.x *= 2;
    if (!uv.isNull()) {
      const dot = G.dot2(uv, uv);
      const rot = uv.clone().rot(Math.PI * (0.5 + time * 0.2));
      uv.x = uv.x / dot + 2.5 * rot.x;
      uv.y = -uv.y / dot + 1.5 * rot.y;
      uv.setLength(Math.log(uv.length()));
    }
    return star(uv);
  };

  const field = pt => {
    const val = func(pt);
    const dir = new G.Vec2(1, 0).rot(2 * Math.PI * val);
    return dir.rot(pt.angle() * .1);
  };

  const light = pt => 0;

  const genParams = new SLGParams();
  genParams.bounds = { left: margin, top: margin, right: w - margin, bottom: h - margin};
  genParams.seed = new G.Vec2(w * 0.5, h * 0.2);
  genParams.step = 1;
  genParams.startSep = 8;
  genParams.endRatio = 0.15;
  genParams.maxLightnessSep = 16;
  genParams.minLinePoints = 2 / genParams.step;

  await generateStreamlines(genParams, field, light);

}

async function generateStreamlines(genParams, field, luma) {

  const drawEveryNLines = 32;
  const slGen = new SLGen(genParams, field, luma);

  const linesToAdd = [];
  while (true) {
    const visiblePaths = slGen.genNextLine();
    if (!visiblePaths) break;
    for (const pts of visiblePaths)
      linesToAdd.push(pts);
    if (linesToAdd.length >= drawEveryNLines) {
      for (const pts of linesToAdd) E.addPath(pts);
      linesToAdd.length = 0;
      await E.spin();
    }
  }
  for (const pts of linesToAdd) E.addPath(pts);
}