d<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2100 2100" width="2100" height="2100" version="1.1" >
  <!-- 
    Johan Karlsson, 2020
    https://twitter.com/DonKarlssonSan 
  -->
</svg>
html, body {
  height: 100%;
  margin: 0;
  cursor: pointer;
  background-color: #223;
  overflow: hidden;
}

svg {
  position: absolute;
  width: 100%;
  height: 100%;
}
/*
  Johan Karlsson, 2020
  https://twitter.com/DonKarlssonSan
  MIT License, see Details View
*/

let svg;
let w = 2100;
let h = 2100;
let m;
let config = {
  n: 6,
  d: 71
};
let settings;

function setup() {
  console.clear();
  svg = document.querySelector("svg");
  document.addEventListener("click", draw);
  document.addEventListener("keydown", onKeyDown);
  m = Math.min(w, h) * 0.45;
  setupQuickSettings();
}

function setupQuickSettings() {
  settings = QuickSettings.create(0, 0, "Parameters");
  settings.addRange("n", 1, 500, config.n, 1, val => config.n = val); 
  settings.addRange("d", 1, 500, config.d, 1, val => config.d = val);
  settings.addButton("Random", random); 
}

function random() {
  config.n = Math.round(Math.random() * 500);
  settings.setValue("n", config.n);
  config.d = Math.round(Math.random() * 500);
  settings.setValue("d", config.d);
}

function onKeyDown (e) {
  if(e.code === "KeyD") {
    download();
  }
}

function download() {
  let svgDoc = svg.outerHTML;
  let filename = `maurer-rose-n${config.n}-d${config.d}.svg`;
  let element = document.createElement("a");
  element.setAttribute("href", "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgDoc));
  element.setAttribute("download", filename);
  element.style.display = "none";
  document.body.appendChild(element);
  element.addEventListener("click", e => e.stopPropagation());
  element.click();
  document.body.removeChild(element);
}

function draw() {
  let group = document.querySelector("#container");
  if (group) {
    group.remove();
  }
  group = createSvgElement("g");
  group.setAttribute("id", "container");
  group.setAttribute("fill", "none");
  group.setAttribute("stroke", "white");
  group.setAttribute("stroke-linecap", "round");
  group.setAttribute("stroke-linejoin", "round");
  
  drawSpirograph(group);

  let logo = new Logo(w * 0.9, h * 0.95, "white");
  logo.draw(group);

  svg.appendChild(group);
}

function drawSpirograph(groupElement) {
  let n = config.n;
  let d = config.d;
  
  let points = [];
  let nrOfPoints = 361;
  for(let i = 0; i < nrOfPoints; i++) {
    let angle = Math.PI * i * d / 180;
    let r = m * Math.sin(n * angle);
    let x = -r * Math.cos(angle) + w / 2;
    let y = -r * Math.sin(angle) + h / 2;
    points.push(`${x} ${y}`);
  }
  let path = createSvgElement("path");
  let commands = convertPointsToCommands(points);
  path.setAttribute("d", commands);
  groupElement.appendChild(path);
}

function createSvgElement(elementName) {
  const svgNs = "http://www.w3.org/2000/svg";
  return document.createElementNS(svgNs, elementName);
}

function convertPointsToCommands(points) {
  let commands = [];
  commands.push(`M ${points[0]}`);
  for (let i = 1; i < points.length; i++) {
    commands.push(`L ${points[i]}`);
  }
  return commands.join(" ");
}

setup();
draw();
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/quicksettings@3.0/quicksettings.min.js
  2. https://codepen.io/DonKarlssonSan/pen/yLOxWGB.js