mbutterick / pollen-users

please use https://forums.matthewbutterick.com/c/typesetting/ instead
https://forums.matthewbutterick.com/c/typesetting/
52 stars 0 forks source link

Graphviz diagrams inline with Pollen #46

Open nixin72 opened 4 years ago

nixin72 commented 4 years ago

One thing that I've wanted out of Markdown for a really long time is the ability to draw graphviz diagrams inline with my Markdown code. Now, this isn't going to happen with Markdown, so I just draw my diagrams in graphviz and include the images normally, but this becomes a huge pain when I'm answering answering assignment questions and naming my files something like q5-c.dot and trying to keep track of over a dozen of them. But Racket has a Graphviz library, so I was thinking if it might be possible to achieve this with Pollen.

What I want to be able to do something like:

#lang pollen
◊(require graphviz)

this is some text 

; Or something like this I guess
◊(include-graph (make-diagraph `(some code for the graphviz library)))

some more text after a picture of my FSM

I was wondering if this would work by first getting graphviz to generate the image and dump into into a temp file in a bin directory or something, then link to the image with an img tag or with whatever rendering strategy it's trying to use.

Is this something that Pollen can already do? I didn't read anything about it in the tutorials. If not, is it something you'd consider including?

sorawee commented 4 years ago

This is easily doable. In your pollen.rkt, create include-digraph as follows:

#lang racket

(require graphviz)
(provide (all-defined-out))

(define (include-digraph name digraph
                         #:ext [ext 'png]
                         #:tag [tag (λ (x) `(image ,x))]
                         #:dir [dir "images"])
  (make-directory* dir)
  (define path (build-path dir (~a name (equal-hash-code digraph) "." ext)))
  (unless (file-exists? path)
    (send (pict->bitmap (digraph->pict (make-digraph digraph))) save-file path ext))
  (tag (path->string path)))

This renders a digraph into an image file if it doesn't exist already, and then returns its path wrapped with tag.

Then, the Pollen program:

#lang pollen

this is some text

◊(include-digraph "first-diagram" 
                  `("v0" "v1" "v2" "v0 -> v0" "v0 -> v1" "v1 -> v2" "v2 -> v0") 
                  #:tag (λ (img-path) `(img ([src ,img-path]))))

some more text after a picture of my FSM

would produce

'(root
  "this is some text"
  "\n"
  "\n"
  (img ((src "images/first-diagram-1065713455284007950.png")))
  "\n"
  "\n"
  "some more text after a picture of my FSM")

The 1065713455284007950 is a (practically) unique identity of your digraph, so if you modify your digraph, it would render your digraph again. But if you make no changes to your digraph, it will reuse the existing file.

mbutterick commented 4 years ago

The {···} notation will produce a list of strings, so it would also be possible to write the tag function to directly support (some subset of) DOT input:

#lang pollen

this is some text

◊include-digraph{
v0;
v1;
v2;
v0 -> v0;
v0 -> v1;
v1 -> v2;
v2 -> v0;
} 

some more text after a picture of my FSM
nixin72 commented 4 years ago

Thank you so much! I'm pretty sure Pollen is going to become my go-to tool from now on for writing, I can't believe it's that easy to extend to include something like this!

sorawee commented 4 years ago

Note: my code above will create a lot of junk files if you modify the digraph several times. One easy improvement is to read the file images/first-digraph.data and compare it against your digraph. If they match, then skip rendering, but if it doesn't match, then update both images/first-digraph.data and images/first-digraph.png. I guess the main point is that you have a full power of programming here, so you can do whatever you want :)

mbutterick commented 4 years ago

PS. You say mixing diagrams inline with Markdown “isn't going to happen” but watch this final trick. You can use the #lang pollen/markdown dialect and write in Markdown normally. When you get to the include-digraph tag function, you’d need it to emit valid Markdown (instead of an X-expression, as it does now). But arbitrary HTML is valid inside a Markdown doc. So just run the output of your tag function through xexpr->html and your dream comes true.

nixin72 commented 4 years ago

:open_mouth:

This is so cool... Thank you so much @mbutterick and @sorawee!

shriram commented 4 years ago

Looks like y'all have solutions, so I don't want to confuse things further, but for people looking to do more visual/graphical content inside Markdown (acknowledging how @mbutterick feels about Markdown <-:), there's Markdeep: https://casual-effects.com/markdeep/.

I don't know what it would take to incorporate into Pollen, but at least Markdeep offers something of real value, especially for diagrams, well beyond the thin veneer over HTML that Markdown is.