├── LICENSE
├── README.md
├── cmd
├── bar3d
│ ├── bar3d.go
│ ├── data.d
│ ├── f.pdf
│ ├── go.mod
│ └── go.sum
├── between
│ ├── between.go
│ └── go.mod
├── bezpoints
│ ├── README.md
│ ├── bezpoints.go
│ ├── f.tex
│ └── go.mod
├── c19chart
│ ├── README.md
│ ├── c19chart.go
│ ├── f.deck-00001.png
│ ├── go.mod
│ └── go.sum
├── cc
│ ├── README.md
│ ├── cc.png
│ ├── d1.png
│ ├── d2.png
│ ├── f.pdf
│ ├── go.mod
│ └── main.go
├── cl
│ └── main.go
├── csv2poly
│ ├── csv2poly
│ ├── csv2poly.go
│ └── go.mod
├── csvread
│ ├── csvread.go
│ └── go.mod
├── ctime
│ ├── ctime.go
│ └── go.mod
├── deckle
│ ├── deckle.go
│ ├── go.mod
│ ├── go.sum
│ └── test.sh
├── desordres
│ ├── 01.png
│ ├── 04-color.png
│ ├── 05.png
│ ├── 14-hot.png
│ ├── README.md
│ ├── ajs.pal
│ ├── black-green-thin-14.png
│ ├── blue-10.png
│ ├── blue-thin-14.png
│ ├── blue-thin.png
│ ├── blue-tick.png
│ ├── def.png
│ ├── default.pal
│ ├── desordres.go
│ ├── f-00001.png
│ ├── go.mod
│ ├── gray.png
│ ├── gray14.png
│ ├── hot-14-20-60.png
│ ├── mist-gb-00001.png
│ ├── rainbow.png
│ ├── rb-00001.png
│ ├── readpalette.go
│ └── red-14.png
├── dicechart
│ ├── README.md
│ ├── big.csv
│ ├── d.csv
│ ├── d.pdf
│ ├── d.xml
│ ├── data.csv
│ ├── dicechart
│ ├── dicechart.png
│ ├── f
│ ├── f.pdf
│ ├── go.mod
│ └── main.go
├── dict
│ ├── dict.go
│ ├── go.mod
│ └── go.sum
├── distable
│ ├── README.md
│ ├── af-km.d
│ ├── africa.pdf
│ ├── data
│ ├── distable.go
│ ├── f.d
│ ├── go.mod
│ ├── go.sum
│ ├── mkall
│ ├── mkt
│ ├── morris.d
│ ├── morris.pdf
│ ├── morris.png
│ ├── original-chart.png
│ ├── us-cities-km.d
│ ├── us-cities-miles.d
│ ├── us-km.pdf
│ └── us-miles.pdf
├── dotspiral
│ ├── README.md
│ ├── dotspiral.png
│ ├── go.mod
│ └── main.go
├── dpi
│ ├── dpi.go
│ └── go.mod
├── fanchart
│ ├── 1900-occupations.csv
│ ├── README.md
│ ├── binaries
│ │ ├── fanchart-arm-mac
│ │ ├── fanchart-intel-mac
│ │ ├── fanchart-linux
│ │ ├── fanchart-win32.exe
│ │ └── fanchart-win64.exe
│ ├── charts.png
│ ├── f.pdf
│ ├── go.mod
│ ├── lr.pdf
│ ├── lrchart.png
│ ├── main.go
│ ├── mkbinaries
│ ├── mkdeck
│ ├── occupations.csv
│ ├── tb.pdf
│ └── tbchart.png
├── feed
│ ├── feed.go
│ ├── go.mod
│ └── go.sum
├── fox
│ ├── 2-bit-demichrome-00001.png
│ ├── 2-bit-grayscale-00001.png
│ ├── README.md
│ ├── ajs.pal
│ ├── ajstarks-00001.png
│ ├── arq4-00001.png
│ ├── autumn-decay-00001.png
│ ├── ayy4-00001.png
│ ├── blk-aqu4-00001.png
│ ├── blu-scribbles-00001.png
│ ├── dark-mode-00001.png
│ ├── default.pal
│ ├── dense-00001.png
│ ├── f-00001.png
│ ├── fox.png
│ ├── funk-it-up-00001.png
│ ├── go.mod
│ ├── hollow-00001.png
│ ├── hot.png
│ ├── ice-cream-gb-00001.png
│ ├── kankei4-00001.png
│ ├── kirokaze-gameboy-00001.png
│ ├── links-awakening-sgb-00001.png
│ ├── main.go
│ ├── mist-gb-00001.png
│ ├── moonlight-gb-00001.png
│ ├── nintendo-gameboy-bgb-00001.png
│ ├── nintendo-super-gameboy-00001.png
│ ├── north-00001.png
│ ├── nostalgia-00001.png
│ ├── pen-n-paper-00001.png
│ ├── pokemon-sgb-00001.png
│ ├── polished-gold-00001.png
│ ├── rb-00001.png
│ ├── readpalette.go
│ ├── red-brick-00001.png
│ ├── rustic-gb-00001.png
│ └── spacehaze-00001.png
├── fstat
│ ├── fstat.go
│ └── go.mod
├── gitdate
│ ├── README.md
│ ├── gitdate.go
│ ├── gitdate.png
│ └── go.mod
├── gurl
│ ├── README.md
│ ├── go.mod
│ └── gurl.go
├── hsv2rgb
│ └── hsv2rgb.go
├── imgcat
│ ├── README.md
│ ├── go.mod
│ └── imgcat.go
├── imgps
│ ├── go.mod
│ ├── go.sum
│ └── main.go
├── ims
│ ├── go.mod
│ └── ims.go
├── jsonfeed
│ ├── go.mod
│ └── jsonfeed.go
├── latlongdeck
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ └── main.go
├── mapcoord
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ └── main.go
├── mdtopdf
│ ├── fpdf.png
│ ├── go.mod
│ ├── go.sum
│ ├── hiking.png
│ ├── main.go
│ ├── test.md
│ └── test.pdf
├── mfunc
│ ├── go.mod
│ └── mfunc.go
├── mkpoly
│ ├── go.mod
│ └── mkpoly.go
├── nythead
│ ├── README.md
│ ├── go.mod
│ └── nythead.go
├── polar
│ ├── go.mod
│ └── polar.go
├── popio
│ └── main.go
├── randgen
│ ├── go.mod
│ └── randgen.go
├── rmcsv
│ ├── README.md
│ ├── go.mod
│ ├── main.go
│ └── ss.png
├── roadmap
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ ├── p2p.png
│ ├── p2p.svg
│ ├── p2p.xml
│ └── roadmap.go
├── setdeckfont
│ ├── go.mod
│ ├── go.sum
│ └── setdeckfont.go
├── slopechart
│ ├── f.pdf
│ ├── go.mod
│ ├── go.sum
│ ├── slope.d
│ └── slopechart.go
├── spl
│ ├── go.mod
│ └── spl.go
├── svgcolor
│ ├── go.mod
│ └── svgcolor.go
├── swiss
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ ├── p1.png
│ ├── p2.png
│ ├── p3.png
│ └── swiss.go
├── thomas
│ ├── go.mod
│ └── thomas.go
├── utab
│ ├── go.mod
│ ├── go.sum
│ └── utab.go
├── vmap
│ ├── go.mod
│ └── vmap.go
└── ws
│ ├── go.mod
│ └── ws.go
└── readpalette
├── go.mod
└── readpalette.go
/README.md:
--------------------------------------------------------------------------------
1 | # Useful commands
2 |
3 | * between - print the time (hours, minutes, seconds, milliseconds) between two dates
4 | * bezpoints - compute the points along a bezier curve
5 | * c19chart - covid-19 charts
6 | * cc - concentric circles
7 | * csvread - parse and display CSV files
8 | * csv2poly - makes x,y pairs in CSV files into polygons
9 | * ctime - show the execution time of a command
10 | * desordres - visuals inspired by Vera Moldar's Des Ordres
11 | * deckle - make a deckled edge using deck markup
12 | * dicechart - make Work-style dice charts
13 | * dict - lookup words via dictionary servers
14 | * distable - make a distance table
15 | * dpi - compute dots per inch and aspect ratio
16 | * dotspiral - make a dot spiral
17 | * fanchart - make Dubois-style fan charts
18 | * feed - read Friday Feed markup, generate to deck, html, RTF, plain text, JSON
19 | * fox - visuals in the form of "Fox I" by Anni Albers
20 | * fstat - show file status
21 | * gitdate - visualize git commits over time
22 | * gurl - get URL
23 | * hsv2rgb - convert hsv to rgb colors
24 | * imgcat - make a image catalog with deck markup
25 | * imgps - show an image's GPS coordinates
26 | * ims - show image size (width and height)
27 | * jsonfeed - parse and list JSON feeds
28 | * mdtopdf - markdown to PDF
29 | * mfunc - math functions
30 | * mkpoly - generate decksh polygons from x,y pairs
31 | * nythead - show New York Times headlines (API key required)
32 | * polar - return Cartesion coordinates from polar coordinate parameters
33 | * randgen - generate random numbers
34 | * rmcsv - convert roadmap CSV files to XML
35 | * roadmap - make planning roadmaps from an XML description
36 | * setdeckfont - make default path for deckfonts
37 | * slopechart - make slope charts
38 | * svgcolor - lookup SVG named colors
39 | * swiss - make a clock based on the iconic Swiss Railway clock
40 | * utab - make PDF unicode tables using TrueType fonts
41 | * vmap - map data ranges
42 | * ws - web server
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/cmd/bar3d/bar3d.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "io"
6 | "os"
7 | "strconv"
8 | "strings"
9 |
10 | "github.com/ajstarks/deck/generate"
11 | )
12 |
13 | // bar3d makes a 3D bar
14 | func bar3d(deck *generate.Deck, x, y, w, h float64, tcolor, lcolor string) {
15 | wh := w / 2
16 | th := w * 0.5
17 | th2 := th / 2
18 | yh := y + h
19 | topx := []float64{x, x - wh, x, x + wh}
20 | topy := []float64{yh - th, yh - th2, yh, yh - th2}
21 | leftx := []float64{x, x - wh, x - wh, x}
22 | liney := []float64{y, y + th2, yh - th2, yh - th}
23 | rightx := []float64{x, x + wh, x + wh, x}
24 |
25 | deck.Polygon(topx, topy, tcolor)
26 | deck.Polygon(leftx, liney, lcolor)
27 | deck.Polygon(rightx, liney, lcolor, 60)
28 | }
29 |
30 | //vmap maps one interval to another
31 | func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
32 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
33 | }
34 |
35 | // bardata reads data from the io.Reader, and plots bars
36 | func bardata(deck *generate.Deck, r io.Reader, left, bottom, top float64, tcolor, lcolor string) {
37 | x := left
38 | y := bottom
39 | width := 5.0
40 | scanner := bufio.NewScanner(r)
41 | for scanner.Scan() {
42 | fields := strings.Fields(scanner.Text())
43 | if len(fields) != 2 {
44 | continue
45 | }
46 | value, err := strconv.ParseFloat(fields[1], 64)
47 | if err != nil {
48 | continue
49 | }
50 | yp := vmap(value, 0, 200, 0, top-bottom)
51 | bar3d(deck, x, y, width, yp, tcolor, lcolor)
52 | deck.TextMid(x, y-2, fields[0], "sans", 1.5, "")
53 | x += width
54 | }
55 | }
56 |
57 | func main() {
58 | deck := generate.NewSlides(os.Stdout, 0, 0)
59 | deck.StartDeck()
60 | deck.StartSlide("rgb(30,10,10)", "linen")
61 | bardata(deck, os.Stdin, 20, 10, 80, "maroon", "linen")
62 | deck.EndSlide()
63 | deck.EndDeck()
64 | }
65 |
--------------------------------------------------------------------------------
/cmd/bar3d/data.d:
--------------------------------------------------------------------------------
1 | one 10
2 | two 20
3 | three 30
4 | four 40
5 | five 50
6 | six 60
7 | seven 70
8 | eight 80
9 | nine 90
10 | ten 100
--------------------------------------------------------------------------------
/cmd/bar3d/f.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/bar3d/f.pdf
--------------------------------------------------------------------------------
/cmd/bar3d/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/bar3d
2 |
3 | go 1.16
4 |
5 | require github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494
6 |
--------------------------------------------------------------------------------
/cmd/bar3d/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
2 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
3 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494 h1:Z5Dn/2Ml6/8fP1PWxv4ijaK2PwHsX8A+gZiKJVP+XDs=
4 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
5 |
--------------------------------------------------------------------------------
/cmd/between/between.go:
--------------------------------------------------------------------------------
1 | // between: compute the time (hours, minutes, or seconds) between two dates
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "os"
8 | "time"
9 | )
10 |
11 | func main() {
12 | const isofmt = "2006-01-02"
13 | var begintime, endtime, unit string
14 | flag.StringVar(&begintime, "begin", "", "begin time")
15 | flag.StringVar(&endtime, "end", "", "end time")
16 | flag.StringVar(&unit, "unit", "hour", "time unit (month, hour, minute, second, ms)")
17 | flag.Parse()
18 |
19 | if begintime == "" || endtime == "" {
20 | fmt.Fprintf(os.Stderr, "usage: between -begin YYYY-MM-DD -end YYYY-MM-DD -unit (hour, minute, or second\n")
21 | os.Exit(1)
22 | }
23 |
24 | t0, err := time.Parse(isofmt, begintime)
25 | if err != nil {
26 | fmt.Fprintf(os.Stderr, "%s is not a valid time\n", begintime)
27 | os.Exit(2)
28 | }
29 | t1, err := time.Parse(isofmt, endtime)
30 | if err != nil {
31 | fmt.Fprintf(os.Stderr, "%s is not a valid time\n", endtime)
32 | os.Exit(3)
33 | }
34 | var between float64
35 | switch unit {
36 | case "hr", "hour", "h":
37 | between = t1.Sub(t0).Hours()
38 | case "min", "minute", "m":
39 | between = t1.Sub(t0).Minutes()
40 | case "sec", "second", "s":
41 | between = t1.Sub(t0).Seconds()
42 | case "ms":
43 | between = float64(t1.Sub(t0).Milliseconds())
44 | default:
45 | fmt.Fprintf(os.Stderr, "%s is not a valid time unit (use one of hr, min, sec, ms)\n", unit)
46 | os.Exit(4)
47 | }
48 | fmt.Printf("%s %s %.2f %s\n", begintime, endtime, between, unit)
49 | }
50 |
--------------------------------------------------------------------------------
/cmd/between/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/between
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/bezpoints/README.md:
--------------------------------------------------------------------------------
1 | # bezpoints -- generate decksh markup for a filled quadratic bezier curve
2 |
3 | ```
4 | sage of bezpoints:
5 | -color string
6 | color (default "gray")
7 | -cx float
8 | control x (default 50)
9 | -cy float
10 | control y (default 80)
11 | -ex float
12 | end x (default 80)
13 | -ey float
14 | end y (default 50)
15 | -n int
16 | number of points (default 100)
17 | -opacity float
18 | opacity (default 50)
19 | -sx float
20 | start x (default 20)
21 | -sy float
22 | start y (default 50)
23 | ```
24 |
25 |
--------------------------------------------------------------------------------
/cmd/bezpoints/bezpoints.go:
--------------------------------------------------------------------------------
1 | // bezpoints generated decksh code for a filled quadratic bezier
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | )
8 |
9 | type point struct {
10 | x, y float64
11 | }
12 |
13 | // bezpoints computes the coordinates of a quadratic bezier curve
14 | // source: https://en.wikipedia.org/wiki/B%C3%A9zier_curve
15 | // for t between 0 and 1, where p0 is the start, p1 is conttol, p2 is end
16 | // p_x = (1-t)^2*p0_x + 2(1-t)*t*p1_x + t^2*p2_x
17 | // p_y = (1-t)^2*p0_y + 2(1-t)*t*p1_y + t^2*p2_y
18 | func bezpoints(start, end, control point, npoints int) []point {
19 | p := make([]point, npoints)
20 | step := 1.0 / float64(npoints)
21 | t := 0.0
22 | for i := 0; i < npoints; i++ {
23 | p[i].x = (1-t)*(1-t)*start.x + 2*(1-t)*t*control.x + t*t*end.x
24 | p[i].y = (1-t)*(1-t)*start.y + 2*(1-t)*t*control.y + t*t*end.y
25 | t += step
26 | }
27 | return p
28 | }
29 |
30 | // showcurve generates the decksh code for the computed coordinates
31 | func showcurve(style string, start, end, control point, n int, color string, opacity float64) {
32 | coordinates := bezpoints(start, end, control, n)
33 | lines := style == "l"
34 | if lines {
35 | fmt.Printf("polyline \"%.3f ", start.x)
36 | } else {
37 | fmt.Printf("polygon \"%.3f ", start.x)
38 | }
39 | for _, p := range coordinates {
40 | fmt.Printf("%.3f ", p.x)
41 | }
42 | fmt.Printf("%.3f\" \"%.3f ", end.x, start.y)
43 | for _, p := range coordinates {
44 | fmt.Printf("%.3f ", p.y)
45 | }
46 | if lines {
47 | fmt.Printf("%.3f\" %g \"%s\" %g\n", end.y, 0.2, color, opacity)
48 | } else {
49 | fmt.Printf("%.3f\" \"%s\" %g\n", end.y, color, opacity)
50 | }
51 | }
52 |
53 | func main() {
54 | var sx, sy, ex, ey, cx, cy, opacity float64
55 | var style, color string
56 | var npoints int
57 |
58 | flag.Float64Var(&sx, "sx", 20, "start x")
59 | flag.Float64Var(&sy, "sy", 50, "start y")
60 | flag.Float64Var(&cx, "cx", 50, "control x")
61 | flag.Float64Var(&cy, "cy", 80, "control y")
62 | flag.Float64Var(&ex, "ex", 80, "end x")
63 | flag.Float64Var(&ey, "ey", 50, "end y")
64 | flag.IntVar(&npoints, "n", 100, "number of points")
65 | flag.StringVar(&color, "color", "gray", "color")
66 | flag.Float64Var(&opacity, "opacity", 50, "opacity")
67 | flag.StringVar(&style, "style", "s", "object style")
68 | flag.Parse()
69 |
70 | start := point{sx, sy}
71 | end := point{ex, ey}
72 | control := point{cx, cy}
73 | showcurve(style, start, end, control, npoints, color, opacity)
74 | }
75 |
--------------------------------------------------------------------------------
/cmd/bezpoints/f.tex:
--------------------------------------------------------------------------------
1 | for $t$ between 0 and 1, where $p0$ is the start point, $p1$ is control point, $p2$ is end point, and $c$ are the computed coordinates.
2 | $$
3 | c_x = (1-t)^2 \times p0_x + 2(1-t) \times t \times p1_x + t^2 \times p2_x
4 | $$
5 |
6 | $$
7 | c_y = (1-t)^2 \times p0_y + 2(1-t) \times t \times p1_y + t^2 \times p2_y
8 | $$
9 | \bye
10 |
--------------------------------------------------------------------------------
/cmd/bezpoints/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/bezpoints
2 |
3 | go 1.17
4 |
--------------------------------------------------------------------------------
/cmd/c19chart/README.md:
--------------------------------------------------------------------------------
1 | # c19chart make covid-19 charts
2 |
3 | 
--------------------------------------------------------------------------------
/cmd/c19chart/f.deck-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/c19chart/f.deck-00001.png
--------------------------------------------------------------------------------
/cmd/c19chart/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/c19chart
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/ajstarks/dchart2 v0.0.0-20200422132333-d422fc36b888
7 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494
8 | )
9 |
--------------------------------------------------------------------------------
/cmd/c19chart/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/dchart2 v0.0.0-20200422132333-d422fc36b888 h1:MWGm3uzeqVvHjdyT15TrMYFpJXqet7swgDfJUlk8RHM=
2 | github.com/ajstarks/dchart2 v0.0.0-20200422132333-d422fc36b888/go.mod h1:VN7x3oiGjh6Xa7pbX1ptjlAQFqSYYyzHnjron9Qom7o=
3 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
4 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
5 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494 h1:Z5Dn/2Ml6/8fP1PWxv4ijaK2PwHsX8A+gZiKJVP+XDs=
6 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
7 |
--------------------------------------------------------------------------------
/cmd/cc/README.md:
--------------------------------------------------------------------------------
1 | # cc -- concentric circles
2 | 
3 | 
4 |
--------------------------------------------------------------------------------
/cmd/cc/cc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/cc/cc.png
--------------------------------------------------------------------------------
/cmd/cc/d1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/cc/d1.png
--------------------------------------------------------------------------------
/cmd/cc/d2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/cc/d2.png
--------------------------------------------------------------------------------
/cmd/cc/f.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/cc/f.pdf
--------------------------------------------------------------------------------
/cmd/cc/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/cc
2 |
3 | go 1.20
4 |
--------------------------------------------------------------------------------
/cmd/cc/main.go:
--------------------------------------------------------------------------------
1 | // cc -- concentric circle designs
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "math"
7 | )
8 |
9 | // circle draws a circle
10 | func circle(x, y, size float64, color string) {
11 | fmt.Printf("circle %.2f %.2f %.2f %q\n", x, y, size, color)
12 | }
13 |
14 | // cpolar places a circle at a polar coordinate
15 | func cpolar(x, y, r, t, size float64, color string) {
16 | fmt.Printf("p=polar %.2f %.2f %.2f %.2f\ncircle p_x p_y %.2f %q\n", x, y, r, t, size, color)
17 | }
18 |
19 | // polar returns Cartiesian coordinates from polar
20 | func polar(x, y, r, deg float64) (float64, float64) {
21 | theta := deg * (math.Pi / 180)
22 | px := x + (r * math.Cos(theta))
23 | py := y + (r * math.Sin(theta))
24 | return px, py
25 | }
26 |
27 | // begindeck begins decksh markup
28 | func begindeck() {
29 | fmt.Println("deck\ncanvas 1000 1000")
30 | }
31 |
32 | // enddeck end a deck
33 | func enddeck() {
34 | fmt.Println("edeck")
35 | }
36 |
37 | // beginslide begins a slide
38 | func beginslide(color string) {
39 | fmt.Printf("slide \"%s\"\n", color)
40 | }
41 |
42 | // endslide ends a slide
43 | func endslide() {
44 | fmt.Println("eslide")
45 | }
46 |
47 | // planet makes circles around a point
48 | func planet(x, y, size, radius, a1, a2, steps float64, color string) {
49 | for t := a1; t < a2; t += steps {
50 | cpolar(x, y, radius, t, size, color)
51 | }
52 | }
53 |
54 | // solar makes circles around a central circle
55 | func solar(x, y, csize, radius, psize, steps float64, ccolor, pcolor string) {
56 | circle(x, y, csize, ccolor)
57 | planet(x, y, psize, radius, 0, 360, steps, pcolor)
58 | }
59 |
60 | // d1 maes two concentric rings
61 | func d1(step float64) {
62 | beginslide("black")
63 | circle(50, 50, step, "red")
64 | for t := 0.0; t <= 360; t += step {
65 | px, py := polar(50, 50, 25, t)
66 | solar(px, py, 5, 5, 1, step, "red", "orange")
67 | }
68 | for t := step / 2; t <= 360; t += step {
69 | px, py := polar(50, 50, 40, t)
70 | solar(px, py, 5, 5, 1, step, "red", "orange")
71 | }
72 | endslide()
73 | }
74 |
75 | // hsv specifies a hue value in the hsv color space
76 | func hsv(hue, sat, value int) string {
77 | return fmt.Sprintf("hsv(%d,%d,%d)", hue, sat, value)
78 | }
79 |
80 | // cchue makes a series of 7 concentric rings, varying bu nue
81 | func cchue(r, step float64, starthue int, bgcolor string) {
82 | beginslide(bgcolor)
83 | cstep := 1.0
84 | c := 1.0
85 | halfstep := step / 2
86 | csize := r * 1.5
87 | hue := starthue
88 |
89 | circle(50, 50, csize, hsv(hue, 100, 100))
90 | planet(50, 50, c, r, 0, 360, step, hsv(hue, 100, 100))
91 | r += 2
92 | c += cstep
93 | hue += 7
94 | planet(50, 50, c, r, halfstep, 360, step, hsv(hue, 100, 100))
95 | r += 3
96 | c += cstep
97 | hue += 7
98 | planet(50, 50, c, r, 0, 360, step, hsv(hue, 100, 100))
99 | r += 4
100 | c += cstep
101 | hue += 7
102 | planet(50, 50, c, r, halfstep, 360, step, hsv(hue, 100, 100))
103 | r += 5
104 | c += cstep
105 | hue += 7
106 | planet(50, 50, c, r, 0, 360, step, hsv(hue, 100, 100))
107 | r += 6
108 | c += cstep
109 | hue += 7
110 | planet(50, 50, c, r, halfstep, 360, step, hsv(hue, 100, 100))
111 | r += 8
112 | hue += 7
113 | planet(50, 50, 7, r, 0, 360, step, hsv(hue, 100, 100))
114 |
115 | for t := 0.0; t <= 360; t += step {
116 | px, py := polar(50, 50, r, t)
117 | planet(px, py, 1, 5, 0, 360, 30, hsv(starthue, 100, 100))
118 | }
119 | endslide()
120 | }
121 |
122 | func main() {
123 | begindeck()
124 | cchue(10, 20, 0, "black")
125 | d1(30)
126 | enddeck()
127 | }
128 |
--------------------------------------------------------------------------------
/cmd/cl/main.go:
--------------------------------------------------------------------------------
1 | // cl: show a file with numbered lines, showing the whole file, a range, or a single line.
2 | package main
3 |
4 | import (
5 | "bufio"
6 | "flag"
7 | "fmt"
8 | "io"
9 | "os"
10 | "strconv"
11 | "strings"
12 | )
13 |
14 | // linerange returns the begin and end using a "-" as delimiter
15 | func linerange(s string) (int, int) {
16 | b := 0
17 | e := 0
18 | var err error
19 | l := strings.Split(s, "-")
20 | switch len(l) {
21 | case 1: // a single number (for example: "10")
22 | b, err = strconv.Atoi(l[0])
23 | if err != nil {
24 | return 0, 0
25 | }
26 | case 2: // two numbers (for example "10-20")
27 | b, err = strconv.Atoi(l[0])
28 | if err != nil {
29 | return 0, 0
30 | }
31 | e, err = strconv.Atoi(l[1])
32 | if err != nil {
33 | return b, 0
34 | }
35 | default:
36 | return 0, 0
37 | }
38 | return b, e
39 | }
40 |
41 | func countLines(r io.Reader, lr string) {
42 | begin, end := linerange(lr)
43 | scanner := bufio.NewScanner(r)
44 | for n := 1; scanner.Scan(); n++ {
45 | t := scanner.Text()
46 |
47 | // show all lines
48 | if begin == 0 && end == 0 {
49 | fmt.Printf("%d: %s\n", n, t)
50 | continue
51 | }
52 | // show a single specified line
53 | if n == begin && end == 0 {
54 | fmt.Printf("%d: %s\n", n, t)
55 | break
56 | }
57 | // show a range of lines
58 | if n >= begin && n <= end {
59 | fmt.Printf("%d: %s\n", n, t)
60 | }
61 | }
62 | }
63 |
64 | func main() {
65 | var linerange string
66 | flag.StringVar(&linerange, "n", "all", "line range (begin-end)")
67 | flag.Parse()
68 | countLines(os.Stdin, linerange)
69 | }
70 |
--------------------------------------------------------------------------------
/cmd/csv2poly/csv2poly:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/csv2poly/csv2poly
--------------------------------------------------------------------------------
/cmd/csv2poly/csv2poly.go:
--------------------------------------------------------------------------------
1 | // csv2poly - generate decksh polygons from x,y pairs
2 | package main
3 |
4 | import (
5 | "bufio"
6 | "flag"
7 | "fmt"
8 | "io"
9 | "math"
10 | "os"
11 | "strconv"
12 | "strings"
13 | )
14 |
15 | const (
16 | smallest = -math.MaxFloat64
17 | largest = math.MaxFloat64
18 | )
19 |
20 | type params struct {
21 | left, right, bottom, top float64
22 | label, color string
23 | }
24 |
25 | func main() {
26 | var p params
27 | flag.Float64Var(&p.left, "left", 0, "left")
28 | flag.Float64Var(&p.right, "right", 100, "right")
29 | flag.Float64Var(&p.bottom, "bottom", 0, "bottom")
30 | flag.Float64Var(&p.top, "top", 100, "top")
31 | flag.StringVar(&p.color, "color", "gray", "color")
32 | flag.StringVar(&p.label, "label", "", "label")
33 | flag.Parse()
34 |
35 | for _, f := range flag.Args() {
36 | if err := process(p, f); err != nil {
37 | fmt.Fprintf(os.Stderr, "%v\n", err)
38 | continue
39 | }
40 | }
41 | }
42 |
43 | // readata reads x, y pairs, checking for errors
44 | func readata(r io.Reader) ([]float64, []float64, error) {
45 | var x, y []float64
46 | var xp, yp float64
47 | var err error
48 | scanner := bufio.NewScanner(r)
49 | for scanner.Scan() {
50 | fields := strings.Split(scanner.Text(), ",")
51 | if len(fields) != 2 {
52 | continue
53 | }
54 | if xp, err = strconv.ParseFloat(fields[0], 64); err != nil {
55 | continue
56 | }
57 | if yp, err = strconv.ParseFloat(fields[1], 64); err != nil {
58 | continue
59 | }
60 | x = append(x, xp)
61 | y = append(y, yp)
62 | }
63 | return x, y, scanner.Err()
64 | }
65 |
66 | // process data in the filename
67 | func process(p params, filename string) error {
68 | r, err := os.Open(filename)
69 | if err != nil {
70 | return err
71 | }
72 |
73 | fmt.Println("#", p.left, p.right, p.bottom, p.top)
74 |
75 | x, y, err := readata(r)
76 | if err != nil {
77 | return err
78 | }
79 |
80 | pminx := largest
81 | pmaxx := smallest
82 | fmt.Printf("polygon \"")
83 | for i := 0; i < len(x); i++ {
84 | px := x[i]
85 | if px > pmaxx {
86 | pmaxx = px
87 | }
88 | if px < pminx {
89 | pminx = px
90 | }
91 | fmt.Printf("%.3g ", px)
92 | }
93 | fmt.Printf("%.3g\"", x[0])
94 |
95 | pminy := largest
96 | pmaxy := smallest
97 | fmt.Printf(" \"")
98 | for i := 0; i < len(y); i++ {
99 | py := y[i]
100 | if py > pmaxy {
101 | pmaxy = py
102 | }
103 | if py < pminy {
104 | pminy = py
105 | }
106 | fmt.Printf("%.3g ", py)
107 | }
108 | fmt.Printf("%.3g\" \"%s\"\n", y[0], p.color)
109 | if len(p.label) > 0 {
110 | fmt.Printf("ctext \"%s\" %g %g 1\n", p.label, pminx+((pmaxx-pminx)/2), pminy+((pmaxy-pminy)/2))
111 | }
112 | return r.Close()
113 | }
114 |
115 | // vmap maps one range to another
116 | func vmap(value, low1, high1, low2, high2 float64) float64 {
117 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
118 | }
119 |
--------------------------------------------------------------------------------
/cmd/csv2poly/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/csv2poly
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/csvread/csvread.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/csv"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 | "strconv"
10 | )
11 |
12 | // column returns a number corresponding to the letter, or just the number
13 | func column(s string) int {
14 | if len(s) == 0 {
15 | return 0
16 | }
17 | first := s[0]
18 | switch {
19 | case first >= 'a' && first <= 'z':
20 | return int(first - 'a')
21 | case first >= 'A' && first <= 'Z':
22 | return int(first - 'A')
23 | default:
24 | n, err := strconv.Atoi(s)
25 | if err != nil {
26 | return 0
27 | }
28 | return n
29 | }
30 | }
31 |
32 | // getf turns a string slice of numbers into a slice of integers
33 | func getf(s []string) []int {
34 | fn := []int{}
35 | for _, f := range s {
36 | fn = append(fn, column(f))
37 | }
38 | return fn
39 | }
40 |
41 | // output displays fields of data
42 | func output(s []string, w *csv.Writer, plain bool) {
43 | if plain {
44 | nl := len(s) - 1
45 | for i := 0; i < nl; i++ {
46 | fmt.Printf("%s\t", s[i])
47 | }
48 | fmt.Println(s[nl])
49 | } else {
50 | w.Write(s)
51 | }
52 | }
53 |
54 | func main() {
55 | var plainout = flag.Bool("plain", true, "plain output")
56 | var headskip = flag.Bool("headskip", false, "skip the first record (header)")
57 | var varfields = flag.Bool("varfields", true, "variable fields")
58 | var err error
59 | var data []string
60 | flag.Parse()
61 | r := csv.NewReader(os.Stdin)
62 | if *varfields {
63 | r.FieldsPerRecord = -1
64 | }
65 | w := csv.NewWriter(os.Stdout)
66 | r.LazyQuotes = true
67 | fields := getf(flag.Args())
68 |
69 | // loop over the input, making output
70 | for n := 0; ; n++ {
71 | data, err = r.Read()
72 | if err == io.EOF {
73 | break
74 | }
75 | if n == 0 && *headskip {
76 | continue
77 | }
78 | if err != nil {
79 | fmt.Fprintf(os.Stderr, "%v\n", err)
80 | continue
81 | }
82 | if len(fields) > 0 { // output selected fields
83 | selection := []string{}
84 | for _, n := range fields {
85 | if n >= 0 && n < len(data) {
86 | selection = append(selection, data[n])
87 | }
88 | }
89 | output(selection, w, *plainout)
90 |
91 | } else { // or output all fields
92 | output(data, w, *plainout)
93 | }
94 | }
95 | w.Flush()
96 | }
97 |
--------------------------------------------------------------------------------
/cmd/csvread/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/csvread
2 |
3 | go 1.21.5
4 |
--------------------------------------------------------------------------------
/cmd/ctime/ctime.go:
--------------------------------------------------------------------------------
1 | /*
2 | ctime - time a command
3 |
4 | Run the specified command, show the execution time in seconds to standard output.
5 | Any output is ignored.
6 |
7 | The -t option shows the specified string before the time display,
8 | otherwise the first argument of the command is shown.
9 |
10 | $ ctime sleep 10
11 | sleep 10.00685
12 |
13 | $ ctime -t "Go to sleep" sleep 5
14 | Go to sleep 5.00442
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "flag"
21 | "fmt"
22 | "os"
23 | "os/exec"
24 | "time"
25 | )
26 |
27 | func work(tag string, s []string) {
28 | if len(s) < 1 {
29 | return
30 | }
31 | if tag == "" {
32 | tag = s[0]
33 | }
34 | b := time.Now()
35 | err := exec.Command(s[0], s[1:]...).Run()
36 | e := time.Now()
37 | if err != nil {
38 | fmt.Fprintf(os.Stderr, "%v\n", err)
39 | return
40 | }
41 | fmt.Printf("%v\t%.5f\n", tag, e.Sub(b).Seconds())
42 | }
43 |
44 | func main() {
45 | var tag = flag.String("t", "", "tag for the command")
46 | flag.Parse()
47 | work(*tag, flag.Args())
48 | }
49 |
--------------------------------------------------------------------------------
/cmd/ctime/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/ctime
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/deckle/deckle.go:
--------------------------------------------------------------------------------
1 | // deckle -- generate deck markup for deckled edges (lines and filled)
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "math/rand"
8 | "os"
9 |
10 | "github.com/ajstarks/deck/generate"
11 | )
12 |
13 | func polygon(deck *generate.Deck, raw bool, x, y []float64, color string) {
14 | if raw {
15 | deck.Polygon(x, y, color)
16 | } else {
17 | fmt.Fprintf(os.Stdout, "polygon \"")
18 | for i := 0; i < len(x); i++ {
19 | fmt.Fprintf(os.Stdout, "%.4g ", x[i])
20 | }
21 | fmt.Fprintf(os.Stdout, "\" \"")
22 | for i := 0; i < len(y); i++ {
23 | fmt.Fprintf(os.Stdout, "%.4g ", y[i])
24 | }
25 | fmt.Fprintf(os.Stdout, "\" %q\n", color)
26 | }
27 | }
28 |
29 | func line(deck *generate.Deck, raw bool, x1, y1, x2, y2, linewidth float64, color string) {
30 | if raw {
31 | deck.Line(x1, y1, x2, y2, linewidth, color)
32 | } else {
33 | fmt.Fprintf(os.Stdout, "line %.4g %.4g %.4g %.4g %.4g %q\n", x1, y1, x2, y2, linewidth, color)
34 | }
35 | }
36 |
37 | // hfill makes a (width long) horizontal deckled edge starting at (x,y)
38 | func hfill(deck *generate.Deck, mtype bool, x, y, width, height float64, color string, n int) {
39 | xp := make([]float64, n)
40 | yp := make([]float64, n)
41 |
42 | // left point
43 | xp[0] = x
44 | yp[0] = y
45 |
46 | // right point
47 | xp[n-2] = x + width
48 | yp[n-2] = y
49 |
50 | // back to the left
51 | xp[n-1] = x
52 | yp[n-1] = y
53 |
54 | xincr := width / float64(n)
55 | for i := 1; i <= n-3; i++ {
56 | xp[i] = xp[i-1] + xincr
57 | yp[i] = y + rand.Float64()*height
58 | }
59 | polygon(deck, mtype, xp, yp, color)
60 | }
61 |
62 | // vfill makes a (height high) vertical deckled edge
63 | func vfill(deck *generate.Deck, mtype bool, x, y, width, height float64, color string, n int) {
64 | xp := make([]float64, n)
65 | yp := make([]float64, n)
66 |
67 | // bottom point
68 | xp[0] = x
69 | yp[0] = y
70 |
71 | // top point
72 | xp[n-2] = x
73 | yp[n-2] = y + height
74 |
75 | // back to the bottom
76 | xp[n-1] = x
77 | yp[n-1] = y
78 |
79 | yincr := height / float64(n)
80 | for i := 1; i <= n-3; i++ {
81 | yp[i] = yp[i-1] + yincr
82 | xp[i] = x + rand.Float64()*width
83 | }
84 | polygon(deck, mtype, xp, yp, color)
85 | }
86 |
87 | // hline makes a (width long) horizontal deckled edge
88 | func hline(deck *generate.Deck, mtype bool, x, y, width, height, linewidth float64, color string, n int) {
89 | xincr := width / float64(n)
90 | hi := xincr / 2
91 | y1 := y
92 | for x1 := x; x1 < x+width; x1 += xincr {
93 | y2 := y1 + rand.Float64()*height
94 | line(deck, mtype, x1, y1, x1+(hi), y2, linewidth, color)
95 | line(deck, mtype, x1+(hi), y2, x1+xincr, y1, linewidth, color)
96 | }
97 | }
98 |
99 | // vline makes a (height high) vertical deckled edge
100 | func vline(deck *generate.Deck, mtype bool, x, y, width, height, linewidth float64, color string, n int) {
101 | yincr := height / float64(n)
102 | hi := yincr / 2
103 | x1 := x
104 | for y1 := y; y1 < y+height; y1 += yincr {
105 | x2 := x1 + rand.Float64()*width
106 | line(deck, mtype, x1, y1, x2, y1+(hi), linewidth, color)
107 | line(deck, mtype, x2, y1+(hi), x1, y1+yincr, linewidth, color)
108 | }
109 | }
110 |
111 | func main() {
112 | deck := generate.NewSlides(os.Stdout, 0, 0)
113 | var (
114 | x, y, width, height, linewidth float64
115 | n int
116 | color, dtype string
117 | mtype bool
118 | )
119 |
120 | flag.Float64Var(&x, "x", 10, "x")
121 | flag.Float64Var(&y, "y", 50, "y")
122 | flag.Float64Var(&width, "w", 80, "width")
123 | flag.Float64Var(&height, "h", 3, "height")
124 | flag.Float64Var(&linewidth, "lw", 0.1, "line width")
125 | flag.BoolVar(&mtype, "raw", false, "type of markup - true for deck, false for decksh")
126 | flag.IntVar(&n, "n", 50, "number of bumps")
127 | flag.StringVar(&color, "color", "gray", "color")
128 | flag.StringVar(&dtype, "type", "lh", "fv: filled vertical, fh: filled horizontal, lv: line vertical, lh: line horizontal")
129 | flag.Parse()
130 |
131 | switch dtype {
132 | case "fv":
133 | vfill(deck, mtype, x, y, width, height, color, n)
134 | case "fh":
135 | hfill(deck, mtype, x, y, width, height, color, n)
136 | case "lv":
137 | vline(deck, mtype, x, y, width, height, linewidth, color, n)
138 | case "lh":
139 | hline(deck, mtype, x, y, width, height, linewidth, color, n)
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/cmd/deckle/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/deckle
2 |
3 | go 1.16
4 |
5 | require github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494
6 |
--------------------------------------------------------------------------------
/cmd/deckle/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
2 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
3 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494 h1:Z5Dn/2Ml6/8fP1PWxv4ijaK2PwHsX8A+gZiKJVP+XDs=
4 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
5 |
--------------------------------------------------------------------------------
/cmd/deckle/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | (
3 | printf ""
4 | deckle -raw=t -x 10 -y 10 -h -1.5 -w 50 -type lh -n 20 -color red
5 | deckle -raw=t -x 10 -y 10 -h 50 -w -1.5 -type lv -n 20 -color blue
6 | deckle -raw=t -x 10 -y 60 -h 1.5 -w 50 -type lh -n 20 -color red
7 | deckle -raw=t -x 60 -y 10 -h 50 -w 1.5 -type lv -n 20 -color blue
8 | printf ""
9 | printf ""
10 | deckle -raw=t -x 10 -y 10 -h -1.5 -w 50 -type fh -n 20 -color red
11 | deckle -raw=t -x 10 -y 10 -h 50 -w -1.5 -type fv -n 20 -color blue
12 | deckle -raw=t -x 10 -y 60 -h 1.5 -w 50 -type fh -n 20 -color red
13 | deckle -raw=t -x 60 -y 10 -h 50 -w 1.5 -type fv -n 20 -color blue
14 | printf ""
15 | printf ""
16 | ) | pdfdeck $* -stdout - > r.pdf
17 |
18 | (
19 | printf "deck\n"
20 | printf "slide\n"
21 | deckle -raw=f -x 10 -y 10 -h -1.5 -w 50 -type lh -n 20 -color red
22 | deckle -raw=f -x 10 -y 10 -h 50 -w -1.5 -type lv -n 20 -color blue
23 | deckle -raw=f -x 10 -y 60 -h 1.5 -w 50 -type lh -n 20 -color red
24 | deckle -raw=f -x 60 -y 10 -h 50 -w 1.5 -type lv -n 20 -color blue
25 | printf "eslide\n"
26 | printf "slide\n"
27 | deckle -raw=f -x 10 -y 10 -h -1.5 -w 50 -type fh -n 20 -color red
28 | deckle -raw=f -x 10 -y 10 -h 50 -w -1.5 -type fv -n 20 -color blue
29 | deckle -raw=f -x 10 -y 60 -h 1.5 -w 50 -type fh -n 20 -color red
30 | deckle -raw=f -x 60 -y 10 -h 50 -w 1.5 -type fv -n 20 -color blue
31 | printf "eslide\n"
32 | printf "edeck\n"
33 | ) | decksh | pdfdeck $* -stdout - > d.pdf
--------------------------------------------------------------------------------
/cmd/desordres/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/01.png
--------------------------------------------------------------------------------
/cmd/desordres/04-color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/04-color.png
--------------------------------------------------------------------------------
/cmd/desordres/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/05.png
--------------------------------------------------------------------------------
/cmd/desordres/14-hot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/14-hot.png
--------------------------------------------------------------------------------
/cmd/desordres/README.md:
--------------------------------------------------------------------------------
1 | # desordres
2 |
3 | desordres make visuals in the style of "Des Ordres" by Vera Molnár, using deck markup.
4 |
5 | For example: make a 1000x1000 pdf using default parameters:
6 |
7 | ```
8 | desordres | pdfdeck -stdout -pagesize 1000x1000 - > gray.pdf
9 | ```
10 |
11 |
12 | Make a 1000x1000 PNG file with random colors HSV(20-60, 100, 100), 14 tiles/row:
13 |
14 | ```
15 | desordres -tiles 14 -color '20:60' -bgcolor=black > f.xml
16 | pngdeck -pagesize 1000x1000 f.xml
17 | ```
18 |
19 |
20 | Use all hues, make a PDF:
21 |
22 | ```
23 | desordres -tiles 5 -color '0:360' -bgcolor black | pdfdeck -stdout -pagesize 1000x1000 - > rainbow.pdf
24 | ```
25 |
26 |
27 |
28 |
29 | Use a built-in color palette:
30 |
31 | ```
32 | desordres -tiles 14 -color mist-gb > mist-gb.xml
33 | pngdeck -pagesize 500x500 mist-gb.xml
34 | ```
35 |
36 |
37 |
38 | Load a custom palette:
39 | ```
40 | desordres -p ajs.pal -color rainbow > rb.xml
41 | pngdeck -pagesize 500x500 rb.xml
42 | ```
43 |
44 |
45 |
46 |
47 | ## options
48 | ```
49 | Option Default Description
50 | .....................................................
51 | -tiles 10 number of tiles/row
52 | -maxlw 1 maximim line thickness
53 | -p "" palette file
54 | -bgcolor white background color
55 | -color gray color name, h1:h2, or palette:
56 |
57 | 2-bit-demichrome [#211e20 #555568 #a0a08b #e9efec]
58 | nintendo-super-gameboy [#331e50 #a63725 #d68e49 #f7e7c6]
59 | blu-scribbles [#051833 #0a4f66 #0f998e #12cc7f]
60 | 2-bit-grayscale [#000000 #676767 #b6b6b6 #ffffff]
61 | hollow [#0f0f1b #565a75 #c6b7be #fafbf6]
62 | ayy4 [#00303b #ff7777 #ffce96 #f1f2da]
63 | pokemon-sgb [#181010 #84739c #f7b58c #ffefff]
64 | rustic-gb [#2c2137 #764462 #edb4a1 #a96868]
65 | spacehaze [#f8e3c4 #cc3495 #6b1fb1 #0b0630]
66 | moonlight-gb [#0f052d #203671 #36868f #5fc75d]
67 | nintendo-gameboy-bgb [#081820 #346856 #88c070 #e0f8d0]
68 | pen-n-paper [#e4dbba #a4929a #4f3a54 #260d1c]
69 | blk-aqu4 [#002b59 #005f8c #00b9be #9ff4e5]
70 | nostalgia [#d0d058 #a0a840 #708028 #405010]
71 | links-awakening-sgb [#5a3921 #6b8c42 #7bc67b #ffffb5]
72 | arq4 [#ffffff #6772a9 #3a3277 #000000]
73 | kankei4 [#ffffff #f42e1f #2f256b #060608]
74 | dark-mode [#212121 #454545 #787878 #a8a5a5]
75 | kirokaze-gameboy [#332c50 #46878f #94e344 #e2f3e4]
76 | mist-gb [#2d1b00 #1e606e #5ab9a8 #c4f0c2]
77 | red-brick [#eff9d6 #ba5044 #7a1c4b #1b0326]
78 | ice-cream-gb [#7c3f58 #eb6b6f #f9a875 #fff6d3]
79 | ```
80 |
81 | ## palette files
82 |
83 | ```desordres``` has a built-in palette which may be overridden by specifying a palette file.
84 |
85 | Custom palette files are of the form of name followed by a list of colors, one name per line.
86 | Colors may be named colors or rgb triples in hex form (#rrggbb).
87 |
88 | name color1 color2 ... colorn
89 |
90 | for example:
91 | ```
92 | ajstarks #aa0000 #aaaaaa #000000 #ffffff
93 | autumn-decay #313638 #574729 #975330 #c57938 #ffad3b #ffe596
94 | rainbow #ff0000 #ffa500 #ffff00 #008000 #0000ff #4b0082 #ee82ee
95 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
96 | polished-gold #000000 #361c1b #754232 #cd894a #e6b983 #fff8bc #ffffff #2d2433 #4f4254 #b092a7
97 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
98 | grayscale #111111 #222222 #333333 #444444 #555555 #666666 #777777 #888888 #999999 #aaaaaa #bbbbbb #cccccc #dddddd #eeeeee
99 | ```
100 |
101 |
102 |
--------------------------------------------------------------------------------
/cmd/desordres/ajs.pal:
--------------------------------------------------------------------------------
1 | ajstarks #aa0000 #aaaaaa #000000 #ffffff
2 | autumn-decay #313638 #574729 #975330 #c57938 #ffad3b #ffe596
3 | rainbow #ff0000 #ffa500 #ffff00 #008000 #0000ff #4b0082 #ee82ee
4 | polished-gold #000000 #361c1b #754232 #cd894a #e6b983 #fff8bc #ffffff #2d2433 #4f4254 #b092a7
5 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
6 | grayscale #111111 #222222 #333333 #444444 #555555 #666666 #777777 #888888 #999999 #aaaaaa #bbbbbb #cccccc #dddddd #eeeeee
7 |
--------------------------------------------------------------------------------
/cmd/desordres/black-green-thin-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/black-green-thin-14.png
--------------------------------------------------------------------------------
/cmd/desordres/blue-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/blue-10.png
--------------------------------------------------------------------------------
/cmd/desordres/blue-thin-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/blue-thin-14.png
--------------------------------------------------------------------------------
/cmd/desordres/blue-thin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/blue-thin.png
--------------------------------------------------------------------------------
/cmd/desordres/blue-tick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/blue-tick.png
--------------------------------------------------------------------------------
/cmd/desordres/def.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/def.png
--------------------------------------------------------------------------------
/cmd/desordres/default.pal:
--------------------------------------------------------------------------------
1 | kirokaze-gameboy #332c50 #46878f #94e344 #e2f3e4
2 | ice-cream-gb #7c3f58 #eb6b6f #f9a875 #fff6d3
3 | 2-bit-demichrome #211e20 #555568 #a0a08b #e9efec
4 | mist-gb #2d1b00 #1e606e #5ab9a8 #c4f0c2
5 | rustic-gb #2c2137 #764462 #edb4a1 #a96868
6 | 2-bit-grayscale #000000 #676767 #b6b6b6 #ffffff
7 | hollow #0f0f1b #565a75 #c6b7be #fafbf6
8 | ayy4 #00303b #ff7777 #ffce96 #f1f2da
9 | nintendo-gameboy-bgb #081820 #346856 #88c070 #e0f8d0
10 | red-brick #eff9d6 #ba5044 #7a1c4b #1b0326
11 | nostalgia #d0d058 #a0a840 #708028 #405010
12 | spacehaze #f8e3c4 #cc3495 #6b1fb1 #0b0630
13 | moonlight-gb #0f052d #203671 #36868f #5fc75d
14 | links-awakening-sgb #5a3921 #6b8c42 #7bc67b #ffffb5
15 | arq4 #ffffff #6772a9 #3a3277 #000000
16 | blk-aqu4 #002b59 #005f8c #00b9be #9ff4e5
17 | pokemon-sgb #181010 #84739c #f7b58c #ffefff
18 | nintendo-super-gameboy #331e50 #a63725 #d68e49 #f7e7c6
19 | blu-scribbles #051833 #0a4f66 #0f998e #12cc7f
20 | kankei4 #ffffff #f42e1f #2f256b #060608
21 | dark-mode #212121 #454545 #787878 #a8a5a5
22 | ajstarks #aa0000 #aaaaaa #000000 #ffffff
23 | pen-n-paper #e4dbba #a4929a #4f3a54 #260d1c
24 | autumn-decay #313638 #574729 #975330 #c57938 #ffad3b #ffe596
25 | polished-gold #000000 #361c1b #754232 #cd894a #e6b983 #fff8bc #ffffff #2d2433 #4f4254 #b092a7
26 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
--------------------------------------------------------------------------------
/cmd/desordres/desordres.go:
--------------------------------------------------------------------------------
1 | // desordres -- tile blocks of lines as in Vera Molnar's Des Ordres, using deck markup
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "math/rand"
8 | "os"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | const (
14 | linefmt = "\n"
15 | squarefmt = "\n"
16 | )
17 |
18 | // random returns a random number between a range
19 | func random(min, max float64) float64 {
20 | return vmap(rand.Float64(), 0, 1, min, max)
21 | }
22 |
23 | // vmap maps one interval to another
24 | func vmap(value, low1, high1, low2, high2 float64) float64 {
25 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
26 | }
27 |
28 | // csquare makes a square with lines, using a specified width and color
29 | // if a hue range is set, the color is randomly selected in that range,
30 | // if a palette is specified, use a random color from it,
31 | // otherwise, the named color is used.
32 | func csquare(x, y, size, maxlw, h1, h2 float64, color string) {
33 |
34 | if c, ok := palette[color]; ok { // use a palette
35 | color = c[rand.Intn(len(c))]
36 | }
37 | if h1 > -1 && h2 > -1 { // hue range set
38 | color = fmt.Sprintf("hsv(%v,100,100)", random(h1, h2))
39 | }
40 | // define the corners
41 | hs := size / 2
42 | tlx, tly := x-hs, y+hs
43 | trx, try := x+hs, y+hs
44 | blx, bly := x-hs, y-hs
45 | brx, bry := x+hs, y-hs
46 |
47 | lw := random(0.1, maxlw)
48 | // make the boundaries
49 | hline(tlx, tly, size, lw, color)
50 | hline(blx, bly, size, lw, color)
51 | vline(blx, bly, size, lw, color)
52 | vline(brx, bry, size, lw, color)
53 | // make the corners
54 | square(tlx, tly, lw, color)
55 | square(blx, bly, lw, color)
56 | square(brx, bry, lw, color)
57 | square(trx, try, lw, color)
58 | }
59 |
60 | // square makes a square
61 | func square(x, y, size float64, color string) {
62 | fmt.Printf(squarefmt, x, y, size, color)
63 | }
64 |
65 | // hline makes a horizontal line
66 | func hline(x, y, size, lw float64, color string) {
67 | fmt.Printf(linefmt, x, y, x+size, y, lw, color)
68 | }
69 |
70 | // vline makes a vertical line
71 | func vline(x, y, size, lw float64, color string) {
72 | fmt.Printf(linefmt, x, y, x, y+size, lw, color)
73 | }
74 |
75 | // desordres makes a series of concentric squares
76 | func desordres(x, y, minsize, maxsize, maxlw, h1, h2 float64, color string) {
77 | step := random(1, 5)
78 | for v := minsize; v < maxsize; v += step {
79 | csquare(x, y, v, maxlw, h1, h2, color)
80 | }
81 | }
82 |
83 | // parseHues parses a color string: if the string is of the form "h1:h2",
84 | // where h1, and h2 are numbers between 0 and 360, they are a range of hues.
85 | // Otherwise, set to -1 for invalid entries (use named colors instead)
86 | func parseHues(color string) (float64, float64) {
87 | var h1, h2 float64 = -1.0, -1.0
88 | hb := strings.Split(color, ":")
89 | if len(hb) == 2 {
90 | var err error
91 | h1, err = strconv.ParseFloat(hb[0], 64)
92 | if err != nil {
93 | h1 = -1
94 | }
95 | h2, err = strconv.ParseFloat(hb[1], 64)
96 | if err != nil {
97 | h2 = -1
98 | }
99 | }
100 | return h1, h2
101 | }
102 |
103 | func usage() {
104 | fmt.Fprintln(os.Stderr)
105 | fmt.Fprintf(os.Stderr, "Option Default Description\n")
106 | fmt.Fprintf(os.Stderr, ".....................................................\n")
107 | fmt.Fprintf(os.Stderr, "-tiles 10 number of tiles/row\n")
108 | fmt.Fprintf(os.Stderr, "-maxlw 1 maximim line thickness\n")
109 | fmt.Fprintf(os.Stderr, "-bgcolor white background color\n")
110 | fmt.Fprintf(os.Stderr, "-p \"\" palette file\n")
111 | fmt.Fprintf(os.Stderr, "-color gray color name, h1:h2, or palette:\n\n")
112 | for p, k := range palette {
113 | fmt.Fprintf(os.Stderr, "%-20s\t%v\n", p, k)
114 | }
115 | os.Exit(1)
116 | }
117 |
118 | func userpalette(pfile string) {
119 | if len(pfile) > 0 {
120 | var err error
121 | palette, err = LoadPalette(pfile)
122 | if err != nil {
123 | fmt.Fprintf(os.Stderr, "%v\n", err)
124 | os.Exit(1)
125 | }
126 | }
127 | }
128 |
129 | // slide generation functions
130 | func beginDeck() { fmt.Println("") }
131 | func endDeck() { fmt.Println("") }
132 | func beginSlide(color string) { fmt.Printf("\n", color) }
133 | func endSlide() { fmt.Println("") }
134 |
135 | func main() {
136 | var tiles, maxlw float64
137 | var bgcolor, color, pfile string
138 | var showhelp bool
139 |
140 | flag.Float64Var(&tiles, "tiles", 10, "tiles/row")
141 | flag.Float64Var(&maxlw, "maxlw", 1, "maximum line thickness")
142 | flag.StringVar(&bgcolor, "bgcolor", "white", "background color")
143 | flag.StringVar(&color, "color", "gray", "pen color: (named color, hue range (h1:h2), or palette name")
144 | flag.StringVar(&pfile, "p", "", "palette file")
145 | flag.BoolVar(&showhelp, "help", false, "show usage")
146 | flag.Parse()
147 | h1, h2 := parseHues(color) // set hue range, or named color/palette
148 | userpalette(pfile)
149 | if showhelp {
150 | usage()
151 | }
152 |
153 | size := 100 / tiles // size of each tile
154 | top := 100 - (size / 2) // top of the beginning row
155 | left := 100 - top // left of the beginning row
156 |
157 | beginDeck()
158 | beginSlide(bgcolor)
159 | for y := top; y > 0; y -= size {
160 | for x := left; x < 100; x += size {
161 | desordres(x, y, 2, size, maxlw, h1, h2, color)
162 | }
163 | }
164 | endSlide()
165 | endDeck()
166 | }
167 |
--------------------------------------------------------------------------------
/cmd/desordres/f-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/f-00001.png
--------------------------------------------------------------------------------
/cmd/desordres/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/desordres
2 |
3 | go 1.21.5
4 |
--------------------------------------------------------------------------------
/cmd/desordres/gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/gray.png
--------------------------------------------------------------------------------
/cmd/desordres/gray14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/gray14.png
--------------------------------------------------------------------------------
/cmd/desordres/hot-14-20-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/hot-14-20-60.png
--------------------------------------------------------------------------------
/cmd/desordres/mist-gb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/mist-gb-00001.png
--------------------------------------------------------------------------------
/cmd/desordres/rainbow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/rainbow.png
--------------------------------------------------------------------------------
/cmd/desordres/rb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/rb-00001.png
--------------------------------------------------------------------------------
/cmd/desordres/readpalette.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "image/color"
7 | "io"
8 | "os"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | type spalette map[string][]string
14 | type rgbpalette map[string][]color.NRGBA
15 |
16 | var palette = spalette{
17 | "kirokaze-gameboy": {"#332c50", "#46878f", "#94e344", "#e2f3e4"},
18 | "ice-cream-gb": {"#7c3f58", "#eb6b6f", "#f9a875", "#fff6d3"},
19 | "2-bit-demichrome": {"#211e20", "#555568", "#a0a08b", "#e9efec"},
20 | "mist-gb": {"#2d1b00", "#1e606e", "#5ab9a8", "#c4f0c2"},
21 | "rustic-gb": {"#2c2137", "#764462", "#edb4a1", "#a96868"},
22 | "2-bit-grayscale": {"#000000", "#676767", "#b6b6b6", "#ffffff"},
23 | "hollow": {"#0f0f1b", "#565a75", "#c6b7be", "#fafbf6"},
24 | "ayy4": {"#00303b", "#ff7777", "#ffce96", "#f1f2da"},
25 | "nintendo-gameboy-bgb": {"#081820", "#346856", "#88c070", "#e0f8d0"},
26 | "red-brick": {"#eff9d6", "#ba5044", "#7a1c4b", "#1b0326"},
27 | "nostalgia": {"#d0d058", "#a0a840", "#708028", "#405010"},
28 | "spacehaze": {"#f8e3c4", "#cc3495", "#6b1fb1", "#0b0630"},
29 | "moonlight-gb": {"#0f052d", "#203671", "#36868f", "#5fc75d"},
30 | "links-awakening-sgb": {"#5a3921", "#6b8c42", "#7bc67b", "#ffffb5"},
31 | "arq4": {"#ffffff", "#6772a9", "#3a3277", "#000000"},
32 | "blk-aqu4": {"#002b59", "#005f8c", "#00b9be", "#9ff4e5"},
33 | "pokemon-sgb": {"#181010", "#84739c", "#f7b58c", "#ffefff"},
34 | "nintendo-super-gameboy": {"#331e50", "#a63725", "#d68e49", "#f7e7c6"},
35 | "blu-scribbles": {"#051833", "#0a4f66", "#0f998e", "#12cc7f"},
36 | "kankei4": {"#ffffff", "#f42e1f", "#2f256b", "#060608"},
37 | "dark-mode": {"#212121", "#454545", "#787878", "#a8a5a5"},
38 | "pen-n-paper": {"#e4dbba", "#a4929a", "#4f3a54", "#260d1c"},
39 | }
40 |
41 | func rgb(x uint32) (uint8, uint8, uint8) {
42 | r := x & 0xff0000 >> 16
43 | g := x & 0x00ff00 >> 8
44 | b := x & 0x0000ff
45 | return uint8(r), uint8(g), uint8(b)
46 | }
47 |
48 | func ReadString(r io.Reader) (spalette, error) {
49 | scanner := bufio.NewScanner(r)
50 | p := make(spalette)
51 | for scanner.Scan() {
52 | args := strings.Fields(scanner.Text())
53 | l := len(args)
54 | if l < 2 {
55 | continue
56 | }
57 | name := args[0]
58 | p[name] = args[1:]
59 | }
60 | return p, scanner.Err()
61 | }
62 |
63 | func ReadRGB(r io.Reader) (rgbpalette, error) {
64 |
65 | palette, err := ReadString(r)
66 | if err != nil {
67 | return nil, err
68 | }
69 |
70 | rp := make(rgbpalette)
71 | for name, value := range palette {
72 | colors := make([]color.NRGBA, len(value))
73 | i := 0
74 | for _, c := range value {
75 | if len(c) != 7 {
76 | continue // must be #nnnnnn
77 | }
78 | x, err := strconv.ParseUint(c[1:], 16, 32)
79 | if err != nil {
80 | fmt.Fprintf(os.Stderr, "%v\n", err)
81 | continue
82 | }
83 | r, g, b := rgb(uint32(x))
84 | colors[i] = color.NRGBA{R: r, G: g, B: b, A: 0xff}
85 | i++
86 | }
87 | rp[name] = colors
88 | }
89 | return rp, nil
90 | }
91 |
92 | func LoadPalette(filename string) (spalette, error) {
93 | r, err := os.Open(filename)
94 | if err != nil {
95 | return nil, err
96 | }
97 | return ReadString(r)
98 | }
99 |
100 | func LoadRGBPalette(filename string) (rgbpalette, error) {
101 | r, err := os.Open(filename)
102 | if err != nil {
103 | return nil, err
104 | }
105 | return ReadRGB(r)
106 | }
107 |
--------------------------------------------------------------------------------
/cmd/desordres/red-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/desordres/red-14.png
--------------------------------------------------------------------------------
/cmd/dicechart/README.md:
--------------------------------------------------------------------------------
1 | # dicechart
2 |
3 | Make dicecharts in the style of [Monroe Work's "Negro Year Book"](https://nightingaledvs.com/monroe-nathan-work-education-in-the-negro-year-book/), using deck markup
4 |
5 | 
6 |
7 | ## Usage
8 |
9 | ```dicechart [options] [file.csv]```
10 |
11 | where file.csv contains "label",value pairs
12 |
13 | for example:
14 |
15 | ```
16 | "MARYLAND",57
17 | "NORTH CAROLINA",50
18 | "GEORGIA",48
19 | "VIRGINIA",47
20 | "TEXAS",47
21 | "FLORIDA",43
22 | "ALABAMA",27
23 | "SOUTH CAROLINA",26
24 | "LOUISIANA",23
25 | ```
26 | If no file is specified, input is from the standard input. Output is always to standard output.
27 | Typical usage is to use pdfdeck to render the chart in pdf:
28 |
29 | ```
30 | $ dicechart data.csv > chart.xml
31 | $ pdfdeck chart.xml
32 | ```
33 |
34 | or in a pipeline:
35 |
36 | ```
37 | $ dicechart data.csv | pdfdeck -stdout - > chart.pdf
38 | ```
39 |
40 | Command options are:
41 | ```
42 |
43 | -color string
44 | dotcolor (default "black")
45 | -dotsize float
46 | dot size (default 1)
47 | -ds float
48 | dice spacing (default 5)
49 | -dw float
50 | dice width (default 1.5)
51 | -dx float
52 | data left position (default 35)
53 | -height float
54 | canvas height (default 612)
55 | -lx float
56 | label left position (default 10)
57 | -textsize float
58 | canvas width (default 2)
59 | -title string
60 | chart title
61 | -top float
62 | top of the chart (default 85)
63 | -unit
64 | dice unit (default 5)
65 | -valsize float
66 | canvas width (default 2)
67 | -vskip float
68 | vertical skip (default 7)
69 | -width float
70 | canvas width (default 792)
71 | ```
72 |
--------------------------------------------------------------------------------
/cmd/dicechart/big.csv:
--------------------------------------------------------------------------------
1 | "MARYLAND",570
2 | "NORTH CAROLINA",500
3 | "GEORGIA",480
4 | "VIRGINIA",470
5 | "TEXAS",470
6 | "FLORIDA",430
7 | "ALABAMA",270
8 | "SOUTH CAROLINA",260
9 | "LOUISIANA",230
--------------------------------------------------------------------------------
/cmd/dicechart/d.csv:
--------------------------------------------------------------------------------
1 | "MARYLAND",57
2 | "NORTH CAROLINA",50
3 | "GEORGIA"
4 | "VIRGINIA",47
5 | "TEXAS",47
6 | "FLORIDA",43
7 | "ALABAMA"
8 | "SOUTH CAROLINA",26
9 | "LOUISIANA",23
--------------------------------------------------------------------------------
/cmd/dicechart/d.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/dicechart/d.pdf
--------------------------------------------------------------------------------
/cmd/dicechart/data.csv:
--------------------------------------------------------------------------------
1 | "MARYLAND",57
2 | "NORTH CAROLINA",50
3 | "GEORGIA",48
4 | "VIRGINIA",47
5 | "TEXAS",47
6 | "FLORIDA",43
7 | "ALABAMA",27
8 | "SOUTH CAROLINA",26
9 | "LOUISIANA",23
--------------------------------------------------------------------------------
/cmd/dicechart/dicechart:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/dicechart/dicechart
--------------------------------------------------------------------------------
/cmd/dicechart/dicechart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/dicechart/dicechart.png
--------------------------------------------------------------------------------
/cmd/dicechart/f:
--------------------------------------------------------------------------------
1 | type config struct {
2 | cw float64
3 | ch float64
4 | top float64
5 | vskip float64
6 | textsize float64
7 | valuesize float64
8 | labelx float64
9 | datax float64
10 | dicewidth float64
11 | dicespacing float64
12 | dotsize float64
13 | dotcolor string
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/dicechart/f.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/dicechart/f.pdf
--------------------------------------------------------------------------------
/cmd/dicechart/go.mod:
--------------------------------------------------------------------------------
1 | module dicechart
2 |
3 | go 1.19
4 |
--------------------------------------------------------------------------------
/cmd/dict/dict.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 |
8 | "golang.org/x/net/dict"
9 | )
10 |
11 | var (
12 | db = flag.String("d", "wn", "Dictionary database")
13 | dserver = flag.String("s", "dict.org:2628", "Dictionary Server")
14 | )
15 |
16 | func main() {
17 | flag.Parse()
18 | c, err := dict.Dial("tcp", *dserver)
19 | if err != nil {
20 | fmt.Fprintln(os.Stderr, err)
21 | os.Exit(1)
22 | }
23 | defer c.Close()
24 |
25 | // no args, list dictionaries, exit
26 | if len(flag.Args()) == 0 {
27 | dicts, err := c.Dicts()
28 | if err != nil {
29 | fmt.Fprintf(os.Stderr, "%v\n", err)
30 | return
31 | }
32 | for _, dl := range dicts {
33 | fmt.Println(dl.Name, dl.Desc)
34 | }
35 | return
36 | }
37 |
38 | // define each word specified on the command line
39 | for _, word := range flag.Args() {
40 | defs, err := c.Define(*db, word)
41 | if err != nil {
42 | fmt.Fprintf(os.Stderr, "%s: %v\n", word, err)
43 | continue
44 | }
45 | for _, result := range defs {
46 | fmt.Println(string(result.Text))
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/cmd/dict/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/dict
2 |
3 | go 1.23.4
4 |
5 | require golang.org/x/net v0.40.0
6 |
--------------------------------------------------------------------------------
/cmd/dict/go.sum:
--------------------------------------------------------------------------------
1 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
2 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
3 | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
4 | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
5 |
--------------------------------------------------------------------------------
/cmd/distable/README.md:
--------------------------------------------------------------------------------
1 | # distable: make a distance table
2 |
3 | 
4 |
5 | Derived from the design seen in the atlas of Morris county, New Jersey from 1868,
6 | ```distable``` makes a distance table using deck markup.
7 |
8 | ## Data format
9 |
10 | distable takes as input a file with this format:
11 |
12 | Place1
13 | place2:distance
14 | place3:distance
15 |
16 | For example:
17 | ```
18 | Beavertown
19 | Boonton
20 | Beavertown:5.30
21 | Budds Lake
22 | Beavertown:21.30
23 | Boonton:16.00
24 | Chatham
25 | Beavertown:12.20
26 | Boonton:10.40
27 | Budds Lake:18.70
28 | Chester
29 | Beavertown:21.40
30 | Boonton:16.30
31 | Budds Lake:5.40
32 | Chatham:15.10
33 | ```
34 |
35 | ## Command options
36 |
37 | Read from named files or standard input.
38 | ```
39 |
40 | distable [options] file...
41 |
42 | Options:
43 | -left float
44 | left margin (default 1)
45 | -size float
46 | text size (default 1.1)
47 | -dsize float
48 | distance text size (default 0.65*size)
49 | -subtitle string
50 | subtitle (default "distance in miles")
51 | -title string
52 | chart title (default "Distances")
53 | -top float
54 | top (default 90)
55 |
56 | ```
57 |
58 |
59 | ## Running
60 |
61 | $ distable.go morris.d | pdfdeck -stdout - > morris.pdf
62 |
63 |
--------------------------------------------------------------------------------
/cmd/distable/africa.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/distable/africa.pdf
--------------------------------------------------------------------------------
/cmd/distable/distable.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 | "strconv"
10 | "strings"
11 |
12 | "github.com/ajstarks/deck/generate"
13 | )
14 |
15 | type place struct {
16 | name string
17 | distance float64
18 | }
19 |
20 | type distanceTable struct {
21 | name string
22 | dist []place
23 | }
24 |
25 | func main() {
26 | var title, subtitle string
27 | var left, top, size, dsize float64
28 | flag.StringVar(&title, "title", "Distances", "chart title")
29 | flag.StringVar(&subtitle, "subtitle", "", "subtitle")
30 | flag.Float64Var(&left, "left", 1, "left margin")
31 | flag.Float64Var(&top, "top", 90, "top")
32 | flag.Float64Var(&size, "size", 1.1, "text size")
33 | flag.Float64Var(&dsize, "dsize", size*0.65, "distance text size")
34 | flag.Parse()
35 | files := flag.Args()
36 | deck := generate.NewSlides(os.Stdout, 0, 0)
37 | deck.StartDeck()
38 | if len(files) == 0 {
39 | makeslide(deck, "-", os.Stdout, title, subtitle, left, top, size, dsize)
40 | } else {
41 | for _, f := range files {
42 | makeslide(deck, f, os.Stdout, title, subtitle, left, top, size, dsize)
43 | }
44 | }
45 | deck.EndDeck()
46 |
47 | }
48 |
49 | // makeside makes the slide deck
50 | func makeslide(deck *generate.Deck, f string, w io.Writer, title, subtitle string, left, top, size, dsize float64) {
51 | var data []distanceTable
52 | var err error
53 | var r io.Reader
54 | if f == "-" {
55 | r = os.Stdin
56 | } else {
57 | r, err = os.Open(f)
58 | if err != nil {
59 | return
60 | }
61 | }
62 | data, err = readtable(r)
63 | if err != nil {
64 | return
65 | }
66 | deck.StartSlide()
67 | deck.Text(40, 89, title, "sans", 3.5, "")
68 | deck.TextBlock(40, 85, subtitle, "serif", 1.5, 50, "")
69 | distable(deck, data, left, top, size, dsize)
70 | deck.EndSlide()
71 | }
72 |
73 | // readtable reads in distance table data
74 | // name1
75 | // place1:distance
76 | // place2:distance
77 | // ...
78 | func readtable(r io.Reader) ([]distanceTable, error) {
79 | var table []distanceTable
80 | var t distanceTable
81 | var p place
82 | var places []place
83 |
84 | scanner := bufio.NewScanner(r)
85 | n := -1
86 | for scanner.Scan() {
87 | text := scanner.Text()
88 | // single name
89 | if !strings.Contains(text, "\t") {
90 | t.name = text
91 | table = append(table, t)
92 | places = make([]place, 0)
93 | n++
94 | continue
95 | }
96 | // name:distance
97 | if strings.Contains(text, "\t") {
98 | i := strings.Index(text, ":")
99 | if i > 0 && len(text) > 3 {
100 | d, _ := strconv.ParseFloat(strings.TrimSpace(text[i+1:]), 64)
101 | p.name = text[1:i]
102 | p.distance = d
103 | places = append(places, p)
104 | table[n].dist = places
105 | }
106 | }
107 | }
108 | return table, scanner.Err()
109 | }
110 |
111 | // dumptable prints out the distance table to an io.Writer
112 | func dumptable(w io.Writer, table []distanceTable, factor float64) {
113 | for _, t := range table {
114 | fmt.Fprintf(w, "%s\n", t.name)
115 | for _, d := range t.dist {
116 | fmt.Fprintf(w, "\t%s:%.2f\n", d.name, d.distance*factor)
117 | }
118 | }
119 | }
120 |
121 | // distable makes a distance table using deck markup
122 | func distable(deck *generate.Deck, table []distanceTable, left, top, size, dsize float64) {
123 | distleft := left + (size * 10)
124 | vspacing := size * 2.4
125 | hspacing := size * 2.4
126 | x := distleft
127 | y := top
128 | bottom := (top - (float64(len(table)) * vspacing)) - size
129 |
130 | // vertical column headings
131 | for _, t := range table {
132 | deck.TextRotate(x, y-vspacing, t.name, "", "serif", 90, size, "")
133 | deck.Line(x-size-0.2, y-1, x-size-0.2, bottom, 0.05, "gray")
134 | x += hspacing
135 | y -= vspacing
136 | }
137 | // horizontal headings, data
138 | x = left
139 | y = top - vspacing
140 | for _, t := range table {
141 | // place names
142 | deck.Text(x, y, t.name, "serif", size, "")
143 | dx := distleft
144 | dy := y
145 | // distances for each place
146 | for _, d := range t.dist {
147 | td := strconv.FormatFloat(d.distance, 'f', 1, 64)
148 | deck.TextMid(dx, dy, td, "mono", dsize, "")
149 | dx += hspacing
150 | }
151 | deck.Line(distleft-size, y-1, dx+size+0.3, y-1, 0.05, "gray")
152 | y -= vspacing
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/cmd/distable/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/distable
2 |
3 | go 1.17
4 |
5 | require github.com/ajstarks/deck/generate v0.0.0-20220116200525-3f887d0c5850
6 |
7 | require github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 // indirect
8 |
--------------------------------------------------------------------------------
/cmd/distable/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
2 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
3 | github.com/ajstarks/deck/generate v0.0.0-20220116200525-3f887d0c5850 h1:3s9s3Z30llE9vs4EdcrP0trpA6tlHNmktm/K/JPw+5w=
4 | github.com/ajstarks/deck/generate v0.0.0-20220116200525-3f887d0c5850/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
5 |
--------------------------------------------------------------------------------
/cmd/distable/mkall:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | mono="Charter-Italic"
3 | serif="Charter-Regular"
4 | sans="PublicSans-Regular"
5 | go build &&
6 | ./distable -title 'Morris County, New Jersey' -subtitle 'showing the distance in miles and tenths on an airline from place to place' morris.d |
7 | pdfdeck -stdout -serif $serif -mono $mono -sans $sans - > morris.pdf
8 |
9 | ./distable -title 'Distances between African Cities' -dsize 0.6 -subtitle 'travel time between African cities, kilometers' af-km.d |
10 | pdfdeck -stdout -serif $serif -mono $mono -sans $sans - > africa.pdf
11 |
12 | ./distable -title 'Distances between US Cities' -dsize 0.6 -subtitle 'travel time between US cities, kilometers' us-cities-km.d |
13 | pdfdeck -stdout -serif $serif -mono $mono -sans $sans - > us-km.pdf
14 |
15 | ./distable -title 'Distances between US Cities' -dsize 0.6 -subtitle 'travel time between US cities, kilometers' us-cities-miles.d |
16 | pdfdeck -stdout -serif $serif -mono $mono -sans $sans - > us-miles.pdf
17 |
18 |
--------------------------------------------------------------------------------
/cmd/distable/mkt:
--------------------------------------------------------------------------------
1 | BEGIN {
2 | city[1]="Anchorage"
3 | city[2]="Atlanta"
4 | city[3]="Austin"
5 | city[4]="Baltimore"
6 | city[5]="Boston"
7 | city[6]="Chicago"
8 | city[7]="Dallas"
9 | city[8]="Denver"
10 | city[9]="Honolulu"
11 | city[10]="Houston"
12 | city[11]="Indianapolis"
13 | city[12]="Jacksonville"
14 | city[13]="Las Vegas"
15 | city[14]="Los Angeles"
16 | city[15]="Memphis"
17 | city[16]="Miami"
18 | city[17]="New Orleans"
19 | city[18]="New York"
20 | city[19]="Newark"
21 | city[20]="Oakland"
22 | city[21]="Philadelphia"
23 | city[22]="Phoenix"
24 | city[23]="Portland"
25 | city[24]="San Antonio"
26 | city[25]="San Diego"
27 | city[26]="San Francisco"
28 | city[27]="San Jose"
29 | city[28]="Seattle"
30 | city[29]="Tampa"
31 | city[30]="Tucson"
32 | city[31]="Washington DC"
33 |
34 | n=10
35 | }
36 | /^:/ {
37 | printf "\t%s%s\n", city[n++],$1
38 | }
39 |
40 | /^[A-Z]/ {
41 | print $0
42 | n=1
43 | }
--------------------------------------------------------------------------------
/cmd/distable/morris.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/distable/morris.pdf
--------------------------------------------------------------------------------
/cmd/distable/morris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/distable/morris.png
--------------------------------------------------------------------------------
/cmd/distable/original-chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/distable/original-chart.png
--------------------------------------------------------------------------------
/cmd/distable/us-km.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/distable/us-km.pdf
--------------------------------------------------------------------------------
/cmd/distable/us-miles.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/distable/us-miles.pdf
--------------------------------------------------------------------------------
/cmd/dotspiral/README.md:
--------------------------------------------------------------------------------
1 | # dotspiral
2 |
3 | Generate decksh code for a spiral where the size of the points increases
4 |
5 | 
6 |
7 | ```
8 | ./dotspiral -end 1200 -bgcolor black -color white | decksh | pdfdeck -stdout -pagesize 500x500 - > f.pdf
9 | ```
10 |
11 | ### usage
12 |
13 | ```
14 | -bgcolor string
15 | background color (default "white")
16 | -color string
17 | dot color (default "red")
18 | -dincr float
19 | size increment (default 0.5)
20 | -end float
21 | end angle (default 360)
22 | -op float
23 | dot opacity (default 50)
24 | -r float
25 | radius (default 10)
26 | -rincr float
27 | radius increment (default 1)
28 | -size float
29 | dot size (default 0.5)
30 | -start float
31 | start angle (default 180)
32 | -tincr float
33 | angle increment (default 10)
34 | ```
35 |
--------------------------------------------------------------------------------
/cmd/dotspiral/dotspiral.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/dotspiral/dotspiral.png
--------------------------------------------------------------------------------
/cmd/dotspiral/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/dotspiral
2 |
3 | go 1.21.4
4 |
--------------------------------------------------------------------------------
/cmd/dotspiral/main.go:
--------------------------------------------------------------------------------
1 | // dotspiral -- concentric circle designs
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | )
8 |
9 | const (
10 | deckfmt = "deck\ncanvas %d %d\n"
11 | circlefmt = "circle %.2f %.2f %.2f %q\n"
12 | polarfmt = "p=polar %.2f %.2f %.2f %.2f\ncircle p_x p_y %.2f %q %.2f\n"
13 | slidefmt = "slide \"%s\"\n"
14 | )
15 |
16 | type config struct {
17 | start, end, r, rincr, dincr, tincr, dotsize, dotop float64
18 | dotcolor, bgcolor string
19 | }
20 |
21 | // circle draws a circle
22 | func circle(x, y, size float64, color string) {
23 | fmt.Printf(circlefmt, x, y, size, color)
24 | }
25 |
26 | // cpolar places a circle at a polar coordinate
27 | func cpolar(x, y, r, t, size float64, color string, op float64) {
28 | fmt.Printf(polarfmt, x, y, r, t, size, color, op)
29 | }
30 |
31 | // begindeck begins decksh markup
32 | func begindeck(w, h int) {
33 | fmt.Printf(deckfmt, w, h)
34 | }
35 |
36 | // enddeck end a deck
37 | func enddeck() {
38 | fmt.Println("edeck")
39 | }
40 |
41 | // beginslide begins a slide
42 | func beginslide(color string) {
43 | fmt.Printf(slidefmt, color)
44 | }
45 |
46 | // endslide ends a slide
47 | func endslide() {
48 | fmt.Println("eslide")
49 | }
50 |
51 | // dotspiral makes a dot spiral
52 | func dotspiral(cx, cy float64, c config) {
53 | r := c.r
54 | dotsize := c.dotsize
55 | beginslide(c.bgcolor)
56 | for t := c.start; t <= c.end; t += c.tincr {
57 | cpolar(cx, cy, r, t, dotsize, c.dotcolor, c.dotop)
58 | r += c.rincr
59 | dotsize += c.dincr
60 | }
61 | endslide()
62 | }
63 |
64 | // configure set command line options
65 | func configure() config {
66 | var c config
67 | flag.Float64Var(&c.start, "start", 180, "start angle")
68 | flag.Float64Var(&c.end, "end", 360, "end angle")
69 | flag.Float64Var(&c.r, "r", 10.0, "radius")
70 | flag.Float64Var(&c.rincr, "rincr", 1.0, "radius increment")
71 | flag.Float64Var(&c.tincr, "tincr", 10.0, "angle increment")
72 | flag.Float64Var(&c.dincr, "dincr", 0.5, "size increment")
73 | flag.Float64Var(&c.dotsize, "size", 0.5, "dot size")
74 | flag.Float64Var(&c.dotop, "op", 50, "dot opacity")
75 | flag.StringVar(&c.dotcolor, "color", "red", "dot color")
76 | flag.StringVar(&c.bgcolor, "bgcolor", "white", "background color")
77 | flag.Parse()
78 | return c
79 |
80 | }
81 |
82 | func main() {
83 | begindeck(500, 500)
84 | dotspiral(50, 50, configure())
85 | enddeck()
86 | }
87 |
--------------------------------------------------------------------------------
/cmd/dpi/dpi.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | "os"
7 | "strconv"
8 | )
9 |
10 | func dpi(w, h, d float64) float64 {
11 | return math.Sqrt((w*w)+(h*h)) / d
12 | }
13 |
14 | func main() {
15 | if len(os.Args) < 4 {
16 | println("Usage: dpi w h diag [device]")
17 | os.Exit(1)
18 | }
19 | w, _ := strconv.ParseFloat(os.Args[1], 64)
20 | h, _ := strconv.ParseFloat(os.Args[2], 64)
21 | d, _ := strconv.ParseFloat(os.Args[3], 64)
22 | if len(os.Args) == 5 {
23 | fmt.Printf("%s ", os.Args[4])
24 | }
25 | if h > 0 && d > 0 {
26 | fmt.Printf("w=%.0f h=%0.f diag=%.2f aspect=%.2f dpi=%.2f\n", w, h, d, w/h, dpi(w, h, d))
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/dpi/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/dpi
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/fanchart/1900-occupations.csv:
--------------------------------------------------------------------------------
1 | name,percent,color
2 | "OCCUPATIONS OF NEGROES AND WHITES IN GEORGIA",,
3 | "NEGROES",,
4 | "AGRICULTURE, FISHERIES\nAND MINING",62,crimson
5 | "DOMESTIC AND\nPERSONAL SERVICE",28,gold
6 | "MANUFACTURING AND\nINDUSTRIES",5,steelblue
7 | "TRADE AND\nTRANSPORTATION",4.5,tan
8 | "PROFESSIONS",0.5,"rgb(101,67,33)"
9 | WHITES,,
10 | "AGRICULTURE, FISHERIES\nAND MINING",64,crimson
11 | "DOMESTIC AND\nPERSONAL SERVICE",5.5,gold
12 | "MANUFACTURING AND\nMECHANICAL INDUSTRIES",13.5,steelblue
13 | "TRADE AND\nTRANSPORTATION",13,tan
14 | "PROFESSIONS",4,"rgb(101,67,33)"
15 |
--------------------------------------------------------------------------------
/cmd/fanchart/README.md:
--------------------------------------------------------------------------------
1 | # fanchart -- make Du Bois style fan charts
2 |
3 | 
4 |
5 | Usage: fanchart [options] file..
6 |
7 | Options:
8 |
9 | -h float
10 | canvas height (default 612)
11 | -dir string
12 | orientation (tb=Top/Bottom, lr=Left/Right) (default "tb")
13 | -size float
14 | fan/wing size (default 30)
15 | -w float
16 | canvas width (default 792)
17 |
18 | Fanchart generates deck markup which can be rendered as PDF, SVG, or PNG
19 |
20 |
21 | Data is a CSV file with this structure:
22 |
23 | column headers
24 | title,footnotes
25 | top/left section name
26 | item,value,color
27 | ...
28 | bottom/right section name
29 | item,value,color
30 |
31 | for example: given data.csv
32 |
33 | name,percent,color
34 | "Occupations of African American and Whites (USA, 2019)","Source, US Bureau of Labor Statistics",
35 | "African American",,
36 | Management,31.8,crimson
37 | Service,23.8,gold
38 | Sales,22.4,steelblue
39 | Construction,5.7,tan
40 | Production,16.3,"rgb(101,67,33)"
41 | White,,
42 | Management,41.4,crimson
43 | Service,15.9,gold
44 | Sales,21.3,steelblue
45 | Construction,10.1,tan
46 | Production,11.3,"rgb(101,67,33)"
47 |
48 | ```fanchart data.csv | pdfdeck -stdout - > tbchart.pdf``` makes
49 |
50 | 
51 |
52 | ```fanchart data.csv -dir=lr | pdfdeck -stdout - > lrchart.pdf``` makes
53 |
54 | 
55 |
56 |
57 |
--------------------------------------------------------------------------------
/cmd/fanchart/binaries/fanchart-arm-mac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/binaries/fanchart-arm-mac
--------------------------------------------------------------------------------
/cmd/fanchart/binaries/fanchart-intel-mac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/binaries/fanchart-intel-mac
--------------------------------------------------------------------------------
/cmd/fanchart/binaries/fanchart-linux:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/binaries/fanchart-linux
--------------------------------------------------------------------------------
/cmd/fanchart/binaries/fanchart-win32.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/binaries/fanchart-win32.exe
--------------------------------------------------------------------------------
/cmd/fanchart/binaries/fanchart-win64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/binaries/fanchart-win64.exe
--------------------------------------------------------------------------------
/cmd/fanchart/charts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/charts.png
--------------------------------------------------------------------------------
/cmd/fanchart/f.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/f.pdf
--------------------------------------------------------------------------------
/cmd/fanchart/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/fanchart
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/fanchart/lr.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/lr.pdf
--------------------------------------------------------------------------------
/cmd/fanchart/lrchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/lrchart.png
--------------------------------------------------------------------------------
/cmd/fanchart/mkbinaries:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | GOOS=windows GOARCH=amd64 go build -o binaries/fanchart-win64.exe
3 | GOOS=windows GOARCH=386 go build -o binaries/fanchart-win32.exe
4 | GOOS=darwin GOARCH=amd64 go build -o binaries/fanchart-intel-mac
5 | GOOS=darwin GOARCH=arm64 go build -o binaries/fanchart-arm-mac
6 | GOOS=linux GOARCH=amd64 go build -o binaries/fanchart-linux
7 |
8 |
--------------------------------------------------------------------------------
/cmd/fanchart/mkdeck:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | go run main.go -dir=lr *.csv | pdfdeck -stdout - > lr.pdf
3 | go run main.go -dir=tb *.csv | pdfdeck -stdout - > tb.pdf
4 |
--------------------------------------------------------------------------------
/cmd/fanchart/occupations.csv:
--------------------------------------------------------------------------------
1 | name,percent,color
2 | "Occupations of African American and Whites (USA, 2019)","Source, US Bureau of Labor Statistics",
3 | "African American",,
4 | Management,31.8,crimson
5 | Service,23.8,gold
6 | Sales,22.4,steelblue
7 | Construction,5.7,tan
8 | Production,16.3,"rgb(101,67,33)"
9 | White,,
10 | Management,41.4,crimson
11 | Service,15.9,gold
12 | Sales,21.3,steelblue
13 | Construction,10.1,tan
14 | Production,11.3,"rgb(101,67,33)"
15 |
--------------------------------------------------------------------------------
/cmd/fanchart/tb.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/tb.pdf
--------------------------------------------------------------------------------
/cmd/fanchart/tbchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fanchart/tbchart.png
--------------------------------------------------------------------------------
/cmd/feed/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/feed
2 |
3 | go 1.16
4 |
5 | require github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494
6 |
--------------------------------------------------------------------------------
/cmd/feed/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
2 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
3 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494 h1:Z5Dn/2Ml6/8fP1PWxv4ijaK2PwHsX8A+gZiKJVP+XDs=
4 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
5 |
--------------------------------------------------------------------------------
/cmd/fox/2-bit-demichrome-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/2-bit-demichrome-00001.png
--------------------------------------------------------------------------------
/cmd/fox/2-bit-grayscale-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/2-bit-grayscale-00001.png
--------------------------------------------------------------------------------
/cmd/fox/README.md:
--------------------------------------------------------------------------------
1 | # fox -- inspired by "Fox I" by Anni Albers (using deck markup)
2 |
3 | 
4 |
5 | ```fox -color 0:40 -shadow 60 -bgcolor black```
6 |
7 | 
8 |
9 | ```fox -color ajstarks -w 20,80,2.5 -h 20,80,2.5 -shadow 30 -bgcolor linen```
10 |
11 | 
12 |
13 | ```fox -w 5,95,1.25 -h 5,95,1.25 -color funk-it-up -shadow 30 -bgcolor black```
14 |
15 | 
16 |
17 | ```fox -p ajs.pal -color rainbow```
18 |
19 | 
20 |
21 | ```fox -p ajs.pal -color rainbow -d "n"```
22 |
23 |
24 | ## options
25 |
26 | ```
27 | Option Default Description
28 | ..................................................................
29 | -help false show usage
30 | -w 10,95,5 percent begin,end,step for the width
31 | -h 10,95,5 percent begin,end,step for the height
32 | -shadow 40 shadow opacity,xoffset,ysoffset
33 | -d "n s e w nw sw ne se" shape directions
34 | -xshift 0.5 shadow x shift
35 | -yshift -0.5 shadow y shift
36 | -bgcolo white background color
37 | -p "" palette file
38 | -color gray color name, hue range (h1:h2), or palette:
39 |
40 | Palette Name Colors
41 | ..........................................................
42 | kirokaze-gameboy [#332c50 #46878f #94e344 #e2f3e4]
43 | rustic-gb [#2c2137 #764462 #edb4a1 #a96868]
44 | nintendo-gameboy-bgb [#081820 #346856 #88c070 #e0f8d0]
45 | nostalgia [#d0d058 #a0a840 #708028 #405010]
46 | arq4 [#ffffff #6772a9 #3a3277 #000000]
47 | blk-aqu4 [#002b59 #005f8c #00b9be #9ff4e5]
48 | kankei4 [#ffffff #f42e1f #2f256b #060608]
49 | spacehaze [#f8e3c4 #cc3495 #6b1fb1 #0b0630]
50 | nintendo-super-gameboy [#331e50 #a63725 #d68e49 #f7e7c6]
51 | dark-mode [#212121 #454545 #787878 #a8a5a5]
52 | ice-cream-gb [#7c3f58 #eb6b6f #f9a875 #fff6d3]
53 | 2-bit-demichrome [#211e20 #555568 #a0a08b #e9efec]
54 | 2-bit-grayscale [#000000 #676767 #b6b6b6 #ffffff]
55 | hollow [#0f0f1b #565a75 #c6b7be #fafbf6]
56 | links-awakening-sgb [#5a3921 #6b8c42 #7bc67b #ffffb5]
57 | blu-scribbles [#051833 #0a4f66 #0f998e #12cc7f]
58 | mist-gb [#2d1b00 #1e606e #5ab9a8 #c4f0c2]
59 | ayy4 [#00303b #ff7777 #ffce96 #f1f2da]
60 | red-brick [#eff9d6 #ba5044 #7a1c4b #1b0326]
61 | moonlight-gb [#0f052d #203671 #36868f #5fc75d]
62 | pokemon-sgb [#181010 #84739c #f7b58c #ffefff]
63 | pen-n-paper [#e4dbba #a4929a #4f3a54 #260d1c]
64 |
65 | ```
66 | ## palette files
67 |
68 | ```fox``` has a built-in palette which may be overridden by specifying a palette file.
69 |
70 | Custom palette files are of the form of name followed by a list of colors, one name per line.
71 | Colors may be named colors or rgb triples in hex form (#rrggbb).
72 |
73 | name color1 color2 ... colorn
74 |
75 | for example:
76 | ```
77 | ajstarks #aa0000 #aaaaaa #000000 #ffffff
78 | autumn-decay #313638 #574729 #975330 #c57938 #ffad3b #ffe596
79 | rainbow #ff0000 #ffa500 #ffff00 #008000 #0000ff #4b0082 #ee82ee
80 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
81 | polished-gold #000000 #361c1b #754232 #cd894a #e6b983 #fff8bc #ffffff #2d2433 #4f4254 #b092a7
82 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
83 | grayscale #111111 #222222 #333333 #444444 #555555 #666666 #777777 #888888 #999999 #aaaaaa #bbbbbb #cccccc #dddddd #eeeeee
84 | ```
85 |
--------------------------------------------------------------------------------
/cmd/fox/ajs.pal:
--------------------------------------------------------------------------------
1 | ajstarks #aa0000 #aaaaaa #000000 #ffffff
2 | autumn-decay #313638 #574729 #975330 #c57938 #ffad3b #ffe596
3 | rainbow #ff0000 #ffa500 #ffff00 #008000 #0000ff #4b0082 #ee82ee
4 | polished-gold #000000 #361c1b #754232 #cd894a #e6b983 #fff8bc #ffffff #2d2433 #4f4254 #b092a7
5 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
6 | grayscale #111111 #222222 #333333 #444444 #555555 #666666 #777777 #888888 #999999 #aaaaaa #bbbbbb #cccccc #dddddd #eeeeee
7 |
--------------------------------------------------------------------------------
/cmd/fox/ajstarks-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/ajstarks-00001.png
--------------------------------------------------------------------------------
/cmd/fox/arq4-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/arq4-00001.png
--------------------------------------------------------------------------------
/cmd/fox/autumn-decay-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/autumn-decay-00001.png
--------------------------------------------------------------------------------
/cmd/fox/ayy4-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/ayy4-00001.png
--------------------------------------------------------------------------------
/cmd/fox/blk-aqu4-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/blk-aqu4-00001.png
--------------------------------------------------------------------------------
/cmd/fox/blu-scribbles-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/blu-scribbles-00001.png
--------------------------------------------------------------------------------
/cmd/fox/dark-mode-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/dark-mode-00001.png
--------------------------------------------------------------------------------
/cmd/fox/default.pal:
--------------------------------------------------------------------------------
1 | kirokaze-gameboy #332c50 #46878f #94e344 #e2f3e4
2 | ice-cream-gb #7c3f58 #eb6b6f #f9a875 #fff6d3
3 | 2-bit-demichrome #211e20 #555568 #a0a08b #e9efec
4 | mist-gb #2d1b00 #1e606e #5ab9a8 #c4f0c2
5 | rustic-gb #2c2137 #764462 #edb4a1 #a96868
6 | 2-bit-grayscale #000000 #676767 #b6b6b6 #ffffff
7 | hollow #0f0f1b #565a75 #c6b7be #fafbf6
8 | ayy4 #00303b #ff7777 #ffce96 #f1f2da
9 | nintendo-gameboy-bgb #081820 #346856 #88c070 #e0f8d0
10 | red-brick #eff9d6 #ba5044 #7a1c4b #1b0326
11 | nostalgia #d0d058 #a0a840 #708028 #405010
12 | spacehaze #f8e3c4 #cc3495 #6b1fb1 #0b0630
13 | moonlight-gb #0f052d #203671 #36868f #5fc75d
14 | links-awakening-sgb #5a3921 #6b8c42 #7bc67b #ffffb5
15 | arq4 #ffffff #6772a9 #3a3277 #000000
16 | blk-aqu4 #002b59 #005f8c #00b9be #9ff4e5
17 | pokemon-sgb #181010 #84739c #f7b58c #ffefff
18 | nintendo-super-gameboy #331e50 #a63725 #d68e49 #f7e7c6
19 | blu-scribbles #051833 #0a4f66 #0f998e #12cc7f
20 | kankei4 #ffffff #f42e1f #2f256b #060608
21 | dark-mode #212121 #454545 #787878 #a8a5a5
22 | ajstarks #aa0000 #aaaaaa #000000 #ffffff
23 | pen-n-paper #e4dbba #a4929a #4f3a54 #260d1c
24 | autumn-decay #313638 #574729 #975330 #c57938 #ffad3b #ffe596
25 | polished-gold #000000 #361c1b #754232 #cd894a #e6b983 #fff8bc #ffffff #2d2433 #4f4254 #b092a7
26 | funk-it-up #e4ffff #e63410 #a23737 #ffec40 #81913b #26f675 #4c714e #40ebda #394e4e #0a0a0a
--------------------------------------------------------------------------------
/cmd/fox/dense-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/dense-00001.png
--------------------------------------------------------------------------------
/cmd/fox/f-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/f-00001.png
--------------------------------------------------------------------------------
/cmd/fox/fox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/fox.png
--------------------------------------------------------------------------------
/cmd/fox/funk-it-up-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/funk-it-up-00001.png
--------------------------------------------------------------------------------
/cmd/fox/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/fox
2 |
3 | go 1.21.6
4 |
--------------------------------------------------------------------------------
/cmd/fox/hollow-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/hollow-00001.png
--------------------------------------------------------------------------------
/cmd/fox/hot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/hot.png
--------------------------------------------------------------------------------
/cmd/fox/ice-cream-gb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/ice-cream-gb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/kankei4-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/kankei4-00001.png
--------------------------------------------------------------------------------
/cmd/fox/kirokaze-gameboy-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/kirokaze-gameboy-00001.png
--------------------------------------------------------------------------------
/cmd/fox/links-awakening-sgb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/links-awakening-sgb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/mist-gb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/mist-gb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/moonlight-gb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/moonlight-gb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/nintendo-gameboy-bgb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/nintendo-gameboy-bgb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/nintendo-super-gameboy-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/nintendo-super-gameboy-00001.png
--------------------------------------------------------------------------------
/cmd/fox/north-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/north-00001.png
--------------------------------------------------------------------------------
/cmd/fox/nostalgia-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/nostalgia-00001.png
--------------------------------------------------------------------------------
/cmd/fox/pen-n-paper-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/pen-n-paper-00001.png
--------------------------------------------------------------------------------
/cmd/fox/pokemon-sgb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/pokemon-sgb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/polished-gold-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/polished-gold-00001.png
--------------------------------------------------------------------------------
/cmd/fox/rb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/rb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/readpalette.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "image/color"
7 | "io"
8 | "os"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | type spalette map[string][]string
14 | type rgbpalette map[string][]color.NRGBA
15 |
16 | var palette = spalette{
17 | "kirokaze-gameboy": {"#332c50", "#46878f", "#94e344", "#e2f3e4"},
18 | "ice-cream-gb": {"#7c3f58", "#eb6b6f", "#f9a875", "#fff6d3"},
19 | "2-bit-demichrome": {"#211e20", "#555568", "#a0a08b", "#e9efec"},
20 | "mist-gb": {"#2d1b00", "#1e606e", "#5ab9a8", "#c4f0c2"},
21 | "rustic-gb": {"#2c2137", "#764462", "#edb4a1", "#a96868"},
22 | "2-bit-grayscale": {"#000000", "#676767", "#b6b6b6", "#ffffff"},
23 | "hollow": {"#0f0f1b", "#565a75", "#c6b7be", "#fafbf6"},
24 | "ayy4": {"#00303b", "#ff7777", "#ffce96", "#f1f2da"},
25 | "nintendo-gameboy-bgb": {"#081820", "#346856", "#88c070", "#e0f8d0"},
26 | "red-brick": {"#eff9d6", "#ba5044", "#7a1c4b", "#1b0326"},
27 | "nostalgia": {"#d0d058", "#a0a840", "#708028", "#405010"},
28 | "spacehaze": {"#f8e3c4", "#cc3495", "#6b1fb1", "#0b0630"},
29 | "moonlight-gb": {"#0f052d", "#203671", "#36868f", "#5fc75d"},
30 | "links-awakening-sgb": {"#5a3921", "#6b8c42", "#7bc67b", "#ffffb5"},
31 | "arq4": {"#ffffff", "#6772a9", "#3a3277", "#000000"},
32 | "blk-aqu4": {"#002b59", "#005f8c", "#00b9be", "#9ff4e5"},
33 | "pokemon-sgb": {"#181010", "#84739c", "#f7b58c", "#ffefff"},
34 | "nintendo-super-gameboy": {"#331e50", "#a63725", "#d68e49", "#f7e7c6"},
35 | "blu-scribbles": {"#051833", "#0a4f66", "#0f998e", "#12cc7f"},
36 | "kankei4": {"#ffffff", "#f42e1f", "#2f256b", "#060608"},
37 | "dark-mode": {"#212121", "#454545", "#787878", "#a8a5a5"},
38 | "pen-n-paper": {"#e4dbba", "#a4929a", "#4f3a54", "#260d1c"},
39 | }
40 |
41 | func rgb(x uint32) (uint8, uint8, uint8) {
42 | r := x & 0xff0000 >> 16
43 | g := x & 0x00ff00 >> 8
44 | b := x & 0x0000ff
45 | return uint8(r), uint8(g), uint8(b)
46 | }
47 |
48 | func ReadString(r io.Reader) (spalette, error) {
49 | scanner := bufio.NewScanner(r)
50 | p := make(spalette)
51 | for scanner.Scan() {
52 | args := strings.Fields(scanner.Text())
53 | l := len(args)
54 | if l < 2 {
55 | continue
56 | }
57 | name := args[0]
58 | p[name] = args[1:]
59 | }
60 | return p, scanner.Err()
61 | }
62 |
63 | func ReadRGB(r io.Reader) (rgbpalette, error) {
64 |
65 | palette, err := ReadString(r)
66 | if err != nil {
67 | return nil, err
68 | }
69 |
70 | rp := make(rgbpalette)
71 | for name, value := range palette {
72 | colors := make([]color.NRGBA, len(value))
73 | i := 0
74 | for _, c := range value {
75 | if len(c) != 7 {
76 | continue // must be #nnnnnn
77 | }
78 | x, err := strconv.ParseUint(c[1:], 16, 32)
79 | if err != nil {
80 | fmt.Fprintf(os.Stderr, "%v\n", err)
81 | continue
82 | }
83 | r, g, b := rgb(uint32(x))
84 | colors[i] = color.NRGBA{R: r, G: g, B: b, A: 0xff}
85 | i++
86 | }
87 | rp[name] = colors
88 | }
89 | return rp, nil
90 | }
91 |
92 | func LoadPalette(filename string) (spalette, error) {
93 | r, err := os.Open(filename)
94 | if err != nil {
95 | return nil, err
96 | }
97 | return ReadString(r)
98 | }
99 |
100 | func LoadRGBPalette(filename string) (rgbpalette, error) {
101 | r, err := os.Open(filename)
102 | if err != nil {
103 | return nil, err
104 | }
105 | return ReadRGB(r)
106 | }
107 |
--------------------------------------------------------------------------------
/cmd/fox/red-brick-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/red-brick-00001.png
--------------------------------------------------------------------------------
/cmd/fox/rustic-gb-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/rustic-gb-00001.png
--------------------------------------------------------------------------------
/cmd/fox/spacehaze-00001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/fox/spacehaze-00001.png
--------------------------------------------------------------------------------
/cmd/fstat/fstat.go:
--------------------------------------------------------------------------------
1 | // fstat -- file status
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "io/fs"
8 | "os"
9 | "sort"
10 | )
11 |
12 | const (
13 | dirfmt = "%-15s %20s %15d\t%s\n"
14 | timefmt = "2006-01-02 15:04:05"
15 | )
16 |
17 | // flags for sorting and printing
18 | type dirflags struct {
19 | na bool // name ascending
20 | sa bool // size ascending
21 | nd bool // name descending
22 | sd bool // size descending
23 | older bool // date oldest first
24 | newer bool // date newest first
25 | showdot bool // show dotfiles
26 | }
27 |
28 | // status gets file status
29 | func status(filename string) (os.FileInfo, bool, error) {
30 | f, err := os.Stat(filename)
31 | if err != nil {
32 | return nil, false, err
33 | }
34 | return f, f.IsDir(), err
35 | }
36 |
37 | // nameSortAsc sorts directory entries by name (ascending)
38 | func nameSortAsc(files []fs.DirEntry) {
39 | sort.Slice(files, func(i, j int) bool {
40 | return files[i].Name() < files[j].Name()
41 | })
42 | }
43 |
44 | // nameSortDec sorts directory entries by name (ascending)
45 | func nameSortDec(files []fs.DirEntry) {
46 | sort.Slice(files, func(i, j int) bool {
47 | return files[i].Name() > files[j].Name()
48 | })
49 | }
50 |
51 | // sizeSortAsc sort directory entries by file sizes (ascendiing)
52 | func sizeSortAsc(files []fs.DirEntry) {
53 | sort.Slice(files, func(i, j int) bool {
54 | fi, erri := files[i].Info()
55 | fj, errj := files[j].Info()
56 | if erri != nil || errj != nil {
57 | return false
58 | }
59 | ni := fi.Size()
60 | nj := fj.Size()
61 | return ni < nj
62 | })
63 | }
64 |
65 | // sizeSortDec sort directory entries by file sizes (ascendiing)
66 | func sizeSortDec(files []fs.DirEntry) {
67 | sort.Slice(files, func(i, j int) bool {
68 | fi, erri := files[i].Info()
69 | fj, errj := files[j].Info()
70 | if erri != nil || errj != nil {
71 | return false
72 | }
73 | ni := fi.Size()
74 | nj := fj.Size()
75 | return ni > nj
76 | })
77 | }
78 |
79 | // timeSortOlder sorts directory entries by time older to newer
80 | func timeSortOlder(files []fs.DirEntry) {
81 | sort.Slice(files, func(i, j int) bool {
82 | fi, erri := files[i].Info()
83 | fj, errj := files[j].Info()
84 | if erri != nil || errj != nil {
85 | return false
86 | }
87 | ti := fi.ModTime()
88 | tj := fj.ModTime()
89 | return ti.Before(tj)
90 | })
91 | }
92 |
93 | // timeSortNewer sorts directory entries by time newer to older
94 | func timeSortNewer(files []fs.DirEntry) {
95 | sort.Slice(files, func(i, j int) bool {
96 | fi, erri := files[i].Info()
97 | fj, errj := files[j].Info()
98 | if erri != nil || errj != nil {
99 | return false
100 | }
101 | ti := fi.ModTime()
102 | tj := fj.ModTime()
103 | return ti.After(tj)
104 | })
105 | }
106 |
107 | // dirstat shows directory infomation
108 | func dirstat(dirname string, sf dirflags) {
109 | f, err := os.Open(dirname)
110 | if err != nil {
111 | fmt.Fprintf(os.Stderr, "%v\n", err)
112 | return
113 | }
114 | di, err := f.ReadDir(0)
115 | if err != nil {
116 | fmt.Fprintf(os.Stderr, "%v\n", err)
117 | return
118 | }
119 | // set the sort option
120 | if sf.na {
121 | nameSortAsc(di)
122 | }
123 | if sf.nd {
124 | nameSortDec(di)
125 | }
126 | if sf.sa {
127 | sizeSortAsc(di)
128 | }
129 | if sf.sd {
130 | sizeSortDec(di)
131 | }
132 | if sf.older {
133 | timeSortOlder(di)
134 | }
135 | if sf.newer {
136 | timeSortNewer(di)
137 | }
138 |
139 | // print the entries
140 | for _, d := range di {
141 | fi, err := d.Info()
142 | if err != nil {
143 | fmt.Fprintf(os.Stderr, "%v\n", err)
144 | continue
145 | }
146 | if !sf.showdot && fi.Name()[0] == '.' {
147 | continue
148 | }
149 | printstat(fi)
150 | }
151 | }
152 |
153 | // printstat shows file status
154 | func printstat(f os.FileInfo) {
155 | fmt.Printf(dirfmt, f.Mode(), f.ModTime().Format(timefmt), f.Size(), f.Name())
156 | }
157 |
158 | func main() {
159 | var df dirflags
160 | flag.BoolVar(&df.showdot, "a", false, "show dot files")
161 | flag.BoolVar(&df.na, "na", true, "sort by name ascending")
162 | flag.BoolVar(&df.nd, "nd", false, "sort by name descending")
163 | flag.BoolVar(&df.sa, "sa", false, "sort by size ascending")
164 | flag.BoolVar(&df.sd, "sd", false, "sort by size descending")
165 | flag.BoolVar(&df.older, "old", false, "sort by age oldest first")
166 | flag.BoolVar(&df.newer, "new", false, "sort by age newest first")
167 | flag.Parse()
168 | args := flag.Args()
169 |
170 | // if other options are set turn off the default
171 | if df.nd || df.sa || df.sd || df.older || df.newer {
172 | df.na = false
173 | }
174 | // if the default is set turn off other options
175 | if df.na {
176 | df.nd, df.sa, df.sd, df.older, df.newer = false, false, false, false, false
177 | }
178 | // make size and time sorting option are exclusive; either ascending OR descending
179 | if df.sa && df.sd {
180 | fmt.Fprintln(os.Stderr, "pick one: size ascending or descending")
181 | os.Exit(1)
182 | }
183 | if df.older && df.newer {
184 | fmt.Fprintln(os.Stderr, "pick one: old or new")
185 | os.Exit(1)
186 | }
187 | // if no args, show the current directory
188 | if len(args) == 0 {
189 | dirstat(".", df)
190 | return
191 | }
192 | // for every argument, print directory or file info
193 | for i, filename := range args {
194 | s, isdir, err := status(filename)
195 | if err != nil {
196 | fmt.Fprintf(os.Stderr, "%v\n", err)
197 | continue
198 | }
199 | if isdir {
200 | if i > 0 {
201 | fmt.Printf("%s:\n", filename)
202 | }
203 | dirstat(filename, df)
204 | } else {
205 | printstat(s)
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/cmd/fstat/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/fstat
2 |
3 | go 1.19
4 |
--------------------------------------------------------------------------------
/cmd/gitdate/README.md:
--------------------------------------------------------------------------------
1 | # Visualize git history
2 | 
3 |
4 | Reads git log output, formatted in ISO date format and shows the git history between two points in time.
5 |
6 |
7 | $ git log --date=iso | awk '/^Date:/ {print $2,$3,$4}' | gitdate -color red -begin 2018-03-01T00:00:00+00:00 | decksh | pdfdeck -stdout - > git.pdf
8 |
9 | ```
10 | Usage of gitdate:
11 | -begin string
12 | begin time (default "1970-01-01T00:00:00+00:00")
13 | -color string
14 | color (default "black")
15 | -end string
16 | end time (default "2021-12-29T15:48:36-05:00")
17 | -fulldeck
18 | full deck markup (default true)
19 | -left float
20 | left (default 10)
21 | -opacity float
22 | opacity (default 20)
23 | -r float
24 | radius (default 2)
25 | -right float
26 | right (default 90)
27 | -title string
28 | title (default "commit history")
29 | -y float
30 | y point (default 50)
31 | ```
--------------------------------------------------------------------------------
/cmd/gitdate/gitdate.go:
--------------------------------------------------------------------------------
1 | // gitdate: visualize git commit history
2 | // git log --date iso | awk '/^Date:/ {print $2, $3, $4}' | gitdate ... | decksh | ...
3 | package main
4 |
5 | import (
6 | "bufio"
7 | "flag"
8 | "fmt"
9 | "io"
10 | "os"
11 | "time"
12 | )
13 |
14 | const (
15 | gitime = "2006-01-02 15:04:05 -0700"
16 | isotime = "2006-01-02T15:04:05-07:00"
17 | )
18 |
19 | type config struct {
20 | title, btime, etime, color string
21 | left, right, radius, ypoint, opacity float64
22 | fulldeck bool
23 | }
24 |
25 | // seconds returns the number of seconds of the specified time
26 | // since the Unix epoch (Jan 1, 1970, 00:00:00 UTC)
27 | func seconds(s string) int64 {
28 | t, err := time.Parse(gitime, s)
29 | if err != nil {
30 | return -1
31 | }
32 | return t.Unix()
33 | }
34 |
35 | //vmap maps one interval to another
36 | func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
37 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
38 | }
39 |
40 | // process reads a series of line containing timestamps
41 | // in the ("Mon Jan 2 15:04:05 2006 -0700") format
42 | // and maps each time time to a linear scale.
43 | func process(w io.Writer, r io.Reader, c config) error {
44 | b, err := time.Parse(isotime, c.btime)
45 | if err != nil {
46 | return err
47 | }
48 | e, err := time.Parse(isotime, c.etime)
49 | if err != nil {
50 | return err
51 | }
52 | beg := b.Unix()
53 | end := e.Unix()
54 |
55 | labely := c.ypoint + 5
56 | if c.fulldeck {
57 | fmt.Fprintln(w, "deck\nslide")
58 | }
59 | fmt.Fprintf(w, "ctext %q %v %v %v\n", c.btime, c.left, labely, 1)
60 | fmt.Fprintf(w, "ctext %q %v %v %v\n", c.etime, c.right, labely, 1)
61 | fmt.Fprintf(w, "ctext %q %v %v 2\n", c.title, c.left+((c.right-c.left)/2), labely)
62 | fmt.Fprintf(w, "vline %v %v %v 0.1\n", c.left, c.ypoint, 4)
63 | fmt.Fprintf(w, "vline %v %v %v 0.1\n", c.right, c.ypoint, 4)
64 | scanner := bufio.NewScanner(r)
65 | for scanner.Scan() {
66 | t := scanner.Text()
67 | s := seconds(t)
68 | x := vmap(float64(s), float64(beg), float64(end), c.left, c.right)
69 | fmt.Fprintf(w, "circle %v %v %v %q %v\n", x, c.ypoint, c.radius, c.color, c.opacity)
70 | }
71 | if c.fulldeck {
72 | fmt.Fprintln(w, "eslide\nedeck")
73 | }
74 | return scanner.Err()
75 | }
76 |
77 | func main() {
78 | title := flag.String("title", "commit history", "title")
79 | btime := flag.String("begin", "1970-01-01T00:00:00+00:00", "begin time")
80 | etime := flag.String("end", time.Now().Format(isotime), "end time")
81 | ypoint := flag.Float64("y", 50, "y point")
82 | radius := flag.Float64("r", 2, "radius")
83 | color := flag.String("color", "black", "color")
84 | left := flag.Float64("left", 10, "left")
85 | right := flag.Float64("right", 90, "right")
86 | opacity := flag.Float64("opacity", 20, "opacity")
87 | fulldeck := flag.Bool("fulldeck", true, "full deck markup")
88 | flag.Parse()
89 |
90 | c := config{
91 | title: *title,
92 | btime: *btime,
93 | etime: *etime,
94 | ypoint: *ypoint,
95 | radius: *radius,
96 | color: *color,
97 | opacity: *opacity,
98 | left: *left,
99 | right: *right,
100 | fulldeck: *fulldeck,
101 | }
102 |
103 | err := process(os.Stdout, os.Stdin, c)
104 | if err != nil {
105 | fmt.Fprintf(os.Stderr, "%v\n", err)
106 | os.Exit(1)
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/cmd/gitdate/gitdate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/gitdate/gitdate.png
--------------------------------------------------------------------------------
/cmd/gitdate/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/gitdate
2 |
3 | go 1.13
4 |
--------------------------------------------------------------------------------
/cmd/gurl/README.md:
--------------------------------------------------------------------------------
1 | # gurl -- get url (with timeout)
2 |
3 | ```
4 | gurl [-timeout n] url...
5 | ```
6 |
7 | gurl retrieves the specified URLs and prints the response on the standard output.
8 | The retrieval will timeout using the specified interval (seconds).
9 | The default timeout is 30 seconds.
--------------------------------------------------------------------------------
/cmd/gurl/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/gurl
2 |
3 | go 1.14
4 |
--------------------------------------------------------------------------------
/cmd/gurl/gurl.go:
--------------------------------------------------------------------------------
1 | // gurl - get url
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "os"
10 | "time"
11 | )
12 |
13 | // netread derefernces a URL, returning the Reader, with an error
14 | func netread(url string, timeout int) (io.ReadCloser, error) {
15 | client := &http.Client{Timeout: time.Duration(timeout) * time.Second}
16 | resp, err := client.Get(url)
17 | if err != nil {
18 | return nil, err
19 | }
20 | if resp.StatusCode != http.StatusOK {
21 | return nil, fmt.Errorf("unable to get network data for %s (%s)", url, resp.Status)
22 | }
23 | return resp.Body, nil
24 | }
25 |
26 | func main() {
27 | timeout := flag.Int("timeout", 30, "time out (sec)")
28 | flag.Parse()
29 | for _, url := range flag.Args() {
30 | r, err := netread(url, *timeout)
31 | if err != nil {
32 | fmt.Fprintf(os.Stderr, "%v\n", err)
33 | continue
34 | }
35 | io.Copy(os.Stdout, r)
36 | r.Close()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/cmd/hsv2rgb/hsv2rgb.go:
--------------------------------------------------------------------------------
1 | // hsv2rgb -- convert hsv to rgb colors
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "math"
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | func main() {
13 | var hue, saturation, value float64
14 | var name string
15 | flag.Float64Var(&hue, "h", 360, "hue")
16 | flag.Float64Var(&saturation, "s", 50, "saturation")
17 | flag.Float64Var(&value, "v", 50, "value")
18 | flag.StringVar(&name, "n", "", "named color like 'hsv(0,20,50)'")
19 | flag.Parse()
20 |
21 | var r, g, b int
22 | if len(name) > 0 {
23 | v := colorNumbers(name)
24 | if len(v) == 3 {
25 | hue, _ = strconv.ParseFloat(v[0], 64)
26 | saturation, _ = strconv.ParseFloat(v[1], 64)
27 | value, _ = strconv.ParseFloat(v[2], 64)
28 | r, g, b = hsv2rgb(hue, saturation, value)
29 | }
30 | } else {
31 | r, g, b = hsv2rgb(hue, saturation, value)
32 | }
33 | fmt.Printf("hsv(%g, %g, %g) => rgb(%d, %d, %d)\n", hue, saturation, value, r, g, b)
34 | }
35 |
36 | // colorNumbers returns a list of numbers from a comma separated list,
37 | // in the form of xxx(n1, n2, n3), after removing tabs and spaces.
38 | func colorNumbers(s string) []string {
39 | return strings.Split(strings.NewReplacer(" ", "", "\t", "").Replace(s[4:len(s)-1]), ",")
40 | }
41 |
42 | // hsv2rgb converts hsv(h (0-360), s (0-100), v (0-100)) to rgb
43 | // reference: https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
44 | func hsv2rgb(h, s, v float64) (int, int, int) {
45 | s /= 100
46 | v /= 100
47 | if s > 1 || v > 1 {
48 | return 0, 0, 0
49 | }
50 | h = math.Mod(h, 360)
51 | c := v * s
52 | section := h / 60
53 | x := c * (1 - math.Abs(math.Mod(section, 2)-1))
54 |
55 | var r, g, b float64
56 | switch {
57 | case section >= 0 && section <= 1:
58 | r = c
59 | g = x
60 | b = 0
61 | case section > 1 && section <= 2:
62 | r = x
63 | g = c
64 | b = 0
65 | case section > 2 && section <= 3:
66 | r = 0
67 | g = c
68 | b = x
69 | case section > 3 && section <= 4:
70 | r = 0
71 | g = x
72 | b = c
73 | case section > 4 && section <= 5:
74 | r = x
75 | g = 0
76 | b = c
77 | case section > 5 && section <= 6:
78 | r = c
79 | g = 0
80 | b = x
81 | default:
82 | return 0, 0, 0
83 | }
84 | m := v - c
85 | r += m
86 | g += m
87 | b += m
88 | return int(r * 255), int(g * 255), int(b * 255)
89 | }
90 |
--------------------------------------------------------------------------------
/cmd/imgcat/README.md:
--------------------------------------------------------------------------------
1 | # imgcat -- make a multipage image catalog using deck markup
2 |
3 | imgcat generates deck markup for an image catalog, given a list of supported images (PNG, JPEG, GIF). The generated markup is usually fed to pdfdeck for rendering.
4 |
5 | ## usage
6 |
7 | ```
8 | Usage of imgcat:
9 | -a int
10 | all n
11 | -bg string
12 | background color (default "white")
13 | -bottom float
14 | bottom margin (default 5)
15 | -h int
16 | canvas height (default 720)
17 | -l int
18 | landscape n
19 | -left float
20 | left margin (default 5)
21 | -p int
22 | portrait n
23 | -right float
24 | right margin (default 5)
25 | -showname
26 | show name
27 | -top float
28 | top margin (default 5)
29 | -w int
30 | canvas width (default 1280)
31 | ```
32 |
33 | ## examples
34 |
35 | make a 3up (3 image/page) catalog for both portrait and landscape images
36 | ```
37 | $ imgcat -a 3 *.jpg | pdfdeck -stdout - > 3up-catalog.pdf
38 | ```
39 |
40 | make a 2 image/page catalog for portrait images
41 |
42 | ```
43 | $ imgcat -p 2 *.jpg | pdfdeck -stdout - > 2-up.pdf
44 | ```
45 |
46 | make a 3 up catalog of landscape images; include the filenames
47 |
48 | ```
49 | $ imgcat -showname -l 3 *.png | pdfdeck -stdout - > catalog.pdf
50 | ```
--------------------------------------------------------------------------------
/cmd/imgcat/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/imgcat
2 |
3 | go 1.19
4 |
--------------------------------------------------------------------------------
/cmd/imgcat/imgcat.go:
--------------------------------------------------------------------------------
1 | // imgcat: make a multipage image catalog, using deck markup
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "image"
8 | _ "image/gif"
9 | _ "image/jpeg"
10 | _ "image/png"
11 |
12 | "os"
13 | )
14 |
15 | type Picture struct {
16 | x, y float64
17 | width, height int
18 | name string
19 | orientation string
20 | }
21 |
22 | type Pictures []Picture
23 |
24 | type Canvas struct {
25 | width, height int
26 | left, right, top, bottom float64
27 | bgcolor string
28 | showname bool
29 | }
30 |
31 | func truncstring(s string, n int) string {
32 | l := len(s)
33 | if n >= l || n < 5 {
34 | return s
35 | }
36 | return s[0:n] + "..." + s[l-5:]
37 | }
38 |
39 | func marginw(c Canvas, p Picture) (int, int) {
40 | aspect := float64(p.height) / float64(p.width)
41 | pw := float64(c.width) * (100.0 - (c.left + c.right)) / 100.0
42 | if int(pw) > p.width {
43 | return p.width, p.height
44 | }
45 | return int(pw), int(aspect * pw)
46 | }
47 |
48 | func marginh(c Canvas, p Picture) (int, int) {
49 | aspect := float64(p.height) / float64(p.width)
50 | ph := float64(c.height) * (100.0 - (c.top + c.bottom)) / 100.0
51 | if int(ph) > p.height {
52 | return p.width, p.height
53 | }
54 | return int(ph / aspect), int(ph)
55 | }
56 |
57 | func layout(c Canvas, p []Picture) {
58 | switch len(p) {
59 | case 1:
60 | p[0].x, p[0].y = 50, 50
61 | placepics(c, p[0:1], 90)
62 | case 2:
63 | p[0].x, p[1].x = 25, 75
64 | p[0].y, p[1].y = 50, 50
65 | placepics(c, p[0:2], 45)
66 | case 3:
67 | p[0].x, p[1].x, p[2].x = 17, 50, 83
68 | p[0].y, p[1].y, p[2].y = 50, 50, 50
69 | placepics(c, p[0:3], 30)
70 | }
71 | }
72 |
73 | func piclist(filelist []string) []Picture {
74 | var pic Picture
75 | p := []Picture{}
76 | for _, imagefile := range filelist {
77 | imf, err := os.Open(imagefile)
78 | if err != nil {
79 | fmt.Fprintln(os.Stderr, err)
80 | continue
81 | }
82 | im, _, err := image.DecodeConfig(imf)
83 | if err != nil {
84 | imf.Close()
85 | continue
86 | }
87 | pic.width = im.Width
88 | pic.height = im.Height
89 | pic.name = imagefile
90 | p = append(p, pic)
91 | imf.Close()
92 | }
93 | return p
94 | }
95 |
96 | func placepics(c Canvas, pics []Picture, targetpct float64) {
97 | fmt.Printf("\n", c.bgcolor)
98 | for _, p := range pics {
99 | fmt.Printf("\n", p.x, p.y, int(targetpct), p.name)
100 | if c.showname {
101 | fmt.Printf("%s\n", p.x, 5.0, 1.2, truncstring(p.name, 25))
102 | }
103 | }
104 | fmt.Println("")
105 | }
106 |
107 | func ll(c Canvas, pics []Picture, n int) {
108 | lands := []Picture{}
109 | e := []Picture{}
110 |
111 | nl := 0
112 | for _, p := range pics {
113 | if p.width > p.height {
114 | nl++
115 | lands = append(lands, p)
116 | if nl%n == 0 {
117 | layout(c, lands)
118 | lands = e
119 | }
120 | }
121 | }
122 | }
123 |
124 | func lp(c Canvas, pics []Picture, n int) {
125 | ports := []Picture{}
126 | e := []Picture{}
127 |
128 | np := 0
129 | for _, p := range pics {
130 | if p.width < p.height {
131 | np++
132 | ports = append(ports, p)
133 | if np%n == 0 {
134 | layout(c, ports)
135 | ports = e
136 | }
137 | }
138 | }
139 | }
140 |
141 | func single(c Canvas, pics []Picture) {
142 | for i := 0; i < len(pics); i++ {
143 | if pics[i].width >= pics[i].height {
144 | layout(c, pics[i:i+1])
145 | } else {
146 | layout(c, pics[i:i+1])
147 | }
148 | }
149 | }
150 |
151 | func msingle(c Canvas, pics []Picture) {
152 |
153 | var pw, ph int
154 | for _, p := range pics {
155 | p.x, p.y = 50, 50
156 | if p.width > p.height {
157 | pw, ph = marginw(c, p)
158 | } else {
159 | pw, ph = marginh(c, p)
160 | }
161 | fmt.Printf("\n", c.bgcolor)
162 | fmt.Printf("\n", p.x, p.y, pw, ph, p.name)
163 | if c.showname {
164 | fmt.Printf("%s\n", p.name)
165 | }
166 | fmt.Printf("\n")
167 | }
168 | }
169 |
170 | func main() {
171 | cw := flag.Int("w", 1280, "canvas width")
172 | ch := flag.Int("h", 720, "canvas height")
173 | tm := flag.Float64("top", 5, "top margin")
174 | bm := flag.Float64("bottom", 5, "bottom margin")
175 | lm := flag.Float64("left", 5, "left margin")
176 | rm := flag.Float64("right", 5, "right margin")
177 | port := flag.Int("p", 0, "portrait n")
178 | land := flag.Int("l", 0, "landscape n")
179 | all := flag.Int("a", 0, "all n")
180 | showname := flag.Bool("showname", false, "show name")
181 | bgcolor := flag.String("bg", "white", "background color")
182 | flag.Parse()
183 |
184 | pics := piclist(flag.Args())
185 | c := Canvas{width: *cw, height: *ch, left: *lm, right: *rm, top: *tm, bottom: *bm, bgcolor: *bgcolor, showname: *showname}
186 | fmt.Println("")
187 | switch {
188 | case *port > 0:
189 | lp(c, pics, *port)
190 | case *land > 0:
191 | ll(c, pics, *land)
192 | case *all > 0:
193 | ll(c, pics, *all)
194 | lp(c, pics, *all)
195 | default:
196 | msingle(c, pics)
197 | }
198 | fmt.Println("")
199 | }
200 |
--------------------------------------------------------------------------------
/cmd/imgps/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/imgps
2 |
3 | go 1.21.5
4 |
5 | require github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
6 |
--------------------------------------------------------------------------------
/cmd/imgps/go.sum:
--------------------------------------------------------------------------------
1 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
2 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
3 |
--------------------------------------------------------------------------------
/cmd/imgps/main.go:
--------------------------------------------------------------------------------
1 | // imgps -- show GPS coordinates contained in EXIF data
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "io"
7 | "os"
8 |
9 | "github.com/rwcarlsen/goexif/exif"
10 | )
11 |
12 | // for every file on the command line, report GPS coordinates
13 | func main() {
14 | for _, f := range os.Args[1:] {
15 | err := process(f)
16 | if err != nil {
17 | fmt.Fprintf(os.Stderr, "%s: %v\n", f, err)
18 | continue
19 | }
20 | }
21 | }
22 |
23 | // process retrieves GPS coordinates from a file
24 | func process(filename string) error {
25 | f, err := os.Open(filename)
26 | if err != nil {
27 | return err
28 | }
29 | defer f.Close()
30 | x, err := exif.Decode(f)
31 | if err == io.EOF {
32 | return fmt.Errorf("no exif data")
33 | }
34 | if err != nil {
35 | return err
36 | }
37 | lat, long, err := x.LatLong()
38 | if err != nil {
39 | return err
40 | }
41 | fmt.Printf("%s %.8f %.8f\n", filename, lat, long)
42 |
43 | return nil
44 | }
45 |
--------------------------------------------------------------------------------
/cmd/ims/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/ims
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/ims/ims.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "image"
6 | _ "image/gif"
7 | _ "image/jpeg"
8 | _ "image/png"
9 | "os"
10 | )
11 |
12 | func main() {
13 | for _, fname := range os.Args[1:] {
14 | f, ferr := os.Open(fname)
15 | if ferr != nil {
16 | fmt.Fprintf(os.Stderr, "%v\n", ferr)
17 | continue
18 | }
19 | im, _, err := image.DecodeConfig(f)
20 | if err != nil {
21 | fmt.Fprintf(os.Stderr, "%s: %v\n", fname, err)
22 | continue
23 | }
24 | fmt.Printf("%s %d %d\n", fname, im.Width, im.Height)
25 | f.Close()
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/jsonfeed/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/jsonfeed
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/jsonfeed/jsonfeed.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 | )
10 |
11 | type JSONFeed struct {
12 | Version string `json:"version"`
13 | Title string `json:"title"`
14 | HomePageURL string `json:"home_page_url"`
15 | FeedURL string `json:"feed_url"`
16 | Description string `json:"description"`
17 | UserComment string `json:"user_comment"`
18 | NextURL string `json:"next_url"`
19 | Icon string `json:"icon"`
20 | Favicon string `json:"favicon"`
21 | Author author `json:"author"`
22 | Items []item `json:"items"`
23 | Expired bool `json:"expired"`
24 | Hubs []hub `json:"hubs"`
25 | }
26 |
27 | type author struct {
28 | Name string `json:"name"`
29 | URL string `json:"url"`
30 | Avatar string `json:"avatar"`
31 | }
32 |
33 | type hub struct {
34 | Type string `json:"type"`
35 | URL string `json:"url"`
36 | }
37 |
38 | type item struct {
39 | Id string `json:"id"`
40 | ContentText string `json:"content_text"`
41 | ContentHTML string `json:"content_html"`
42 | URL string `json:"url"`
43 | ExternalURL string `json:"external_url"`
44 | Title string `json:"title"`
45 | Summary string `json:"summary"`
46 | Image string `json:"image"`
47 | BannerImage string `json:"banner_image"`
48 | DatePublished string `json:"date_published"`
49 | DateModified string `json:"date_modified"`
50 | Author author `json:"author"`
51 | Tags []string `json:"tags"`
52 | Attachments []attachment `json:"attachments"`
53 | }
54 |
55 | type attachment struct {
56 | URL string `json:"url"`
57 | MIMEType string `json:"mime_type"`
58 | Title string `json:"title"`
59 | ByteSize int64 `json:"size_in_bytes"`
60 | Duration int64 `json:"duration_in_seconds"`
61 | }
62 |
63 | func ParseFeed(r io.Reader) (JSONFeed, error) {
64 | var feed JSONFeed
65 | err := json.NewDecoder(r).Decode(&feed)
66 | return feed, err
67 | }
68 |
69 | func Titles(w io.Writer, feed JSONFeed) {
70 | for _, items := range feed.Items {
71 | fmt.Fprintf(w, "Title: %s\n", items.Title)
72 | }
73 | }
74 |
75 | func Content(w io.Writer, feed JSONFeed) {
76 | for _, items := range feed.Items {
77 | if len(items.ContentText) > 0 {
78 | fmt.Fprintf(w, "Content: %s\n", items.ContentText)
79 | }
80 | if len(items.ContentHTML) > 0 {
81 | fmt.Fprintf(w, "HTML: %s\n", items.ContentHTML)
82 | }
83 | }
84 | }
85 |
86 | func Top(w io.Writer, feed JSONFeed) {
87 | fmt.Fprintln(w, "Top Level")
88 | fmt.Fprintf(w, "\tVersion: %s\n", feed.Version)
89 | fmt.Fprintf(w, "\tTitle: %s\n", feed.Title)
90 | fmt.Fprintf(w, "\tFeed URL: %s\n", feed.FeedURL)
91 | fmt.Fprintf(w, "\tDescription:%s\n", feed.Description)
92 | fmt.Fprintf(w, "\tComment: %s\n", feed.UserComment)
93 | fmt.Fprintf(w, "\tNext URL: %s\n", feed.NextURL)
94 | fmt.Fprintf(w, "\tIcon: %s\n", feed.Icon)
95 | fmt.Fprintf(w, "\tFavicon: %s\n", feed.Favicon)
96 | fmt.Fprintf(w, "\tExpired: %v\n", feed.Expired)
97 | fmt.Fprintf(w, "\tAuthor: %s\n", feed.Author.Name)
98 | fmt.Fprintf(w, "\tAuthor URL: %s\n", feed.Author.URL)
99 | fmt.Fprintf(w, "\tAvatar: %s\n", feed.Author.Avatar)
100 | }
101 |
102 | func Items(w io.Writer, feed JSONFeed) {
103 | for i, items := range feed.Items {
104 | fmt.Fprintf(w, "\nItem [%d]\n", i+1)
105 | fmt.Fprintf(w, "\tID: %s\n", items.Id)
106 | fmt.Fprintf(w, "\tURL: %s\n", items.URL)
107 | fmt.Fprintf(w, "\tExternal URL: %s\n", items.ExternalURL)
108 | fmt.Fprintf(w, "\tTitle: %s\n", items.Title)
109 | fmt.Fprintf(w, "\tContent: %s\n", items.ContentText)
110 | fmt.Fprintf(w, "\tHTML: %s\n", items.ContentHTML)
111 | fmt.Fprintf(w, "\tSummary: %s\n", items.Summary)
112 | fmt.Fprintf(w, "\tImage: %s\n", items.Image)
113 | fmt.Fprintf(w, "\tBanner Image: %s\n", items.BannerImage)
114 | fmt.Fprintf(w, "\tPublished on: %s\n", items.DatePublished)
115 | fmt.Fprintf(w, "\tModified on: %s\n", items.DateModified)
116 | fmt.Fprintf(w, "\tAuthor: %s\n", items.Author.Name)
117 | fmt.Fprintf(w, "\tAuthor URL: %s\n", items.Author.URL)
118 | fmt.Fprintf(w, "\tAvatar: %s\n", items.Author.Avatar)
119 | fmt.Fprintf(w, "\tTags:")
120 | for _, t := range items.Tags {
121 | fmt.Fprintf(w, " `%s`", t)
122 | }
123 | fmt.Fprintln(w)
124 | fmt.Fprintln(w, "\tAttachments:")
125 | for _, att := range items.Attachments {
126 | fmt.Fprintf(w, "\t\tTitle: %s\n", att.Title)
127 | fmt.Fprintf(w, "\t\tURL: %s\n", att.URL)
128 | fmt.Fprintf(w, "\t\tMIME Type: %s\n", att.MIMEType)
129 | if att.ByteSize > 0 {
130 | fmt.Fprintf(w, "\t\tSize: %d bytes\n", att.ByteSize)
131 | }
132 | if att.Duration > 0 {
133 | fmt.Fprintf(w, "\t\tDuration: %d seconds\n", att.Duration)
134 | }
135 | }
136 | }
137 | }
138 |
139 | func DumpFeed(w io.Writer, feed JSONFeed) {
140 | Top(w, feed)
141 | Items(w, feed)
142 | }
143 |
144 | func main() {
145 | var showtitle = flag.Bool("title", false, "show titles")
146 | var showcontent = flag.Bool("content", false, "show content")
147 | var showtop = flag.Bool("top", false, "show top level")
148 | var showall = flag.Bool("all", false, "show all attributes")
149 | var showitem = flag.Bool("item", false, "show items")
150 | flag.Parse()
151 | f, err := ParseFeed(os.Stdin)
152 | if err != nil {
153 | fmt.Fprintf(os.Stderr, "%v\n", err)
154 | return
155 | }
156 | if *showall {
157 | DumpFeed(os.Stdout, f)
158 | }
159 | if *showtop {
160 | Top(os.Stdout, f)
161 | }
162 | if *showtitle {
163 | Titles(os.Stdout, f)
164 | }
165 | if *showcontent {
166 | Content(os.Stdout, f)
167 | }
168 | if *showitem {
169 | Items(os.Stdout, f)
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/cmd/latlongdeck/README.md:
--------------------------------------------------------------------------------
1 | # latlongdeck -- convert lat/long pairs to deck/decksh markup
2 |
3 | mapcoord reads space separated lat/long pairs from stdin or specified files, and emits deck/decksh markup denoting the path to stdout.
4 | Typically other programs will generate the input, for example ```fitscsvcoord``` reads CSV files with FITS data:
5 |
6 | ```
7 | $ fitscsvcoord path.csv | latlongdeck [options] > path.dsh
8 | ```
9 |
10 | ## Options
11 |
12 | ```
13 | $ latlomgdeck --help
14 | Usage of mapcoord:
15 | -bbox string
16 | bounding box color ("" no box)
17 | -bgcolor string
18 | background color
19 | -color string
20 | line color (default "black")
21 | -fulldeck
22 | make a full deck (default true)
23 | -info
24 | only report bounding box, and center
25 | -latmax float
26 | latitude x maxmum (default 90)
27 | -latmin float
28 | latitude x minimum (default -90)
29 | -linewidth float
30 | line width (default 0.1)
31 | -longmax float
32 | longitude y maximum (default 180)
33 | -longmin float
34 | longitude y minimum (default -180)
35 | -shape string
36 | polygon, polyline (default "polyline")
37 | -style string
38 | deck, decksh, plain (default "deck")
39 | -xmax float
40 | canvas x maxmum (default 95)
41 | -xmin float
42 | canvas x minimum (default 5)
43 | -ymax float
44 | canvas y maximum (default 95)
45 | -ymin float
46 | canvas y minimum (default 5)
47 | ```
48 |
49 |
--------------------------------------------------------------------------------
/cmd/latlongdeck/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/latlongdeck
2 |
3 | go 1.21.5
4 |
5 | require github.com/ajstarks/kml v0.0.0-20231216032752-dd72e94de437
6 |
--------------------------------------------------------------------------------
/cmd/latlongdeck/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/kml v0.0.0-20231216032752-dd72e94de437 h1:U2Wj3bR9XXLgS6rOkD5TOEmQTsUjulc57RDrv6CVwNw=
2 | github.com/ajstarks/kml v0.0.0-20231216032752-dd72e94de437/go.mod h1:cxil5A25+/6kc8Y3uk573iVqB7x0KTcF+G3EclM6U4g=
3 |
--------------------------------------------------------------------------------
/cmd/latlongdeck/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 | "strconv"
10 | "strings"
11 |
12 | "github.com/ajstarks/kml"
13 | )
14 |
15 | // vmap maps one interval to another
16 | func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
17 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
18 | }
19 |
20 | // readData reads lat/long pairs (separated by white space) from a file, mapping to deck coordinates
21 | func readData(r io.Reader, g kml.Geometry) ([]float64, []float64, error) {
22 | x := []float64{}
23 | y := []float64{}
24 | s := bufio.NewScanner(r)
25 | for s.Scan() {
26 | t := s.Text()
27 | f := strings.Fields(t)
28 | if len(f) != 2 {
29 | continue
30 | }
31 | xp, err := strconv.ParseFloat(f[1], 64) // latitude
32 | if err != nil {
33 | continue
34 | }
35 | yp, err := strconv.ParseFloat(f[0], 64) // longitude
36 | if err != nil {
37 | continue
38 | }
39 | x = append(x, vmap(xp, g.Longmin, g.Longmax, g.Xmin, g.Xmax))
40 | y = append(y, vmap(yp, g.Latmin, g.Latmax, g.Ymin, g.Ymax))
41 | }
42 | return x, y, s.Err()
43 | }
44 |
45 | // readStats reads lat/long pairs, report on the computed bounding box and center
46 | func readStats(r io.Reader) {
47 | maxxval := -100000000.0
48 | minxval := 100000000.0
49 | maxyval := -100000000.0
50 | minyval := 100000000.0
51 |
52 | s := bufio.NewScanner(r)
53 | for s.Scan() {
54 | t := s.Text()
55 | f := strings.Fields(t)
56 | if len(f) != 2 {
57 | continue
58 | }
59 | xp, err := strconv.ParseFloat(f[1], 64) // latitude
60 | if err != nil {
61 | continue
62 | }
63 | yp, err := strconv.ParseFloat(f[0], 64) // longitude
64 | if err != nil {
65 | continue
66 | }
67 | if xp > maxxval {
68 | maxxval = xp
69 | }
70 | if xp < minxval {
71 | minxval = xp
72 | }
73 |
74 | if yp > maxyval {
75 | maxyval = yp
76 | }
77 | if yp < minyval {
78 | minyval = yp
79 | }
80 | }
81 | centerLong := minxval + (maxxval-minxval)/2
82 | centerLat := minyval + (maxyval-minyval)/2
83 | fmt.Fprintf(os.Stdout, "center=%v,%v -longmin=%v -longmax=%v -latmin=%v -latmax=%v\n", centerLat, centerLong, minxval, maxxval, minyval, maxyval)
84 | }
85 |
86 | // process processing input and options, making markup
87 | func process(filename string, info bool, shape, style, color, bbox string, linewidth float64, mapgeo kml.Geometry) {
88 |
89 | // read from stdin by default, open a file if specified
90 | r := os.Stdin
91 | if len(filename) > 0 {
92 | var rerr error
93 | r, rerr = os.Open(filename)
94 | if rerr != nil {
95 | fmt.Fprintf(os.Stderr, "%v\n", rerr)
96 | return
97 | }
98 | }
99 |
100 | // just show info, then return, if specified
101 | if info {
102 | readStats(r)
103 | return
104 | }
105 |
106 | // read coordinates
107 | x, y, err := readData(r, mapgeo)
108 | if err != nil {
109 | fmt.Fprintf(os.Stderr, "%v\n", err)
110 | return
111 | }
112 | // make a bounding box, if specified
113 | if len(bbox) > 0 {
114 | kml.BoundingBox(mapgeo, bbox, style)
115 | }
116 | // make the drawing
117 | kml.Deckshape(shape, style, x, y, linewidth, color, mapgeo)
118 | r.Close()
119 | }
120 |
121 | func main() {
122 | var mapgeo kml.Geometry
123 | var fulldeck, info bool
124 | var linewidth float64
125 | var color, bbox, shape, bgcolor, style string
126 |
127 | // options
128 | flag.Float64Var(&mapgeo.Xmin, "xmin", 5, "canvas x minimum")
129 | flag.Float64Var(&mapgeo.Xmax, "xmax", 95, "canvas x maxmum")
130 | flag.Float64Var(&mapgeo.Ymin, "ymin", 5, "canvas y minimum")
131 | flag.Float64Var(&mapgeo.Ymax, "ymax", 95, "canvas y maximum")
132 | flag.Float64Var(&mapgeo.Latmin, "latmin", -90, "latitude x minimum")
133 | flag.Float64Var(&mapgeo.Latmax, "latmax", 90, "latitude x maxmum")
134 | flag.Float64Var(&mapgeo.Longmin, "longmin", -180, "longitude y minimum")
135 | flag.Float64Var(&mapgeo.Longmax, "longmax", 180, "longitude y maximum")
136 | flag.Float64Var(&linewidth, "linewidth", 0.1, "line width")
137 | flag.StringVar(&color, "color", "black", "line color")
138 | flag.StringVar(&bbox, "bbox", "", "bounding box color (\"\" no box)")
139 | flag.StringVar(&shape, "shape", "polyline", "polygon, polyline")
140 | flag.StringVar(&style, "style", "deck", "deck, decksh, plain")
141 | flag.StringVar(&bgcolor, "bgcolor", "", "background color")
142 | flag.BoolVar(&fulldeck, "fulldeck", true, "make a full deck")
143 | flag.BoolVar(&info, "info", false, "only report bounding box, and center")
144 | flag.Parse()
145 |
146 | // don't do any generation if info only
147 | if info {
148 | fulldeck = false
149 | }
150 | // add deck/slide markup, if specified
151 | if fulldeck {
152 | kml.Deckshbegin(bgcolor)
153 | }
154 | // for every file (or stdin if no files are specified), make markup
155 | if len(flag.Args()) == 0 {
156 | process("", info, shape, style, color, bbox, linewidth, mapgeo)
157 | } else {
158 | for _, filename := range flag.Args() {
159 | process(filename, info, shape, style, color, bbox, linewidth, mapgeo)
160 | }
161 | }
162 | // end the deck, if specified
163 | if fulldeck {
164 | kml.Deckshend()
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/cmd/mapcoord/README.md:
--------------------------------------------------------------------------------
1 | # mapcoord -- convert lat/long pairs to deck/decksh markup
2 |
3 | mapcoord reads space separated lat/long pairs from stdin or specified files, and emits deck/decksh markup denoting the path to stdout.
4 | Typically other programs will generate the input, for example ```fitscsvcoord``` reads CSV files with FITS data:
5 |
6 | ```
7 | $ fitscsvcoord path.csv | mapcoord [options] > path.dsh
8 | ```
9 |
10 | ## Options
11 |
12 | ```
13 | $ mapcoord --help
14 | Usage of mapcoord:
15 | -bbox string
16 | bounding box color ("" no box)
17 | -bgcolor string
18 | background color
19 | -color string
20 | line color (default "black")
21 | -fulldeck
22 | make a full deck (default true)
23 | -info
24 | only report bounding box, and center
25 | -latmax float
26 | latitude x maxmum (default 90)
27 | -latmin float
28 | latitude x minimum (default -90)
29 | -linewidth float
30 | line width (default 0.1)
31 | -longmax float
32 | longitude y maximum (default 180)
33 | -longmin float
34 | longitude y minimum (default -180)
35 | -shape string
36 | polygon, polyline (default "polyline")
37 | -style string
38 | deck, decksh, plain (default "deck")
39 | -xmax float
40 | canvas x maxmum (default 95)
41 | -xmin float
42 | canvas x minimum (default 5)
43 | -ymax float
44 | canvas y maximum (default 95)
45 | -ymin float
46 | canvas y minimum (default 5)
47 | ```
48 |
49 |
--------------------------------------------------------------------------------
/cmd/mapcoord/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/mapcoord
2 |
3 | go 1.21.5
4 |
5 | require github.com/ajstarks/kml v0.0.0-20231216032752-dd72e94de437
6 |
--------------------------------------------------------------------------------
/cmd/mapcoord/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/kml v0.0.0-20231216032752-dd72e94de437 h1:U2Wj3bR9XXLgS6rOkD5TOEmQTsUjulc57RDrv6CVwNw=
2 | github.com/ajstarks/kml v0.0.0-20231216032752-dd72e94de437/go.mod h1:cxil5A25+/6kc8Y3uk573iVqB7x0KTcF+G3EclM6U4g=
3 |
--------------------------------------------------------------------------------
/cmd/mapcoord/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 | "strconv"
10 | "strings"
11 |
12 | "github.com/ajstarks/kml"
13 | )
14 |
15 | // vmap maps one interval to another
16 | func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
17 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
18 | }
19 |
20 | // readData reads lat/long pairs (separated by white space) from a file, mapping to deck coordinates
21 | func readData(r io.Reader, g kml.Geometry) ([]float64, []float64, error) {
22 | x := []float64{}
23 | y := []float64{}
24 | s := bufio.NewScanner(r)
25 | for s.Scan() {
26 | t := s.Text()
27 | f := strings.Fields(t)
28 | if len(f) != 2 {
29 | continue
30 | }
31 | xp, err := strconv.ParseFloat(f[1], 64) // latitude
32 | if err != nil {
33 | continue
34 | }
35 | yp, err := strconv.ParseFloat(f[0], 64) // longitude
36 | if err != nil {
37 | continue
38 | }
39 | x = append(x, vmap(xp, g.Longmin, g.Longmax, g.Xmin, g.Xmax))
40 | y = append(y, vmap(yp, g.Latmin, g.Latmax, g.Ymin, g.Ymax))
41 | }
42 | return x, y, s.Err()
43 | }
44 |
45 | // readStats reads lat/long pairs, report on the computed bounding box and center
46 | func readStats(r io.Reader) {
47 | maxxval := -100000000.0
48 | minxval := 100000000.0
49 | maxyval := -100000000.0
50 | minyval := 100000000.0
51 |
52 | s := bufio.NewScanner(r)
53 | for s.Scan() {
54 | t := s.Text()
55 | f := strings.Fields(t)
56 | if len(f) != 2 {
57 | continue
58 | }
59 | xp, err := strconv.ParseFloat(f[1], 64) // latitude
60 | if err != nil {
61 | continue
62 | }
63 | yp, err := strconv.ParseFloat(f[0], 64) // longitude
64 | if err != nil {
65 | continue
66 | }
67 | if xp > maxxval {
68 | maxxval = xp
69 | }
70 | if xp < minxval {
71 | minxval = xp
72 | }
73 |
74 | if yp > maxyval {
75 | maxyval = yp
76 | }
77 | if yp < minyval {
78 | minyval = yp
79 | }
80 | }
81 | centerLong := minxval + (maxxval-minxval)/2
82 | centerLat := minyval + (maxyval-minyval)/2
83 | fmt.Fprintf(os.Stdout, "center=%v,%v -longmin=%v -longmax=%v -latmin=%v -latmax=%v\n", centerLat, centerLong, minxval, maxxval, minyval, maxyval)
84 | }
85 |
86 | // process processing input and options, making markup
87 | func process(filename string, info bool, shape, style, color, bbox string, linewidth float64, mapgeo kml.Geometry) {
88 |
89 | // read from stdin by default, open a file if specified
90 | r := os.Stdin
91 | if len(filename) > 0 {
92 | var rerr error
93 | r, rerr = os.Open(filename)
94 | if rerr != nil {
95 | fmt.Fprintf(os.Stderr, "%v\n", rerr)
96 | return
97 | }
98 | }
99 |
100 | // just show info, then return, if specified
101 | if info {
102 | readStats(r)
103 | return
104 | }
105 |
106 | // read coordinates
107 | x, y, err := readData(r, mapgeo)
108 | if err != nil {
109 | fmt.Fprintf(os.Stderr, "%v\n", err)
110 | return
111 | }
112 | // make a bounding box, if specified
113 | if len(bbox) > 0 {
114 | kml.BoundingBox(mapgeo, bbox, style)
115 | }
116 | // make the drawing
117 | kml.Deckshape(shape, style, x, y, linewidth, color, mapgeo)
118 | r.Close()
119 | }
120 |
121 | func main() {
122 | var mapgeo kml.Geometry
123 | var fulldeck, info bool
124 | var linewidth float64
125 | var color, bbox, shape, bgcolor, style string
126 |
127 | // options
128 | flag.Float64Var(&mapgeo.Xmin, "xmin", 5, "canvas x minimum")
129 | flag.Float64Var(&mapgeo.Xmax, "xmax", 95, "canvas x maxmum")
130 | flag.Float64Var(&mapgeo.Ymin, "ymin", 5, "canvas y minimum")
131 | flag.Float64Var(&mapgeo.Ymax, "ymax", 95, "canvas y maximum")
132 | flag.Float64Var(&mapgeo.Latmin, "latmin", -90, "latitude x minimum")
133 | flag.Float64Var(&mapgeo.Latmax, "latmax", 90, "latitude x maxmum")
134 | flag.Float64Var(&mapgeo.Longmin, "longmin", -180, "longitude y minimum")
135 | flag.Float64Var(&mapgeo.Longmax, "longmax", 180, "longitude y maximum")
136 | flag.Float64Var(&linewidth, "linewidth", 0.1, "line width")
137 | flag.StringVar(&color, "color", "black", "line color")
138 | flag.StringVar(&bbox, "bbox", "", "bounding box color (\"\" no box)")
139 | flag.StringVar(&shape, "shape", "polyline", "polygon, polyline")
140 | flag.StringVar(&style, "style", "deck", "deck, decksh, plain")
141 | flag.StringVar(&bgcolor, "bgcolor", "", "background color")
142 | flag.BoolVar(&fulldeck, "fulldeck", true, "make a full deck")
143 | flag.BoolVar(&info, "info", false, "only report bounding box, and center")
144 | flag.Parse()
145 |
146 | // don't do any generation if info only
147 | if info {
148 | fulldeck = false
149 | }
150 | // add deck/slide markup, if specified
151 | if fulldeck {
152 | kml.Deckshbegin(bgcolor)
153 | }
154 | // for every file (or stdin if no files are specified), make markup
155 | if len(flag.Args()) == 0 {
156 | process("", info, shape, style, color, bbox, linewidth, mapgeo)
157 | } else {
158 | for _, filename := range flag.Args() {
159 | process(filename, info, shape, style, color, bbox, linewidth, mapgeo)
160 | }
161 | }
162 | // end the deck, if specified
163 | if fulldeck {
164 | kml.Deckshend()
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/cmd/mdtopdf/fpdf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/mdtopdf/fpdf.png
--------------------------------------------------------------------------------
/cmd/mdtopdf/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/mdtopdf
2 |
3 | go 1.20
4 |
5 | require github.com/mandolyte/mdtopdf v1.3.2
6 |
7 | require (
8 | github.com/jung-kurt/gofpdf v1.16.2 // indirect
9 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
10 | )
11 |
--------------------------------------------------------------------------------
/cmd/mdtopdf/go.sum:
--------------------------------------------------------------------------------
1 | github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
4 | github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
5 | github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
6 | github.com/mandolyte/mdtopdf v1.3.2 h1:255HiBnsxXdFPPrh9cUbV8VxHsphGqPKRq8i7qAr8/s=
7 | github.com/mandolyte/mdtopdf v1.3.2/go.mod h1:c28Ldk+tVc/y7QQcEcILStS/OFlerdXGGdBUzJQBgEo=
8 | github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
9 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
12 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
13 | github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
14 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
15 | golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
16 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
17 |
--------------------------------------------------------------------------------
/cmd/mdtopdf/hiking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/mdtopdf/hiking.png
--------------------------------------------------------------------------------
/cmd/mdtopdf/main.go:
--------------------------------------------------------------------------------
1 | // mdtopdf -- convert markdown to PDF
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 |
10 | "github.com/mandolyte/mdtopdf"
11 | )
12 |
13 | func die(err error) {
14 | fmt.Fprintf(os.Stderr, "%v\n", err)
15 | os.Exit(1)
16 | }
17 |
18 | func main() {
19 | var input, output string
20 | flag.StringVar(&input, "i", "", "input markdown file (default is standard input)")
21 | flag.StringVar(&output, "o", "", "output PDF file (required)")
22 | flag.Parse()
23 |
24 | if output == "" {
25 | flag.PrintDefaults()
26 | os.Exit(1)
27 | }
28 | var content []byte
29 | var err error
30 |
31 | if input == "" {
32 | content, err = io.ReadAll(os.Stdin)
33 | if err != nil {
34 | die(err)
35 | }
36 | } else {
37 | content, err = os.ReadFile(input)
38 | if err != nil {
39 | die(err)
40 | }
41 | }
42 | pf := mdtopdf.NewPdfRenderer("", "", output, "")
43 | err = pf.Process(content)
44 | if err != nil {
45 | die(err)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/cmd/mdtopdf/test.md:
--------------------------------------------------------------------------------
1 | # Heading One
2 | This is a paragraph with some *emphasized text* - OK?
3 |
4 | Here is an unordered list:
5 |
6 | - this is an unordered list
7 | - subitem 1.a
8 | - subitem 1.b
9 | - this is second item in unordered list
10 | - subitem 2.a
11 | - subitem 2.a.i
12 | - subitem 2.a.ii
13 | - subitem 2.b
14 |
15 | Here is an ordered/numbered list:
16 |
17 | 1. This is a numbered list
18 | 1. one
19 | 1. two
20 | 1. This is second item in numbered list
21 | 1. one
22 | 1. two
23 | 1. A
24 | 1. B
25 | 1. x
26 | 1. y
27 | 1. z
28 | 1. last numbered item
29 |
30 | ## Heading Two
31 | This is a paragraph with some **emphasized text** - OK?
32 |
33 | ### Heading Three
34 | This is a paragraph with some ___emphasized text___ - OK?
35 |
36 | #### Heading Four
37 | *A wee bit of text*
38 |
39 | #### Heading Five
40 | Just some normal text... nothing fancy
41 |
42 | This is a simple table:
43 |
44 | | Header | Another header |
45 | |---------|----------------|
46 | | field 1 | something |
47 | | field 2 | something else |
48 | | field 3 | something |
49 | | field 4 | something else |
50 | | field 5 | something |
51 | | field 6 | something else |
52 |
53 | ###### Heading Six
54 | This is really deeply nested.
55 |
56 | Here is some code:
57 | ```
58 | 10 REM Old huh?
59 | 20 REM Yup, way old!
60 | ```
61 | The file name is `oldcode.bas`.
62 |
63 | These three lines should
64 | only be one paragaph, but with BF retaining
65 | the newlines, the newlines must be replaced with spaces.
66 |
67 | The below is a blockquote with inner codeblocks.
68 |
69 | > Example:
70 | >
71 | > sub status {
72 | > print "working";
73 | > }
74 | >
75 | > Or:
76 | >
77 | > sub status {
78 | > return "working";
79 | > }
80 |
81 | Here is the gofpdf logo (inline): 
82 |
83 | Here is a Gopher:
84 |
85 | 
86 |
87 | *The Go gopher was designed by Renee French. The Gopher character design is licensed under the Creative Commons 3.0 Attributions license. Read http://blog.golang.org/gopher for more details.*
88 |
89 |
90 | __This is the last line of the document.__
91 |
--------------------------------------------------------------------------------
/cmd/mdtopdf/test.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/mdtopdf/test.pdf
--------------------------------------------------------------------------------
/cmd/mfunc/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/mfunc
2 |
3 | go 1.17
4 |
--------------------------------------------------------------------------------
/cmd/mfunc/mfunc.go:
--------------------------------------------------------------------------------
1 | // mfunc: math functions
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "math"
8 | )
9 |
10 | type tfunc struct {
11 | label string
12 | function func(x float64) float64
13 | }
14 |
15 | func main() {
16 | funcname := flag.String("f", "sine", "function name")
17 | min := flag.Float64("min", 0.0, "minimum")
18 | max := flag.Float64("max", math.Pi*2, "maximum")
19 | incr := flag.Float64("incr", 0.1, "increment")
20 | xfmt := flag.String("xfmt", "%.2f", "x format")
21 | yfmt := flag.String("yfmt", "%.4f", "y format")
22 | flag.Parse()
23 | var f tfunc
24 | switch *funcname {
25 | case "sine", "sin":
26 | f = tfunc{"y=sin(x)", math.Sin}
27 | case "cosine", "cos":
28 | f = tfunc{"y=cos(x)", math.Cos}
29 | case "sqrt":
30 | f = tfunc{"y=sqrt(x)", math.Sqrt}
31 | case "log":
32 | f = tfunc{"y=log(x)", math.Log}
33 | case "log10":
34 | f = tfunc{"y=log10(x)", math.Log10}
35 | case "log2":
36 | f = tfunc{"y=log2(x)", math.Log2}
37 | case "tan":
38 | f = tfunc{"y=tan(x)", math.Tan}
39 | case "exp":
40 | f = tfunc{"y=exp(x)", math.Tan}
41 | case "sincos":
42 | f = tfunc{"y=sin(x) * cos(x)",
43 | func(x float64) float64 { return math.Sin(x) * math.Cos(x) }}
44 | default:
45 | f = tfunc{"y=1", func(float64) float64 { return 1 }}
46 | }
47 | fmt.Printf("# %s\n", f.label)
48 | format := *xfmt + "\t" + *yfmt + "\n"
49 | for x := *min; x <= *max; x += *incr {
50 | fmt.Printf(format, x, f.function(x))
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/cmd/mkpoly/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/mkpoly
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/mkpoly/mkpoly.go:
--------------------------------------------------------------------------------
1 | // mkpoly - generate decksh polygons from x,y pairs
2 | package main
3 |
4 | import (
5 | "bufio"
6 | "flag"
7 | "fmt"
8 | "io"
9 | "math"
10 | "os"
11 | "strconv"
12 | "strings"
13 | )
14 |
15 | const (
16 | smallest = -math.MaxFloat64
17 | largest = math.MaxFloat64
18 | )
19 |
20 | type params struct {
21 | left, right, bottom, top, minx, maxx, miny, maxy float64
22 | label, color string
23 | }
24 |
25 | type pfunc func(p params, s string)
26 |
27 | func main() {
28 | var p params
29 | var outstyle string
30 |
31 | flag.Float64Var(&p.left, "left", 10, "left")
32 | flag.Float64Var(&p.right, "right", 90, "right")
33 | flag.Float64Var(&p.bottom, "bottom", 10, "bottom")
34 | flag.Float64Var(&p.top, "top", 90, "top")
35 | flag.Float64Var(&p.minx, "minx", smallest, "top")
36 | flag.Float64Var(&p.maxx, "maxx", largest, "minx")
37 | flag.Float64Var(&p.miny, "miny", smallest, "maxx")
38 | flag.Float64Var(&p.maxy, "maxy", largest, "miny")
39 | flag.StringVar(&p.color, "color", "gray", "color")
40 | flag.StringVar(&p.label, "label", "", "label")
41 | flag.StringVar(&outstyle, "style", "deck", "output style (deck or decksh")
42 | flag.Parse()
43 |
44 | process := deck
45 | if outstyle == "decksh" {
46 | process = decksh
47 | }
48 |
49 | for _, f := range flag.Args() {
50 | if err := process(p, f); err != nil {
51 | fmt.Fprintf(os.Stderr, "%v\n", err)
52 | continue
53 | }
54 | }
55 | }
56 |
57 | // readata reads x, y pairs, checking for errors
58 | func readata(r io.Reader) ([]float64, []float64, error) {
59 | var x, y []float64
60 | var xp, yp float64
61 | var err error
62 | scanner := bufio.NewScanner(r)
63 | for scanner.Scan() {
64 | fields := strings.Split(scanner.Text(), ",")
65 | if len(fields) != 2 {
66 | continue
67 | }
68 | if xp, err = strconv.ParseFloat(fields[0], 64); err != nil {
69 | continue
70 | }
71 | if yp, err = strconv.ParseFloat(fields[1], 64); err != nil {
72 | continue
73 | }
74 | x = append(x, xp)
75 | y = append(y, yp)
76 | }
77 | return x, y, scanner.Err()
78 | }
79 |
80 | func deck(p params, filename string) error {
81 | r, err := os.Open(filename)
82 | if err != nil {
83 | return err
84 | }
85 |
86 | fmt.Println("")
87 |
88 | x, y, err := readata(r)
89 | if err != nil {
90 | return err
91 | }
92 |
93 | pminx := largest
94 | pmaxx := smallest
95 | fmt.Printf(" pmaxx {
99 | pmaxx = px
100 | }
101 | if px < pminx {
102 | pminx = px
103 | }
104 | fmt.Printf("%.3g ", px)
105 | }
106 | fmt.Printf("%.3g\"", vmap(x[0], p.minx, p.maxx, p.left, p.right))
107 |
108 | pminy := largest
109 | pmaxy := smallest
110 | fmt.Printf(" yc=\"")
111 | for i := 0; i < len(y); i++ {
112 | py := vmap(y[i], p.miny, p.maxy, p.bottom, p.top)
113 | if py > pmaxy {
114 | pmaxy = py
115 | }
116 | if py < pminy {
117 | pminy = py
118 | }
119 | fmt.Printf("%.3g ", py)
120 | }
121 | fmt.Printf("%.3g\" color=\"%s\"/>\n", vmap(y[0], p.miny, p.maxy, p.bottom, p.top), p.color)
122 | if len(p.label) > 0 {
123 | fmt.Printf("%s/>\n", pminx+((pmaxx-pminx)/2), pminy+((pmaxy-pminy)/2), p.label)
124 | }
125 | return r.Close()
126 | }
127 |
128 | // process data in the filename
129 | func decksh(p params, filename string) error {
130 | r, err := os.Open(filename)
131 | if err != nil {
132 | return err
133 | }
134 |
135 | fmt.Println("#", p.minx, p.maxx, p.miny, p.maxy, p.left, p.right, p.bottom, p.top)
136 |
137 | x, y, err := readata(r)
138 | if err != nil {
139 | return err
140 | }
141 |
142 | pminx := largest
143 | pmaxx := smallest
144 | fmt.Printf("polygon \"")
145 | for i := 0; i < len(x); i++ {
146 | px := vmap(x[i], p.minx, p.maxx, p.left, p.right)
147 | if px > pmaxx {
148 | pmaxx = px
149 | }
150 | if px < pminx {
151 | pminx = px
152 | }
153 | fmt.Printf("%.3g ", px)
154 | }
155 | fmt.Printf("%.3g\"", vmap(x[0], p.minx, p.maxx, p.left, p.right))
156 |
157 | pminy := largest
158 | pmaxy := smallest
159 | fmt.Printf(" \"")
160 | for i := 0; i < len(y); i++ {
161 | py := vmap(y[i], p.miny, p.maxy, p.bottom, p.top)
162 | if py > pmaxy {
163 | pmaxy = py
164 | }
165 | if py < pminy {
166 | pminy = py
167 | }
168 | fmt.Printf("%.3g ", py)
169 | }
170 | fmt.Printf("%.3g\" \"%s\"\n", vmap(y[0], p.miny, p.maxy, p.bottom, p.top), p.color)
171 | if len(p.label) > 0 {
172 | fmt.Printf("ctext \"%s\" %g %g 1\n", p.label, pminx+((pmaxx-pminx)/2), pminy+((pmaxy-pminy)/2))
173 | }
174 | return r.Close()
175 | }
176 |
177 | // vmap maps one range to another
178 | func vmap(value, low1, high1, low2, high2 float64) float64 {
179 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
180 | }
181 |
--------------------------------------------------------------------------------
/cmd/nythead/README.md:
--------------------------------------------------------------------------------
1 | # nythead - print headlines from the New York Times
2 |
3 | ## usage
4 |
5 | ```
6 | usage of nythead:
7 | -s string
8 | headline type (arts, health, sports, science, technology, u.s., world) (default "u.s.")
9 | ```
10 |
11 | ## examples
12 |
13 | ```
14 | $ nythead
15 | Key Takeaways From Trump’s Tax Returns
16 | Trump Tax Returns Released by House Democrats
17 | Reported Sexual Abuse at California Prep School Won’t Be Prosecuted
18 | Biden Signs Government Funding Bill, Preventing Shutdown
19 | Southwest Is California’s ‘Unofficial Airline.’ The Meltdown Has Residents Anxious.
20 | ```
21 |
22 | ```
23 | $ nythead -s arts
24 | Giancarlo Esposito Plays Other People So He Can Know Himself
25 | These Young Musicians Made an Album. Now It’s Nominated for a Grammy.
26 | The Complex History Behind a Vienna Philharmonic Tradition
27 | 5 Things to Do This Weekend
28 | Ian Tyson, Revered Canadian Folk Singer, Dies at 89
29 | ```
30 |
31 | This program looks for the NYT API key in the environment variable ```NYTAPIKEY```, and fails if is not set.
--------------------------------------------------------------------------------
/cmd/nythead/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/nythead
2 |
3 | go 1.19
4 |
--------------------------------------------------------------------------------
/cmd/nythead/nythead.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "os"
10 | "time"
11 | )
12 |
13 | // API Info
14 | const (
15 | NYTAPIkey = "NYTAPIKEY" // obtained from the environment
16 | NYTfmt = "http://api.nytimes.com/svc/news/v3/content/all/%s/.json?api-key=%s&limit=5"
17 | )
18 |
19 | // NYTHeadlines is the headline info from the New York Times
20 | type NYTHeadlines struct {
21 | Status string `json:"status"`
22 | Copyright string `json:"copyright"`
23 | NumResults int `json:"num_results"`
24 | Results []result `json:"results"`
25 | }
26 |
27 | type result struct {
28 | Section string `json:"section"`
29 | Subsection string `json:"subsection"`
30 | Title string `json:"title"`
31 | Abstract string `json:"abstract"`
32 | Thumbnail string `json:"thumbnail_standard"`
33 | }
34 |
35 | func main() {
36 | var section = flag.String("s", "u.s.", "headline type (arts, health, sports, science, technology, u.s., world)")
37 | flag.Parse()
38 | nytheadlines(*section)
39 | }
40 |
41 | // apikey returns the API key from the environment, or the empty string if not found.
42 | func apikey(s string) string {
43 | key, ok := os.LookupEnv(s)
44 | if !ok {
45 | return ""
46 | }
47 | return key
48 | }
49 |
50 | // netread derefernces a URL, returning the Reader, with an error
51 | func netread(url string) (io.ReadCloser, error) {
52 | client := &http.Client{Timeout: 30 * time.Second}
53 | resp, err := client.Get(url)
54 | if err != nil {
55 | return nil, err
56 | }
57 | if resp.StatusCode != http.StatusOK {
58 | return nil, fmt.Errorf("unable to get network data for %s (%s)", url, resp.Status)
59 | }
60 | return resp.Body, nil
61 | }
62 |
63 | // nytheadlines retrieves data from the New York Times API, decodes and displays it.
64 | func nytheadlines(section string) {
65 | key := apikey(NYTAPIkey)
66 | if len(key) == 0 {
67 | fmt.Fprintln(os.Stderr, "invalid API key")
68 | return
69 | }
70 | r, err := netread(fmt.Sprintf(NYTfmt, section, key))
71 | if err != nil {
72 | fmt.Fprintf(os.Stderr, "headline read error: %v\n", err)
73 | return
74 | }
75 | defer r.Close()
76 | var data NYTHeadlines
77 | err = json.NewDecoder(r).Decode(&data)
78 | if err != nil {
79 | fmt.Fprintf(os.Stderr, "decode: %v\n", err)
80 | return
81 | }
82 | for i := 0; i < len(data.Results); i++ {
83 | fmt.Printf("%v\n", data.Results[i].Title)
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/cmd/polar/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/polar
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/cmd/polar/polar.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "math"
7 | )
8 |
9 | func main() {
10 | var x, y, r, theta float64
11 | flag.Float64Var(&x, "x", 50, "x coordinate")
12 | flag.Float64Var(&y, "y", 50, "y coordinate")
13 | flag.Float64Var(&r, "r", 10, "radius")
14 | flag.Float64Var(&theta, "t", 90, "angle (degrees)")
15 | flag.Parse()
16 |
17 | rad := theta * (math.Pi / 180)
18 | fmt.Printf("x=%g y=%g r=%g t=%g -> %g %g\n", x, y, r, theta, x+(r*math.Cos(rad)), y+(r*math.Sin(rad)))
19 | }
20 |
--------------------------------------------------------------------------------
/cmd/popio/main.go:
--------------------------------------------------------------------------------
1 | // popio: import and export images for popi
2 | package main
3 |
4 | import (
5 | "bufio"
6 | "flag"
7 | "fmt"
8 | "image"
9 | "image/color"
10 | _ "image/jpeg"
11 | "image/png"
12 | "io"
13 | "os"
14 | )
15 |
16 | // popin: image to raw
17 | func popin(w io.Writer, r io.Reader) (int, error) {
18 | img, _, err := image.Decode(r)
19 | if err != nil {
20 | return 0, err
21 | }
22 | // get image dimensions
23 | bounds := img.Bounds()
24 | width := bounds.Max.X - bounds.Min.X
25 | height := bounds.Max.Y - bounds.Min.Y
26 | // convert image pixels to grayscale
27 | data := make([]byte, width*height)
28 | i := 0
29 | for y := 0; y < height; y++ {
30 | for x := 0; x < width; x++ {
31 | r, g, b, _ := img.At(x, y).RGBA()
32 | data[i] = uint8((19595*r + 38470*g + 7471*b + 1<<15) >> 24)
33 | i++
34 | }
35 | }
36 | return bufio.NewWriter(w).Write(data)
37 | }
38 |
39 | // popout: raw to PNG
40 | func popout(w io.Writer, r io.Reader, width, height int) error {
41 | data, err := io.ReadAll(r)
42 | if err != nil {
43 | return err
44 | }
45 | // convert raw data to grayscale pixels
46 | img := image.NewGray(image.Rect(0, 0, width, height))
47 | i := 0
48 | for y := 0; y < height; y++ {
49 | for x := 0; x < width; x++ {
50 | img.Set(x, y, color.Gray{data[i]})
51 | i++
52 | }
53 | }
54 | // write the png
55 | if err := png.Encode(w, img); err != nil {
56 | return err
57 | }
58 | return nil
59 | }
60 |
61 | func main() {
62 | var read, write bool
63 | var width, height int
64 | flag.BoolVar(&read, "import", false, "image to raw popi grayscale")
65 | flag.BoolVar(&write, "export", false, "popi raw grayscale to PNG")
66 | flag.IntVar(&width, "width", 512, "image width")
67 | flag.IntVar(&height, "height", 512, "image height")
68 | flag.Parse()
69 |
70 | if read && write {
71 | fmt.Fprintln(os.Stderr, "pick one: -import or -export")
72 | os.Exit(3)
73 | }
74 |
75 | if read {
76 | n, err := popin(os.Stdout, os.Stdin)
77 | if err != nil {
78 | fmt.Fprintf(os.Stderr, "%v (%d bytes written)\n", err, n)
79 | os.Exit(1)
80 | }
81 | os.Exit(0)
82 | }
83 | if write {
84 | err := popout(os.Stdout, os.Stdin, width, height)
85 | if err != nil {
86 | fmt.Fprintf(os.Stderr, "%v\n", err)
87 | os.Exit(2)
88 | }
89 | os.Exit(0)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/cmd/randgen/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/randgen
2 |
3 | go 1.22.2
4 |
--------------------------------------------------------------------------------
/cmd/randgen/randgen.go:
--------------------------------------------------------------------------------
1 | // randgen makes random numbers
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "math/rand/v2"
8 | )
9 |
10 | func vmap(v, l1, h1, l2, h2 float64) float64 {
11 | return l2 + (h2-l2)*(v-l1)/(h1-l1)
12 | }
13 |
14 | func main() {
15 | nrand := flag.Int("n", 100, "number of items")
16 | min := flag.Float64("min", 0, "minimum value")
17 | max := flag.Float64("max", 1e6, "minimum value")
18 | ndec := flag.Int("dec", 3, "number of decimals")
19 | xint := flag.Float64("xint", 0, "x value interval")
20 | flag.Parse()
21 | xval := 0.0
22 | f := fmt.Sprintf("%%.%df", *ndec)
23 | for i := 0; i < *nrand; i++ {
24 | if *xint > 0 {
25 | fmt.Printf(f+"\t", xval)
26 | xval += *xint
27 | }
28 | fmt.Printf(f+"\n", vmap(rand.Float64(), 0, 1, *min, *max))
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/rmcsv/README.md:
--------------------------------------------------------------------------------
1 | # rmcsv -- convert roadmap csv files to XML
2 |
3 | Produces XML from CSV files named on the command line or standard input.
4 | The convered XML goes to standard output.
5 |
6 | ```
7 | rmcsv [options] file.csv... > file.xml
8 | ```
9 |
10 | The CSV files have five fields: Category,begin,duration,id and connection.
11 |
12 | The input:
13 | ```
14 | "Category/Item",Begin,Duration,Id,Connection
15 | ,,,,
16 | "Facilities",,,,
17 | "Phase 1",2010/3,2,,
18 | "Phase 2",2010/6,2,,
19 | "Patch",2010/9,1,,
20 | ,,,,
21 | "Network",,,,
22 | "Install",2010/2,6,,
23 | "Decomission",2010/9,2,,
24 | ,,,,
25 | "Servers",,,,
26 | "Install",2010/5,3,,
27 | "Decomission",2010/9,2,,gl
28 | ,,,,
29 | "Applications",,,,
30 | "Develop",2010/1,8,,
31 | "Test",2010/5,4,,
32 | "Data Migration",2010/9,1,dm,gl
33 | ,,,,
34 | "Support",,,,
35 | "Transition",2010/1,10,,gl
36 | ,,,,
37 | "EVENTS",,,,
38 | "Freeze",2010/6,3,,
39 | "Go Live",2010/12,1,gl,
40 |
41 |
42 | ```
43 | 
44 |
45 | Produces:
46 |
47 | ```
48 |
49 |
50 | - 2022
51 |
52 |
53 | - Jan
54 | - Feb
55 | - Mar
56 | - Apr
57 | - May
58 | - Jun
59 | - Jul
60 | - Aug
61 | - Sep
62 | - Oct
63 | - Nov
64 | - Dec
65 |
66 |
67 | - Phase 1
68 | - Phase 2
69 | - Patch
70 |
71 |
72 | - Install
73 | - Decomission
74 |
75 |
76 | - Install
77 | - Decomission
78 |
79 |
80 | - Develop
81 | - Test
82 | - Data Migration
83 |
84 |
85 | - Transition
86 |
87 |
88 | - Freeze
89 | - Go Live
90 |
91 |
92 | ```
93 |
94 |
95 |
96 | # options
97 | ```
98 | Usage of rmcsv:
99 | -begin int
100 | begin year (default 2022)
101 | -cp int
102 | category percent (default 12)
103 | -datehead
104 | include date header (default true)
105 | -end int
106 | end year (default 2023)
107 | -font string
108 | roadmap font (default "Calibri,sans-serif")
109 | -itemh int
110 | itemheight (default 30)
111 | -scale int
112 | scale (default 12)
113 | -shape string
114 | item shape (default "r")
115 | -title string
116 | Roadmap Title (default "Title")
117 | -vspace int
118 | vspace (default 35)
119 | ```
120 |
--------------------------------------------------------------------------------
/cmd/rmcsv/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/rmcsv
2 |
3 | go 1.21.0
4 |
--------------------------------------------------------------------------------
/cmd/rmcsv/main.go:
--------------------------------------------------------------------------------
1 | // rmcsv -- convert csv roadmap files to xml
2 | package main
3 |
4 | import (
5 | "encoding/csv"
6 | "flag"
7 | "fmt"
8 | "io"
9 | "os"
10 | "strings"
11 | )
12 |
13 | const (
14 | rmfmt = "\n"
15 | endrmfmt = "\t\n\n"
16 | yearcatfmt = "\t\n"
17 | yearitemfmt = "\t\t- %d
\n"
18 | endcat = "\t\n"
19 | monthcatfmt = "\t\n"
20 | monthitemfmt = "\t\t- %s
\n"
21 | catfmt = "\t\n"
22 | itemfmt1 = "\t\t- %s
\n"
23 | itemfmt2 = "\t\t- %s
\n"
24 | itemfmt3 = "\t\t- %s
\n"
25 | )
26 |
27 | type rmconfig struct {
28 | title, shape, font string
29 | begin, end, catpercent, scale, vspace, itemheight int
30 | dh bool
31 | }
32 |
33 | // main: process roadmap files on the command line,
34 | // use stdin if no files specified.
35 | func main() {
36 | var config rmconfig
37 | flag.StringVar(&config.title, "title", "Title", "Roadmap Title")
38 | flag.StringVar(&config.shape, "shape", "r", "item shape")
39 | flag.StringVar(&config.font, "font", "Calibri,sans-serif", "roadmap font")
40 | flag.IntVar(&config.begin, "begin", 2022, "begin year")
41 | flag.IntVar(&config.end, "end", 2023, "end year")
42 | flag.IntVar(&config.catpercent, "cp", 12, "category percent")
43 | flag.IntVar(&config.scale, "scale", 12, "scale")
44 | flag.IntVar(&config.vspace, "vspace", 35, "vspace")
45 | flag.IntVar(&config.itemheight, "itemh", 30, "itemheight")
46 | flag.BoolVar(&config.dh, "datehead", true, "include date header")
47 |
48 | flag.Parse()
49 | files := flag.Args()
50 | nf := len(files)
51 | if nf == 0 {
52 | process("", config, os.Stdout)
53 | return
54 | }
55 | for i := 0; i < nf; i++ {
56 | process(files[i], config, os.Stdout)
57 | }
58 | }
59 |
60 | // process reads and processes a roadmap XML file
61 | func process(location string, config rmconfig, w io.Writer) {
62 | var r *os.File
63 | var err error
64 | r = os.Stdin
65 | if len(location) > 0 {
66 | r, err = os.Open(location)
67 | if err != nil {
68 | fmt.Fprintf(os.Stderr, "%v\n", err)
69 | return
70 | }
71 | }
72 | csvtoxml(w, r, config)
73 | r.Close()
74 | }
75 |
76 | // csvtoxml reads the roadmap CSV, converting to XML
77 | func csvtoxml(w io.Writer, r io.Reader, config rmconfig) {
78 | // roadmap root element
79 | fmt.Fprintf(w, rmfmt, xmlesc(config.title), xmlesc(config.font), xmlesc(config.shape),
80 | config.begin, config.end, config.catpercent, config.scale, config.itemheight, config.vspace)
81 |
82 | // read categories and items from csv, write XML
83 | input := csv.NewReader(r)
84 | nr := 0
85 | nc := 0
86 | if config.dh {
87 | dateheader(w, config.begin, config.end)
88 | }
89 | for {
90 | fields, csverr := input.Read()
91 | if csverr == io.EOF {
92 | break
93 | }
94 | if csverr != nil {
95 | fmt.Fprintf(os.Stderr, "%v %v\n", csverr, fields)
96 | continue
97 | }
98 | nr++
99 | // skip header
100 | if nr == 1 {
101 | continue
102 | }
103 | // skip invalid fields
104 | if len(fields) < 5 {
105 | continue
106 | }
107 | if len(fields[0]) == 0 {
108 | continue
109 | }
110 | // process categories
111 | if len(fields[0]) > 0 && len(fields[1]) == 0 && len(fields[2]) == 0 {
112 | nc++
113 | if nc > 1 {
114 | fmt.Fprint(w, endcat)
115 | }
116 | fmt.Fprintf(w, catfmt, xmlesc(fields[0]), config.itemheight, config.vspace)
117 | continue
118 | }
119 | // process items
120 | switch {
121 | case len(fields[3]) > 0 && len(fields[4]) > 0:
122 | fmt.Fprintf(w, itemfmt1, xmlesc(fields[3]), xmlesc(fields[1]), xmlesc(fields[2]), xmlesc(fields[4]), xmlesc(fields[0]))
123 | case len(fields[3]) > 0 && len(fields[4]) == 0:
124 | fmt.Fprintf(w, itemfmt2, xmlesc(fields[3]), xmlesc(fields[1]), xmlesc(fields[2]), xmlesc(fields[0]))
125 | default:
126 | fmt.Fprintf(w, itemfmt3, xmlesc(fields[1]), xmlesc(fields[2]), xmlesc(fields[0]))
127 | }
128 | }
129 | // end the XML
130 | fmt.Fprint(w, endrmfmt)
131 | }
132 |
133 | // xmlmap defines the XML substitutions
134 | var xmlmap = strings.NewReplacer(
135 | "&", "&",
136 | "<", "<",
137 | ">", ">")
138 |
139 | // xmlesc escapes XML
140 | func xmlesc(s string) string {
141 | return xmlmap.Replace(s)
142 | }
143 |
144 | // dateheader makes a date header
145 | func dateheader(w io.Writer, begin, end int) {
146 | // years
147 | fmt.Fprint(w, yearcatfmt)
148 | for y := begin; y < end; y++ {
149 | fmt.Fprintf(w, yearitemfmt, y, y)
150 | }
151 | fmt.Fprint(w, endcat)
152 |
153 | // months
154 | fmt.Fprint(w, monthcatfmt)
155 | for y := begin; y < end; y++ {
156 | for i, m := range []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} {
157 | fmt.Fprintf(w, monthitemfmt, y, i+1, m)
158 | }
159 | }
160 | fmt.Fprint(w, endcat)
161 | }
162 |
--------------------------------------------------------------------------------
/cmd/rmcsv/ss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/rmcsv/ss.png
--------------------------------------------------------------------------------
/cmd/roadmap/README.md:
--------------------------------------------------------------------------------
1 | # Roadmap
2 |
3 | roadmap reads a XML description and outputs an SVG rendition of a roadmap.
4 |
5 | The XML description specifies a roadmap that contain categories, which in turn contain an arbitratry number of items.
6 | Items may contains dependencies to other items.
7 |
8 | ```
9 |
10 |
11 | - Item 1
12 | - Item 2
13 |
14 |
15 |
16 | - Item 3
17 |
18 |
19 | ```
20 |
21 | ## Roadmap attributes
22 | * title -- title of the roadmap
23 | * begin -- begining year of the roadmap
24 | * end -- ending year of the roadmap (for example to specfiy a span of 2023-2025, begin="2023", end="2026")
25 | * scale -- the number of items per period, for example 12 for 12 months of the year
26 | * catpercent -- the canvas percentage from the left where catagory labels are placed
27 | * vspace -- the default amount of vertical space between items within a category
28 | * itemheight -- the default height of an item
29 | * fontname -- font used for the text
30 | * shape -- default shape of an item: 'r': rectangle (default), 'rr': rounded rectangle, 'c': circle, 'e': ellipse, 'a': arrow, 'l': line
31 |
32 | ## Category attributes
33 | * name -- the name of the category
34 | * color -- the category color
35 | * vspace -- vertical space between items
36 | * itemheight -- the height of items
37 | * shape -- shape of items in this category
38 | * bline -- if "on" draw a boundary line on top of the category
39 | * catdesc -- category description
40 |
41 | ## Item attributes
42 | * id -- item unique id used to connect items
43 | * begin -- beginning of the item in them ```year/number``` for example 2023/01 for January 2023
44 | * duration -- duration of the item in scale units, for example if the scale is set to 12, duration of 6 is six months
45 | * color -- item color, overriding the category color
46 | * shape -- item shape, overriding the category shape
47 | * align -- text alignment of the item (middle (default), end, start)
48 | * vspace -- vertical space between items
49 | * bline -- if set to "on" draw a boundary line
50 | * dep -- dependencies between items
51 | * desc -- item description
52 |
53 | ## Dependency attributes
54 | * dest -- the item id to point to
55 | * desc -- dependency description
56 |
57 | For example, given:
58 | ```
59 |
61 |
62 | - Jan
63 | - Feb
64 | - Mar
65 | - Apr
66 | - May
67 | - Jun
68 | - Jul
69 | - Aug
70 | - Sep
71 | - Oct
72 | - Nov
73 | - Dec
74 |
75 |
76 | - Phase 1
77 | - Phase 2
78 | - Patch
79 |
80 |
81 | - Install
82 | - Decomission
83 |
84 |
85 | - Install
86 | - Decomission
87 |
88 |
89 | - Develop
90 | - Test
91 | - Data Migration
92 |
93 |
94 | - Migration planning
95 |
96 |
97 | - Transition
98 |
99 |
100 | - Freeze
101 | - Go Live
102 |
103 |
104 |
105 | ```
106 |
107 | where the above file is ```p2p.xml```, the command ```roadmap p2p.xml > p2p.svg``` Produces:
108 |
109 | 
110 |
111 |
112 | ## Command line options
113 | ```
114 | Usage of roadmap:
115 | -align string
116 | label alignment (default "end")
117 | -b bold categories
118 | -bb
119 | bottom border
120 | -bg string
121 | background color (default "white")
122 | -cb
123 | category border
124 | -cc string
125 | connection color (default "red")
126 | -cfs float
127 | category font size (px) (default 14)
128 | -csv string
129 | write CSV to specified file
130 | -curves string
131 | curve line connections (default "0,0")
132 | -dc string
133 | description color (default "red")
134 | -de
135 | description at the end of the item (default true)
136 | -h float
137 | height (default 768)
138 | -ifs float
139 | item fontsize (px) (default 12)
140 | -lb
141 | left border (default true)
142 | -margin float
143 | margin (default 10)
144 | -rb
145 | right border
146 | -tb
147 | top border
148 | -tfs float
149 | title font size (px) (default 24)
150 | -w float
151 | width (default 1024)
152 | -wrap float
153 | text wrap (default 20)
154 | ```
155 |
--------------------------------------------------------------------------------
/cmd/roadmap/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/roadmap
2 |
3 | go 1.21.0
4 |
5 | require github.com/ajstarks/gensvg v0.0.0-20210923152200-4042c242e95e
6 |
--------------------------------------------------------------------------------
/cmd/roadmap/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/gensvg v0.0.0-20210923152200-4042c242e95e h1:450GYH87+/+YEC6/RRshDbWgm3uzl5U8SOdOoDl+gaw=
2 | github.com/ajstarks/gensvg v0.0.0-20210923152200-4042c242e95e/go.mod h1:PtU6ofchbU0VxGlg+klpebsi0DY7V9GrxtYeMJkWutM=
3 |
--------------------------------------------------------------------------------
/cmd/roadmap/p2p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/roadmap/p2p.png
--------------------------------------------------------------------------------
/cmd/roadmap/p2p.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | - Jan
5 | - Feb
6 | - Mar
7 | - Apr
8 | - May
9 | - Jun
10 | - Jul
11 | - Aug
12 | - Sep
13 | - Oct
14 | - Nov
15 | - Dec
16 |
17 |
18 | - Phase 1
19 | - Phase 2
20 | - Patch
21 |
22 |
23 | - Install
24 | - Decomission
25 |
26 |
27 | - Install
28 | - Decomission
29 |
30 |
31 | - Develop
32 | - Test
33 | - Data Migration
34 |
35 |
36 | - Migration planning
37 |
38 |
39 | - Transition
40 |
41 |
42 | - Freeze
43 | - Go Live
44 |
45 |
46 |
--------------------------------------------------------------------------------
/cmd/setdeckfont/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/setdeckfont
2 |
3 | go 1.18
4 |
5 | require github.com/flopp/go-findfont v0.1.0
6 |
--------------------------------------------------------------------------------
/cmd/setdeckfont/go.sum:
--------------------------------------------------------------------------------
1 | github.com/flopp/go-findfont v0.1.0 h1:lPn0BymDUtJo+ZkV01VS3661HL6F4qFlkhcJN55u6mU=
2 | github.com/flopp/go-findfont v0.1.0/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw=
3 |
--------------------------------------------------------------------------------
/cmd/setdeckfont/setdeckfont.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 |
8 | "github.com/flopp/go-findfont"
9 | )
10 |
11 | func main() {
12 | sample := ""
13 | if len(os.Args) > 1 {
14 | sample = os.Args[1]
15 | }
16 | fontPath, err := findfont.Find(sample)
17 | if err != nil {
18 | fmt.Fprintf(os.Stderr, "%v\n", err)
19 | os.Exit(1)
20 | }
21 | fmt.Printf("DECKFONTS=%s\n", filepath.Dir(fontPath))
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/cmd/slopechart/f.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajstarks/utils/9db796a2d889eb0a2a123287cf805713234ed1e1/cmd/slopechart/f.pdf
--------------------------------------------------------------------------------
/cmd/slopechart/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/slopechart
2 |
3 | go 1.16
4 |
5 | require github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494
6 |
--------------------------------------------------------------------------------
/cmd/slopechart/go.sum:
--------------------------------------------------------------------------------
1 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
2 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
3 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494 h1:Z5Dn/2Ml6/8fP1PWxv4ijaK2PwHsX8A+gZiKJVP+XDs=
4 | github.com/ajstarks/deck/generate v0.0.0-20210223212949-8bd01c798494/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
5 |
--------------------------------------------------------------------------------
/cmd/slopechart/slope.d:
--------------------------------------------------------------------------------
1 | # Test Slope Graphs
2 | one 20 First
3 | two 100
4 | three 40 Second
5 | four 50
6 | five 100 Third
7 | six 50
8 | seven 50 Fourth
9 | eight 50
10 | nine 100 Fifth
11 | ten 40
12 | eleven 20 Sixth
13 | twelve 5
14 |
--------------------------------------------------------------------------------
/cmd/slopechart/slopechart.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "math"
9 | "os"
10 | "strconv"
11 | "strings"
12 |
13 | "github.com/ajstarks/deck/generate"
14 | )
15 |
16 | type nameval struct {
17 | name string
18 | value float64
19 | label string
20 | }
21 |
22 | type options struct {
23 | min, max, left, right, bottom, top, textsize, linewidth float64
24 | color, vcolor string
25 | }
26 |
27 | var xmlmap = strings.NewReplacer(
28 | "&", "&",
29 | "<", "<",
30 | ">", ">")
31 |
32 | const (
33 | largest = math.MaxFloat64
34 | smallest = -math.MaxFloat64
35 | )
36 |
37 | // xmlesc escapes XML
38 | func xmlesc(s string) string {
39 | return xmlmap.Replace(s)
40 | }
41 |
42 | func readData(r io.ReadCloser) ([]nameval, string, float64, float64, error) {
43 | var d nameval
44 | var data []nameval
45 | var err error
46 | maxval := smallest
47 | minval := largest
48 | title := ""
49 | scanner := bufio.NewScanner(r)
50 | // read a line, parse into name, value pairs
51 | // compute min and max values
52 | for scanner.Scan() {
53 | t := scanner.Text()
54 | if len(t) == 0 { // skip blank lines
55 | continue
56 | }
57 | if len(t) > 2 && t[0] == '#' {
58 | title = strings.TrimSpace(t[1:])
59 | }
60 | fields := strings.Split(t, "\t")
61 | if len(fields) < 2 {
62 | continue
63 | }
64 | if len(fields) > 2 {
65 | d.label = fields[2]
66 | }
67 | d.name = fields[0]
68 | d.value, err = strconv.ParseFloat(fields[1], 64)
69 | if err != nil {
70 | d.value = 0
71 | }
72 | if d.value > maxval {
73 | maxval = d.value
74 | }
75 | if d.value < minval {
76 | minval = d.value
77 | }
78 | data = append(data, d)
79 | }
80 | r.Close()
81 | return data, title, minval, maxval, err
82 |
83 | }
84 |
85 | // vmap maps one range into another
86 | func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
87 | return low2 + (high2-low2)*(value-low1)/(high1-low1)
88 | }
89 |
90 | func slopechart(deck *generate.Deck, opts options, r io.ReadCloser) error {
91 | data, title, _, _, err := readData(r)
92 | if err != nil {
93 | return err
94 | }
95 | if len(data) < 2 {
96 | return fmt.Errorf("need at least two data points")
97 | }
98 | min := opts.min
99 | max := opts.max
100 | left := opts.left
101 | right := opts.right
102 | top := opts.top
103 | bottom := opts.bottom
104 | color := opts.color
105 | vcolor := opts.vcolor
106 | textsize := opts.textsize
107 | linewidth := opts.linewidth
108 | lw := linewidth / 2
109 |
110 | lsize := textsize * 0.75
111 | tsize := textsize * 1.5
112 | w := right - left
113 | h := top - bottom
114 | if len(title) > 0 {
115 | hsize := textsize * 2
116 | deck.Text(left, top+10, title, "sans", hsize, "")
117 | }
118 |
119 | hskip := w * .60
120 | vskip := h * 1.4
121 | x1 := left
122 | x2 := right
123 | for i := 0; i < len(data)-1; i += 2 {
124 | if len(data[i].label) > 0 {
125 | deck.TextMid(x1+(w/2), top+3, data[i].label, "sans", tsize, "")
126 | }
127 | v1 := data[i].value
128 | v2 := data[i+1].value
129 | v1y := vmap(v1, min, max, bottom, top)
130 | v2y := vmap(v2, min, max, bottom, top)
131 | deck.Line(x1, bottom, x1, top, lw, "black")
132 | deck.Line(x2, bottom, x2, top, lw, "black")
133 | deck.Circle(x1, v1y, textsize, color)
134 | deck.Circle(x2, v2y, textsize, color)
135 | deck.Line(x1, v1y, x2, v2y, linewidth, color)
136 | deck.TextMid(x1, bottom-2, data[i].name, "sans", textsize, "")
137 | deck.TextMid(x2, bottom-2, data[i+1].name, "sans", textsize, "")
138 | deck.TextEnd(x1-1, top, fmt.Sprintf("%g", max), "sans", lsize, "")
139 | deck.TextEnd(x1-1, v1y, fmt.Sprintf("%g", v1), "sans", lsize, vcolor)
140 | deck.Text(x2+1, v2y, fmt.Sprintf("%g", v2), "sans", lsize, vcolor)
141 | x1 += w + hskip
142 | x2 += w + hskip
143 | if x2 > 100 {
144 | x1 = left
145 | x2 = right
146 | top -= vskip
147 | bottom -= vskip
148 | }
149 | }
150 | return err
151 | }
152 |
153 | func main() {
154 |
155 | left := flag.Float64("left", 20, "left")
156 | right := flag.Float64("right", 40, "right")
157 | bottom := flag.Float64("bottom", 20, "bottom")
158 | top := flag.Float64("top", 60, "top")
159 | color := flag.String("color", "steelblue", "color")
160 | vcolor := flag.String("vcolor", "maroon", "value color")
161 | max := flag.Float64("max", 100, "max value")
162 | textsize := flag.Float64("textsize", 1.5, "text size")
163 | linewidth := flag.Float64("linewidth", 0.2, "line width")
164 | flag.Parse()
165 |
166 | opts := options{
167 | min: 0,
168 | max: *max,
169 | left: *left,
170 | right: *right,
171 | top: *top,
172 | bottom: *bottom,
173 | linewidth: *linewidth,
174 | textsize: *textsize,
175 | color: *color,
176 | vcolor: *vcolor,
177 | }
178 |
179 | deck := generate.NewSlides(os.Stdout, 0, 0)
180 | deck.StartDeck()
181 | deck.StartSlide()
182 | if err := slopechart(deck, opts, os.Stdin); err != nil {
183 | fmt.Fprintf(os.Stderr, "%v\n", err)
184 | os.Exit(1)
185 | }
186 | deck.EndSlide()
187 | deck.EndDeck()
188 | os.Exit(0)
189 | }
190 |
--------------------------------------------------------------------------------
/cmd/spl/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ajstarks/utils/cmd/spl
2 |
3 | go 1.22.0
4 |
--------------------------------------------------------------------------------
/cmd/spl/spl.go:
--------------------------------------------------------------------------------
1 | // spl -- image catalogs
2 | package main
3 |
4 | import (
5 | "flag"
6 | "fmt"
7 | "image"
8 | _ "image/gif"
9 | _ "image/jpeg"
10 | _ "image/png"
11 | "os"
12 | )
13 |
14 | const (
15 | bslide = "\n"
16 | eslide = ""
17 | imfmt = "\n"
18 | namefmt = "%s\n"
19 | snamefmt = "%s\n"
20 | simgfmt = "\n"
21 | sdeck = ""
22 | edeck = ""
23 | )
24 |
25 | type Picture struct {
26 | x, y float64
27 | width, height int
28 | name string
29 | orientation string
30 | }
31 |
32 | type Pictures []Picture
33 |
34 | type Canvas struct {
35 | width, height int
36 | left, right, top, bottom float64
37 | bgcolor string
38 | showname bool
39 | }
40 |
41 | func marginw(c Canvas, p Picture) (int, int) {
42 | aspect := float64(p.height) / float64(p.width)
43 | pw := float64(c.width) * (100.0 - (c.left + c.right)) / 100.0
44 | if int(pw) > p.width {
45 | return p.width, p.height
46 | }
47 | return int(pw), int(aspect * pw)
48 | }
49 |
50 | func marginh(c Canvas, p Picture) (int, int) {
51 | aspect := float64(p.height) / float64(p.width)
52 | ph := float64(c.height) * (100.0 - (c.top + c.bottom)) / 100.0
53 | if int(ph) > p.height {
54 | return p.width, p.height
55 | }
56 | return int(ph / aspect), int(ph)
57 | }
58 |
59 | func landlayout(c Canvas, p []Picture) {
60 | switch len(p) {
61 | case 1:
62 | p[0].x, p[0].y = 50, 50
63 | placepicsh(c, p[0:1], 90)
64 | case 2:
65 | p[0].x, p[1].x = 25, 75
66 | p[0].y, p[1].y = 50, 50
67 | placepicsh(c, p[0:2], 45)
68 | case 3:
69 | p[0].x, p[1].x, p[2].x = 17, 50, 83
70 | p[0].y, p[1].y, p[2].y = 50, 50, 50
71 | placepicsh(c, p[0:3], 30)
72 | }
73 | }
74 |
75 | func portlayout(c Canvas, p []Picture) {
76 | switch len(p) {
77 | case 1:
78 | p[0].x, p[0].y = 50, 50
79 | placepicsw(c, p[0:1], 90)
80 | case 2:
81 | p[0].x, p[1].x = 25, 75
82 | p[0].y, p[1].y = 50, 50
83 | placepicsw(c, p[0:2], 45)
84 | case 3:
85 | p[0].x, p[1].x, p[2].x = 17, 50, 83
86 | p[0].y, p[1].y, p[2].y = 50, 50, 50
87 | placepicsw(c, p[0:3], 30)
88 | }
89 | }
90 |
91 | func placepicsh(c Canvas, pics []Picture, targetpct float64) {
92 | fmt.Printf(bslide, c.bgcolor)
93 | tp := (targetpct / 100) * float64(c.height)
94 | for _, p := range pics {
95 | scalepct := (tp / float64(p.height)) * 100
96 | fmt.Printf(imfmt, p.x, p.y, p.width, p.height, scalepct, p.name)
97 | if c.showname {
98 | fmt.Printf(namefmt, p.x, 5.0, p.name)
99 | }
100 | }
101 | fmt.Println(eslide)
102 | }
103 |
104 | func placepicsw(c Canvas, pics []Picture, targetpct float64) {
105 | fmt.Printf(bslide, c.bgcolor)
106 | tp := (targetpct / 100) * float64(c.width)
107 | for _, p := range pics {
108 | scalepct := (tp / float64(p.width)) * 100
109 | fmt.Printf(imfmt, p.x, p.y, p.width, p.height, scalepct, p.name)
110 | if c.showname {
111 | fmt.Printf(namefmt, p.x, 5.0, p.name)
112 | }
113 | }
114 | fmt.Println(eslide)
115 | }
116 |
117 | func piclist(filelist []string) []Picture {
118 | var pic Picture
119 | p := []Picture{}
120 | for _, imagefile := range filelist {
121 | imf, err := os.Open(imagefile)
122 | if err != nil {
123 | fmt.Fprintln(os.Stderr, err)
124 | continue
125 | }
126 | im, _, err := image.DecodeConfig(imf)
127 | if err != nil {
128 | imf.Close()
129 | continue
130 | }
131 | pic.width = im.Width
132 | pic.height = im.Height
133 | pic.name = imagefile
134 | p = append(p, pic)
135 | imf.Close()
136 | }
137 | return p
138 | }
139 |
140 | func ll(c Canvas, pics []Picture, n int) {
141 | lands := []Picture{}
142 | e := []Picture{}
143 |
144 | nl := 0
145 | for _, p := range pics {
146 | if p.width > p.height {
147 | nl++
148 | lands = append(lands, p)
149 | if nl%n == 0 {
150 | landlayout(c, lands)
151 | lands = e
152 | }
153 | }
154 | }
155 | }
156 |
157 | func lp(c Canvas, pics []Picture, n int) {
158 | ports := []Picture{}
159 | e := []Picture{}
160 |
161 | np := 0
162 | for _, p := range pics {
163 | if p.width < p.height {
164 | np++
165 | ports = append(ports, p)
166 | if np%n == 0 {
167 | portlayout(c, ports)
168 | ports = e
169 | }
170 | }
171 | }
172 | }
173 |
174 | func single(c Canvas, pics []Picture) {
175 | for i := 0; i < len(pics); i++ {
176 | if pics[i].width >= pics[i].height {
177 | landlayout(c, pics[i:i+1])
178 | } else {
179 | portlayout(c, pics[i:i+1])
180 | }
181 | }
182 | }
183 |
184 | func msingle(c Canvas, pics []Picture) {
185 |
186 | var pw, ph int
187 | for _, p := range pics {
188 | p.x, p.y = 50, 50
189 | if p.width > p.height {
190 | pw, ph = marginw(c, p)
191 | } else {
192 | pw, ph = marginh(c, p)
193 | }
194 | fmt.Printf(bslide, c.bgcolor)
195 | if c.showname {
196 | fmt.Printf(snamefmt, p.name)
197 | }
198 | fmt.Printf(simgfmt, p.x, p.y, pw, ph, p.name)
199 | fmt.Printf(eslide)
200 | }
201 | }
202 |
203 | func main() {
204 | cw := flag.Int("w", 1280, "canvas width")
205 | ch := flag.Int("h", 720, "canvas height")
206 | tm := flag.Float64("top", 5, "top margin")
207 | bm := flag.Float64("bottom", 5, "bottom margin")
208 | lm := flag.Float64("left", 5, "left margin")
209 | rm := flag.Float64("right", 5, "right margin")
210 | port := flag.Int("p", 0, "portrait n")
211 | land := flag.Int("l", 0, "landscape n")
212 | all := flag.Int("a", 0, "all n")
213 | showname := flag.Bool("showname", false, "show name")
214 | bgcolor := flag.String("bg", "white", "background color")
215 | flag.Parse()
216 |
217 | pics := piclist(flag.Args())
218 | c := Canvas{width: *cw, height: *ch, left: *lm, right: *rm, top: *tm, bottom: *bm, bgcolor: *bgcolor, showname: *showname}
219 | fmt.Println(sdeck)
220 | // fmt.Printf("