Aligning math functions on top of graphics in Typst?

Hello!

I have the following code:

Code
#import "@preview/cetz:0.3.1"
#import "@preview/cetz-plot:0.1.0": *

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

    let style = (stroke: black, fill: rgb(0, 0, 200, 75))

    let f1(x) = calc.sin(x)
    let nonum(eq) = math.equation(block: true, numbering: none, eq)
    let fn = (
      (black, nonum($ "Wendland Quintic" $), q => float(q >= 0) * float(q <= 2) * ((1-q/2)*(1-q/2)*(1-q/2)*(1-q/2)*(2*q + 1)) + float(-q >= 0) * float(-q <= 2) * ((1--q/2)*(1--q/2)*(1--q/2)*(1--q/2)*(2*-q + 1))),
    )
      let fn_der = (
      (black, nonum($ "Wendland Quintic - dWdq" $), q => float(q >= 0) * float(q <= 2) * ((5/8)*q*(q - 2)*(q - 2)*(q - 2)) + float(-q >= 0) * float(-q <= 2) * ((5/8)*(q)*(-q - 2)*(-q - 2)*(-q - 2))),
    )

    set-style(
      mark: (fill: black, scale: .3),
    )

    // Center of Circle
    let (cx,cy) = (0,0); let radius = 3;
    circle((cx,cy), radius: radius, fill: red.transparentize(80%), stroke: (dash: "dashed"))

    line((cx, cy), (cx + 1, cy), mark: (end: "stealth"))
    content((), nonum($ q $), anchor: "west")
    line((cx, cy), (cx    , cy + 1), mark: (end: "stealth"))
    content((), nonum($ W $), anchor: "west")
    circle((cx,cy), radius: .15, fill: green, stroke: (thickness: 0.5pt), name: "green_dot")
    content((), nonum($ i $), anchor: "north")
    circle((cx - 2.25 , cy - 1), radius: .15, fill: yellow, stroke: (thickness: 0.5pt), name: "yellow_dot")
    content((), nonum($ j $), anchor: "north")

    line( (cx - 2.25, cy - 1),(cx, cy), mark: (start: "stealth", end: "stealth"), name: "ij_line")
    content(
      ("ij_line.start", 50%, "ij_line.end"),
      angle: "ij_line.end",
      padding: .1,
      anchor: "north",
      nonum($ norm(bold(x)_i - bold(x)_j) $)
    )

    // Perform plot
    plot.plot(size: (12, 8),
      scale: 0.1,
      x-label: none,
      y-label: none,
      // x-tick-step: 1,
      // x-format: plot.formats.multiple-of,
      // y-tick-step: 0.25, y-min: -1.25, y-max: 1,
      axis-style: none,
      legend: none,
      {
        let domain = (-2, 2)

        for ((paint, title, f)) in fn {
          plot.add(f, style: (stroke: (paint: paint)), domain: domain, label: title)
        }
        for ((paint, title, f)) in fn_der {
          plot.add(f, style: (stroke: (paint: paint, dash: "dashed")), domain: domain, label: title)
        }
        
      })


  })

It produces the following image:

image

I need it to produce something closer to:

image

So basically I need to:

  1. Align the figure in the middle
  2. Insert the functions in the span of the circle
  3. Modify text labels, so they are not on top of dots
  4. Bonus: Add blue dots in a nice manner

Would anyone be able to show me how to reach this kind of graphic?

Kind regards

I think of set-origin so that the plot moves to the new defined origin.
adding the following line of code:

// the drawings 
    set-origin((-3,-2))

    // Perform plot
    plot.plot(size: (6, 4),
// ..rest as the same ...

Thanks, now I have this:

#import "@preview/cetz:0.3.1"
#import "@preview/cetz-plot:0.1.0": *

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

    let style = (stroke: black, fill: rgb(0, 0, 200, 75))

    let f1(x) = calc.sin(x)
    let nonum(eq) = math.equation(block: true, numbering: none, eq)
    let fn = (
      (orange, nonum($ "Wendland Quintic" $), q => float(q >= 0) * float(q <= 2) * ((1-q/2)*(1-q/2)*(1-q/2)*(1-q/2)*(2*q + 1)) + float(-q >= 0) * float(-q <= 2) * ((1--q/2)*(1--q/2)*(1--q/2)*(1--q/2)*(2*-q + 1))),
    )
      let fn_der = (
      (orange, nonum($ "Wendland Quintic - dWdq" $), q => float(q >= 0) * float(q <= 2) * ((5/8)*q*(q - 2)*(q - 2)*(q - 2)) + float(-q >= 0) * float(-q <= 2) * ((5/8)*(q)*(-q - 2)*(-q - 2)*(-q - 2))),
    )

    // Scale the y-axis
    set-style(
      mark: (fill: black, scale: .3),
    )

    // Center of Circle
    let (cx,cy) = (0,0); let radius = 3;
    circle((cx,cy), radius: radius, fill: red.transparentize(80%), stroke: (dash: "dashed"))

    line((cx, cy), (cx + 1, cy), mark: (end: "stealth"))
    content((), nonum($ q $), anchor: "west")
    line((cx, cy), (cx    , cy + 1), mark: (end: "stealth"))
    content((), nonum($ W $), anchor: "west")
    circle((cx,cy), radius: .15, fill: green, stroke: (thickness: 0.5pt), name: "green_dot")
    content((), nonum($ i $), anchor: "north")
    circle((cx - 2.25 , cy - 1), radius: .15, fill: yellow, stroke: (thickness: 0.5pt), name: "yellow_dot")
    content((), nonum($ j $), anchor: "north")
    line( (cx - 2.25, cy - 1),(cx, cy), mark: (start: "stealth", end: "stealth"), name: "ij_line")
    content(
      ("ij_line.start", 50%, "ij_line.end"),
      angle: "ij_line.end",
      padding: .1,
      anchor: "north",
      nonum($ norm(bold(x)_i - bold(x)_j) $)
    )
    
    scale(x: 50%, y: 50%)  
    set-origin((-6,-4))

    // Perform plot
    plot.plot(size: (12, 8),
      x-label: none,
      y-label: none,
      // x-tick-step: 1,
      // x-format: plot.formats.multiple-of,
      // y-tick-step: 0.25, y-min: -1.25, y-max: 1,
      axis-style: none,
      legend: none,
      {
        let domain = (-2, 2)

        for ((paint, title, f)) in fn {
          plot.add(f, style: (stroke: (paint: paint)), domain: domain, label: title)
        }
        for ((paint, title, f)) in fn_der {
          plot.add(f, style: (stroke: (paint: paint, dash: "dashed")), domain: domain, label: title)
        }
        
      })
  })

Where to get the scale and set-origin right, I had to change the draw order… I want the functions to be drawn first but cant get it to work and still need to edit the placement of labels…

1 Like

Do you want more space of the labels? If so, you can set set-style(content: (padding: 10pt)) so that the labels placed farther from its coordinates.
And, the order of the drawings depends on the line of code, the code that appear first, it will be at the back.

Thanks!

#import "@preview/cetz:0.3.1"
#import "@preview/cetz-plot:0.1.0": *

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

    let style = (stroke: black, fill: rgb(0, 0, 200, 75))

    let f1(x) = calc.sin(x)
    let nonum(eq) = math.equation(block: true, numbering: none, eq)
    let fn = (
      (orange, nonum($ "Wendland Quintic" $), q => float(q >= 0) * float(q <= 2) * ((1-q/2)*(1-q/2)*(1-q/2)*(1-q/2)*(2*q + 1)) + float(-q >= 0) * float(-q <= 2) * ((1--q/2)*(1--q/2)*(1--q/2)*(1--q/2)*(2*-q + 1))),
    )
      let fn_der = (
      (orange, nonum($ "Wendland Quintic - dWdq" $), q => float(q >= 0) * float(q <= 2) * ((5/8)*q*(q - 2)*(q - 2)*(q - 2)) + float(-q >= 0) * float(-q <= 2) * ((5/8)*(q)*(-q - 2)*(-q - 2)*(-q - 2))),
    )

    // Scale the y-axis
    set-style(
      mark: (fill: black, scale: .3),
      content: (padding: 5pt)
    )

    // Center of Circle
    let (cx,cy) = (0,0); let radius = 3;
    circle((cx,cy), radius: radius, fill: red.transparentize(80%), stroke: (dash: "dashed"))

    line((cx, cy), (cx + 1, cy), mark: (end: "stealth"))
    content((), nonum($ q $), anchor: "west")
    line((cx, cy), (cx    , cy + 1), mark: (end: "stealth"))
    content((), nonum($ W $), anchor: "west")
    circle((cx,cy), radius: .15, fill: green, stroke: (thickness: 0.5pt), name: "green_dot")
    content((), nonum($ i $), anchor: "north")
    circle((cx - 2.25 , cy - 1), radius: .15, fill: yellow, stroke: (thickness: 0.5pt), name: "yellow_dot")
    content((), nonum($ j $), anchor: "north")
    line( (cx - 2.25, cy - 1),(cx, cy), mark: (start: "stealth", end: "stealth"), name: "ij_line")
    content(
      ("ij_line.start", 50%, "ij_line.end"),
      angle: "ij_line.end",
      padding: .1,
      anchor: "north",
      nonum($ norm(bold(x)_i - bold(x)_j) $)
    )
    
    scale(x: 50%, y: 50%)  
    set-origin((-6,-4))

    // Perform plot
    plot.plot(size: (12, 8),
      x-label: none,
      y-label: none,
      // x-tick-step: 1,
      // x-format: plot.formats.multiple-of,
      // y-tick-step: 0.25, y-min: -1.25, y-max: 1,
      axis-style: none,
      legend: none,
      {
        let domain = (-2, 2)

        for ((paint, title, f)) in fn {
          plot.add(f, style: (stroke: (paint: paint)), domain: domain, label: title)
        }
        for ((paint, title, f)) in fn_der {
          plot.add(f, style: (stroke: (paint: paint, dash: "dashed")), domain: domain, label: title)
        }
        
      })
  })

Now I get:

When I rearrange I get this:

Could you kindly show me how to properly rearrange? Due to the scale and set-origin it does not seem straight forward. I had hoped there would be a keyword to control layering as well, in cases like mine.

Kind regards