Source code of plot #061 back to plot
Download full working sketch as 061.tar.gz.
Unzip, then start a local web server and load the page in a browser.
Unless otherwise noted, code published here is © Gábor L Ugray, shared under the Creative Commons
BY-NC-SA license (Attribution, Non-Commercial, Share-Alike). Files in lib/thirdparty
, and additional
libraries in the downloadable archive, are shared under their respective open-source licenses, attributed
to their authors.
const groups = [];
const plotElmId = "plot";
const canvasElmId = "canvas";
const saveElmId = "save";
const hiddenLinkElmId = "hiddenLink";
const defaultLayerName = "0-base";
const svgNS = "http://www.w3.org/2000/svg";
let dims = {
w: 1480, // Image width (10px/mm)
h: 1050, // Image height (10px/mm)
pw: 2100, // Paper width (10px/mm)
ph: 1480, // Paper height (10px/mm)
}
let strokeWidthPx = 2;
function buildPieceName() {
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 '116-spiralines!a' + "-" + dateStr;
}
export function initEnv(w, h, pw, ph, showCanvas) {
dims.w = dims.pw = w;
dims.h = dims.ph = h;
dims.pw = pw;
dims.ph = ph;
document.title = buildPieceName();
const elmSvg = document.getElementById(plotElmId);
elmSvg.setAttribute("viewBox", `0, 0, ${w}, ${h}`);
addLayer(defaultLayerName);
sizeSvgAndCanvas(w, h, showCanvas);
window.addEventListener("resize", () => {
sizeSvgAndCanvas(w, h, showCanvas);
});
document.getElementById(saveElmId).addEventListener("click", e =>
{
e.preventDefault();
saveSvg();
return false;
});
}
function sizeSvgAndCanvas(w, h, showCanvas) {
const elmSvg = document.getElementById(plotElmId);
const elmCanvas = document.getElementById(canvasElmId);
const elmViz = document.getElementById("viz");
if (!showCanvas) {
const vspace = elmViz.clientHeight;
const widthPx = Math.round(vspace / h * w);
elmSvg.setAttribute("width", `${widthPx}px`);
if (elmCanvas) elmCanvas.style.display = "none";
}
else {
const elmContent = document.getElementById("content");
const contentStyle = window.getComputedStyle(elmContent);
const hwhite = parseFloat(contentStyle.borderLeftWidth) + parseFloat(contentStyle.borderRightWidth) +
parseFloat(contentStyle.paddingLeft) + parseFloat(contentStyle.paddingRight);
elmViz.style.width = null;
elmViz.style.maxHeight = null;
const vspace = elmViz.clientHeight;
const widthPx = Math.round(vspace / h * w);
const desiredContentWidth = 2 * widthPx + hwhite;
const contentWidth = Math.min(desiredContentWidth, document.body.clientWidth);
const matchingPanelHeight = (contentWidth - hwhite) / 2 / w * h;
elmContent.style.width = `${contentWidth}px`;
elmViz.style.maxHeight = `${matchingPanelHeight}px`;
elmSvg.style.width = "50%";
elmCanvas.style.width = "50%";
elmCanvas.style.display = "block";
}
}
export function info(str, seed) {
document.getElementById("info").getElementsByTagName("label")[0].textContent = str;
}
export function spin(delay = 0) {
return new Promise(resolve => {
setTimeout(resolve, delay);
});
}
function setPathAttributes(elmPath) {
elmPath.setAttribute("stroke", "black");
elmPath.setAttribute("stroke-width", `${strokeWidthPx}px`);
elmPath.setAttribute("fill", "none");
}
function xCommaY(pt) {
const x = Math.round(pt.x * 100) / 100;
const y = Math.round(pt.y * 100) / 100;
return `${x},${y}`;
}
export function addPath(pts, closed = false, layerIx = 0) {
let pathData = "";
for (let i = 0; i < pts.length; ++i) {
const pt = pts[i];
if (i == 0) pathData += "M";
else pathData += " L";
pathData += xCommaY(pt);
}
const lpt = pts[pts.length-1];
if (closed && (lpt.x != pts[0].x || lpt.y != pts[0].y)) {
pathData += " L" + xCommaY(pts[0]);
}
const elmGroup = groups[layerIx];
const elmPath = document.createElementNS(svgNS, "path");
setPathAttributes(elmPath);
elmPath.setAttribute("d", pathData);
elmGroup.appendChild(elmPath);
return elmPath;
}
export function addLinesAsPath(lines, layerIx = 0) {
let pathData = "";
for (let i = 0; i < lines.length; ++i) {
const ln = lines[i];
pathData += `M${xCommaY(ln[0])}L${xCommaY(ln[1])}`
}
const elmGroup = groups[layerIx];
const elmPath = document.createElementNS(svgNS, "path");
setPathAttributes(elmPath);
elmPath.setAttribute("d", pathData);
elmGroup.appendChild(elmPath);
return elmPath;
}
export function addGroup(g, layerIx = 0) {
const elmGroup = groups[layerIx];
elmGroup.appendChild(g);
}
export function addLayer(name) {
const elmGroup = document.createElementNS(svgNS, "g");
elmGroup.setAttribute("id", name);
elmGroup.setAttribute("inkscape:groupmode", "layer");
elmGroup.setAttribute("inkscape:label", name);
const elmSvg = document.getElementById(plotElmId);
elmSvg.appendChild(elmGroup);
groups.push(elmGroup);
}
function saveSvg() {
let svgStr = document.getElementById(plotElmId).outerHTML;
const wmm = Math.round(dims.pw / 10);
const hmm = Math.round(dims.ph / 10);
const left = (dims.w - dims.pw) / 2;
const top = (dims.h - dims.ph) / 2;
const openTag = `<svg version="1.1" xmlns="${svgNS}" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="${wmm}mm" height="${hmm}mm" viewBox="${left},${top},${dims.pw},${dims.ph}">\n`;
svgStr = svgStr.replace(/<svg [^>]+>/g, openTag);
let file;
let data = [];
data.push(svgStr);
let properties = { type: 'image/svg+xml' };
try { file = new File(data, buildPieceName() + ".svg", properties); }
catch { file = new Blob(data, properties); }
let url = URL.createObjectURL(file);
const elmDownload = document.getElementById(hiddenLinkElmId);
elmDownload.href = url;
elmDownload.download = buildPieceName() + ".svg";
elmDownload.click();
}