diff --git a/CHANGES.md b/CHANGES.md index 093f65a7..1718ea0d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,16 +1,37 @@ # 0.2.0 ## Libs +### Draw +- **BREAKING** Default anchors are now using TikZ like compass names: north, south, east, west, north-west, north-east, south-west and south-east +- **BREAKING** Element anchors have changed! See the documentation for details. +- **BREAKING** Rotation direction changed to CCW +- **BREAKING** Removed the `shadow` function +- **BREAKING** Changed the behaviour of `mark` +- **BREAKING** Changed the behaviour of `translate` by changing the transformation order, changed arguments of `scale` and `translate` +- Content padding has been improved to be configurable per side +- Groups support same padding options as content +- Mark offsetting has been fixed and improved +- Added Hobby curves (`hobby`) in addition to catmull (thanks to @Enivex) +- Catmull-Rom curves, Hobby curves and arcs now can have marks +- Fixed and improved intersection calculation +- Fixed marks pointing to +/- z + ### Plot - Added `plot.add-contour(..)` for plotting contour plots - Added `plot.add-hline(..)` and `plot.add-vline(..)` for plotting h/v lines - Added `plot.add-between(..)` for filling the area between two line plots +- Added `plot.add-boxwhisker(..)` for displaying boxwhisker plots (thanks to @JamesxX) - Added `fill-type` option to `plot.add(..)` for specifying a fill type (axis or shape) - Changed default samples from 100 to 50! - Fixed plot filling in some cases +- Axes can now be locked (equal) to keep aspect ratio +- Axes can be reversed by setting min > max +- Axis orientation can be changed, enabling rotation of plots +- Plots now support legends! # 0.1.2 CeTZ requires Typst 0.8.0. + ## Draw - New `on-layer(layer, body)` function for drawing with a given layer/z-index - New `catmull(..)` function for drawing catmull-rom curves diff --git a/README.md b/README.md index 7c9661be..a96cd314 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CeTZ -CeTZ (CeTZ, ein Typst Zeichenpacket) is a library for drawing with [Typst](https://typst.app) with an API inspired by TikZ and [Processing](https://processing.org/). +CeTZ (CeTZ, ein Typst Zeichenpaket) is a library for drawing with [Typst](https://typst.app) with an API inspired by TikZ and [Processing](https://processing.org/). ## Examples @@ -56,7 +56,7 @@ For information, see the [manual](manual.pdf?raw=true). To use this package, simply add the following code to your document: ``` -#import "@preview/cetz:0.1.2" +#import "@preview/cetz:0.2.0" #cetz.canvas({ import cetz.draw: * @@ -75,7 +75,7 @@ just install The installed version can be imported by prefixing the package name with `@local`. ```typ -#import "@local/cetz:0.1.2" +#import "@local/cetz:0.2.0" #cetz.canvas({ import cetz.draw: * diff --git a/doc/example.typ b/doc/example.typ new file mode 100644 index 00000000..6310e841 --- /dev/null +++ b/doc/example.typ @@ -0,0 +1,75 @@ +#import "/src/lib.typ" + +// String that gets prefixed to every example code +// for compilation only! +#let example-preamble = "import cetz.draw: *;" +#let example-scope = (cetz: lib) + + +/// 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 = lib.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 00000000..7ca077dc --- /dev/null +++ b/doc/style.typ @@ -0,0 +1,105 @@ +#import "example.typ": example + +#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(level: style-args.first-heading-level + 1 + fields.remove("level"), 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 + ) + ), + show-outline: false, + sort-functions: none, + style: style + ) +} diff --git a/doc/util.typ b/doc/util.typ new file mode 100644 index 00000000..0ea3a98a --- /dev/null +++ b/doc/util.typ @@ -0,0 +1,117 @@ +#import "/src/lib.typ" as cetz + +/// Make the title-page +#let make-title() = { + let left-fringe = 39% + let left-color = blue.darken(30%) + let right-color = white + + let url = "https://github.com/johannes-wolf/cetz" + 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(29pt)[ein Typst Zeichenpaket] + ) + 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.version] + ) + ) + ) + pagebreak(weak: true) +} + +/// Make chapter title-page +#let make-chapter-title(left-text, right-text, sub-title: none) = { + let left-fringe = 39% + let left-color = blue.darken(30%) + let right-color = white + + 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, left-text)) + + text(29pt, right-text)) + + block( + v(1cm) + + text(20pt, if sub-title != none { sub-title } else { [] })) + + pagebreak(weak: true) +} + + +#let def-arg(term, t, default: none, description) = { + if type(t) == str { + t = t.replace("?", "|none") + t = `<` + t.split("|").map(s => { + if s == "b" { + `boolean` + } else if s == "s" { + `string` + } else if s == "i" { + `integer` + } else if s == "f" { + `float` + } else if s == "c" { + `coordinate` + } else if s == "d" { + `dictionary` + } else if s == "a" { + `array` + } else if s == "n" { + `number` + } else { + raw(s) + } + }).join(`|`) + `>` + } + + stack(dir: ltr, [/ #term: #t \ #description], align(right, if default != none {[(default: #default)]})) +} diff --git a/manual.pdf b/manual.pdf index 048067a4..8244db44 100644 Binary files a/manual.pdf and b/manual.pdf differ diff --git a/manual.typ b/manual.typ index 95a6f261..227be492 100644 --- a/manual.typ +++ b/manual.typ @@ -1,125 +1,26 @@ -#import "src/lib.typ" +#import "doc/util.typ": * +#import "doc/example.typ": example +#import "doc/style.typ" as doc-style + +#import "src/lib.typ": * #import "src/styles.typ" #import "@preview/tidy:0.1.0" -#import lib: * - -// This is a wrapper around typs-doc show-module that -// strips all but one function from the module first. -// As soon as typst-doc supports examples, this is no longer -// needed. -#let show-module-fn(module, fn, ..args) = { - module.functions = module.functions.filter(f => f.name == fn) - tidy.show-module(module, ..args.pos(), ..args.named(), - show-module-name: false, - show-outline: false) -} -#let canvas-background = gray.lighten(75%) - -// String that gets prefixed to every example code -// for compilation only! -#let example-preamble = "import lib.draw: *;" -#let example-scope = (cetz: lib, lib: lib) - -#let example(source, ..args, vertical: false) = { - let picture = canvas(eval(example-preamble + source.text, - scope: example-scope), ..args) - block(if vertical { - align( - center, - stack( - dir: ttb, - spacing: 1em, - block(width: 100%, - picture, - fill: canvas-background, - inset: 1em - ), - align(left, source) - ) - ) - } else { - table( - columns: (auto, auto), - stroke: none, - fill: (x,y) => (canvas-background, none).at(x), - align: (x,y) => (center, left).at(x), - picture, - source - ) - }, breakable: false) -} // Usage: // ```example // /* canvas drawing code */ // ``` #show raw.where(lang: "example"): text => { - example(raw(text.text, lang: "typc")) + example(text.text) } - -#let def-arg(term, t, default: none, description) = { - if type(t) == str { - t = t.replace("?", "|none") - t = `<` + t.split("|").map(s => { - if s == "b" { - `boolean` - } else if s == "s" { - `string` - } else if s == "i" { - `integer` - } else if s == "f" { - `float` - } else if s == "c" { - `coordinate` - } else if s == "d" { - `dictionary` - } else if s == "a" { - `array` - } else if s == "n" { - `number` - } else { - raw(s) - } - }).join(`|`) + `>` - } - - stack(dir: ltr, [/ #term: #t \ #description], align(right, if default != none {[(default: #default)]})) +#show raw.where(lang: "example-vertical"): text => { + example(text.text, vertical: true) } -// Title Page -#{ - let left-fringe = 39% - let left-color = blue.darken(30%) - let right-color = white - - let url = "https://github.com/johannes-wolf/cetz" - 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(29pt)[ein Typst Zeichenpacket]) - - 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 ] + lib.version.map(v => [#v]).join(".")))) - - pagebreak(weak: true) -} +#make-title() #set terms(indent: 1em) #set par(justify: true) @@ -128,7 +29,6 @@ }) #show link: set text(blue) - // Outline #{ show heading: none @@ -136,36 +36,41 @@ pagebreak(weak: true) } -#set page(numbering: "1/1", - header: align(right)[CeTZ]) +#set page(numbering: "1/1", header: align(right)[CeTZ]) = Introduction This package provides a way to draw stuff using a similar API to #link("https://processing.org/")[Processing] but with relative coordinates and anchors from #link("https://tikz.dev/")[Ti#[_k_]Z]. You also won't have to worry about accidentally drawing over other content as the canvas will automatically resize. And remember: up is positive! -The name CeTZ is a recursive acronym for "CeTZ, ein Typst Zeichenpacket" (german for "CeTZ, a Typst drawing package") and is pronounced like the word "Cats". +The name CeTZ is a recursive acronym for "CeTZ, ein Typst Zeichenpaket" (german for "CeTZ, a Typst drawing package"). = Usage This is the minimal starting point: - ```typ - #import "@preview/cetz:0.2.0" - #cetz.canvas({ - import cetz.draw: * - ... - }) - ``` +#pad(left: 1em)[```typ +#import "@preview/cetz:0.2.0" +#cetz.canvas({ + import cetz.draw: * + ... +}) +```] Note that draw functions are imported inside the scope of the `canvas` block. This is recommended as draw functions override Typst's functions such as `line`. -== Argument Types -Argument types in this document are formatted in `monospace` and encased in angle brackets `<>`. Types such as `` and `` are the same as Typst but additional are required: - / ``: Any coordinate system. See @coordinate-systems. - / ``: ` or ` +#show raw.where(block: false): it => if it.text.starts-with("<") and it.text.ends-with(">") { + set text(1.2em) + doc-style.show-type(it.text.slice(1, -1)) + } else { + it + } -== Anchors -Anchors are named positions relative to named elements. +== CeTZ Unique Argument Types +Many CeTZ functions expect data in certain formats which we will call types. Note that these are actually made up of Typst primitives. + / ``: Any coordinate system. See coordinate-systems. + / ``: Any of ``, `` or ``. + / `