├── index.js
├── .gitignore
├── tests
└── index.js
├── package.json
├── contributing.md
├── README.md
├── test.html
├── typograms.css
├── LICENSE
├── google-sans.css
├── index.html
└── typograms.js
/index.js:
--------------------------------------------------------------------------------
1 | console.log("hi");
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | _site/
3 | vendor/
4 | Gemfile.lock
5 | *~
6 | .jekyll-metadata
7 | node_modules/
8 | package-lock.json
9 |
--------------------------------------------------------------------------------
/tests/index.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 |
3 | describe("Typograms", function() {
4 |
5 | it("", () => {
6 | });
7 |
8 | function assertThat(x) {
9 | return {
10 | equalsTo(y) {
11 | assert.deepEqual(x, y);
12 | }
13 | }
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typograms",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "mocha -w tests"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/samuelgoto/typograms.git"
12 | },
13 | "author": "",
14 | "license": "Apache 2",
15 | "bugs": {
16 | "url": "https://github.com/samuelgoto/typograms/issues"
17 | },
18 | "homepage": "https://github.com/samuelgoto/typograms#readme",
19 | "devDependencies": {
20 | "minifyify": "^7.3.5",
21 | "mocha": "^5.2.0",
22 | "tinyify": "^3.0.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code Reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google/conduct/).
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # typograms
2 |
3 | Typograms (short for typographic diagrams) is a lightweight image format
4 | (`text/typogram`) useful for defining simple diagrams in technical
5 | documentation.
6 |
7 | See it in action here:
8 |
9 | https://google.github.io/typograms/
10 |
11 | Like markdown, typograms is heavily inspired by pre-existing conventions
12 | found in ASCII diagrams. A small set of primitives and rules to connect
13 | them is defined, which you can use to build larger diagrams.
14 |
15 | Typograms optimizes for editability and portability (e.g. plain text is
16 | easy to maintain, change, store and transmit), at the cost of expressivity
17 | (e.g. SVG is more expressive) and ergonomics (e.g. higher level tools
18 | produce diagrams faster).
19 |
20 | You can embed typograms into pages using the polyfill library:
21 |
22 | ```html
23 |
24 |
25 |
30 |
31 | ```
32 |
33 | A comparison with related work is available below.
34 |
35 | A polyfill is available that allows you use to use it in browsers.
36 |
37 |
38 |
--------------------------------------------------------------------------------
/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
87 |
120 |
121 |
122 | Overview
123 |
124 | Typograms (typographic diagrams) is a lightweight image format (text/typogram) useful for defining simple diagrams in technical documentation.
125 |
126 | Like markdown, typograms is heavily inspired by pre-existing conventions found in ASCII diagrams. A small set of primitives and rules to connect them is defined, which you can use to build larger diagrams.
127 |
128 | Typograms optimizes for editability and portability (e.g. plain text is easy to maintain, change, store and transmit), at the cost of expressivity (e.g. SVG is more expressive) and ergonomics (e.g. higher level tools produce diagrams faster).
129 |
130 | A comparison with related work is available below.
131 |
132 | A polyfill is available that allows you use to use it in browsers.
133 |
134 | Installation
135 |
136 | To get started, the easiest way to use typograms is to link from our CDN:
137 |
138 |
139 |
140 | <body>
141 | <script src="https://google.github.io/typograms/typograms.js"></script>
142 | <script type="text/typogram">
143 | +----+
144 | | |---> My first diagram!
145 | +----+
146 | </script>
147 | </body>
148 |
149 |
150 |
151 | From there, you can download the library directly to serve yourself.
152 |
153 | You can also use it as a command line tool or a node library:
154 |
155 | https://github.com/google/typograms
156 |
157 | Primitives
158 |
159 | Typograms are composed of primitives and connectors that combine them: pipes (| - _ \ / : ~), dots arrows (> ^ * o # v < ) and Connectors (+ . -).
160 |
161 | By putting them together, you can form a lot of different diagrams.
162 |
163 | Pipes
164 |
188 |
189 | Dots
190 |
195 |
196 | Arrows
197 |
217 |
218 | Text
219 |
222 |
223 | Examples
224 |
225 | Protocols
226 |
227 |
240 |
241 | Mocks
242 |
243 |
267 |
268 | Architecture
269 |
270 | Components
271 |
272 |
287 |
288 | Layers
289 |
290 |
309 |
310 | Tables
311 |
312 |
327 |
328 | Flow charts
329 |
330 |
345 |
346 | Trees
347 |
348 |
378 |
379 | Shapes
380 | Big Shapes
381 |
389 |
390 | Small Shapes
391 |
397 |
398 | Overlaps and intersections
399 |
408 |
409 | Grids
410 | Big grids
411 |
424 |
425 | Small grids
426 |
435 |
436 | Dot grids
437 |
444 |
445 | Time series
446 |
447 |
489 |
490 | Chips
491 |
519 |
520 | Circuits
521 |
559 |
560 | Mindmaps
561 |
591 |
592 | Scribbles
593 | Lines with decorations
594 |
609 |
610 | Line ends
611 |
632 |
633 | Graph with small nodes
634 |
642 |
643 | Graphic diagrams
644 |
645 |
656 |
657 |
658 | Work in Progress
659 |
660 | This is a list of constructions that are currently looking into supporting.
661 |
662 | UT8 Diagram Characters Are Off
663 |
664 |
688 |
689 | Triangles connected without + aren't pointy
690 |
694 |
695 | Diagonals don't join with underscores
696 |
700 |
701 | Text detection gets confused
702 |
706 |
707 | Small curved steps
708 |
711 |
712 | Parenthesis and spheres
713 |
714 |
722 |
723 | Diagonal Side-way Arrows
724 |
728 |
729 |
730 |
731 | The closest work to typograms is svgbob: it renders diagrams from ASCII and works in browsers (through a webassembly port which spits out SVGs). It supports a similar set of features (with the notable exception of its support for circle drawing) and picks a similar set of typographic conventions (with the notable exception of text rendering). It is well-specified. I ran into enough challenges with (a) the text rendering and (b) running in the browser through the webassembly port that motivated me to rewrite it in JS (svgbob is written in rust), but is otherwise a perfectly valid (more mature) alternative.
732 |
733 | From there, there is a series of projects that rendered images from ASCII diagrams but weren't either (a) particularly good to be used in isolation (i.e. just diagrams) or (b) on webpages (i.e. running natively in browsers): asciitosvg, ditaa and markdeep (and the go port GoAT) most notably.
734 |
735 | Unlike libraries like mermaid, typograms are defined typographically (WYSWYG), rather than semantically (a transformation from a high level description to graphics), which picks a different trade-off: it gives you more control over the rendering (expressivity) at the cost of making you type more (productivity).
736 |
737 | Specification
738 |
739 |
740 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
767 |
--------------------------------------------------------------------------------
/typograms.js:
--------------------------------------------------------------------------------
1 |
2 | const ratio = 2;
3 |
4 | function grid(width, height) {
5 | const result = document.createElementNS(
6 | "http://www.w3.org/2000/svg", "g");
7 |
8 | const vertical = document.createElementNS(
9 | "http://www.w3.org/2000/svg", "line");
10 | vertical.setAttribute("x1", 15);
11 | vertical.setAttribute("y1", 0);
12 | vertical.setAttribute("x2", 15);
13 | vertical.setAttribute("y2", 54);
14 | vertical.setAttribute("class", "center");
15 | //result.appendChild(vertical);
16 |
17 | const horizontal = document.createElementNS(
18 | "http://www.w3.org/2000/svg", "line");
19 | horizontal.setAttribute("x1", 0);
20 | horizontal.setAttribute("y1", 30);
21 | horizontal.setAttribute("x2", 30);
22 | horizontal.setAttribute("y2", 54);
23 | horizontal.setAttribute("class", "center");
24 | //result.appendChild(horizontal);
25 |
26 | for (let i = 0; i <= width * 30; i+= 3) {
27 | const line = document.createElementNS(
28 | "http://www.w3.org/2000/svg", "line");
29 | line.setAttribute("x1", i);
30 | line.setAttribute("y1", 0);
31 | line.setAttribute("x2", i);
32 | line.setAttribute("y2", 54 * height);
33 | line.setAttribute("class", "grid");
34 | result.appendChild(line);
35 | }
36 |
37 | for (let i = 0; i <= height * 54; i+= 3) {
38 | const line = document.createElementNS(
39 | "http://www.w3.org/2000/svg", "line");
40 | line.setAttribute("x1", 0);
41 | line.setAttribute("y1", i);
42 | line.setAttribute("x2", 30 * width);
43 | line.setAttribute("y2", i);
44 | line.setAttribute("class", "grid");
45 | result.appendChild(line);
46 | }
47 |
48 | return result;
49 | }
50 |
51 | const glyphs = {};
52 |
53 | glyphs["|"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
54 | const result = document.createElementNS(
55 | "http://www.w3.org/2000/svg", "g");
56 | if (right == "_") {
57 | const line = document.createElementNS(
58 | "http://www.w3.org/2000/svg", "line");
59 | line.setAttribute("x1", "18");
60 | line.setAttribute("y1", "51");
61 | line.setAttribute("x2", "30");
62 | line.setAttribute("y2", "51");
63 | line.setAttribute("class", "part");
64 | result.appendChild(line);
65 | }
66 | if (left == "_") {
67 | const line = document.createElementNS(
68 | "http://www.w3.org/2000/svg", "line");
69 | line.setAttribute("x1", "0");
70 | line.setAttribute("y1", "51");
71 | line.setAttribute("x2", "12");
72 | line.setAttribute("y2", "51");
73 | line.setAttribute("class", "part");
74 | result.appendChild(line);
75 | }
76 | if (topRight == "_") {
77 | const line = document.createElementNS(
78 | "http://www.w3.org/2000/svg", "line");
79 | line.setAttribute("x1", "12");
80 | line.setAttribute("y1", "-3");
81 | line.setAttribute("x2", "30");
82 | line.setAttribute("y2", "-3");
83 | line.setAttribute("class", "part");
84 | result.appendChild(line);
85 | }
86 | if (topLeft == "_") {
87 | const line = document.createElementNS(
88 | "http://www.w3.org/2000/svg", "line");
89 | line.setAttribute("x1", "0");
90 | line.setAttribute("y1", "-3");
91 | line.setAttribute("x2", "18");
92 | line.setAttribute("y2", "-3");
93 | line.setAttribute("class", "part");
94 | result.appendChild(line);
95 | }
96 | // const leg = && ;
97 | // const head = && ;
98 | //console.log(!(bottomLeft == "/" && bottomRight == "\\"));
99 | //console.log(!(topRight == "/" && topLeft == "\\"));
100 | result.appendChild(cross([
101 | !(topRight == "/" && topLeft == "\\"), // top
102 | ["-"].includes(right), // right
103 | !(bottomLeft == "/" && bottomRight == "\\"), // bottom
104 | ["-"].includes(left), // left
105 | topRight == "/", // topRight
106 | bottomRight == "\\", // bottomRight
107 | bottomLeft == "/", // bottomLeft
108 | topLeft == "\\" // topLeft
109 | ]));
110 | return result;
111 | }
112 |
113 | glyphs["-"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
114 | return cross([
115 | ["|"].includes(top), // top
116 | true, // right
117 | ["|"].includes(bottom), // bottom
118 | true, // left
119 | false, // topRight
120 | false, // bottomRight
121 | false, // bottomLeft
122 | false // topLeft
123 | ]);
124 | }
125 |
126 | glyphs["~"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
127 | const result = document.createElementNS(
128 | "http://www.w3.org/2000/svg", "g");
129 | const line = document.createElementNS(
130 | "http://www.w3.org/2000/svg", "line");
131 | line.setAttribute("x1", "9");
132 | line.setAttribute("y1", "27");
133 | line.setAttribute("x2", "24");
134 | line.setAttribute("y2", "27");
135 | line.setAttribute("class", "part");
136 | result.appendChild(line);
137 | return result;
138 | }
139 |
140 | glyphs["_"] = (around) => {
141 | const line = glyphs["-"](around);
142 | line.setAttribute("transform", "translate(0 24)");
143 | return line;
144 | }
145 |
146 | glyphs[":"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
147 | const result = document.createElementNS(
148 | "http://www.w3.org/2000/svg", "g");
149 | const line = document.createElementNS(
150 | "http://www.w3.org/2000/svg", "line");
151 | line.setAttribute("x1", "15");
152 | line.setAttribute("y1", "0");
153 | line.setAttribute("x2", "15");
154 | line.setAttribute("y2", "60");
155 | line.setAttribute("class", "part");
156 | line.setAttribute("style", "stroke-dasharray: 15; stroke-dashoffset: 0;");
157 | result.appendChild(line);
158 | if (top == "+") {
159 | const line = document.createElementNS(
160 | "http://www.w3.org/2000/svg", "line");
161 | line.setAttribute("x1", "15");
162 | line.setAttribute("y1", "-24");
163 | line.setAttribute("x2", "15");
164 | line.setAttribute("y2", "-15");
165 | line.setAttribute("class", "part");
166 | result.appendChild(line);
167 | }
168 | if (bottom == "+") {
169 | const line = document.createElementNS(
170 | "http://www.w3.org/2000/svg", "line");
171 | line.setAttribute("x1", "15");
172 | line.setAttribute("y1", "60");
173 | line.setAttribute("x2", "15");
174 | line.setAttribute("y2", "78");
175 | line.setAttribute("class", "part");
176 | result.appendChild(line);
177 | }
178 | return result;
179 | }
180 |
181 | glyphs["="] = (around) => {
182 | const result = document.createElementNS(
183 | "http://www.w3.org/2000/svg", "g");
184 | const first = document.createElementNS(
185 | "http://www.w3.org/2000/svg", "line");
186 | first.setAttribute("x1", "0");
187 | first.setAttribute("y1", "21");
188 | first.setAttribute("x2", "30");
189 | first.setAttribute("y2", "21");
190 | first.setAttribute("class", "part");
191 | result.appendChild(first);
192 | const second = document.createElementNS(
193 | "http://www.w3.org/2000/svg", "line");
194 | second.setAttribute("x1", "0");
195 | second.setAttribute("y1", "30");
196 | second.setAttribute("x2", "30");
197 | second.setAttribute("y2", "30");
198 | second.setAttribute("class", "part");
199 | result.appendChild(second);
200 | return result;
201 | }
202 |
203 | glyphs["*"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
204 | const result = document.createElementNS(
205 | "http://www.w3.org/2000/svg", "g");
206 | const circle = document.createElementNS(
207 | "http://www.w3.org/2000/svg", "circle");
208 | circle.setAttribute("cx", "0");
209 | circle.setAttribute("cy", "0");
210 | circle.setAttribute("r", "21");
211 | circle.setAttribute("stroke", "none");
212 | circle.setAttribute("transform", "translate(15, 27)");
213 | result.appendChild(circle);
214 |
215 | result.appendChild(cross([
216 | ["+", "|"].includes(top),
217 | ["+", "-"].includes(right),
218 | ["+", "|"].includes(bottom),
219 | ["+", "-"].includes(left),
220 | ["/"].includes(topRight),
221 | ["\\"].includes(bottomRight),
222 | ["/"].includes(bottomLeft),
223 | ["\\"].includes(topLeft)
224 | ]));
225 |
226 | return result;
227 | }
228 |
229 | glyphs["o"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
230 | const result = document.createElementNS(
231 | "http://www.w3.org/2000/svg", "g");
232 | const circle = document.createElementNS(
233 | "http://www.w3.org/2000/svg", "circle");
234 | circle.setAttribute("cx", "0");
235 | circle.setAttribute("cy", "0");
236 | circle.setAttribute("r", "18");
237 | circle.setAttribute("stroke-width", "6");
238 | circle.setAttribute("fill", "none");
239 | circle.setAttribute("stroke", "black");
240 | circle.setAttribute("transform", "translate(15, 27)");
241 | result.appendChild(circle);
242 |
243 | const connectors = cross([
244 | ["+", "|"].includes(top),
245 | ["+", "-"].includes(right),
246 | ["+", "|"].includes(bottom),
247 | ["+", "-"].includes(left),
248 | ["/"].includes(topRight),
249 | ["\\"].includes(bottomRight),
250 | ["/"].includes(bottomLeft),
251 | ["\\"].includes(topLeft)
252 | ]);
253 |
254 | result.appendChild(connectors);
255 |
256 | const inner = document.createElementNS(
257 | "http://www.w3.org/2000/svg", "circle");
258 | inner.setAttribute("cx", "0");
259 | inner.setAttribute("cy", "0");
260 | inner.setAttribute("r", "15");
261 | inner.setAttribute("fill", "white");
262 | inner.setAttribute("opacity", "100%");
263 | inner.setAttribute("transform", "translate(15, 27)");
264 | result.appendChild(inner);
265 |
266 | return result;
267 | }
268 |
269 | glyphs["/"] = (around) => {
270 | const [top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft] = around;
271 | const result = document.createElementNS(
272 | "http://www.w3.org/2000/svg", "g");
273 | result.appendChild(cross([
274 | ["|"].includes(top), // top
275 | false, // right
276 | ["|"].includes(bottom), // bottom
277 | false, // left
278 | true, // topRight
279 | false, // bottomRight
280 | true, // bottomLeft
281 | false // topLeft
282 | ]));
283 | if (right == "\\") {
284 | const tip = cross([
285 | false,
286 | false,
287 | false,
288 | false,
289 | false,
290 | false,
291 | true, // bottomLeft
292 | false
293 | ]);
294 | tip.setAttribute("transform", "translate(30 -54)");
295 | tip.setAttribute("clip-path", "polygon(-3 0, 0 0, 0 54, -3 54)");
296 | result.appendChild(tip);
297 | }
298 | if (left == "\\") {
299 | const tip = cross([
300 | false,
301 | false,
302 | false,
303 | false,
304 | true, // topRight
305 | false,
306 | false, // bottomLeft
307 | false
308 | ]);
309 | tip.setAttribute("transform", "translate(-30 54)");
310 | tip.setAttribute("clip-path", "polygon(15 -6, 33 -6, 33 6, 15 6)");
311 | result.appendChild(tip);
312 | }
313 |
314 | if (right == "_") {
315 | const line = glyphs["_"](around);
316 | result.appendChild(line);
317 | }
318 |
319 | return result;
320 | }
321 |
322 | glyphs["\\"] = (around) => {
323 | const [top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft] = around;
324 | const result = document.createElementNS(
325 | "http://www.w3.org/2000/svg", "g");
326 | result.appendChild(cross([
327 | ["|"].includes(top), // top
328 | false, // right
329 | ["|"].includes(bottom), // bottom
330 | false, // left
331 | false, // topRight
332 | true, // bottomRight
333 | false, // bottomLeft
334 | true // topLeft
335 | ]));
336 | if (left == "/") {
337 | const tip = cross([
338 | false,
339 | false,
340 | false,
341 | false,
342 | false,
343 | true, // bottomRight
344 | false,
345 | false
346 | ]);
347 | tip.setAttribute("transform", "translate(-30 -54)");
348 | tip.setAttribute("clip-path", "polygon(15 0, 30 0, 30 54, 15 54)");
349 | result.appendChild(tip);
350 | }
351 | if (right == "/") {
352 | const tip = cross([
353 | false,
354 | false,
355 | false,
356 | false,
357 | false,
358 | false,
359 | false,
360 | true
361 | ]);
362 | tip.setAttribute("transform", "translate(30 54)");
363 | tip.setAttribute("clip-path", "polygon(-3 0, 0 0, 0 6, -3 6)");
364 | result.appendChild(tip);
365 | }
366 |
367 | if (left == "_") {
368 | const line = glyphs["_"](around);
369 | result.appendChild(line);
370 | }
371 |
372 | return result;
373 | }
374 |
375 | glyphs["#"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
376 | const result = document.createElementNS(
377 | "http://www.w3.org/2000/svg", "g");
378 | const polygon = document.createElementNS(
379 | "http://www.w3.org/2000/svg", "polygon");
380 | const points = [
381 | [0, 0],
382 | [42, 0],
383 | [42, 42],
384 | [0, 42],
385 | ];
386 | polygon.setAttribute("points", points.map(([x, y]) => `${x},${y}`).join(" "));
387 | polygon.setAttribute("transform", "translate(-6, 6)");
388 | result.appendChild(polygon);
389 |
390 | result.appendChild(cross([
391 | ["+", "|"].includes(top),
392 | ["+", "-"].includes(right),
393 | ["+", "|"].includes(bottom),
394 | ["+", "-"].includes(left),
395 | ["/"].includes(topRight),
396 | ["\\"].includes(bottomRight),
397 | ["/"].includes(bottomLeft),
398 | ["\\"].includes(topLeft)
399 | ]));
400 |
401 | return result;
402 | }
403 |
404 | glyphs["+"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
405 | const result = document.createElementNS(
406 | "http://www.w3.org/2000/svg", "g");
407 | const r = ["*", "#", "-", "+", "~", ">", ".", "'", "`"].includes(right);
408 | const l = ["*", "#", "-", "+", "~", "<", ".", "'", "`"].includes(left);
409 | const t = ["*", "#", "|", "+", ".", "`", "^"].includes(top);
410 | const b = ["*", "#", "|", "+", "'", "`", "v"].includes(bottom);
411 | const tR = ["/", "*", "#"].includes(topRight);
412 | const bR = ["\\", "*", "#"].includes(bottomRight);
413 | const tL = ["\\", "*", "#"].includes(topLeft);
414 | const bL = ["/", "*", "#"].includes(bottomLeft);
415 |
416 | // cross
417 | result.appendChild(cross([
418 | t,
419 | r,
420 | b,
421 | l,
422 | tR,
423 | bR,
424 | bL,
425 | tL
426 | ]));
427 |
428 | // center
429 | if ((l || r) && (b || t)) {
430 | const center = document.createElementNS(
431 | "http://www.w3.org/2000/svg", "polygon");
432 | center.setAttribute("points", "0,0 6,0 6,6 0,6");
433 | center.setAttribute("transform", "translate(-3 -3) translate(15 27)");
434 | result.appendChild(center);
435 | }
436 |
437 | // tip
438 | if (tR || tL) {
439 | const center = cross([
440 | false, // top
441 | false, // right
442 | false, // bottom
443 | false, // left
444 | false, // topRight
445 | tL, // bottomRight
446 | tR, // bottomLeft
447 | false, // topLeft
448 | ]);
449 | center.setAttribute("clip-path", "polygon(0 -3, 30 -3, 30 0, 0 0)");
450 | result.appendChild(center);
451 | }
452 |
453 | if (bR || bL) {
454 | const center = cross([
455 | false, // top
456 | false, // right
457 | false, // bottom
458 | false, // left
459 | bL, // topRight
460 | false, // bottomRight
461 | false, // bottomLeft
462 | bR, // topLeft
463 | ]);
464 | center.setAttribute("clip-path", "polygon(0 27, 15 27, 15 30, 0 30)");
465 | result.appendChild(center);
466 | }
467 |
468 | if (bL || tL) {
469 | const center = cross([
470 | false, // top
471 | false, // right
472 | false, // bottom
473 | false, // left
474 | bL && bR, // topRight
475 | tL && tR, // bottomRight
476 | false, // bottomLeft
477 | false, // topLeft
478 | ]);
479 | center.setAttribute("clip-path", "polygon(-3 0, 0 0, 0 54, -3 54)");
480 | result.appendChild(center);
481 | }
482 |
483 | if (bR || tR) {
484 | const center = cross([
485 | false, // top
486 | false, // right
487 | false, // bottom
488 | false, // left
489 | false, // topRight
490 | false, // bottomRight
491 | tR && tL, // bottomLeft
492 | bR && bL, // topLeft
493 | ]);
494 | //console.log(center);
495 | center.setAttribute("clip-path", "polygon(15 0, 30 0, 30 54, 15 54)");
496 | result.appendChild(center);
497 | }
498 |
499 | if (r || l) {
500 | const center = cross([
501 | false, // top
502 | false, // right
503 | false, // bottom
504 | false, // left
505 | r || bL, // topRight
506 | tL, // bottomRight
507 | tR, // bottomLeft
508 | l || bR, // topLeft
509 | ]);
510 | center.setAttribute("clip-path", "polygon(-3 24, 30 24, 30 30, -3 30)");
511 | result.appendChild(center);
512 | }
513 | return result;
514 | }
515 |
516 | glyphs["."] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
517 | const result = document.createElementNS(
518 | "http://www.w3.org/2000/svg", "g");
519 |
520 | // top-right
521 | if ((right == "-" || right == "+") && (bottom == "|" || bottom == "'" || bottom == "`" || bottom == "+")) {
522 | const path = document.createElementNS(
523 | "http://www.w3.org/2000/svg", "path");
524 | path.setAttribute("d", `
525 | M 30 24
526 | A 18 18, 0, 0, 0, 12 42
527 | L 12 54
528 | L 18 54
529 | L 18 42
530 | A 12 12, 0, 0, 1, 30 30
531 | Z`);
532 | result.appendChild(path);
533 | }
534 |
535 | // top-left
536 | if ((left == "-" || left == "+") && (bottom == "|" || bottom == "'" || bottom == "`" || bottom == "+")) {
537 | const path = document.createElementNS(
538 | "http://www.w3.org/2000/svg", "path");
539 | path.setAttribute("d", `
540 | M 0 24
541 | A 18 18, 0, 0, 1, 18 42
542 | L 18 54
543 | L 12 54
544 | L 12 42
545 | A 12 12, 0, 0, 0, 0 30
546 | Z`);
547 | result.appendChild(path);
548 | }
549 |
550 | // top-right
551 | if ((right == "-" || right == "+") && (top == "|" || top == "." || top == "+")) {
552 | const path = document.createElementNS(
553 | "http://www.w3.org/2000/svg", "path");
554 | path.setAttribute("d", `
555 | M 30 30
556 | A 18 18, 0, 0, 1, 12 12
557 | L 12 0
558 | L 18 0
559 | L 18 12
560 | A 12 12, 0, 0, 0, 30 24
561 | Z`);
562 | result.appendChild(path);
563 | }
564 |
565 | // bottom-left
566 | if ((left == "-" || left == "+") && (top == "|" || top == "." || top == "+")) {
567 | const path = document.createElementNS(
568 | "http://www.w3.org/2000/svg", "path");
569 | path.setAttribute("d", `
570 | M 0 30
571 | A 18 18, 0, 0, 0, 18 12
572 | L 18 0
573 | L 12 0
574 | L 12 12
575 | A 12 12, 0, 0, 1, 0 24
576 | Z`);
577 | result.appendChild(path);
578 | }
579 |
580 | // bottom right-topRight
581 | if (right == "-" && topRight == "/") {
582 | const path = document.createElementNS(
583 | "http://www.w3.org/2000/svg", "path");
584 | path.setAttribute("d", `
585 | M 30 30
586 | A 12 12, 0, 0, 1, 18 18
587 | L 18 15
588 | L 24 15
589 | L 24 18
590 | A 6 6, 0, 0, 0, 30 24
591 | Z`);
592 | result.appendChild(path);
593 | const line = cross([
594 | false, // top
595 | false, // right
596 | false, // bottom
597 | false, // left
598 | true, // topRight
599 | false, // bottomRight
600 | false, // bottomLeft
601 | false // topLeft
602 | ]);
603 | line.setAttribute("clip-path", "polygon(15px -10px, 30px -10px, 30px 30px, 2px 15px)");
604 | result.appendChild(line);
605 | }
606 |
607 | // right-topLeft
608 | if (right == "-" && topLeft == "\\") {
609 | const path = document.createElementNS(
610 | "http://www.w3.org/2000/svg", "path");
611 | path.setAttribute("d", `
612 | M -3 0
613 | A 60 60, 0, 0, 0, 30 30
614 | L 30 24
615 | A 60 60, 0, 0, 1, 0 -6
616 | Z`);
617 | result.appendChild(path);
618 | }
619 |
620 | // left-topRight
621 | if (left == "-" && topRight == "/") {
622 | const path = document.createElementNS(
623 | "http://www.w3.org/2000/svg", "path");
624 | path.setAttribute("d", `
625 | M 0 30
626 | A 60 60, 0, 0, 0, 33 0
627 | L 30 -6
628 | A 60 60, 0, 0, 1, 0 24
629 | Z`);
630 | result.appendChild(path);
631 | }
632 |
633 | // bottom left-topLeft
634 | if (left == "-" && topLeft == "\\") {
635 | const path = document.createElementNS(
636 | "http://www.w3.org/2000/svg", "path");
637 | path.setAttribute("d", `
638 | M 0 30
639 | A 12 12, 0, 0, 0, 12 18
640 | L 12 15
641 | L 6 15
642 | L 6 18
643 | A 6 6, 0, 0, 1, 0 24
644 | Z`);
645 | result.appendChild(path);
646 | const line = cross([
647 | false, // top
648 | false, // right
649 | false, // bottom
650 | false, // left
651 | false, // topRight
652 | false, // bottomRight
653 | false, // bottomLeft
654 | true // topLeft
655 | ]);
656 | line.setAttribute("clip-path", "polygon(-3 -3, 12 -3, 12 18, -3 18)");
657 | result.appendChild(line);
658 | }
659 |
660 | // bottom-topRight
661 | if (bottom == "|" && topRight == "/") {
662 | const path = document.createElementNS(
663 | "http://www.w3.org/2000/svg", "path");
664 | path.setAttribute("d", `
665 | M 12 54
666 | A 120 120, 0, 0, 1, 30 -6
667 | L 37 -6
668 | A 120 120, 0, 0, 0, 18 54
669 | Z`);
670 | result.appendChild(path);
671 | }
672 |
673 | // top-bottomRight
674 | if (top == "|" && bottomRight == "\\") {
675 | const path = document.createElementNS(
676 | "http://www.w3.org/2000/svg", "path");
677 | path.setAttribute("d", `
678 | M 30 60
679 | A 120 120, 0, 0, 1, 12 0
680 | L 18 0
681 | A 120 120, 0, 0, 0, 37 60
682 | Z`);
683 | result.appendChild(path);
684 | }
685 |
686 | // top-bottomLeft
687 | if (top == "|" && bottomLeft == "/") {
688 | const path = document.createElementNS(
689 | "http://www.w3.org/2000/svg", "path");
690 | path.setAttribute("d", `
691 | M 0 60
692 | A 120 120, 0, 0, 0, 18 0
693 | L 12 0
694 | A 120 120, 0, 0, 1, -7 60
695 | Z`);
696 | result.appendChild(path);
697 | }
698 |
699 | // bottom-topLeft
700 | if (bottom == "|" && topLeft == "\\") {
701 | const path = document.createElementNS(
702 | "http://www.w3.org/2000/svg", "path");
703 | path.setAttribute("d", `
704 | M 12 54
705 | A 120 120, 0, 0, 0, -7 -6
706 | L 0 -6
707 | A 120 120, 0, 0, 1, 18 54
708 | Z`);
709 | result.appendChild(path);
710 | }
711 |
712 | // right-bottomLeft
713 | if (right == "-" && bottomLeft == "/") {
714 | const path = document.createElementNS(
715 | "http://www.w3.org/2000/svg", "path");
716 | path.setAttribute("d", `
717 | M 0 48
718 | A 42 42, 0, 0, 1, 30 24
719 | L 30 30
720 | A 42 42, 0, 0, 0, 6 48
721 | Z`);
722 | result.appendChild(path);
723 | const line = cross([
724 | false, // top
725 | false, // right
726 | false, // bottom
727 | false, // left
728 | false, // topRight
729 | false, // bottomRight
730 | true, // bottomLeft
731 | false // topLeft
732 | ]);
733 | line.setAttribute("clip-path", "polygon(-3 15, 12 15, 12 30, -3 30)");
734 | result.appendChild(line);
735 | }
736 |
737 | // left-bottomRight
738 | if (left == "-" && bottomRight == "\\") {
739 | const path = document.createElementNS(
740 | "http://www.w3.org/2000/svg", "path");
741 | path.setAttribute("d", `
742 | M 0 24
743 | A 42 42, 0, 0, 1, 30 48
744 | L 24 48
745 | A 42 42, 0, 0, 0, 0 30
746 | Z`);
747 |
748 | result.appendChild(path);
749 | const line = cross([
750 | false, // top
751 | false, // right
752 | false, // bottom
753 | false, // left
754 | false, // topRight
755 | true, // bottomRight
756 | false, // bottomLeft
757 | false // topLeft
758 | ]);
759 | line.setAttribute("clip-path", "polygon(-3 15, 12 15, 21 30, -3 30)");
760 | result.appendChild(line);
761 | }
762 |
763 | // left-bottomLeft
764 | if (left == "-" && bottomLeft == "/") {
765 | const path = document.createElementNS(
766 | "http://www.w3.org/2000/svg", "path");
767 | path.setAttribute("d", `
768 | M 0 24
769 | A 12 12, 0, 0, 1, 12 39
770 | L 6 39
771 | A 6 6, 0, 0, 0, 0 30
772 | Z`);
773 | result.appendChild(path);
774 | const line = cross([
775 | false, // top
776 | false, // right
777 | false, // bottom
778 | false, // left
779 | false, // topRight
780 | false, // bottomRight
781 | true, // bottomLeft
782 | false // topLeft
783 | ]);
784 | line.setAttribute("clip-path", "polygon(-3 6, 12 6, 12 30, -3 30)");
785 | result.appendChild(line);
786 | }
787 |
788 | // right-bottomRight
789 | if (right == "-" && bottomRight == "\\") {
790 | const path = document.createElementNS(
791 | "http://www.w3.org/2000/svg", "path");
792 | path.setAttribute("d", `
793 | M 30 24
794 | A 12 12, 0, 0, 0, 18 39
795 | L 24 39
796 | A 6 6, 0, 0, 1, 30 30
797 | Z`);
798 | result.appendChild(path);
799 | const line = cross([
800 | false, // top
801 | false, // right
802 | false, // bottom
803 | false, // left
804 | false, // topRight
805 | true, // bottomRight
806 | false, // bottomLeft
807 | false // topLeft
808 | ]);
809 | line.setAttribute("clip-path", "polygon(3 6, 18 6, 18 30, 3 30)");
810 | result.appendChild(line);
811 | }
812 |
813 | // bottomLeft-bottomRight
814 | if (bottomLeft == "/" && bottomRight == "\\") {
815 | const path = document.createElementNS(
816 | "http://www.w3.org/2000/svg", "path");
817 | path.setAttribute("d", `
818 | M 3 42
819 | A 15 15, 0, 0, 1, 27 42
820 | L 25 51
821 | A 9 9, 0, 0, 0, 5 51
822 | Z`);
823 | result.appendChild(path);
824 | const line = cross([
825 | false, // top
826 | false, // right
827 | false, // bottom
828 | false, // left
829 | false, // topRight
830 | true, // bottomRight
831 | true, // bottomLeft
832 | false // topLeft
833 | ]);
834 | line.setAttribute("clip-path", "polygon(-3 15, 33 15, 33 30, -3 30)");
835 | result.appendChild(line);
836 | }
837 |
838 | // topLeft-topRight
839 | if (topLeft == "\\" && topRight == "/") {
840 | const path = document.createElementNS(
841 | "http://www.w3.org/2000/svg", "path");
842 | path.setAttribute("d", `
843 | M 3 12
844 | A 15 15, 0, 0, 0, 27 12
845 | L 22 9
846 | A 9 9, 0, 0, 1, 8 9
847 | Z`);
848 | result.appendChild(path);
849 | const line = cross([
850 | false, // top
851 | false, // right
852 | false, // bottom
853 | false, // left
854 | true, // topRight
855 | false, // bottomRight
856 | false, // bottomLeft
857 | true // topLeft
858 | ]);
859 | line.setAttribute("clip-path", "polygon(-3 -3, 33 -3, 33 12, -3 12)");
860 | result.appendChild(line);
861 | }
862 |
863 | // topRight-bottomRight
864 | if (topRight == "/" && bottomRight == "\\") {
865 | const path = document.createElementNS(
866 | "http://www.w3.org/2000/svg", "path");
867 | path.setAttribute("d", `
868 | M 22 9
869 | A 30 30, 0, 0, 0, 22 45
870 | L 28 45
871 | A 30 30, 0, 0, 1, 28 9
872 | Z`);
873 | result.appendChild(path);
874 | const line = cross([
875 | false, // top
876 | false, // right
877 | false, // bottom
878 | false, // left
879 | true, // topRight
880 | true, // bottomRight
881 | false, // bottomLeft
882 | false // topLeft
883 | ]);
884 | line.setAttribute("clip-path", "polygon(6 -3, 33 -3, 33 57, 6 57)");
885 | result.appendChild(line);
886 | }
887 |
888 | // topLeft-bottomLeft
889 | if (topLeft == "\\" && bottomLeft == "/") {
890 | const path = document.createElementNS(
891 | "http://www.w3.org/2000/svg", "path");
892 | path.setAttribute("d", `
893 | M 8 9
894 | A 30 30, 0, 0, 1, 8 45
895 | L 2 45
896 | A 30 30, 0, 0, 0, 2 9
897 | Z`);
898 | result.appendChild(path);
899 | const line = cross([
900 | false, // top
901 | false, // right
902 | false, // bottom
903 | false, // left
904 | false, // topRight
905 | false, // bottomRight
906 | true, // bottomLeft
907 | true // topLeft
908 | ]);
909 | line.setAttribute("clip-path", "polygon(-3 -3, 9 -3, 9 57, -3 57)");
910 | result.appendChild(line);
911 | }
912 |
913 | return result;
914 | }
915 |
916 | const alias = {
917 | "┌": "+",
918 | "┐": "+",
919 | "└": "+",
920 | "┘": "+",
921 | "─": "-",
922 | "►": ">",
923 | "'": ".",
924 | "`": ".",
925 | "V": "v",
926 | }
927 |
928 | for (const [key, value] of Object.entries(alias)) {
929 | glyphs[key] = (around) => {
930 | return glyphs[value](around);
931 | }
932 | }
933 |
934 |
935 | glyphs[">"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
936 | const result = document.createElementNS(
937 | "http://www.w3.org/2000/svg", "g");
938 | const arrow = document.createElementNS(
939 | "http://www.w3.org/2000/svg", "polygon");
940 | arrow.setAttribute("points", "0,0 42,18 0,36");
941 | let reach = 0;
942 | if (right == "*" || right == "o" || right == "#") {
943 | reach -= 18;
944 | }
945 | arrow.setAttribute("transform", `translate(${reach} 9)`);
946 | result.appendChild(arrow);
947 | return result;
948 | const center = document.createElementNS(
949 | "http://www.w3.org/2000/svg", "polygon");
950 | center.setAttribute("points", "-3,0 6,0 6,6 -3,6");
951 | center.setAttribute("transform", "translate(15 24)");
952 | result.appendChild(center);
953 | result.appendChild(cross([
954 | false, // top
955 | false, // right
956 | false, // bottom
957 | ["-", "+"].includes(left), // left
958 | false, // topRight
959 | false, // bottomRight
960 | ["/"].includes(bottomLeft), // bottomLeft
961 | ["\\"].includes(topLeft) // topLeft
962 | ]));
963 | return result;
964 | }
965 |
966 | glyphs["<"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
967 | const result = document.createElementNS(
968 | "http://www.w3.org/2000/svg", "g");
969 | const arrow = document.createElementNS(
970 | "http://www.w3.org/2000/svg", "polygon");
971 | arrow.setAttribute("points", "0,0 42,18 0,36");
972 | let reach = 30;
973 | if (left == "*" || left == "o" || left == "#") {
974 | reach += 18;
975 | }
976 | arrow.setAttribute("transform", `translate(${reach} 9) translate(0 36) rotate(180)`);
977 | result.appendChild(arrow);
978 | return result;
979 | //const center = document.createElementNS(
980 | // "http://www.w3.org/2000/svg", "polygon");
981 | //center.setAttribute("points", "0,0 9,0 9,6 0,6");
982 | //center.setAttribute("transform", "translate(9 24)");
983 | //result.appendChild(center);
984 | result.appendChild(cross([
985 | false, // top
986 | ["-", "+"].includes(right), // right
987 | false, // bottom
988 | false, // left
989 | ["/"].includes(topRight), // topRight
990 | ["\\"].includes(bottomRight), // bottomRight
991 | false, // bottomLeft
992 | false // topLeft
993 | ]));
994 | return result;
995 | }
996 |
997 | glyphs["v"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
998 | const result = document.createElementNS(
999 | "http://www.w3.org/2000/svg", "g");
1000 | const arrow = document.createElementNS(
1001 | "http://www.w3.org/2000/svg", "polygon");
1002 | arrow.setAttribute("points", "0,0 42,18 0,36");
1003 | let reach = 36;
1004 | if (bottom == " ") {
1005 | reach = 12;
1006 | } else if (bottom == "_") {
1007 | reach += 18;
1008 | } else if (bottom == "*" || bottom == "o" || bottom == "#") {
1009 | reach -= 18;
1010 | }
1011 | if (topRight == "/") {
1012 | arrow.setAttribute("transform", `translate(-36 33) rotate(${90 + 22.5}, 42, 18)`);
1013 | } else if (topLeft == "\\") {
1014 | arrow.setAttribute("transform", `translate(-18 33) rotate(${90 - 22.5}, 42, 18)`);
1015 | } else {
1016 | arrow.setAttribute("transform", `translate(33 ${reach}) rotate(90)`);
1017 | }
1018 | result.appendChild(arrow);
1019 | result.appendChild(cross([
1020 | ["|", "+"].includes(top), // top
1021 | false, // right
1022 | ["|", "+"].includes(top), // bottom
1023 | false, // left
1024 | ["/"].includes(topRight), // topRight
1025 | false, // bottomRight
1026 | false, // bottomLeft
1027 | ["\\"].includes(topLeft) // topLeft
1028 | ]));
1029 | return result;
1030 | }
1031 |
1032 | glyphs["^"] = ([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) => {
1033 | const result = document.createElementNS(
1034 | "http://www.w3.org/2000/svg", "g");
1035 | const arrow = document.createElementNS(
1036 | "http://www.w3.org/2000/svg", "polygon");
1037 | arrow.setAttribute("points", "0,0 42,18 0,36");
1038 | let reach = 42;
1039 | if (top == "-") {
1040 | reach -= 15;
1041 | }
1042 | if (bottomLeft == "/") {
1043 | arrow.setAttribute("transform", `translate(-18 -15) rotate(${- 45 - 22.5}, 42, 18)`);
1044 | } else if (bottomRight == "\\") {
1045 | arrow.setAttribute("transform", `translate(-36 -15) rotate(${- 90 - 22.5}, 42, 18)`);
1046 | } else {
1047 | arrow.setAttribute("transform", `translate(-3 ${reach}) rotate(-90)`);
1048 | }
1049 | result.appendChild(arrow);
1050 | result.appendChild(cross([
1051 | false, // top
1052 | false, // right
1053 | ["+", "|"].includes(bottom), // bottom
1054 | false, // left
1055 | false, // topRight
1056 | ["\\"].includes(bottomRight), // bottomRight
1057 | ["/"].includes(bottomLeft), // bottomLeft
1058 | false // topLeft
1059 | ]));
1060 | return result;
1061 | }
1062 |
1063 |
1064 | function cross([top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft]) {
1065 | const result = document.createElementNS(
1066 | "http://www.w3.org/2000/svg", "g");
1067 | if (top) {
1068 | // {
1069 | const line = document.createElementNS(
1070 | "http://www.w3.org/2000/svg", "line");
1071 | line.setAttribute("x1", 15);
1072 | line.setAttribute("y1", 0);
1073 | line.setAttribute("x2", 15);
1074 | line.setAttribute("y2", 27);
1075 | line.setAttribute("class", "part");
1076 | result.appendChild(line);
1077 | }
1078 |
1079 | if (right) {
1080 | //{
1081 | const line = document.createElementNS(
1082 | "http://www.w3.org/2000/svg", "line");
1083 | line.setAttribute("x1", 15);
1084 | line.setAttribute("y1", 27);
1085 | line.setAttribute("x2", 30);
1086 | line.setAttribute("y2", 27);
1087 | line.setAttribute("class", "part");
1088 | result.appendChild(line);
1089 | }
1090 |
1091 | if (bottom) {
1092 | //{
1093 | const line = document.createElementNS(
1094 | "http://www.w3.org/2000/svg", "line");
1095 | line.setAttribute("x1", 15);
1096 | line.setAttribute("y1", 27);
1097 | line.setAttribute("x2", 15);
1098 | line.setAttribute("y2", 54);
1099 | line.setAttribute("class", "part");
1100 | result.appendChild(line);
1101 | }
1102 |
1103 | if (left) {
1104 | //{
1105 | const line = document.createElementNS(
1106 | "http://www.w3.org/2000/svg", "line");
1107 | line.setAttribute("x1", 0);
1108 | line.setAttribute("y1", 27);
1109 | line.setAttribute("x2", 15);
1110 | line.setAttribute("y2", 27);
1111 | line.setAttribute("class", "part");
1112 | result.appendChild(line);
1113 | }
1114 |
1115 | const diagonal = document.createElementNS(
1116 | "http://www.w3.org/2000/svg", "polygon");
1117 |
1118 | diagonal.setAttribute("points", [
1119 | [0, 0],
1120 | [20.6, 0],
1121 | [20.6, 3],
1122 | [0, 3],
1123 | ].map(([x, y]) => `${x},${y}`).join(" "));
1124 |
1125 | if (topRight) {
1126 | //{
1127 | const line = document.createElementNS(
1128 | "http://www.w3.org/2000/svg", "line");
1129 | line.setAttribute("x1", 30);
1130 | line.setAttribute("y1", 0);
1131 | line.setAttribute("x2", 15);
1132 | line.setAttribute("y2", 27);
1133 | line.setAttribute("class", "part");
1134 | // line.setAttribute("transform", "scale(1, 1)");
1135 | // line.setAttribute("clip-path", "polygon(-6 -6, 15 -6, 15 30, -6 30)");
1136 | // line.setAttribute("stroke-linecap", "square !important");
1137 | result.appendChild(line);
1138 | //const mask = document.createElementNS(
1139 | // "http://www.w3.org/2000/svg", "polygon");
1140 | //mask.setAttribute("points", "0 0, 15 0, 15 18, 0 18");
1141 | //result.appendChild(mask);
1142 | //console.log("hi");
1143 | }
1144 |
1145 | if (bottomRight) {
1146 | //{
1147 | const line = document.createElementNS(
1148 | "http://www.w3.org/2000/svg", "line");
1149 | line.setAttribute("x1", 15);
1150 | line.setAttribute("y1", 27);
1151 | line.setAttribute("x2", 30);
1152 | line.setAttribute("y2", 54);
1153 | line.setAttribute("class", "part");
1154 | result.appendChild(line);
1155 | }
1156 |
1157 | if (bottomLeft) {
1158 | // {
1159 | const line = document.createElementNS(
1160 | "http://www.w3.org/2000/svg", "line");
1161 | line.setAttribute("x1", 15);
1162 | line.setAttribute("y1", 27);
1163 | line.setAttribute("x2", 0);
1164 | line.setAttribute("y2", 54);
1165 | line.setAttribute("class", "part");
1166 | result.appendChild(line);
1167 | }
1168 |
1169 | if (topLeft) {
1170 | //{
1171 | const line = document.createElementNS(
1172 | "http://www.w3.org/2000/svg", "line");
1173 | line.setAttribute("x1", 0);
1174 | line.setAttribute("y1", 0);
1175 | line.setAttribute("x2", 15);
1176 | line.setAttribute("y2", 27);
1177 | line.setAttribute("class", "part");
1178 | result.appendChild(line);
1179 | }
1180 |
1181 | return result;
1182 | }
1183 |
1184 | function text(char, reserved) {
1185 | const g = document.createElementNS(
1186 | "http://www.w3.org/2000/svg", "g");
1187 | const result = document.createElementNS(
1188 | "http://www.w3.org/2000/svg", "text");
1189 | //result.setAttribute("xml:space", "preserve");
1190 | //result.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:space", "preserve");
1191 | const value = document.createTextNode(char);
1192 | result.appendChild(value);
1193 | if (reserved) {
1194 | result.setAttribute("class", "reserved");
1195 | }
1196 | const translation = [
1197 | [15, 24],
1198 | //[1.5, 1.5 * ratio]
1199 | ];
1200 | result.setAttribute("transform", translation.map(([x, y]) => `translate(${x}, ${y})`).join(" "));
1201 | g.appendChild(result);
1202 | return g;
1203 | }
1204 |
1205 | function render(diagram) {
1206 | const result = document.createElementNS(
1207 | "http://www.w3.org/2000/svg", "g");
1208 |
1209 | for (let y = 0; y < diagram.length; y++) {
1210 | for (let x = 0; x < diagram[y].length; x++) {
1211 | const char = diagram[y][x];
1212 |
1213 | if (char == ' ' || char == '"') {
1214 | continue;
1215 | }
1216 |
1217 | let reserved = glyphs[char];
1218 |
1219 | const g = document.createElementNS(
1220 | "http://www.w3.org/2000/svg", "g");
1221 |
1222 | let str = false;
1223 | for (let i = 0; i < x; i++) {
1224 | if (diagram[y][i] == '"') {
1225 | str = !str;
1226 | }
1227 | }
1228 |
1229 | const neighbors = around(diagram, [x, y]);
1230 |
1231 | if (char.match(/[A-Za-z0-9]/)) {
1232 | const [, right, , left] = neighbors;
1233 | // We special case "v", which is a down arrow, and also a text character.
1234 | str = str || (left.match(/[A-Za-uw-z0-9]/) || right.match(/[A-Za-uw-z0-9]/));
1235 | }
1236 |
1237 | reserved = reserved && !str;
1238 |
1239 | if (reserved) {
1240 | g.appendChild(glyphs[char](neighbors));
1241 | }
1242 |
1243 | g.appendChild(text(char, reserved));
1244 |
1245 | g.setAttribute("transform", `translate(${x*30} ${y*54})`);
1246 | result.appendChild(g);
1247 | }
1248 | }
1249 | return result;
1250 | }
1251 |
1252 | function create(script) {
1253 | const source = script.innerText;
1254 | const zoom = Number(script.getAttribute("zoom") || 0.3);
1255 | const debug = script.hasAttribute("grid");
1256 |
1257 | const diagram = source
1258 | .split("\n")
1259 | .map((line) => line.trimEnd().split(""));
1260 |
1261 | diagram.shift();
1262 | diagram.splice(-1);
1263 |
1264 | let width = 0;
1265 | const height = diagram.length;
1266 |
1267 | for (let y = 0; y < diagram.length; y++) {
1268 | for (let x = 0; x < diagram[y].length; x++) {
1269 | if (diagram[y].length > width) {
1270 | width = diagram[x].length;
1271 | }
1272 | }
1273 | }
1274 |
1275 | var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1276 | svg.setAttribute("width", width * 30 * zoom);
1277 | svg.setAttribute("height", height * 54 * zoom);
1278 | svg.setAttribute("debug", debug);
1279 | const padding = 0;
1280 |
1281 | svg.setAttribute("viewBox", `${-padding} ${-padding} ${width * 30 + 2 * padding} ${height * 54 + 2 * padding}`);
1282 | svg.setAttribute("class", "debug");
1283 |
1284 | var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
1285 | style.innerHTML = `
1286 | .diagram {
1287 | display: block;
1288 | }
1289 |
1290 | .diagram line, .diagram circle, .diagram rect {
1291 | stroke: black;
1292 | }
1293 |
1294 | .diagram line {
1295 | stroke-width: 2;
1296 | }
1297 |
1298 | .diagram circle {
1299 | r: 3.5;
1300 | }
1301 |
1302 | .diagram rect {
1303 | width: 6px;
1304 | height: 6px;
1305 | }
1306 |
1307 | .diagram text, .glyph, .debug text {
1308 | /** font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; **/
1309 | font-family: Iosevka Fixed, monospace;
1310 | font-size: 3em;
1311 | text-anchor: middle;
1312 | alignment-baseline: central;
1313 | white-space: pre;
1314 | }
1315 |
1316 | .reserved {
1317 | fill: transparent;
1318 | white-space: pre;
1319 | }
1320 |
1321 | .debug[debug="true"] .reserved {
1322 | fill: black;
1323 | opacity: 0.5;
1324 | }
1325 |
1326 | .debug[debug="true"] line.grid {
1327 | stroke: black;
1328 | stroke-width: 0.2;
1329 | stroke-linecap: butt;
1330 | fill: black;
1331 | opacity: 1%;
1332 | }
1333 |
1334 | polygon {
1335 | stroke-width: 0;
1336 | }
1337 |
1338 | .debug[debug="true"] polygon.inner {
1339 | fill: black;
1340 | stroke: black;
1341 | opacity: 5%;
1342 | }
1343 |
1344 | polygon {
1345 | stroke: black;
1346 | /** stroke-width: 0.2; **/
1347 | stroke-linecap: butt;
1348 | fill: black;
1349 | }
1350 |
1351 | .debug[debug="true"] polygon,
1352 | .debug[debug="true"] line.grid
1353 | {
1354 | opacity: 10%;
1355 | }
1356 |
1357 | .debug[debug="true"] polygon,
1358 | .debug[debug="true"] path,
1359 | .debug[debug="true"] circle
1360 | {
1361 | opacity: 50%;
1362 | }
1363 |
1364 | .debug[debug="true"] polygon {
1365 | fill: red;
1366 | stroke: red;
1367 | }
1368 |
1369 | /**
1370 | circle {
1371 | fill: black;
1372 | }
1373 | **/
1374 |
1375 | .debug[debug="true"] circle,
1376 | .debug[debug="true"] path
1377 | {
1378 | opacity: 50%;
1379 | fill: red;
1380 | }
1381 |
1382 | .debug[debug="true"] circle {
1383 | stroke: red;
1384 | }
1385 |
1386 | .debug[debug="true"] .inner {
1387 | stroke-width: 0.2;
1388 | }
1389 |
1390 | line.part {
1391 | stroke-width: 6;
1392 | stroke-linecap: butt;
1393 | stroke: black;
1394 | }
1395 |
1396 | .debug[debug="true"] line.part {
1397 | opacity: 50%;
1398 | stroke: red;
1399 | }
1400 |
1401 | .debug[debug="true"] line.center {
1402 | stroke-width: 3;
1403 | stroke-linecap: butt;
1404 | opacity: 10%;
1405 | stroke: black;
1406 | }
1407 |
1408 | text::selection {
1409 | fill: black;
1410 | background-color: #EEE;
1411 | }
1412 | `;
1413 |
1414 | svg.appendChild(style);
1415 |
1416 | svg.appendChild(render(diagram));
1417 |
1418 | if (debug) {
1419 | svg.appendChild(grid(width, height));
1420 | }
1421 |
1422 | return svg;
1423 | }
1424 |
1425 | function around(diagram, [x, y]) {
1426 | let left = " ";
1427 | let top = " ";
1428 | let right = " ";
1429 | let bottom = " ";
1430 | let topRight = " ";
1431 | let bottomRight = " ";
1432 | let bottomLeft = " ";
1433 | let topLeft = " ";
1434 | if (y > 0) {
1435 | top = diagram[y - 1][x] || " ";
1436 | }
1437 | if (x < (diagram[y].length - 1)) {
1438 | right = diagram[y][x + 1] || " ";
1439 | }
1440 | if (y < (diagram.length - 1)) {
1441 | bottom = diagram[y + 1][x] || " ";
1442 | }
1443 | if (x > 0) {
1444 | left = diagram[y][x - 1] || " ";
1445 | }
1446 | if (y > 0 && x < (diagram[y - 1].length - 1)) {
1447 | // console.log(`@${diagram[y][x]}: ${diagram[y - 1][x + 1]}`);
1448 | topRight = diagram[y - 1][x + 1] || " ";
1449 | }
1450 | //if (diagram[y][x] == ".") {
1451 | //console.log(`${diagram[y][x]}}: ${(y + 1) < (diagram.length)}`);
1452 | //console.log(diagram[y + 1]);
1453 | //throw new Error("hi");
1454 | //}
1455 | if ((y + 1) < diagram.length && (x < diagram[y + 1].length)) {
1456 | bottomRight = diagram[y + 1][x + 1] || " ";
1457 | //console.log(diagram[y + 1]);
1458 | //console.log(`${diagram[y][x]}: ${x} ${y} ${bottomRight}`);
1459 | //throw new Error("hi");
1460 | }
1461 | if (y < (diagram.length - 1) && x > 0) {
1462 | bottomLeft = diagram[y + 1][x - 1] || " ";
1463 | }
1464 | if (y > 0 && x > 0) {
1465 | topLeft = diagram[y - 1][x - 1] || " ";
1466 | }
1467 | return [top, right, bottom, left, topRight, bottomRight, bottomLeft, topLeft];
1468 | //.map((el) => alias[el] ? alias[el] : el);
1469 | }
1470 |
1471 | document.addEventListener("DOMContentLoaded", function() {
1472 | // replace all of the