How to draw bagua with fletcher/CeTZ?

I am beginner to use these libraries, could you please help?

https://upload.wikimedia.org/wikipedia/commons/4/4d/Bagua-name-earlier.svg

I think it is probably easiest to use the svg from wikmedia (as it is free to use when giving attribution (CC-BY-SA license)), as creating complicated drawings from text is rarely worth the hassle. If you need to recreate the image yourself, use a graphical editor like Inkscape and use the SVG generated there.

If you want to use CeTZ, you will have to type the start and end coordinates of every single line in the image (see Getting Started | CeTZ Documentation). (And fletcher does not help you here.) The SVG you link is 82 KB, and SVG is probably more compact than CeTZ – do you want to type out 82KB worth of image data by hand?

2 Likes

I agree with your conclusions, but I don’t think the argument is the right one.

There is nothing wrong with reusing high-quality images that already fit your need. If they are available, that’s the right move. But if I had to recreate this, I would rather use CeTZ than Inkscape, because there is so much symmetry and regularity in it.

The SVG is 78 KiB according to my system, but a lot of it is the glyphs. You would type the characters, not recreate them from scratch; removing them leaves only 24 KiB.

A lot of the rest is styling, for example repeated occurrences of fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: black; stroke-width: 2; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1; – you wouldn’t type that out in Typst either. Then there’s metadata, such as the title, author, licence, etc. information embedded directly in the file. I’d conservatively estimate that these two together make up at least 50% of the remaining file size.

tl;dr: the file size of an SVG does not meaningfully correspond to the amount of information one would need to manually specify when recreating it.


So to repeat: just use the existing file. But to demonstrate that especially images like this lend themselves to programmatically drawing them, here’s part of it (the trigrams) as a proof of concept:

Trigrams, 1 057 B (823 B after reducing whitespace)
#import "@preview/cetz:0.4.0"

#cetz.canvas({
  import cetz.draw: *

  let trapeze(r1, r2, _gap) = {
    let tan = calc.tan(360deg/16)
    line(
      (r1, tan*r1),
      (r2, tan*r2),
      (r2, -tan*r2),
      (r1, -tan*r1),
      close: true,
      fill: black,
      stroke: 2pt+gray,
    )
  }

  let split-trapeze(r1, r2, gap) = {
    let tan = calc.tan(360deg/16)
    line(
      (r1, tan*r1),
      (r2, tan*r2),
      (r2, gap/2),
      (r1, gap/2),
      close: true,
      fill: black,
      stroke: 2pt+gray,
    )
    line(
      (r1, -gap/2),
      (r2, -gap/2),
      (r2, -tan*r2),
      (r1, -tan*r1),
      close: true,
      fill: black,
      stroke: 2pt+gray,
    )
  }

  for (i, num) in (5, 1, 0, 4, 2, 6, 7, 3).enumerate() {
    let decode(num, bit) = {
      if num.bit-and(1.bit-lshift(bit)) == 0 { trapeze }
      else { split-trapeze }
    }
    group({
      rotate(360deg / 8 * i)
      translate(x: 0.8)
      decode(num, 0)(2.7, 2.2, 0.5)
      decode(num, 1)(3.7, 3.2, 0.5)
      decode(num, 2)(4.7, 4.2, 0.5)
    })
  }
})
3 Likes

fair enough – and impressive. Would’ve took me a while to create that demo…

tbf, it did take me a while – about 40 minutes if my browser history can be trusted for estimating it – and definitely not everyone will share my preference for code-centered workflows. But the advantage is that there is no possible way that a stroke is off by a few pixels, which is something I’d constantly worry about with Inkscape :slight_smile:

4 Likes