Source code of plot #040 back to plot

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

let libsPending = 0;
let sketchFun;
let theSeed;

function setSketch(fun) {
  if (typeof document !== 'undefined') {
    document.addEventListener('DOMContentLoaded', () => {
      sketchFun = fun;
      if (libsPending == 0) sketchFun();
    });
  }
}

function scriptname() {

  if (window.fxhash) return '89-cloth';

  const d = new Date();
  const dateStr = d.getFullYear() + "-" + ("0" + (d.getMonth() + 1)).slice(-2) + "-" +
    ("0" + d.getDate()).slice(-2) + "!" +
    ("0" + d.getHours()).slice(-2) + "-" + ("0" + d.getMinutes()).slice(-2);
  return '89-cloth' + "-" + dateStr;
}

function loadLib(name) {

  var script = document.createElement('script');
  script.src = "lib/" + name + ".js";
  script.onload = function () {
    --libsPending;
    if (libsPending == 0 && sketchFun) sketchFun();
  };
  ++libsPending;
  document.head.appendChild(script);
}

function init(w, h, pw, ph) {
  document.title = scriptname();

  paper.install(window);
  paper.settings.insertItems = false;

  const elmDrawing = document.getElementById("drawing");
  const elmCanvasHost = document.getElementById("canvasHost");
  paper.setup("paper-canvas");
  paper.project.activeLayer.name = "0-base";

  if (!pw) {
    pw = w;
    ph = h;
  }
  const elmCanvas = document.getElementById("paper-canvas");
  const elmBGCanvas = document.getElementById("bg-canvas");
  elmCanvas.width = w;
  elmCanvas.height = h;
  elmCanvas.style.width = "100%";
  elmCanvas.style.height = "100%";
  if (elmBGCanvas) {
    elmBGCanvas.width = w;
    elmBGCanvas.height = h;
    elmBGCanvas.style.width = "100%";
    elmBGCanvas.style.height = "100%";
  }

  sizeCanvas(elmDrawing, elmCanvasHost, w, h);

  if (window.fxhash) {
    window.addEventListener("resize", () => sizeCanvas(elmDrawing, elmCanvasHost, w, h));
    document.addEventListener("keydown", (e) => {
      if (e.code != "KeyS") return;
      let elmDownload = document.getElementById("download");
      if (elmDownload.href != "") {
        elmDownload.click();
        return;
      }
      let elmP = document.createElement("p");
      elmP.innerHTML = "Generating plottable SVG<br><i>Hang in there... this takes long.</i>";
      elmCanvasHost.append(elmP);
      setTimeout(() => {
        renderSvg(w, h, pw, ph);
        elmP.remove();
        elmDownload.click();
      }, 10);
    });
  }
  else {
    const elmRender = document.getElementById("render");
    elmRender.addEventListener("click", function (e) {
      e.preventDefault();
      e.stopPropagation();
      elmRender.setAttribute("href", "");
      setTimeout(function () {
        renderSvg(w, h, pw, ph);
        elmRender.style.display = "none";
        document.getElementById("download").style.display = "block";
      }, 50);
    });
  }
}

function sizeCanvas(elmDrawing, elmCanvasHost, w, h) {

  if (!window.fxhash) {
    const pxWidth = 600;
    elmCanvasHost.style.width = pxWidth + "px";
    elmCanvasHost.style.height = (pxWidth * h / w) + "px";
  }
  else {
    // Strategy: fit to width, but if too high, then fit to height
    const parentWidth = elmDrawing.clientWidth;
    const parentHeight = elmDrawing.clientHeight;
    let canvasHeight = parentWidth * h / w;
    if (canvasHeight <= parentHeight) {
      elmCanvasHost.style.width = parentWidth + "px";
      elmCanvasHost.style.height = canvasHeight + "px";
    }
    else {
      let canvasWidth = parentHeight / h * w;
      elmCanvasHost.style.width = canvasWidth + "px";
      elmCanvasHost.style.height = parentHeight + "px";
    }
  }
}

function renderSvg(w, h, pw, ph) {
  const elmSvg = project.exportSVG({
    asString: false,
    precision: 3,
    matchShapes: false,
  });
  elmSvg.setAttribute("width", (pw / 10) + "mm");
  elmSvg.setAttribute("height", (ph / 10) + "mm");
  let left = (w - pw) / 2;
  let top = (h - ph) / 2;
  elmSvg.setAttribute("viewBox", left + "," + top + "," + pw + "," + ph);

  elmSvg.setAttribute("xmlns:sodipodi", "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd");
  elmSvg.setAttribute("xmlns:inkscape", "http://www.inkscape.org/namespaces/inkscape");

  let svgStr = elmSvg.outerHTML;
  svgStr = svgStr.replaceAll("<path></path>", "");
  svgStr = svgStr.replaceAll(/<g id="([^"]+)"/g, "$& inkscape:groupmode='layer' inkscape:label='$1'");

  // <g id="0-base"

  var file;
  var data = [];
  data.push(svgStr);
  var properties = { type: 'image/svg+xml' };
  try { file = new File(data, scriptname() + ".svg", properties); }
  catch { file = new Blob(data, properties); }
  var url = URL.createObjectURL(file);
  document.getElementById('download').href = url;
  if (window.fxhash) {
    document.getElementById('download').download = scriptname() + "-" + theSeed + ".svg";
  }
  else {
    document.getElementById('download').download = scriptname() + ".svg";
  }
}

function info(str, seed) {
  if (window.fxhash) {
    theSeed = seed;
    console.log(str);
    return;
  }
  document.getElementById("info").getElementsByTagName("label")[0].textContent = str;
}

function clear() {
  paper.project.clear();
  dbgRedraw();
}

function dbgRedraw() {
  const canvas = document.getElementById("paper-canvas");
  const context = canvas.getContext('2d');
  context.clearRect(0, 0, canvas.width, canvas.height);
  paper.view.draw();
}

export { init, renderSvg, info, loadLib, setSketch, clear, dbgRedraw };