diff --git a/doc/example.typ b/doc/example.typ new file mode 100644 index 0000000..924c432 --- /dev/null +++ b/doc/example.typ @@ -0,0 +1,76 @@ +#import "/src/cetz.typ" as cetz +#import "/src/lib.typ" as cetz-plot + +// String that gets prefixed to every example code +// for compilation only! +#let example-preamble = "import cetz.draw: *; import cetz-plot: *;" +#let example-scope = (cetz: cetz, cetz-plot: cetz-plot) + + +/// Render an example from a string +/// - source (string, raw): Example source code +/// - args (arguments): Arguments passed down to the canvas +/// - vertical (boolean): If true, show the code below the canvas +#let example(source, ..args, vertical: false) = { + if type(source) == content { + source = source.text + } + + let radius = .25cm + let border = 1pt + gray + let canvas-background = yellow.lighten(95%) + + let picture = cetz.canvas( + eval( + example-preamble + source, + scope: example-scope + ), + ..args + ) + let source = box( + raw( + source, + lang: "typc" + ), + width: 100% + ) + + block( + if vertical { + align( + center, + stack( + dir: ttb, + spacing: 1em, + block( + width: 100%, + clip: true, + radius: radius, + stroke: border, + table( + columns: 1, + stroke: none, + fill: (c,r) => (canvas-background, white).at(r), + picture, + align(left, source) + ) + ), + ) + ) + } else { + block( + table( + columns: 2, + stroke: none, + fill: (canvas-background, white), + align: (center + horizon, left), + picture, + source + ), + width: 100%, + radius: radius, + clip: true, + stroke: border + ) + }, breakable: false) +} diff --git a/doc/style.typ b/doc/style.typ new file mode 100644 index 0000000..95512a7 --- /dev/null +++ b/doc/style.typ @@ -0,0 +1,107 @@ +#import "example.typ": example +#import "/src/lib.typ" + +#import "@preview/tidy:0.1.0" +#import "@preview/t4t:0.3.2": is + +#let show-function(fn, style-args) = { + [ + #heading(fn.name, level: style-args.first-heading-level + 1) + #label(style-args.label-prefix + fn.name + "()") + ] + let description = if is.sequence(fn.description) { + fn.description.children + } else { + (fn.description,) + } + let parameter-index = description.position(e => { + e.func() == heading and e.body == [parameters] + }) + + description = description.map(e => if e.func() == heading { + let fields = e.fields() + let label = fields.remove("label", default: none) + heading(offset: style-args.first-heading-level + 1, fields.remove("body"), ..fields); [#label] + } else { e }) + + if parameter-index != none { + description.slice(0, parameter-index).join() + } else { + description.join() + } + + set heading(level: style-args.first-heading-level + 2) + + block(breakable: style-args.break-param-descriptions, { + heading("Parameters", level: style-args.first-heading-level + 2) + (style-args.style.show-parameter-list)(fn, style-args.style.show-type) + }) + + for (name, info) in fn.args { + let types = info.at("types", default: ()) + let description = info.at("description", default: "") + if description == [] and style-args.omit-empty-param-descriptions { continue } + (style-args.style.show-parameter-block)( + name, types, description, + style-args, + show-default: "default" in info, + default: info.at("default", default: none), + ) + } + + if parameter-index != none { + description.slice(parameter-index+1).join() + } +} + +#let show-parameter-block(name, types, content, show-default: true, default: none, in-tidy: false, ..a) = { + if type(types) != array { + types = (types,) + } + stack(dir: ttb, spacing: 1em, + // name Default: + block(breakable: false, width: 100%, stack(dir: ltr, + [#text(weight: "bold", name + [:]) #types.map(tidy.styles.default.show-type).join(" or ")], + if show-default { + align(right)[ + Default: #raw( + lang: "typc", + // Tidy gives defaults as strings but outside of tidy we pass defaults as the actual values + if in-tidy { default } else { repr(default) } + ) + ] + } + )), + // text + block(inset: (left: .4cm), content) + ) +} + + +#let show-type = tidy.styles.default.show-type +#let show-outline = tidy.styles.default.show-outline +#let show-parameter-list = tidy.styles.default.show-parameter-list + +#let style = ( + show-function: show-function, + show-parameter-block: show-parameter-block.with(in-tidy: true), + show-type: show-type, + show-outline: show-outline, + show-parameter-list: show-parameter-list +) + +#let parse-show-module(path) = { + tidy.show-module( + tidy.parse-module( + read(path), + scope: ( + example: example, + show-parameter-block: show-parameter-block, + cetz: lib + ) + ), + show-outline: false, + sort-functions: none, + style: style + ) +} diff --git a/doc/util.typ b/doc/util.typ new file mode 100644 index 0000000..d0715c2 --- /dev/null +++ b/doc/util.typ @@ -0,0 +1,62 @@ +#import "/src/lib.typ" as cetz-plot + +/// Make the title-page +#let make-title() = { + let left-fringe = 39% + let left-color = rgb(140,90,255).darken(30%) + let right-color = white + + let url = "https://github.com/cetz-package/cetz-plot" + let authors = ( + ([Johannes Wolf], "https://github.com/johannes-wolf"), + ([fenjalien], "https://github.com/fenjalien"), + ) + + set page( + numbering: none, + background: place( + top + left, + rect( + width: left-fringe, + height: 100%, + fill: left-color + ) + ), + margin: ( + left: left-fringe * 22cm, + top: 12% * 29cm + ), + header: none, + footer: none + ) + + set text(weight: "bold", left-color) + show link: set text(left-color) + + block( + place( + top + left, + dx: -left-fringe * 22cm + 5mm, + text(3cm, right-color)[CeTZ] + ) + + text(3cm)[Plot] + ) + block( + v(1cm) + + text( + 20pt, + authors.map(v => link(v.at(1), [#v.at(0)])).join("\n") + ) + ) + block( + v(2cm) + + text( + 20pt, + link( + url, + [Version ] + [#cetz-plot.version] + ) + ) + ) + pagebreak(weak: true) +} diff --git a/manual.pdf b/manual.pdf index 30abe3f..8947a66 100644 Binary files a/manual.pdf and b/manual.pdf differ diff --git a/manual.typ b/manual.typ index 1333ed7..ad4d930 100644 --- a/manual.typ +++ b/manual.typ @@ -1 +1,66 @@ -TODO +#import "/doc/util.typ": * +#import "/doc/example.typ": example +#import "/doc/style.typ" as doc-style +#import "/src/lib.typ": * +#import "/src/cetz.typ": * +#import "@preview/tidy:0.2.0" + + +// Usage: +// ```example +// /* canvas drawing code */ +// ``` +#show raw.where(lang: "example"): example +#show raw.where(lang: "example-vertical"): example.with(vertical: true) + +#make-title() + +#set terms(indent: 1em) +#set par(justify: true) +#set heading(numbering: (..num) => if num.pos().len() < 4 { + numbering("1.1", ..num) + }) +#show link: set text(blue) + +// Outline +#{ + show heading: none + columns(2, outline(indent: true, depth: 3)) + pagebreak(weak: true) +} + +#set page(numbering: "1/1", header: align(right)[CeTZ-Plot]) + += Introduction + +CeTZ-Plot is a simple plotting library for use with CeTZ. + += Usage + +This is the minimal starting point: +#pad(left: 1em)[```typ +#import "@preview/cetz:0.2.2" +#import "@preview/cetz-plot:0.1.0" +#cetz.canvas({ + import cetz.draw: * + import cetz-plot: * + ... +}) +```] +Note that plot functions are imported inside the scope of the `canvas` block. +All following example code is expected to be inside a `canvas` block, with the `plot` +module imported into the namespace. + += Plot + +#doc-style.parse-show-module("/src/plot.typ") +#for m in ("line", "bar", "boxwhisker", "contour", "errorbar", "annotation", "formats") { + doc-style.parse-show-module("/src/plot/" + m + ".typ") +} + += Chart + +#doc-style.parse-show-module("/src/chart.typ") +#for m in ("barchart", "boxwhisker", "columnchart", "piechart") { + doc-style.parse-show-module("/src/chart/" + m + ".typ") +} diff --git a/src/chart/barchart.typ b/src/chart/barchart.typ index cbb42b7..ec7df40 100644 --- a/src/chart/barchart.typ +++ b/src/chart/barchart.typ @@ -14,7 +14,7 @@ /// Draw a bar chart. A bar chart is a chart that represents data with /// rectangular bars that grow from left to right, proportional to the values -/// they represent. For examples see @barchart-examples. +/// they represent. /// /// = Styling /// *Root*: `barchart`. diff --git a/src/chart/columnchart.typ b/src/chart/columnchart.typ index d05db24..5a4fcb3 100644 --- a/src/chart/columnchart.typ +++ b/src/chart/columnchart.typ @@ -14,7 +14,7 @@ /// Draw a column chart. A column chart is a chart that represents data with /// rectangular bars that grow from bottom to top, proportional to the values -/// they represent. For examples see @columnchart-examples. +/// they represent. /// /// = Styling /// *Root*: `columnchart`. diff --git a/src/plot.typ b/src/plot.typ index ab5cf2d..112417d 100644 --- a/src/plot.typ +++ b/src/plot.typ @@ -32,7 +32,6 @@ /// axis styles to draw, see its parameter `axis-style:`. /// /// #example(``` -/// import cetz.plot /// plot.plot(size: (2,2), x-tick-step: none, y-tick-step: none, { /// plot.add(((0,0), (1,1), (2,.5), (4,3))) /// }) @@ -59,10 +58,10 @@ /// This can be useful to force one axis to grow or shrink with another one. /// You can only "lock" two axes of different orientations. /// #example(``` -/// cetz.plot.plot(size: (2,1), x-tick-step: 1, y-tick-step: 1, -/// x-equal: "y", +/// plot.plot(size: (2,1), x-tick-step: 1, y-tick-step: 1, +/// x-equal: "y", /// { -/// cetz.plot.add(domain: (0, 2 * calc.pi), +/// plot.add(domain: (0, 2 * calc.pi), /// t => (calc.cos(t), calc.sin(t))) /// }) /// ```) @@ -84,13 +83,13 @@ /// setting custom tick mark labels per mark. /// /// #example(``` -/// cetz.plot.plot(x-tick-step: none, y-tick-step: none, -/// x-min: 0, x-max: 4, -/// x-ticks: (1, 2, 3), -/// y-min: 1, y-max: 2, -/// y-ticks: ((1, [One]), (2, [Two])), +/// plot.plot(x-tick-step: none, y-tick-step: none, +/// x-min: 0, x-max: 4, +/// x-ticks: (1, 2, 3), +/// y-min: 1, y-max: 2, +/// y-ticks: ((1, [One]), (2, [Two])), /// { -/// cetz.plot.add(((0,0),)) +/// plot.add(((0,0),)) /// }) /// ```) /// @@ -103,11 +102,11 @@ /// /// #example(``` /// let formatter(v) = if v != 0 {$ #{v/calc.pi} pi $} else {$ 0 $} -/// cetz.plot.plot(x-tick-step: calc.pi, y-tick-step: none, +/// plot.plot(x-tick-step: calc.pi, y-tick-step: none, /// x-min: 0, x-max: 2 * calc.pi, /// x-format: formatter, /// { -/// cetz.plot.add(((0,0),)) +/// plot.add(((0,0),)) /// }) /// ```) /// ]) @@ -124,11 +123,11 @@ /// The value `"both"` enables grid lines for both, major- and minor ticks. /// /// #example(``` -/// cetz.plot.plot(x-tick-step: 1, y-tick-step: 1, -/// y-minor-tick-step: .2, -/// x-min: 0, x-max: 2, x-grid: true, -/// y-min: 0, y-max: 2, y-grid: "both", { -/// cetz.plot.add(((0,0),)) +/// plot.plot(x-tick-step: 1, y-tick-step: 1, +/// y-minor-tick-step: .2, +/// x-min: 0, x-max: 2, x-grid: true, +/// y-min: 0, y-max: 2, y-grid: "both", { +/// plot.add(((0,0),)) /// }) /// ```) /// ]) @@ -158,7 +157,7 @@ /// let data = cetz.plot.add(((-1,-1), (1,1),), mark: "o") /// /// for name in (none, "school-book", "left", "scientific") { -/// cetz.plot.plot(axis-style: name, ..opts, data, name: "plot") +/// plot.plot(axis-style: name, ..opts, data, name: "plot") /// content(((0,-1), "-|", "plot.south"), repr(name)) /// set-origin((3.5,0)) /// } @@ -184,7 +183,7 @@ /// ]) /// - fill-below (bool): If true, the filled shape of plots is drawn _below_ axes. /// - name (string): The plots element name to be used when referring to anchors -/// - legend (none, auto, coordinate): The position the legend will be drawn at. See @plot-legends for information about legends. If set to ``, the legend's "default-placement" styling will be used. If set to a ``, it will be taken as relative to the plot's origin. +/// - legend (none, auto, coordinate): The position the legend will be drawn at. See plot-legends for information about legends. If set to ``, the legend's "default-placement" styling will be used. If set to a ``, it will be taken as relative to the plot's origin. /// - legend-anchor (auto, string): Anchor of the legend group to use as its origin. /// If set to `auto` and `lengend` is one of the predefined legend anchors, the /// opposite anchor to `legend` gets used. diff --git a/src/plot/formats.typ b/src/plot/formats.typ index 635f256..c51dbc4 100644 --- a/src/plot/formats.typ +++ b/src/plot/formats.typ @@ -40,6 +40,15 @@ /// Fraction tick formatter /// +/// ```example +/// plot.plot(size: (5,1), +/// x-format: plot.formats.fraction, +/// x-tick-step: 1/5, +/// y-tick-step: none, { +/// plot.add(calc.sin, domain: (-1, 1)) +/// }) +/// ``` +/// /// - value (number): Value to format /// - denom (auto, int): Denominator for result fractions. If set to `auto`, /// a hardcoded fraction table is used for finding fractions with a @@ -53,8 +62,10 @@ /// Multiple of tick formatter /// /// ```example -/// plot.plot(x-format: plot.formats.multiple-of, -/// x-tick-step: calc.pi/4, { +/// plot.plot(size: (5,1), +/// x-format: plot.formats.multiple-of, +/// x-tick-step: calc.pi/4, +/// y-tick-step: none, { /// plot.add(calc.sin, domain: (-calc.pi, 1.5 * calc.pi)) /// }) /// ``` @@ -94,6 +105,15 @@ /// Scientific notation tick formatter /// +/// ```example +/// plot.plot(size: (5,1), +/// x-format: plot.formats.sci, +/// x-tick-step: 1e3, +/// y-tick-step: none, { +/// plot.add(x => x, domain: (-2e3, 2e3)) +/// }) +/// ``` +/// /// - value (number): Value to format /// - digits (int): Number of digits for rouding the factor /// -> Content