diff --git a/src/plot.typ b/src/plot.typ index 3e967ff..ab41379 100644 --- a/src/plot.typ +++ b/src/plot.typ @@ -12,6 +12,7 @@ #import "/src/plot/bar.typ": add-bar #import "/src/plot/errorbar.typ": add-errorbar #import "/src/plot/mark.typ" +#import plot-legend: add-legend #let default-colors = (blue, red, green, yellow, black) @@ -261,6 +262,8 @@ // Create axes for data & annotations let axis-dict = (:) for d in data + annotations { + if "axes" not in d { continue } + for (i, name) in d.axes.enumerate() { if not name in axis-dict { axis-dict.insert(name, axes.axis( @@ -304,6 +307,8 @@ // Prepare styles for i in range(data.len()) { + if "style" not in data.at(i) { continue } + let style-base = plot-style if type(style-base) == function { style-base = (style-base)(i) @@ -346,6 +351,8 @@ // Prepare for i in range(data.len()) { + if "axes" not in data.at(i) { continue } + let (x, y) = data.at(i).axes.map(name => axis-dict.at(name)) let plot-ctx = make-ctx(x, y, size) @@ -370,6 +377,8 @@ // Fill if fill-below { for d in data { + if "axes" not in d { continue } + let (x, y) = d.axes.map(name => axis-dict.at(name)) let plot-ctx = make-ctx(x, y, size) @@ -420,6 +429,8 @@ // Stroke + Mark data for d in data { + if "axes" not in d { continue } + let (x, y) = d.axes.map(name => axis-dict.at(name)) let plot-ctx = make-ctx(x, y, size) @@ -487,7 +498,7 @@ mark: item.at("mark", default: none), mark-size: item.at("mark-size", default: none), mark-style: item.at("mark-style", default: none), - ..item.style) + ..item.at("style", default: (:))) } }, ..legend-style) } diff --git a/src/plot/legend.typ b/src/plot/legend.typ index 4a46dd9..074f75e 100644 --- a/src/plot/legend.typ +++ b/src/plot/legend.typ @@ -203,3 +203,30 @@ "items.north-east", fill: style.fill, stroke: style.stroke, radius: style.radius) }) }) + +/// Function for manually adding a legend item from within +/// a plot environment +/// +/// - label (content): Legend label +/// - preview (auto,function): Legend preview function of the format `() => elements`. +/// The preview canvas bounds are between (0,0) and (1,1). +/// If set to `auto`, a straight line is drawn. +/// +/// ```example +/// add-legend([Custom item], preview _ => { +/// draw.rect((0,0), (1,1)) // Draw a rect as preview +/// }) +/// ``` +#let add-legend(label, preview: auto) = { + assert(preview == auto or type(preview) == function, + message: "Expected auto or function, got " + repr(type(preview))) + + return (( + type: "legend-item", + label: label, + style: (:), + axes: ("x", "y"), + ) + if preview != auto { + (plot-legend-preview: _ => { preview() }) + },) +} diff --git a/tests/plot/legend/ref/1.png b/tests/plot/legend/ref/1.png index a5bf299..cc29e58 100644 Binary files a/tests/plot/legend/ref/1.png and b/tests/plot/legend/ref/1.png differ diff --git a/tests/plot/legend/test.typ b/tests/plot/legend/test.typ index b3b2359..400b0af 100644 --- a/tests/plot/legend/test.typ +++ b/tests/plot/legend/test.typ @@ -1,7 +1,7 @@ #set page(width: auto, height: auto) -#import "/src/lib.typ": * -#import "/src/cetz.typ": * #import "/tests/helper.typ": * +#import cetz: draw +#import cetz-plot: plot #let dom = (domain: (0, 2 * calc.pi)) #let fn(x, offset: 0) = {calc.sin(x) + offset} @@ -9,7 +9,6 @@ #for pos in ("north", "south", "west", "east", "north-east", "north-west", "south-east", "south-west",) { - pos = "legend." + pos test-case({ import draw: * @@ -26,7 +25,6 @@ #for pos in ("inner-north", "inner-south", "inner-west", "inner-east", "inner-north-east", "inner-north-west", "inner-south-east", "inner-south-west",) { - pos = "legend." + pos test-case({ import draw: * @@ -117,7 +115,7 @@ import draw: * set-style(legend: (item: (preview: (width: .4), spacing: .7), - orientation: ltr, default-position: "legend.north")) + orientation: ltr, default-position: "north")) plot.plot(size: (4, 2), x-tick-step: none, @@ -136,7 +134,7 @@ padding: .1, stroke: black, fill: white, - orientation: ltr, default-position: "legend.north")) + orientation: ltr, default-position: "north")) plot.plot(size: (4, 2), x-tick-step: none, @@ -164,3 +162,16 @@ plot.add(samples: 10, ..dom, fn.with(offset: .2), mark: "|", style: (stroke: none), label: $ f_3(x) $) }) }) + +#test-case({ + plot.plot(size: (4,2), x-tick-step: none, y-tick-step: none, { + plot.add(domain: (0,1), x => x) + plot.add-legend([Custom 1]) + plot.add-legend([Custom 2], preview: () => { + import draw: * + set-style(stroke: blue) + line((0,0), (1,1)) + line((0,1), (1,0)) + }) + }) +})