From ba53f8996505e766d896f707f96652d97c894be7 Mon Sep 17 00:00:00 2001 From: George <31376482+george-gca@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:26:56 -0300 Subject: [PATCH] Added support for Google Typograms (#2379) Google [Typograms](https://github.com/google/typograms/) is a lightweight image format (text/typogram) useful for defining simple diagrams in technical documentation. ![image](https://github.com/alshedivat/al-folio/assets/31376482/715ba10b-c75d-492b-8869-4ec83d380e50) ![image](https://github.com/alshedivat/al-folio/assets/31376482/935f6ef8-1977-41d0-8797-d226594b82a9) --------- Signed-off-by: George Araujo --- _includes/scripts/diff2html.liquid | 2 +- _includes/scripts/typograms.liquid | 23 + _layouts/default.liquid | 1 + _posts/2024-04-29-typograms.md | 85 ++ _sass/_typograms.scss | 132 +++ assets/css/main.scss | 1 + assets/js/typograms.js | 1322 ++++++++++++++++++++++++++++ 7 files changed, 1565 insertions(+), 1 deletion(-) create mode 100644 _includes/scripts/typograms.liquid create mode 100644 _posts/2024-04-29-typograms.md create mode 100644 _sass/_typograms.scss create mode 100644 assets/js/typograms.js diff --git a/_includes/scripts/diff2html.liquid b/_includes/scripts/diff2html.liquid index 42cdcff8b451..8b5211ddbd10 100644 --- a/_includes/scripts/diff2html.liquid +++ b/_includes/scripts/diff2html.liquid @@ -8,7 +8,7 @@ + + +{% endif %} diff --git a/_layouts/default.liquid b/_layouts/default.liquid index ea8870eb8c40..0a5337d2b560 100644 --- a/_layouts/default.liquid +++ b/_layouts/default.liquid @@ -61,6 +61,7 @@ {% include scripts/echarts.liquid %} {% include scripts/vega.liquid %} {% include scripts/tikzjax.liquid %} + {% include scripts/typograms.liquid %} {% include scripts/misc.liquid %} {% include scripts/badges.liquid %} {% include scripts/mathjax.liquid %} diff --git a/_posts/2024-04-29-typograms.md b/_posts/2024-04-29-typograms.md new file mode 100644 index 000000000000..6caff4e67a65 --- /dev/null +++ b/_posts/2024-04-29-typograms.md @@ -0,0 +1,85 @@ +--- +layout: post +title: a post with typograms +date: 2024-04-29 23:36:10 +description: this is what included typograms code could look like +tags: formatting diagrams +categories: sample-posts +typograms: true +--- + +This is an example post with some [typograms](https://github.com/google/typograms/) code. + +````markdown +```typograms ++----+ +| |---> My first diagram! ++----+ +``` +```` + +Which generates: + +```typograms ++----+ +| |---> My first diagram! ++----+ +``` + +Another example: + +````markdown +```typograms +.------------------------. +|.----------------------.| +||"https://example.com" || +|'----------------------'| +| ______________________ | +|| || +|| Welcome! || +|| || +|| || +|| .----------------. || +|| | username | || +|| '----------------' || +|| .----------------. || +|| |"*******" | || +|| '----------------' || +|| || +|| .----------------. || +|| | "Sign-up" | || +|| '----------------' || +|| || +|+----------------------+| +.------------------------. +``` +```` + +which generates: + +```typograms +.------------------------. +|.----------------------.| +||"https://example.com" || +|'----------------------'| +| ______________________ | +|| || +|| Welcome! || +|| || +|| || +|| .----------------. || +|| | username | || +|| '----------------' || +|| .----------------. || +|| |"*******" | || +|| '----------------' || +|| || +|| .----------------. || +|| | "Sign-up" | || +|| '----------------' || +|| || +|+----------------------+| +.------------------------. +``` + +For more examples, check out the [typograms documentation](https://google.github.io/typograms/#examples). diff --git a/_sass/_typograms.scss b/_sass/_typograms.scss new file mode 100644 index 000000000000..1ea7f3e15c3b --- /dev/null +++ b/_sass/_typograms.scss @@ -0,0 +1,132 @@ +.typogram { + .diagram { + display: block; + } + + .diagram line, + .diagram circle, + .diagram rect { + stroke: var(--global-text-color); + } + + .diagram line { + stroke-width: 2; + } + + .diagram circle { + r: 3.5; + } + + .diagram rect { + width: 6px; + height: 6px; + } + + .diagram text, + .glyph, + .debug text { + /** font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; **/ + font-family: + Iosevka Fixed, + monospace; + font-size: 3em; + text-anchor: middle; + alignment-baseline: central; + white-space: pre; + fill: var(--global-text-color); + } + + .reserved { + fill: transparent; + white-space: pre; + } + + .debug[debug="true"] .reserved { + fill: var(--global-text-color); + opacity: 0.5; + } + + .debug[debug="true"] line.grid { + stroke: var(--global-text-color); + stroke-width: 0.2; + stroke-linecap: butt; + fill: var(--global-text-color); + opacity: 1%; + } + + polygon { + stroke-width: 0; + } + + .debug[debug="true"] polygon.inner { + fill: var(--global-text-color); + stroke: var(--global-text-color); + opacity: 5%; + } + + polygon { + stroke: var(--global-text-color); + /** stroke-width: 0.2; **/ + stroke-linecap: butt; + fill: var(--global-text-color); + } + + .debug[debug="true"] polygon, + .debug[debug="true"] line.grid { + opacity: 10%; + } + + .debug[debug="true"] polygon, + .debug[debug="true"] path, + .debug[debug="true"] circle { + opacity: 50%; + } + + .debug[debug="true"] polygon { + fill: red; + stroke: red; + } + + /** + circle { + fill: var(--global-text-color); + } + **/ + + .debug[debug="true"] circle, + .debug[debug="true"] path { + opacity: 50%; + fill: red; + } + + .debug[debug="true"] circle { + stroke: red; + } + + .debug[debug="true"] .inner { + stroke-width: 0.2; + } + + line.part { + stroke-width: 6; + stroke-linecap: butt; + stroke: var(--global-text-color); + } + + .debug[debug="true"] line.part { + opacity: 50%; + stroke: red; + } + + .debug[debug="true"] line.center { + stroke-width: 3; + stroke-linecap: butt; + opacity: 10%; + stroke: var(--global-text-color); + } + + text::selection { + fill: HighlightText; + background-color: Highlight; + } +} diff --git a/assets/css/main.scss b/assets/css/main.scss index 00e34491f3b8..b2160f62b1b2 100644 --- a/assets/css/main.scss +++ b/assets/css/main.scss @@ -13,6 +13,7 @@ $max-content-width: {{ site.max_width }}; "base", "distill", "cv", + "typograms", "font-awesome/fontawesome", "font-awesome/brands", "font-awesome/solid", diff --git a/assets/js/typograms.js b/assets/js/typograms.js new file mode 100644 index 000000000000..ba5327ee9720 --- /dev/null +++ b/assets/js/typograms.js @@ -0,0 +1,1322 @@ +// based on the original typograms code from https://github.com/google/typograms/blob/main/src/typograms.js +// only moved the css to its own file (_sass/_typograms.scss) and commented the last line of the file +const ratio = 2; + +function grid(width, height) { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + + const vertical = document.createElementNS("http://www.w3.org/2000/svg", "line"); + vertical.setAttribute("x1", 15); + vertical.setAttribute("y1", 0); + vertical.setAttribute("x2", 15); + vertical.setAttribute("y2", 54); + vertical.setAttribute("class", "center"); + //result.appendChild(vertical); + + const horizontal = document.createElementNS("http://www.w3.org/2000/svg", "line"); + horizontal.setAttribute("x1", 0); + horizontal.setAttribute("y1", 30); + horizontal.setAttribute("x2", 30); + horizontal.setAttribute("y2", 54); + horizontal.setAttribute("class", "center"); + //result.appendChild(horizontal); + + for (let i = 0; i <= width * 30; i += 3) { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", i); + line.setAttribute("y1", 0); + line.setAttribute("x2", i); + line.setAttribute("y2", 54 * height); + line.setAttribute("class", "grid"); + result.appendChild(line); + } + + for (let i = 0; i <= height * 54; i += 3) { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 0); + line.setAttribute("y1", i); + line.setAttribute("x2", 30 * width); + line.setAttribute("y2", i); + line.setAttribute("class", "grid"); + result.appendChild(line); + } + + return result; +} + +const glyphs = {}; + +glyphs["|"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + if (right == "_") { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "18"); + line.setAttribute("y1", "51"); + line.setAttribute("x2", "30"); + line.setAttribute("y2", "51"); + line.setAttribute("class", "part"); + result.appendChild(line); + } + if (left == "_") { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "0"); + line.setAttribute("y1", "51"); + line.setAttribute("x2", "12"); + line.setAttribute("y2", "51"); + line.setAttribute("class", "part"); + result.appendChild(line); + } + if (topRight == "_") { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "12"); + line.setAttribute("y1", "-3"); + line.setAttribute("x2", "30"); + line.setAttribute("y2", "-3"); + line.setAttribute("class", "part"); + result.appendChild(line); + } + if (topLeft == "_") { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "0"); + line.setAttribute("y1", "-3"); + line.setAttribute("x2", "18"); + line.setAttribute("y2", "-3"); + line.setAttribute("class", "part"); + result.appendChild(line); + } + // const leg = && ; + // const head = && ; + //console.log(!(bottomLeft == "/" && bottomRight == "\\")); + //console.log(!(topRight == "/" && topLeft == "\\")); + result.appendChild( + cross([ + !(topRight == "/" && topLeft == "\\"), // top + ["-"].includes(right), // right + !(bottomLeft == "/" && bottomRight == "\\"), // bottom + ["-"].includes(left), // left + topRight == "/", // topRight + bottomRight == "\\", // bottomRight + bottomLeft == "/", // bottomLeft + topLeft == "\\", // topLeft + ]) + ); + return result; +}; + +glyphs["-"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + return cross([ + ["|"].includes(top), // top + true, // right + ["|"].includes(bottom), // bottom + true, // left + false, // topRight + false, // bottomRight + false, // bottomLeft + false, // topLeft + ]); +}; + +glyphs["~"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "9"); + line.setAttribute("y1", "27"); + line.setAttribute("x2", "24"); + line.setAttribute("y2", "27"); + line.setAttribute("class", "part"); + result.appendChild(line); + return result; +}; + +glyphs["_"] = (around) => { + const line = glyphs["-"](around); + line.setAttribute("transform", "translate(0 24)"); + return line; +}; + +glyphs[":"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "15"); + line.setAttribute("y1", "0"); + line.setAttribute("x2", "15"); + line.setAttribute("y2", "60"); + line.setAttribute("class", "part"); + line.setAttribute("style", "stroke-dasharray: 15; stroke-dashoffset: 0;"); + result.appendChild(line); + if (top == "+") { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "15"); + line.setAttribute("y1", "-24"); + line.setAttribute("x2", "15"); + line.setAttribute("y2", "-15"); + line.setAttribute("class", "part"); + result.appendChild(line); + } + if (bottom == "+") { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", "15"); + line.setAttribute("y1", "60"); + line.setAttribute("x2", "15"); + line.setAttribute("y2", "78"); + line.setAttribute("class", "part"); + result.appendChild(line); + } + return result; +}; + +glyphs["="] = (around) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const first = document.createElementNS("http://www.w3.org/2000/svg", "line"); + first.setAttribute("x1", "0"); + first.setAttribute("y1", "21"); + first.setAttribute("x2", "30"); + first.setAttribute("y2", "21"); + first.setAttribute("class", "part"); + result.appendChild(first); + const second = document.createElementNS("http://www.w3.org/2000/svg", "line"); + second.setAttribute("x1", "0"); + second.setAttribute("y1", "30"); + second.setAttribute("x2", "30"); + second.setAttribute("y2", "30"); + second.setAttribute("class", "part"); + result.appendChild(second); + return result; +}; + +glyphs["*"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); + circle.setAttribute("cx", "0"); + circle.setAttribute("cy", "0"); + circle.setAttribute("r", "21"); + circle.setAttribute("stroke", "none"); + circle.setAttribute("transform", "translate(15, 27)"); + result.appendChild(circle); + + result.appendChild( + cross([ + ["+", "|"].includes(top), + ["+", "-"].includes(right), + ["+", "|"].includes(bottom), + ["+", "-"].includes(left), + ["/"].includes(topRight), + ["\\"].includes(bottomRight), + ["/"].includes(bottomLeft), + ["\\"].includes(topLeft), + ]) + ); + + return result; +}; + +glyphs["o"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); + circle.setAttribute("cx", "0"); + circle.setAttribute("cy", "0"); + circle.setAttribute("r", "18"); + circle.setAttribute("stroke-width", "6"); + circle.setAttribute("fill", "none"); + circle.setAttribute("stroke", "var(--global-text-color)"); + circle.setAttribute("transform", "translate(15, 27)"); + result.appendChild(circle); + + const connectors = cross([ + ["+", "|"].includes(top), + ["+", "-"].includes(right), + ["+", "|"].includes(bottom), + ["+", "-"].includes(left), + ["/"].includes(topRight), + ["\\"].includes(bottomRight), + ["/"].includes(bottomLeft), + ["\\"].includes(topLeft), + ]); + + result.appendChild(connectors); + + const inner = document.createElementNS("http://www.w3.org/2000/svg", "circle"); + inner.setAttribute("cx", "0"); + inner.setAttribute("cy", "0"); + inner.setAttribute("r", "15"); + inner.setAttribute("fill", "white"); + inner.setAttribute("opacity", "100%"); + inner.setAttribute("transform", "translate(15, 27)"); + result.appendChild(inner); + + return result; +}; + +glyphs["/"] = (around) => { + const [top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft] = around; + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + result.appendChild( + cross([ + ["|"].includes(top), // top + false, // right + ["|"].includes(bottom), // bottom + false, // left + true, // topRight + false, // bottomRight + true, // bottomLeft + false, // topLeft + ]) + ); + if (right == "\\") { + const tip = cross([ + false, + false, + false, + false, + false, + false, + true, // bottomLeft + false, + ]); + tip.setAttribute("transform", "translate(30 -54)"); + tip.setAttribute("clip-path", "polygon(-3 0, 0 0, 0 54, -3 54)"); + result.appendChild(tip); + } + if (left == "\\") { + const tip = cross([ + false, + false, + false, + false, + true, // topRight + false, + false, // bottomLeft + false, + ]); + tip.setAttribute("transform", "translate(-30 54)"); + tip.setAttribute("clip-path", "polygon(15 -6, 33 -6, 33 6, 15 6)"); + result.appendChild(tip); + } + + if (right == "_") { + const line = glyphs["_"](around); + result.appendChild(line); + } + + return result; +}; + +glyphs["\\"] = (around) => { + const [top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft] = around; + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + result.appendChild( + cross([ + ["|"].includes(top), // top + false, // right + ["|"].includes(bottom), // bottom + false, // left + false, // topRight + true, // bottomRight + false, // bottomLeft + true, // topLeft + ]) + ); + if (left == "/") { + const tip = cross([ + false, + false, + false, + false, + false, + true, // bottomRight + false, + false, + ]); + tip.setAttribute("transform", "translate(-30 -54)"); + tip.setAttribute("clip-path", "polygon(15 0, 30 0, 30 54, 15 54)"); + result.appendChild(tip); + } + if (right == "/") { + const tip = cross([false, false, false, false, false, false, false, true]); + tip.setAttribute("transform", "translate(30 54)"); + tip.setAttribute("clip-path", "polygon(-3 0, 0 0, 0 6, -3 6)"); + result.appendChild(tip); + } + + if (left == "_") { + const line = glyphs["_"](around); + result.appendChild(line); + } + + return result; +}; + +glyphs["#"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + const points = [ + [0, 0], + [42, 0], + [42, 42], + [0, 42], + ]; + polygon.setAttribute("points", points.map(([x, y]) => `${x},${y}`).join(" ")); + polygon.setAttribute("transform", "translate(-6, 6)"); + result.appendChild(polygon); + + result.appendChild( + cross([ + ["+", "|"].includes(top), + ["+", "-"].includes(right), + ["+", "|"].includes(bottom), + ["+", "-"].includes(left), + ["/"].includes(topRight), + ["\\"].includes(bottomRight), + ["/"].includes(bottomLeft), + ["\\"].includes(topLeft), + ]) + ); + + return result; +}; + +glyphs["+"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const r = ["*", "#", "-", "+", "~", ">", ".", "'", "`"].includes(right); + const l = ["*", "#", "-", "+", "~", "<", ".", "'", "`"].includes(left); + const t = ["*", "#", "|", "+", ".", "`", "^"].includes(top); + const b = ["*", "#", "|", "+", "'", "`", "v"].includes(bottom); + const tR = ["/", "*", "#"].includes(topRight); + const bR = ["\\", "*", "#"].includes(bottomRight); + const tL = ["\\", "*", "#"].includes(topLeft); + const bL = ["/", "*", "#"].includes(bottomLeft); + + // cross + result.appendChild(cross([t, r, b, l, tR, bR, bL, tL])); + + // center + if ((l || r) && (b || t)) { + const center = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + center.setAttribute("points", "0,0 6,0 6,6 0,6"); + center.setAttribute("transform", "translate(-3 -3) translate(15 27)"); + result.appendChild(center); + } + + // tip + if (tR || tL) { + const center = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + tL, // bottomRight + tR, // bottomLeft + false, // topLeft + ]); + center.setAttribute("clip-path", "polygon(0 -3, 30 -3, 30 0, 0 0)"); + result.appendChild(center); + } + + if (bR || bL) { + const center = cross([ + false, // top + false, // right + false, // bottom + false, // left + bL, // topRight + false, // bottomRight + false, // bottomLeft + bR, // topLeft + ]); + center.setAttribute("clip-path", "polygon(0 27, 15 27, 15 30, 0 30)"); + result.appendChild(center); + } + + if (bL || tL) { + const center = cross([ + false, // top + false, // right + false, // bottom + false, // left + bL && bR, // topRight + tL && tR, // bottomRight + false, // bottomLeft + false, // topLeft + ]); + center.setAttribute("clip-path", "polygon(-3 0, 0 0, 0 54, -3 54)"); + result.appendChild(center); + } + + if (bR || tR) { + const center = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + false, // bottomRight + tR && tL, // bottomLeft + bR && bL, // topLeft + ]); + //console.log(center); + center.setAttribute("clip-path", "polygon(15 0, 30 0, 30 54, 15 54)"); + result.appendChild(center); + } + + if (r || l) { + const center = cross([ + false, // top + false, // right + false, // bottom + false, // left + r || bL, // topRight + tL, // bottomRight + tR, // bottomLeft + l || bR, // topLeft + ]); + center.setAttribute("clip-path", "polygon(-3 24, 30 24, 30 30, -3 30)"); + result.appendChild(center); + } + return result; +}; + +glyphs["."] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + + // top-right + if ((right == "-" || right == "+") && (bottom == "|" || bottom == "'" || bottom == "`" || bottom == "+")) { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 30 24 + A 18 18, 0, 0, 0, 12 42 + L 12 54 + L 18 54 + L 18 42 + A 12 12, 0, 0, 1, 30 30 + Z` + ); + result.appendChild(path); + } + + // top-left + if ((left == "-" || left == "+") && (bottom == "|" || bottom == "'" || bottom == "`" || bottom == "+")) { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 24 + A 18 18, 0, 0, 1, 18 42 + L 18 54 + L 12 54 + L 12 42 + A 12 12, 0, 0, 0, 0 30 + Z` + ); + result.appendChild(path); + } + + // top-right + if ((right == "-" || right == "+") && (top == "|" || top == "." || top == "+")) { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 30 30 + A 18 18, 0, 0, 1, 12 12 + L 12 0 + L 18 0 + L 18 12 + A 12 12, 0, 0, 0, 30 24 + Z` + ); + result.appendChild(path); + } + + // bottom-left + if ((left == "-" || left == "+") && (top == "|" || top == "." || top == "+")) { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 30 + A 18 18, 0, 0, 0, 18 12 + L 18 0 + L 12 0 + L 12 12 + A 12 12, 0, 0, 1, 0 24 + Z` + ); + result.appendChild(path); + } + + // bottom right-topRight + if (right == "-" && topRight == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 30 30 + A 12 12, 0, 0, 1, 18 18 + L 18 15 + L 24 15 + L 24 18 + A 6 6, 0, 0, 0, 30 24 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + true, // topRight + false, // bottomRight + false, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(15px -10px, 30px -10px, 30px 30px, 2px 15px)"); + result.appendChild(line); + } + + // right-topLeft + if (right == "-" && topLeft == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M -3 0 + A 60 60, 0, 0, 0, 30 30 + L 30 24 + A 60 60, 0, 0, 1, 0 -6 + Z` + ); + result.appendChild(path); + } + + // left-topRight + if (left == "-" && topRight == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 30 + A 60 60, 0, 0, 0, 33 0 + L 30 -6 + A 60 60, 0, 0, 1, 0 24 + Z` + ); + result.appendChild(path); + } + + // bottom left-topLeft + if (left == "-" && topLeft == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 30 + A 12 12, 0, 0, 0, 12 18 + L 12 15 + L 6 15 + L 6 18 + A 6 6, 0, 0, 1, 0 24 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + false, // bottomRight + false, // bottomLeft + true, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 -3, 12 -3, 12 18, -3 18)"); + result.appendChild(line); + } + + // bottom-topRight + if (bottom == "|" && topRight == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 12 54 + A 120 120, 0, 0, 1, 30 -6 + L 37 -6 + A 120 120, 0, 0, 0, 18 54 + Z` + ); + result.appendChild(path); + } + + // top-bottomRight + if (top == "|" && bottomRight == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 30 60 + A 120 120, 0, 0, 1, 12 0 + L 18 0 + A 120 120, 0, 0, 0, 37 60 + Z` + ); + result.appendChild(path); + } + + // top-bottomLeft + if (top == "|" && bottomLeft == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 60 + A 120 120, 0, 0, 0, 18 0 + L 12 0 + A 120 120, 0, 0, 1, -7 60 + Z` + ); + result.appendChild(path); + } + + // bottom-topLeft + if (bottom == "|" && topLeft == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 12 54 + A 120 120, 0, 0, 0, -7 -6 + L 0 -6 + A 120 120, 0, 0, 1, 18 54 + Z` + ); + result.appendChild(path); + } + + // right-bottomLeft + if (right == "-" && bottomLeft == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 48 + A 42 42, 0, 0, 1, 30 24 + L 30 30 + A 42 42, 0, 0, 0, 6 48 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + false, // bottomRight + true, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 15, 12 15, 12 30, -3 30)"); + result.appendChild(line); + } + + // left-bottomRight + if (left == "-" && bottomRight == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 24 + A 42 42, 0, 0, 1, 30 48 + L 24 48 + A 42 42, 0, 0, 0, 0 30 + Z` + ); + + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + true, // bottomRight + false, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 15, 12 15, 21 30, -3 30)"); + result.appendChild(line); + } + + // left-bottomLeft + if (left == "-" && bottomLeft == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 0 24 + A 12 12, 0, 0, 1, 12 39 + L 6 39 + A 6 6, 0, 0, 0, 0 30 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + false, // bottomRight + true, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 6, 12 6, 12 30, -3 30)"); + result.appendChild(line); + } + + // right-bottomRight + if (right == "-" && bottomRight == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 30 24 + A 12 12, 0, 0, 0, 18 39 + L 24 39 + A 6 6, 0, 0, 1, 30 30 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + true, // bottomRight + false, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(3 6, 18 6, 18 30, 3 30)"); + result.appendChild(line); + } + + // bottomLeft-bottomRight + if (bottomLeft == "/" && bottomRight == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 3 42 + A 15 15, 0, 0, 1, 27 42 + L 25 51 + A 9 9, 0, 0, 0, 5 51 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + true, // bottomRight + true, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 15, 33 15, 33 30, -3 30)"); + result.appendChild(line); + } + + // topLeft-topRight + if (topLeft == "\\" && topRight == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 3 12 + A 15 15, 0, 0, 0, 27 12 + L 22 9 + A 9 9, 0, 0, 1, 8 9 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + true, // topRight + false, // bottomRight + false, // bottomLeft + true, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 -3, 33 -3, 33 12, -3 12)"); + result.appendChild(line); + } + + // topRight-bottomRight + if (topRight == "/" && bottomRight == "\\") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 22 9 + A 30 30, 0, 0, 0, 22 45 + L 28 45 + A 30 30, 0, 0, 1, 28 9 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + true, // topRight + true, // bottomRight + false, // bottomLeft + false, // topLeft + ]); + line.setAttribute("clip-path", "polygon(6 -3, 33 -3, 33 57, 6 57)"); + result.appendChild(line); + } + + // topLeft-bottomLeft + if (topLeft == "\\" && bottomLeft == "/") { + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute( + "d", + ` + M 8 9 + A 30 30, 0, 0, 1, 8 45 + L 2 45 + A 30 30, 0, 0, 0, 2 9 + Z` + ); + result.appendChild(path); + const line = cross([ + false, // top + false, // right + false, // bottom + false, // left + false, // topRight + false, // bottomRight + true, // bottomLeft + true, // topLeft + ]); + line.setAttribute("clip-path", "polygon(-3 -3, 9 -3, 9 57, -3 57)"); + result.appendChild(line); + } + + return result; +}; + +const alias = { + "┌": "+", + "┐": "+", + "└": "+", + "┘": "+", + "─": "-", + "►": ">", + "'": ".", + "`": ".", + V: "v", +}; + +for (const [key, value] of Object.entries(alias)) { + glyphs[key] = (around) => { + return glyphs[value](around); + }; +} + +glyphs[">"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const arrow = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + arrow.setAttribute("points", "0,0 42,18 0,36"); + let reach = 0; + if (right == "*" || right == "o" || right == "#") { + reach -= 18; + } + arrow.setAttribute("transform", `translate(${reach} 9)`); + result.appendChild(arrow); + return result; + const center = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + center.setAttribute("points", "-3,0 6,0 6,6 -3,6"); + center.setAttribute("transform", "translate(15 24)"); + result.appendChild(center); + result.appendChild( + cross([ + false, // top + false, // right + false, // bottom + ["-", "+"].includes(left), // left + false, // topRight + false, // bottomRight + ["/"].includes(bottomLeft), // bottomLeft + ["\\"].includes(topLeft), // topLeft + ]) + ); + return result; +}; + +glyphs["<"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const arrow = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + arrow.setAttribute("points", "0,0 42,18 0,36"); + let reach = 30; + if (left == "*" || left == "o" || left == "#") { + reach += 18; + } + arrow.setAttribute("transform", `translate(${reach} 9) translate(0 36) rotate(180)`); + result.appendChild(arrow); + return result; + //const center = document.createElementNS( + // "http://www.w3.org/2000/svg", "polygon"); + //center.setAttribute("points", "0,0 9,0 9,6 0,6"); + //center.setAttribute("transform", "translate(9 24)"); + //result.appendChild(center); + result.appendChild( + cross([ + false, // top + ["-", "+"].includes(right), // right + false, // bottom + false, // left + ["/"].includes(topRight), // topRight + ["\\"].includes(bottomRight), // bottomRight + false, // bottomLeft + false, // topLeft + ]) + ); + return result; +}; + +glyphs["v"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const arrow = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + arrow.setAttribute("points", "0,0 42,18 0,36"); + let reach = 36; + if (bottom == " ") { + reach = 12; + } else if (bottom == "_") { + reach += 18; + } else if (bottom == "*" || bottom == "o" || bottom == "#") { + reach -= 18; + } + if (topRight == "/") { + arrow.setAttribute("transform", `translate(-36 33) rotate(${90 + 22.5}, 42, 18)`); + } else if (topLeft == "\\") { + arrow.setAttribute("transform", `translate(-18 33) rotate(${90 - 22.5}, 42, 18)`); + } else { + arrow.setAttribute("transform", `translate(33 ${reach}) rotate(90)`); + } + result.appendChild(arrow); + result.appendChild( + cross([ + ["|", "+"].includes(top), // top + false, // right + ["|", "+"].includes(top), // bottom + false, // left + ["/"].includes(topRight), // topRight + false, // bottomRight + false, // bottomLeft + ["\\"].includes(topLeft), // topLeft + ]) + ); + return result; +}; + +glyphs["^"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const arrow = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + arrow.setAttribute("points", "0,0 42,18 0,36"); + let reach = 42; + if (top == "-") { + reach -= 15; + } + if (bottomLeft == "/") { + arrow.setAttribute("transform", `translate(-18 -15) rotate(${-45 - 22.5}, 42, 18)`); + } else if (bottomRight == "\\") { + arrow.setAttribute("transform", `translate(-36 -15) rotate(${-90 - 22.5}, 42, 18)`); + } else { + arrow.setAttribute("transform", `translate(-3 ${reach}) rotate(-90)`); + } + result.appendChild(arrow); + result.appendChild( + cross([ + false, // top + false, // right + ["+", "|"].includes(bottom), // bottom + false, // left + false, // topRight + ["\\"].includes(bottomRight), // bottomRight + ["/"].includes(bottomLeft), // bottomLeft + false, // topLeft + ]) + ); + return result; +}; + +function cross([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + if (top) { + // { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 15); + line.setAttribute("y1", 0); + line.setAttribute("x2", 15); + line.setAttribute("y2", 27); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + if (right) { + //{ + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 15); + line.setAttribute("y1", 27); + line.setAttribute("x2", 30); + line.setAttribute("y2", 27); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + if (bottom) { + //{ + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 15); + line.setAttribute("y1", 27); + line.setAttribute("x2", 15); + line.setAttribute("y2", 54); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + if (left) { + //{ + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 0); + line.setAttribute("y1", 27); + line.setAttribute("x2", 15); + line.setAttribute("y2", 27); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + const diagonal = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); + + diagonal.setAttribute( + "points", + [ + [0, 0], + [20.6, 0], + [20.6, 3], + [0, 3], + ] + .map(([x, y]) => `${x},${y}`) + .join(" ") + ); + + if (topRight) { + //{ + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 30); + line.setAttribute("y1", 0); + line.setAttribute("x2", 15); + line.setAttribute("y2", 27); + line.setAttribute("class", "part"); + // line.setAttribute("transform", "scale(1, 1)"); + // line.setAttribute("clip-path", "polygon(-6 -6, 15 -6, 15 30, -6 30)"); + // line.setAttribute("stroke-linecap", "square !important"); + result.appendChild(line); + //const mask = document.createElementNS( + // "http://www.w3.org/2000/svg", "polygon"); + //mask.setAttribute("points", "0 0, 15 0, 15 18, 0 18"); + //result.appendChild(mask); + //console.log("hi"); + } + + if (bottomRight) { + //{ + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 15); + line.setAttribute("y1", 27); + line.setAttribute("x2", 30); + line.setAttribute("y2", 54); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + if (bottomLeft) { + // { + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 15); + line.setAttribute("y1", 27); + line.setAttribute("x2", 0); + line.setAttribute("y2", 54); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + if (topLeft) { + //{ + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", 0); + line.setAttribute("y1", 0); + line.setAttribute("x2", 15); + line.setAttribute("y2", 27); + line.setAttribute("class", "part"); + result.appendChild(line); + } + + return result; +} + +function text(char, reserved) { + const g = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const result = document.createElementNS("http://www.w3.org/2000/svg", "text"); + //result.setAttribute("xml:space", "preserve"); + //result.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:space", "preserve"); + const value = document.createTextNode(char); + result.appendChild(value); + if (reserved) { + result.setAttribute("class", "reserved"); + } + const translation = [ + [15, 24], + //[1.5, 1.5 * ratio] + ]; + result.setAttribute("transform", translation.map(([x, y]) => `translate(${x}, ${y})`).join(" ")); + g.appendChild(result); + return g; +} + +function render(diagram) { + const result = document.createElementNS("http://www.w3.org/2000/svg", "g"); + + for (let y = 0; y < diagram.length; y++) { + for (let x = 0; x < diagram[y].length; x++) { + const char = diagram[y][x]; + + if (char == " " || char == '"') { + continue; + } + + let reserved = glyphs[char]; + + const g = document.createElementNS("http://www.w3.org/2000/svg", "g"); + + let str = false; + for (let i = 0; i < x; i++) { + if (diagram[y][i] == '"') { + str = !str; + } + } + + const neighbors = around(diagram, [x, y]); + + if (char.match(/[A-Za-z0-9]/)) { + const [, right, , left] = neighbors; + // We special case "v", which is a down arrow, and also a text character. + str = str || left.match(/[A-Za-uw-z0-9]/) || right.match(/[A-Za-uw-z0-9]/); + } + + reserved = reserved && !str; + + if (reserved) { + g.appendChild(glyphs[char](neighbors)); + } + + g.appendChild(text(char, reserved)); + + g.setAttribute("transform", `translate(${x * 30} ${y * 54})`); + result.appendChild(g); + } + } + return result; +} + +function create(source, zoom, debug) { + const diagram = source.split("\n").map((line) => line.trimEnd().split("")); + + diagram.shift(); + diagram.splice(-1); + + let width = 0; + const height = diagram.length; + + for (let y = 0; y < diagram.length; y++) { + for (let x = 0; x < diagram[y].length; x++) { + if (diagram[y].length > width) { + width = diagram[x].length; + } + } + } + + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttribute("width", width * 30 * zoom); + svg.setAttribute("height", height * 54 * zoom); + svg.setAttribute("debug", debug); + const padding = 0; + + svg.setAttribute("viewBox", `${-padding} ${-padding} ${width * 30 + 2 * padding} ${height * 54 + 2 * padding}`); + svg.setAttribute("class", "debug"); + svg.appendChild(render(diagram)); + + if (debug) { + svg.appendChild(grid(width, height)); + } + + return svg; +} + +function around(diagram, [x, y]) { + let left = " "; + let top = " "; + let right = " "; + let bottom = " "; + let topRight = " "; + let bottomRight = " "; + let bottomLeft = " "; + let topLeft = " "; + if (y > 0) { + top = diagram[y - 1][x] || " "; + } + if (x < diagram[y].length - 1) { + right = diagram[y][x + 1] || " "; + } + if (y < diagram.length - 1) { + bottom = diagram[y + 1][x] || " "; + } + if (x > 0) { + left = diagram[y][x - 1] || " "; + } + if (y > 0 && x < diagram[y - 1].length - 1) { + // console.log(`@${diagram[y][x]}: ${diagram[y - 1][x + 1]}`); + topRight = diagram[y - 1][x + 1] || " "; + } + //if (diagram[y][x] == ".") { + //console.log(`${diagram[y][x]}}: ${(y + 1) < (diagram.length)}`); + //console.log(diagram[y + 1]); + //throw new Error("hi"); + //} + if (y + 1 < diagram.length && x < diagram[y + 1].length) { + bottomRight = diagram[y + 1][x + 1] || " "; + //console.log(diagram[y + 1]); + //console.log(`${diagram[y][x]}: ${x} ${y} ${bottomRight}`); + //throw new Error("hi"); + } + if (y < diagram.length - 1 && x > 0) { + bottomLeft = diagram[y + 1][x - 1] || " "; + } + if (y > 0 && x > 0) { + topLeft = diagram[y - 1][x - 1] || " "; + } + return [top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]; + //.map((el) => alias[el] ? alias[el] : el); +} + +// module.exports = create;