How to have multiple stroke effect with different colors on shape?

Hi, I wanted to reproduce a figure similar to this one using typst and fletcher.

I looked at the extrude option of node to reproduce the double circles part around the start and end states circles. But I can’t see a way to change the stroking color of the extruded stroke only? Is there a way to easily do that?

Hi,
It looks like the start and end nodes have one stroke colour anyway, something like node(.., shape: circle, fill: black, stroke: red, extrude: (0, 5)). Do you mean you want the two outlines on the End node to be different colours, not both red? Unfortunately, that feature has not come up before and the only way to do it currently is do add another node at the same position but with a different stroke.

1 Like

Yes, indeed I tried a bit and converge to the same idea. I used extrude only for the end nodes. And just declare a second node for the label and put it just below. It’s just a bit weird to declare a second node below to just put the label to avoid the issue. Also, extrude is a bit confusing to work with since the edge collides with it.

My code for the moment is this

  diagram(
    node-stroke: 0.8pt,
    {
      let diamond(node, extrude) = fletcher.shapes.diamond(
        node,
        extrude,
        fit: 0.5,
      )
      node(
        (0, 1),
        shape: circle,
        fill: black,
        stroke: red,
        radius: 2.5mm,
      )
      let poll(..args) = {
        edge(..args, label: "poll()", marks: "-|>")
      }
      node((0, 1.4), "Start", stroke: none)
      poll(
        (0, 1),
        (0, 0),
        (1, 0),
        label-pos: 20%,
      )
      node((1, 0), "A file \nready?", shape: diamond)
      let light_red = red.desaturate(70%)
      let mid_red = red.darken(30%)
      edge((1, 0), (2, 0), marks: "-|>", label: "yes")
      node(
        (2, 0),
        "",
        width: 5mm,
        height: 5mm,
        shape: diamond,
        fill: light_red,
        stroke: mid_red,
      )
      edge((2, 0), (3, 0), marks: "-|>")
      node((3, 0), "B file \nready?", shape: diamond)
      node(
        (4, 1),
        shape: circle,
        fill: black,
        stroke: red,
        radius: 2.5mm,
        extrude: (0, 3),
      )
      node((4, 1.4), "End", stroke: none)
      edge((3, 0), (4, 0), (4, 1), marks: "-|>")
    },
  ),

And the rendering is that.

I ran into the same issue some while back and wrote myself a helper function. I hope that it is useful to you too :slightly_smiling_face:

#let mynode(..args) = {
  // draws multiple nodes on top of one another, with different strokes
  let (pos, named) = (args.pos(), args.named())
  let extrude = named.remove("extrude", default: none)
  if extrude == none {
    return node(..args)
  }
  let stroke = named.remove("stroke", default: ())
  let text = pos.at(1, default: none)
  assert(extrude.len() == stroke.len(), message: "amount of extrudes must match amount of strokes")
  return extrude
    .zip(stroke)
    .enumerate()
    .map(((idx, (ext, stro))) => {
      if idx == 0 {
        node(..pos, ..named, stroke: stro, extrude: ext)
      }
      // prevent text from getting ugly when there are multiple overlapping nodes
      node(pos.first(), ..named, stroke: stro, extrude: ext)
    })
}

#diagram(
	mynode(
   (0,0), 
   "multicolored node",
   radius: 4em, 
   extrude: (-2.5, 0, 2.5), 
   stroke: (red, black, blue)
 ),
	mynode(
   (3,0), 
   "spiral node",
   radius: 4em, 
   extrude: range(25), 
   stroke: range(25).map(it => gradient.linear(green, purple, angle: it * 15deg))
 ),
	mynode(
   (6,0), 
   "rainbow node",
   radius: 4em, 
   extrude: range(18).map(i => 2*i - 9), 
   stroke: color.map.turbo.chunks(15).map(arr => arr.first()),
 ),
)

3 Likes