Skip to content

Commit 950a876

Browse files
author
dmullis
committed
Command-line specification of two alternate colors to be drawn
More general dark-mode support. Continue to rely on the CSS query: @media (prefers-color-scheme: dark) Specific alternative colors to be drawn for "dark" or non-"dark" query results are exposed on the command line. An alternate, more Github-specific approach might use: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to Simplify drawing semantics by applying only that one color: existing code depending on CSS color inversion function "invert()" is replaced with transparency to background. Add one-line script to generate a README.md specific to local debug environment, or to the Github 'Code' page of a forker of blampe/goat.
1 parent e8e6af9 commit 950a876

File tree

4 files changed

+272
-15
lines changed

4 files changed

+272
-15
lines changed

README.md.tmpl

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# GoAT: Go ASCII Tool
2+
<!--
3+
NOTE to maintainers
4+
---
5+
SVG examples/ regeneration.
6+
go test -run . -v -write
7+
8+
Github home page README.md, specific to $USER:
9+
sed "s,{{\.Root}},https://cdn.rawgit.com/${USER}/goat/main," README.md.tmpl >README.md
10+
11+
Local preview of home page:
12+
sed "s,{{.Root}},.," README.md.tmpl >README.md
13+
# See https://github.github.com/gfm/#introduction
14+
(echo '<!DOCTYPE html>'; marked -gfm README.md) >README.html
15+
16+
The @media query from SVG may be verified in Firefox by switching between Themes
17+
"Light" and "Dark" in Firefox's "Add-ons Manager".
18+
-->
19+
20+
This is a Go implementation of [markdeep.mini.js]'s ASCII diagram
21+
generation.
22+
23+
## Update (2022-02-07)
24+
25+
I hacked together GoAT a number of years ago while trying to embed some
26+
diagrams in a Hugo project I was playing with. Through an odd twist of fate
27+
GoAT eventually made its way into the upstream Hugo project, and if you're
28+
using [v0.93.0] you can embed these diagrams natively. Neat!
29+
30+
My original implementation was certainly buggy and not on par with markdeep.
31+
I'm grateful for the folks who've helped smooth out the rough edges, and I've
32+
updated this project to reflect the good changes made in the Hugo fork,
33+
including a long-overdue `go.mod`.
34+
35+
There's a lot I would like to do with this project that I will never get to, so
36+
instead I recommend you look at these forks:
37+
38+
* [@bep] is the fork currently used by Hugo, which I expect to be more active
39+
over time.
40+
* [@dmacvicar] has improved SVG/PNG/PDF rendering.
41+
* [@sw46] has implemented a really wonderful hand-drawn style worth checking
42+
out.
43+
44+
## Usage
45+
46+
```bash
47+
$ go get github.com/blampe/goat
48+
$ cat my-cool-diagram.txt | goat > my-cool-diagram.svg
49+
```
50+
51+
By default, the program reads from stdin, unless `-i infile` is given.
52+
53+
By default, the program writes to stdout, unless `-o outfile` is given or a
54+
binary format with `-f` is selected.
55+
56+
By default, it writes in [SVG] format, unless another format is specified with
57+
`-f`.
58+
59+
## TODO
60+
61+
- Dashed lines signaled by `:` or `=`.
62+
- Bold lines signaled by ???.
63+
64+
## Examples
65+
66+
Here are some SVGs and the UTF-8 input they were generated from:
67+
68+
### Trees
69+
70+
![Trees Example]({{.Root}}/examples/trees.svg)
71+
72+
```
73+
. . . .--- 1 .-- 1 / 1
74+
/ \ | | .---+ .-+ +
75+
/ \ .---+---. .--+--. | '--- 2 | '-- 2 / \ 2
76+
+ + | | | | ---+ ---+ +
77+
/ \ / \ .-+-. .-+-. .+. .+. | .--- 3 | .-- 3 \ / 3
78+
/ \ / \ | | | | | | | | '---+ '-+ +
79+
1 2 3 4 1 2 3 4 1 2 3 4 '--- 4 '-- 4 \ 4
80+
```
81+
82+
### Overlaps
83+
84+
![Overlaps Example]({{.Root}}/examples/overlaps.svg)
85+
86+
```
87+
.-. .-. .-. .-. .-. .-.
88+
| | | | | | | | | | | |
89+
.---------. .--+---+--. .--+---+--. .--| |--. .--+ +--. .------|--.
90+
| | | | | | | | | | | | | | | | | |
91+
'---------' '--+---+--' '--+---+--' '--| |--' '--+ +--' '--|------'
92+
| | | | | | | | | | | |
93+
'-' '-' '-' '-' '-' '-'
94+
```
95+
96+
### Line Decorations
97+
98+
![Line Decorations Example]({{.Root}}/examples/line-decorations.svg)
99+
100+
```
101+
________ o * * .--------------.
102+
*---+--. | | o o | ^ \ / | .----------. |
103+
| | '--* -+- | | v / \ / | | <------. | |
104+
| '-----> .---(---' --->*<--- / .+->*<--o----' | | | | |
105+
<--' ^ ^ | | | | | ^ \ | '--------' | |
106+
\/ *-----' o |<----->| '-----' |__| v '------------' |
107+
/\ *---------------'
108+
```
109+
110+
### Line Ends
111+
112+
![Line Ends Example]({{.Root}}/examples/line-ends.svg)
113+
114+
```
115+
o--o *--o / / * o o o o o * * * * o o o o * * * * o o o o * * * *
116+
o--* *--* v v ^ ^ | | | | | | | | \ \ \ \ \ \ \ \ / / / / / / / /
117+
o--> *--> * o / / o * v ' o * v ' o * v \ o * v \ o * v / o * v /
118+
o--- *---
119+
^ ^ ^ ^ . . . . ^ ^ ^ ^ \ \ \ \ ^ ^ ^ ^ / / / /
120+
| | * o \ \ * o | | | | | | | | \ \ \ \ \ \ \ \ / / / / / / / /
121+
v v ^ ^ v v ^ ^ o * v ' o * v ' o * v \ o * v \ o * v / o * v /
122+
* o | | * o \ \
123+
124+
<--o <--* <--> <--- ---o ---* ---> ---- *<-- o<-- -->o -->*
125+
```
126+
127+
### Dot Grids
128+
129+
![Dot Grids Example]({{.Root}}/examples/dot-grids.svg)
130+
131+
```
132+
o o o o o * * * * * * * o o * o o o * * * o o o · * · · · · · ·
133+
o o o o o * * * * * o o o o * o o o o * * * * * o * * · * * · · · · · ·
134+
o o o o o * * * * * o * o o o o o o o o * * * * * o o o o o · o · · o · · * * ·
135+
o o o o o * * * * * o * o o o o o o o * * * * o * o o · · · · o · · * ·
136+
o o o o o * * * * * * * * * o o o o * * * o * o · · · · · · · *
137+
```
138+
Note that '·' above is not ASCII, but rather Unicode, the MIDDLE DOT character, encoded with UTF-8.
139+
140+
### Large Nodes
141+
142+
![Large Node Example]({{.Root}}/examples/large-nodes.svg)
143+
144+
```
145+
.---. .-. .-. .-. .-.
146+
| A +----->| 1 +<---->| 2 |<----+ 4 +------------------. | 8 |
147+
'---' '-' '+' '-' | '-'
148+
| ^ | ^
149+
v | v |
150+
.-. .-+-. .-. .-+-. .-. .+. .---.
151+
| 3 +---->| B |<----->| 5 +---->| C +---->| 6 +---->| 7 |<---->| D |
152+
'-' '---' '-' '---' '-' '-' '---'
153+
```
154+
155+
### Small Grids
156+
157+
![Small Grids Example]({{.Root}}/examples/small-grids.svg)
158+
159+
```
160+
___ ___ .---+---+---+---+---. .---+---+---+---. .---. .---.
161+
___/ \___/ \ | | | | | | / \ / \ / \ / \ / | +---+ |
162+
/ \___/ \___/ +---+---+---+---+---+ +---+---+---+---+ +---+ +---+
163+
\___/ b \___/ \ | | | b | | | \ / \a/ \b/ \ / \ | +---+ |
164+
/ a \___/ \___/ +---+---+---+---+---+ +---+---+---+---+ +---+ b +---+
165+
\___/ \___/ \ | | a | | | | / \ / \ / \ / \ / | a +---+ |
166+
\___/ \___/ '---+---+---+---+---' '---+---+---+---' '---' '---'
167+
```
168+
169+
### Big Grids
170+
171+
![Big Grids Example]({{.Root}}/examples/big-grids.svg)
172+
173+
```
174+
.----. .----.
175+
/ \ / \ .-----+-----+-----.
176+
+ +----+ +----. | | | | .-----+-----+-----+-----+
177+
\ / \ / \ | | | | / / / / /
178+
+----+ B +----+ + +-----+-----+-----+ +-----+-----+-----+-----+
179+
/ \ / \ / | | | | / / / / /
180+
+ A +----+ +----+ | | B | | +-----+-----+-----+-----+
181+
\ / \ / \ +-----+-----+-----+ / / A / B / /
182+
'----+ +----+ + | | | | +-----+-----+-----+-----+
183+
\ / \ / | A | | | / / / / /
184+
'----' '----' '-----+-----+-----' '-----+-----+-----+-----+
185+
```
186+
187+
### Complicated
188+
189+
![Complicated Example]({{.Root}}/examples/complicated.svg)
190+
191+
```
192+
+-------------------+ ^ .---.
193+
| A Box |__.--.__ __.--> | .-. | |
194+
| | '--' v | * |<--- | |
195+
+-------------------+ '-' | |
196+
Round *---(-. |
197+
.-----------------. .-------. .----------. .-------. | | |
198+
| Mixed Rounded | | | / Diagonals \ | | | | | |
199+
| & Square Corners | '--. .--' / \ |---+---| '-)-' .--------.
200+
'--+------------+-' .--. | '-------+--------' | | | | / Search /
201+
| | | | '---. | '-------' | '-+------'
202+
|<---------->| | | | v Interior | ^
203+
' <---' '----' .-----------. ---. .--- v |
204+
.------------------. Diag line | .-------. +---. \ / . |
205+
| if (a > b) +---. .--->| | | | | Curved line \ / / \ |
206+
| obj->fcn() | \ / | '-------' |<--' + / \ |
207+
'------------------' '--' '--+--------' .--. .--. | .-. +Done?+-'
208+
.---+-----. | ^ |\ | | /| .--+ | | \ /
209+
| | | Join \|/ | | Curved | \| |/ | | \ | \ /
210+
| | +----> o --o-- '-' Vertical '--' '--' '-- '--' + .---.
211+
<--+---+-----' | /|\ | | 3 |
212+
v not:line 'quotes' .-' '---'
213+
.-. .---+--------. / A || B *bold* | ^
214+
| | | Not a dot | <---+---<-- A dash--is not a line v |
215+
'-' '---------+--' / Nor/is this. ---
216+
```
217+
218+
More examples are available [here](examples).
219+
220+
[@bep]: https://github.com/bep/goat/
221+
[@dmacvicar]: https://github.com/dmacvicar/goat
222+
[@sw46]: https://github.com/sw46/goat/
223+
[SVG]: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
224+
[markdeep.mini.js]: http://casual-effects.com/markdeep/
225+
[v0.93.0]: https://github.com/gohugoio/hugo/releases/tag/v0.93.0

cmd/goat/main.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,25 @@ func main() {
1717
var inputFilename string
1818
var outputFilename string
1919
var format string
20+
var svgColorLightScheme string
21+
var svgColorDarkScheme string
2022

2123
flag.StringVar(&inputFilename, "i", "", "Input filename (default stdin)")
2224
flag.StringVar(&outputFilename, "o", "", "Output filename (default stdout for SVG)")
2325
flag.StringVar(&format, "f", "svg", "Output format: svg (default: svg)")
26+
flag.StringVar(&svgColorLightScheme, "sls", "#000000", `short for -svg-color-light-scheme`)
27+
flag.StringVar(&svgColorLightScheme, "svg-color-light-scheme", "#000000",
28+
`See help for -svg-color-dark-scheme`)
29+
flag.StringVar(&svgColorDarkScheme, "sds", "#FFFFFF", `short for -svg-color-dark-scheme`)
30+
flag.StringVar(&svgColorDarkScheme, "svg-color-dark-scheme", "#FFFFFF",
31+
`Goat's SVG output attempts to learn something about the background being
32+
drawn on top of by means of a CSS @media query, which returns a string.
33+
If the string is "dark", Goat draws with the color specified by
34+
this option; otherwise, Goat draws with the color specified by option
35+
-svg-color-light-scheme.
36+
37+
See https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
38+
`)
2439
flag.Parse()
2540

2641
format = strings.ToLower(format)
@@ -66,6 +81,7 @@ func main() {
6681

6782
switch format {
6883
case "svg":
69-
goat.BuildAndWriteSVG(input, output)
84+
goat.BuildAndWriteSVG(input, output,
85+
svgColorLightScheme, svgColorDarkScheme)
7086
}
7187
}

examples_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func TestExamplesStableOutput(t *testing.T) {
2525
t.Fatal(err)
2626
}
2727
var out bytes.Buffer
28-
BuildAndWriteSVG(in, &out)
28+
BuildAndWriteSVG(in, &out, "black", "white")
2929
in.Close()
3030
if i > 0 && previous != out.String() {
3131
c.Fail()
@@ -63,7 +63,10 @@ func TestExamples(t *testing.T) {
6363
}
6464
}
6565

66-
BuildAndWriteSVG(in, out)
66+
svgColorLightScheme := "#323232"
67+
svgColorDarkScheme := "#C8C8C8"
68+
BuildAndWriteSVG(in, out, svgColorLightScheme, svgColorDarkScheme)
69+
6770
in.Close()
6871
out.Close()
6972

@@ -84,7 +87,7 @@ func BenchmarkComplicated(b *testing.B) {
8487
in := getIn(filepath.FromSlash("examples/complicated.txt"))
8588
b.ResetTimer()
8689
for i := 0; i < b.N; i++ {
87-
BuildAndWriteSVG(in, io.Discard)
90+
BuildAndWriteSVG(in, io.Discard, "black", "white")
8891
}
8992
}
9093

svg.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,26 @@ type SVG struct {
1212
Height int
1313
}
1414

15-
func (s SVG) String() string {
16-
style := `<style type="text/css">
17-
svg { color: #323232; }
18-
@media (prefers-color-scheme: dark) {
19-
svg { color: #C8C8C8; }
20-
</style>`
21-
return fmt.Sprintf("<svg class='%s' xmlns='%s' version='%s' height='%d' width='%d' font-family='Menlo,Lucida Console,monospace'>\n%s\n%s</svg>\n",
22-
"diagram",
15+
func (s SVG) String(svgColorLightScheme string, svgColorDarkScheme string) string {
16+
style := fmt.Sprintf(
17+
`<style type="text/css">
18+
svg {
19+
color: %s;
20+
}
21+
@media (prefers-color-scheme: dark) {
22+
svg {
23+
color: %s;
24+
}
25+
}
26+
</style>`,
27+
svgColorLightScheme,
28+
svgColorDarkScheme)
29+
30+
return fmt.Sprintf(
31+
"<svg class='%s' xmlns='%s' version='%s' height='%d' width='%d' font-family='Menlo,Lucida Console,monospace'>\n" +
32+
"%s\n" +
33+
"%s</svg>\n",
34+
"diagram", // XX can this have any effect?
2335
"http://www.w3.org/2000/svg",
2436
"1.1", s.Height, s.Width, style, s.Body)
2537
}
@@ -38,9 +50,10 @@ func BuildSVG(src io.Reader) SVG {
3850

3951
// BuildAndWriteSVG reads in a newline-delimited ASCII diagram from src and writes a
4052
// corresponding SVG diagram to dst.
41-
func BuildAndWriteSVG(src io.Reader, dst io.Writer) {
53+
func BuildAndWriteSVG(src io.Reader, dst io.Writer,
54+
svgColorLightScheme string, svgColorDarkScheme string) {
4255
svg := BuildSVG(src)
43-
writeBytes(dst, svg.String())
56+
writeBytes(dst, svg.String(svgColorLightScheme, svgColorDarkScheme))
4457
}
4558

4659
func writeBytes(out io.Writer, format string, args ...interface{}) {
@@ -247,7 +260,7 @@ func (t Triangle) Draw(out io.Writer) {
247260

248261
// Draw a solid circle as an SVG circle element.
249262
func (c *Circle) Draw(out io.Writer) {
250-
fill := "invert(currentColor)"
263+
fill := "none"
251264

252265
if c.bold {
253266
fill = "currentColor"

0 commit comments

Comments
 (0)