cetz-plot icon indicating copy to clipboard operation
cetz-plot copied to clipboard

Mixing charts and plots

Open har7an opened this issue 10 months ago • 3 comments

Hello,

I stumbled upon this package today looking for something to draw small charts in a Document of mine. In particular I'd like to draw a barchart (or columchart as you call it) but with an added hline for clarity. I've read the docs (though not entirely) and I might be missing something trivial, but currently I'm at a loss at how to mix plot and chart elements.

According to the docs:

The barchart function is a wrapper of the plot API. Arguments passed to ..plot-args are passed to the plot.plot function.

and I've had a look at the source code as well and it appears there is no mechanism to "inject" other elements into the plot.plot call. In the docs I see that plot has a body argument which reads as if it allows injecting other drawable objects, but using it (see my example below) doesn't turn up anything.

Am I doing something wrong, or is this currently not supported?

I guess I could always copy out the columnchart function into my own document and use/adapt it from there, but I'd prefer not to duplicate your code.

Here's a MWE:

#import "@preview/cetz:0.3.2"
#import "@preview/cetz-plot:0.1.1"

#let plot_data = (
    ([A], 100),
    ([B], 85),
    ([C], 61),
    ([D], 30),
    ([E], 20),
)

#cetz.canvas({
    import cetz: draw
    import cetz-plot: chart, plot

    draw.set-style(legend: (fill: white), barchart: (bar-width: .8, cluster-gap: .0))

    chart.columnchart(
        mode: "basic",
        size: (15, 5),
        label-key: 0,
        value-key: 1,
        y-label: [reduction of something],
        x-label: [cumulated stuff],
        y-format: x => [#x],
        y-tick-step: 10,
        plot_data,
        // Doesn't work...
        //plot-args: plot.add-hline(60),
        // Doesn't work either...
        //plot-args: (
        //    body: plot.add-hline(60)
        //),
    )
    // Doesn't work...
    //plot.add-hline(60),
})

har7an avatar Jan 27 '25 10:01 har7an

Hi, this is indeed suboptimal. The chart API is a wrapper of plot.plot that does some things for you, but currently, you cannot really mix them. I will create a ticket to allow passing further plot commands to charts!

Here is a starting point for doing your chart with the plot API:

#import "@preview/cetz:0.3.2"
#import "@preview/cetz-plot:0.1.1"

#let plot-data = (
    ([A], 100),
    ([B], 85),
    ([C], 61),
    ([D], 30),
    ([E], 20),
)

#cetz.canvas({
    import cetz: draw
    import cetz-plot: chart, plot

    draw.set-style(legend: (fill: white), barchart: (bar-width: .8, cluster-gap: .0))

    let plot-data = plot-data.enumerate().map(((i, item)) => {
      // x, y, label
      (i, item.at(1), item.at(0))
    })
    
    plot.plot(
        mode: "basic",
        size: (15, 5),
        label-key: 0,
        value-key: 1,
        y-label: [reduction of something],
        x-label: [cumulated stuff],
        y-format: x => [#x],
        y-tick-step: 10,
        x-tick-step: none,
        x-ticks: plot-data.map(((x, _, label)) => (x, label)),
        x-min: -.5,
        x-max: plot-data.len() - .5,
        {
          plot.add-bar(plot-data, x-key: 0, y-key: 1, bar-width: .8)
          plot.add-hline(60)
        }
    )
})

johannes-wolf avatar Jan 27 '25 11:01 johannes-wolf

Link #108

johannes-wolf avatar Jan 27 '25 11:01 johannes-wolf

Thanks for clarifying!

In that case I'll give copying and manually modifying your code a stab for now.

har7an avatar Jan 27 '25 12:01 har7an