diff --git a/CHANGES.md b/CHANGES.md index 8d301aa75..3113ca363 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +# 0.5.3 +- Fixed mark `inset:` style getting applied twice (#1099) +- Fixes `matrix.diag` function typo +- Added a new `make-ctx` function for creating a new canvas context without a canvas +- Fixed a bug with hobby and boolean rejecting two component vectors + # 0.5.1 - Added the `boolean` draw function for path boolean operations. - Fixed border-anchor calculation for small rounded rects (#1083) diff --git a/README.md b/README.md index 6e7c604b3..e1e3e5d01 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ For information, see the [API Reference (PDF)](./manual.pdf?raw=true) or the [on To use this package, simply add the following code to your document: ```typ -#import "@preview/cetz:0.5.2" +#import "@preview/cetz:0.5.3" #cetz.canvas({ import cetz.draw: * @@ -127,7 +127,7 @@ just install The installed version can be imported by prefixing the package name with `@local`. ```typ -#import "@local/cetz:0.5.2" +#import "@local/cetz:0.5.3" #cetz.canvas({ import cetz.draw: * diff --git a/docs/basics/canvas.md b/docs/basics/canvas.md index 1837b1b0d..e36bae91a 100644 --- a/docs/basics/canvas.md +++ b/docs/basics/canvas.md @@ -15,4 +15,4 @@ You can now call the draw functions within the body and they'll produce some gra The canvas does not have typical `width` and `height` parameters. Instead its size will grow and shrink to fit the drawn graphic. -By default 1 [coordinate](/basics/coordinate-systems) unit is `1cm`, this can be changed by setting the `length` parameter. If a ratio is given, the length will be the size of the canvas' parent's width! +By default 1 [coordinate](/basics/coordinate-systems) unit is `1cm`, this can be changed by setting the `length` parameter. diff --git a/docs/basics/coordinate-systems.mdx b/docs/basics/coordinate-systems.mdx index 83f2444d3..3c46e34b8 100644 --- a/docs/basics/coordinate-systems.mdx +++ b/docs/basics/coordinate-systems.mdx @@ -7,6 +7,8 @@ import Type from "@site/src/components/Type"; A coordinate is a position on the canvas on which the picture is drawn. They take the form of dictionaries and the following subsections define the key-value pairs for each system. Some systems have a more implicit form as an array of values and CeTZ attempts to infer the system based on the element types. +By default 1 coordinate unit is `1cm`, this can be changed by setting the `length` parameter of [`canvas`](/api/internal/canvas). + ## XYZ Defines a point `x` units right, `y` units upward and `z` units away. diff --git a/docs/genhtml.py b/docs/genhtml.py index 7f2f17385..8d1c0bf8d 100755 --- a/docs/genhtml.py +++ b/docs/genhtml.py @@ -10,7 +10,7 @@ from pathlib import Path -DEFAULT_CETZ_VERSION="@preview/cetz:0.5.2" +DEFAULT_CETZ_VERSION="@preview/cetz:0.5.3" def typst_template(code, cetz_path): diff --git a/docs/getting-started.mdx b/docs/getting-started.mdx index 8031ae364..efeac7b46 100644 --- a/docs/getting-started.mdx +++ b/docs/getting-started.mdx @@ -8,7 +8,7 @@ sidebar_position: 1 This is the minimal starting point in a `.typ` file: ```typ -#import "@preview/cetz:0.5.2" +#import "@preview/cetz:0.5.3" #cetz.canvas({ import cetz.draw: * ... diff --git a/docs/tutorials/karl.mdx b/docs/tutorials/karl.mdx index f9a619564..66e1a2d43 100644 --- a/docs/tutorials/karl.mdx +++ b/docs/tutorials/karl.mdx @@ -20,7 +20,7 @@ In CeTZ, to draw a picture, two imports and a function call is all you need. Kar ```typ #set page(width: auto, height: auto) -#import "@preview/cetz:0.5.2" +#import "@preview/cetz:0.5.3" We are working on #cetz.canvas({ diff --git a/gallery/karls-picture.png b/gallery/karls-picture.png index b3a602eb3..a9bfe226f 100644 Binary files a/gallery/karls-picture.png and b/gallery/karls-picture.png differ diff --git a/gallery/karls-picture.typ b/gallery/karls-picture.typ index b86771bb2..3525cf6ca 100644 --- a/gallery/karls-picture.typ +++ b/gallery/karls-picture.typ @@ -1,12 +1,15 @@ -#import "@preview/cetz:0.5.2" +#import "@preview/cetz:0.5.3" #set page(width: auto, height: auto, margin: .5cm) -#show math.equation: block.with(fill: white, inset: 1pt) +#show math.equation: block.with(fill: white.transparentize(30%), inset: 1pt, radius: 2pt) // Create a new canvas to draw on #cetz.canvas(length: 3cm, { import cetz.draw: * + // Control the angle + let angle = 212deg + // Change the design for all elements after it set-style( // Design of arrow tips at the end of lines @@ -18,14 +21,15 @@ radius: 0.3, label-radius: .22, fill: green.lighten(80%), - stroke: (paint: green.darken(50%)) + stroke: green.darken(50%) ), // Design of all text elements with an anchor content: (padding: 1pt) ) - // Draws the grid behind the circle - grid((-1.5, -1.5), (1.4, 1.4), step: 0.5, stroke: gray + 0.2pt) + // Draw the grids behind the circle + grid((-1.5, -1.5), (1.5, 1.5), step: 0.25, stroke: gray + 0.1pt) + grid((-1.5, -1.5), (1.5, 1.5), step: 0.5, stroke: gray + 0.2pt) // Draw the unit circle circle((0,0), radius: 1) @@ -48,29 +52,35 @@ content((), anchor: "east", ct) } + // Position on the unit circle (in polar coordinates) + let pos = (angle, 1) + // Draw the green angle - cetz.angle.angle((0,0), (1,0), (1, calc.tan(30deg)), - label: text(green, [#sym.alpha])) + cetz.angle.angle((0,0), (1,0), pos, + label: text(green, $alpha = angle.deg() degree$)) + + // Draw the hypotenuse of the triangle + line(pos, (1, calc.tan(angle))) + line(pos, (0, 0)) - // Draw the hypothenuse of the triangle - line((0,0), (1, calc.tan(30deg))) // Change the stroke for all upcoming elements set-style(stroke: (thickness: 1.2pt)) // Draw the inner opposite leg of the triangle: // "The intersection of a vertical line (|-) through (30deg, 1) and a horizontal line through (0, 0)" - line((30deg, 1), ((), "|-", (0,0)), stroke: (paint: red), name: "sin") + line(pos, ((), "|-", (0,0)), stroke: red, name: "sin") // Place the text halfway through on the opposite leg content(("sin.start", 50%, "sin.end"), text(red)[$ sin alpha $]) - + // Draw the adjacent leg of the triangle - line("sin.end", (0,0), stroke: (paint: blue), name: "cos") + line("sin.end", (0,0), stroke: blue, name: "cos") // Place the text halfway and position it below the line content(("cos.start", 50%, "cos.end"), text(blue)[$ cos alpha $], anchor: "north") // Draw the outer opposite leg of the triangle - line((1, 0), (1, calc.tan(30deg)), name: "tan", stroke: (paint: orange)) - // Draw the tangent equasion at the top and to the right of the line + line((1, 0), (1, calc.tan(angle)), name: "tan", stroke: orange) + + // Draw the tangent equation at the top and to the right of the line content("tan.end", $ text(#orange, tan alpha) = text(#red, sin alpha) / text(#blue, cos alpha) $, anchor: "west") }) diff --git a/gallery/paciolis.typ b/gallery/paciolis.typ index 812f68064..ada2de405 100644 --- a/gallery/paciolis.typ +++ b/gallery/paciolis.typ @@ -1,5 +1,5 @@ // Example by @samuelireson -#import "@preview/cetz:0.5.2": canvas, draw +#import "@preview/cetz:0.5.3": canvas, draw #set page(width: auto, height: auto, margin: .5cm) diff --git a/gallery/periodic-table.typ b/gallery/periodic-table.typ index de6660da2..9eeff68ba 100644 --- a/gallery/periodic-table.typ +++ b/gallery/periodic-table.typ @@ -1,5 +1,5 @@ // Copied from https://github.com/janosh/tikz/blob/da1b4582/assets/periodic-table/periodic-table.typ -#import "@preview/cetz:0.5.2": canvas, draw +#import "@preview/cetz:0.5.3": canvas, draw #set page(width: auto, height: auto, margin: 15pt) diff --git a/gallery/plate-capacitor.typ b/gallery/plate-capacitor.typ index 66aafb088..278bc7e00 100644 --- a/gallery/plate-capacitor.typ +++ b/gallery/plate-capacitor.typ @@ -1,5 +1,5 @@ // Copied from https://github.com/janosh/tikz/blob/87754ea/assets/plate-capacitor/plate-capacitor.typ -#import "@preview/cetz:0.5.2": canvas, draw +#import "@preview/cetz:0.5.3": canvas, draw #import draw: line, rect, content, bezier, group, anchor #set page(width: auto, height: auto, margin: 5pt) diff --git a/gallery/torus.typ b/gallery/torus.typ index ffba55e84..585e27c86 100644 --- a/gallery/torus.typ +++ b/gallery/torus.typ @@ -1,6 +1,6 @@ // From https://forum.typst.app/t/how-to-best-draw-a-3d-torus/4744/4 // Note: current settings use about 2 GiB of RAM and 20 s of compilation time. -#import "@preview/cetz:0.5.2" +#import "@preview/cetz:0.5.3" #set page(width: auto, height: auto, margin: 0.6pt) diff --git a/gallery/tree.typ b/gallery/tree.typ index 9e9a3c317..235f2cf97 100644 --- a/gallery/tree.typ +++ b/gallery/tree.typ @@ -1,4 +1,4 @@ -#import "@preview/cetz:0.5.2": canvas, draw, tree +#import "@preview/cetz:0.5.3": canvas, draw, tree #set page(width: auto, height: auto, margin: .5cm) diff --git a/gallery/waves.typ b/gallery/waves.typ index 956a42af9..0895afcb6 100644 --- a/gallery/waves.typ +++ b/gallery/waves.typ @@ -1,4 +1,4 @@ -#import "@preview/cetz:0.5.2": canvas, draw +#import "@preview/cetz:0.5.3": canvas, draw #set page(width: auto, height: auto, margin: .5cm) diff --git a/manual.typ b/manual.typ index f50ce400e..0e87bcd3b 100644 --- a/manual.typ +++ b/manual.typ @@ -93,7 +93,7 @@ automatically resize. And remember: up is positive! == Usage This is the minimal starting point in a `.typ` file: ```typst -#import "@preview/cetz:0.5.2" +#import "@preview/cetz:0.5.3" #cetz.canvas({ import cetz.draw: * ... diff --git a/src/canvas.typ b/src/canvas.typ index 84d685cf6..69e8afbd6 100644 --- a/src/canvas.typ +++ b/src/canvas.typ @@ -9,33 +9,25 @@ #import "process.typ" #import "coordinate.typ" -/// Sets up a canvas for drawing on. +/// Returns an empty context object usable for calling cetz internal functions +/// outsides a canvas. /// -/// - length (length): Used to specify what 1 coordinate unit is. Note that ratios are no longer supported! You can wrap the canvas into a `layout(ly => canvas(length: ly.width * , ...))`. -/// - baseline (none,number,coordinate): Specifies the coordinate to use as the baseline. Setting this the canvas behaves like a `box` element instead of a `block`. -/// - body (none, array, element): A code block in which functions from the `draw` module have been called. -/// - background (none, color): A color to be used for the background of the canvas. -/// - stroke (none, stroke): Stroke style to apply to the canvas top-level element (box or block) -/// - padding (none, number, array, dictionary) = none: How much padding to add to the canvas. `none` applies no padding. A number applies padding to all sides equally. A dictionary applies padding following Typst's `pad` function: https://typst.app/docs/reference/layout/pad/. An array follows CSS like padding: `(y, x)`, `(top, x, bottom)` or `(top, right, bottom, left)`. +/// - length (length): Used to specify what 1 coordinate unit is. /// - x (number, vector) = 1.0: Sets up the x vector of the coordinate system to `(x, 0, 0)` or to the given vector. /// - y (number, vector) = 1.0: Sets up the y vector of the coordinate system to `(0, y, 0)` or to the given vector. /// - z (number, vector) = 1.0: Sets up the z vector of the coordinate system to `(0, 0, z)` or to the given vector. -/// - debug (bool): Shows the bounding boxes of each element when `true`. -/// -> content -#let canvas(length: 1cm, x: 1.0, y: 1.0, z: 1.0, baseline: none, debug: false, background: none, stroke: none, padding: none, body) = context { - if body == none { - return [] - } - if type(body) != array { - panic("Incorrect type for body: " + repr(type(body))) - } +/// -> context +#let make-ctx(length: 1cm, x: 1.0, y: 1.0, z: 1.0) = { if type(length) != std.length { panic("Expected `length` to be of type length, got " + repr(length)) } - let length = length.to-absolute() - assert(length / 1cm != 0, - message: "Canvas length must be != 0!") + let length = if length.em != 0 { + length.to-absolute() + } else { + length + } + assert(length / 1cm != 0, message: "Canvas length must be != 0!") // Prepare the coordinate system let resolve-number(x) = { @@ -49,11 +41,11 @@ let y = (if type(y) != array { (0.0, y, 0.0) } else { y }).map(resolve-number) let z = (if type(z) != array { (0.0, 0.0, z) } else { z }).map(resolve-number) - let ctx = ( + return ( version: version.version, length: length, - debug: debug, - background: background, + debug: false, + background: none, // Previous element position & bbox prev: (pt: (0.0, 0.0, 0.0)), style: styles.default, @@ -78,6 +70,32 @@ // CeTZ itself does not use this dictionary for data. shared-state: (:), ) +} + +/// Sets up a canvas for drawing on. +/// +/// - length (length): Used to specify what 1 coordinate unit is. Note that ratios are no longer supported! You can wrap the canvas into a `layout(ly => canvas(length: ly.width * , ...))`. +/// - baseline (none,number,coordinate): Specifies the coordinate to use as the baseline. Setting this the canvas behaves like a `box` element instead of a `block`. +/// - body (none, array, element): A code block in which functions from the `draw` module have been called. +/// - background (none, color): A color to be used for the background of the canvas. +/// - stroke (none, stroke): Stroke style to apply to the canvas top-level element (box or block) +/// - padding (none, number, array, dictionary) = none: How much padding to add to the canvas. `none` applies no padding. A number applies padding to all sides equally. A dictionary applies padding following Typst's `pad` function: https://typst.app/docs/reference/layout/pad/. An array follows CSS like padding: `(y, x)`, `(top, x, bottom)` or `(top, right, bottom, left)`. +/// - x (number, vector) = 1.0: Sets up the x vector of the coordinate system to `(x, 0, 0)` or to the given vector. +/// - y (number, vector) = 1.0: Sets up the y vector of the coordinate system to `(0, y, 0)` or to the given vector. +/// - z (number, vector) = 1.0: Sets up the z vector of the coordinate system to `(0, 0, z)` or to the given vector. +/// - debug (bool): Shows the bounding boxes of each element when `true`. +/// -> content +#let canvas(length: 1cm, x: 1.0, y: 1.0, z: 1.0, baseline: none, debug: false, background: none, stroke: none, padding: none, body) = context { + if body == none { + return [] + } + if type(body) != array { + panic("Incorrect type for body: " + repr(type(body))) + } + + let ctx = make-ctx(length: length, x: x, y: y, z: z) + ctx.debug = debug + ctx.background = background let (ctx, bounds, drawables) = process.many(ctx, body) if bounds == none { diff --git a/src/draw/boolean.typ b/src/draw/boolean.typ index 44f54b6dc..d550720bc 100644 --- a/src/draw/boolean.typ +++ b/src/draw/boolean.typ @@ -75,12 +75,12 @@ // Projects a CeTZ 3D path to a 2D wire path, asserting all vertices share the // same z-plane (within `tol`) and all subpaths are closed. -#let _path3d-to-wire2d(path3d, tol: 1e-6) = { +#let _path3d-to-wire2d(path3d, eps: 1e-6) = { if path3d.len() == 0 { return ((subpaths: ()), 0.0) } - let (z0, same-z) = path-util.same-z-plane(path3d, tol: tol) + let (z0, same-z) = path-util.same-z-plane(path3d, eps: eps) assert(same-z, message: "boolean: all input vertices must lie in a single z-plane.") let drop-z(v) = (v.at(0), v.at(1)) diff --git a/src/draw/projection.typ b/src/draw/projection.typ index 4b4694400..17fe3b162 100644 --- a/src/draw/projection.typ +++ b/src/draw/projection.typ @@ -193,11 +193,14 @@ drawables = _sort-by-distance(drawables) } - ctx.transform = transform if perspective-mode { - drawable.apply-transform(projection-matrix, drawables) + drawables = drawable.apply-transform(projection-matrix, drawables) ctx._perspective-projection = previous-perspective-mode } + + // Restore the old transformation + ctx.transform = transform + if not reset-transform { drawables = drawable.apply-transform(ctx.transform, drawables) } diff --git a/src/draw/shapes.typ b/src/draw/shapes.typ index ad75690c4..563fca707 100644 --- a/src/draw/shapes.typ +++ b/src/draw/shapes.typ @@ -1136,7 +1136,8 @@ let angle = if type(angle) != std.angle { let (_, c) = coordinate.resolve(ctx, angle) - vector.angle2(a, util.apply-transform(ctx.transform, c)) + vector.angle2(util.apply-transform(ctx.transform, a), + util.apply-transform(ctx.transform, c)) } else { angle } @@ -1692,7 +1693,7 @@ #let catmull(..pts-style, close: false, name: none) = { let (pts, style) = (pts-style.pos(), pts-style.named()) - assert(pts.len() >= 2, message: "Catmull-rom curve requires at least two points. Got " + repr(pts.len()) + "instead.") + assert(pts.len() >= 2, message: "Catmull-rom curve requires at least two points. Got " + repr(pts.len()) + " instead.") return (ctx => { let (ctx, ..pts) = coordinate.resolve(ctx, ..pts) @@ -1764,7 +1765,7 @@ #let hobby(..pts-style, ta: auto, tb: auto, close: false, name: none) = { let (pts, style) = (pts-style.pos(), pts-style.named()) - assert(pts.len() >= 2, message: "Hobby curve requires at least two points. Got " + repr(pts.len()) + "instead.") + assert(pts.len() >= 2, message: "Hobby curve requires at least two points. Got " + repr(pts.len()) + " instead.") return (ctx => { let (ctx, ..pts) = coordinate.resolve(ctx, ..pts) diff --git a/src/lib.typ b/src/lib.typ index aa00d816a..233631fb3 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -1,6 +1,8 @@ +// expose name +#let name = "cetz" #import "version.typ": version -#import "canvas.typ": canvas +#import "canvas.typ": canvas, make-ctx #import "draw.typ" // Expose utilities @@ -22,6 +24,3 @@ #import "lib/angle.typ" #import "lib/tree.typ" #import "lib/decorations.typ" - -// expose name -#let name = "cetz" \ No newline at end of file diff --git a/src/mark-shapes.typ b/src/mark-shapes.typ index b9c29adfb..d3a4ff7d6 100644 --- a/src/mark-shapes.typ +++ b/src/mark-shapes.typ @@ -205,8 +205,9 @@ fast-line(..pts, stroke: style.stroke, fill: style.fill) let offset = style.canvas-thickness / 2 - create-tip-and-base-anchor(style, (-offset, 0), (offset, 0)) anchor("center", (0, 0)) + anchor("tip", (offset, 0)) + anchor("base", (-offset, 0)) }, ellipse: (style) => { import "/src/draw.typ": arc, circle diff --git a/src/mark.typ b/src/mark.typ index be7e5dd6b..2b14df388 100644 --- a/src/mark.typ +++ b/src/mark.typ @@ -278,14 +278,11 @@ style.mark = none let mark = _eval-mark-shape-and-anchors(ctx, mark-fn(style), style) - let offset = style.at("offset", default: 0) - let inset = style.at("inset", default: 0) - let mark-tip-info = path-util.point-at( segments, distance, reverse: is-end) let mark-base-info = if mark.length != 0 { path-util.point-at( - segments, distance + mark.length - inset, reverse: is-end) + segments, distance + mark.length, reverse: is-end) } else { mark-tip-info } diff --git a/src/matrix.typ b/src/matrix.typ index 1cc8d2fc9..26946676f 100644 --- a/src/matrix.typ +++ b/src/matrix.typ @@ -19,7 +19,7 @@ // List of identity matrices of dimension 1 x 1 to 4 x 4 #let _ident = ( - ((1.0),), + ((1.0,),), ((1.0, 0.0), (0.0, 1.0)), ((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)), ((1.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 0.0, 1.0)), @@ -31,10 +31,12 @@ /// -> matrix #let ident(size) = { assert(size >= 1, message: "Invalid dimension") - return _ident.at(size - 1, default: - range(0, size).map(j => range(0, size).map(k => { - if j == k { 1.0 } else { 0.0 } - }))) + if _ident.len() >= size { + return _ident.at(size - 1) + } + return range(0, size).map(j => range(0, size).map(k => { + if j == k { 1.0 } else { 0.0 } + })) } /// Create a square matrix with the diagonal set to the @@ -69,176 +71,12 @@ /// Rounds each value in the matrix to a precision. /// - mat (matrix): Input matrix -/// - precision (int) = 8: Rounding precision (digits) +/// - precision (int) = 10: Rounding precision (digits) /// -> matrix #let round(mat, precision: precision) = { mat.map(r => r.map(v => _round(v, digits: precision))) } -/// Returns a $4 times 4$ translation matrix -/// - x (float): The translation in the $x$ direction. -/// - y (float): The translation in the $y$ direction. -/// - z (float): The translation in the $x$ direction. -/// -> matrix -#let transform-translate(x, y, z) = { - ((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z), (0, 0, 0, 1)) -} - -/// Returns a $4 times 4$ x-shear matrix -/// - factor (float): The shear in the $x$ direction. -/// -> matrix -#let transform-shear-x(factor) = { - ((1, factor, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) -} - - -/// Returns a $4 times 4$ z-shear matrix -/// - factor (float): The shear in the $z$ direction. -/// -> matrix -#let transform-shear-z(factor) = { - ((1, 0, factor, 0), (0, 1, -factor, 0), (0, 0, 1, 0), (0, 0, 0, 1)) -} - -/// Returns a $4 times 4$ scale matrix -/// - f (float,array,dictionary): The scale factor(s) of the matrix. An {{array}} of at least 3 {{float}}s sets the x, y and z scale factors. A {{dictionary}} sets the scale in the direction of the corresponding x, y and z keys. A single {{float}} sets the scale for all directions. -/// -> matrix -#let transform-scale(f) = { - let (x, y, z) = if type(f) == array { - vector.as-vec(f, init: (1, 1, 1)) - } else if type(f) == dictionary { - (f.at("x", default: 1), f.at("y", default: 1), f.at("z", default: 1)) - } else { - (f, f, f) - } - return ( - (x, 0, 0, 0), - (0, y, 0, 0), - (0, 0, z, 0), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation xyz matrix for a direction and up vector -/// - dir (vector): idk -/// - up (vector): idk -/// -> matrix -#let transform-rotate-dir(dir, up) = { - dir = vector.norm(dir.slice(0, 3)) - up = vector.norm(up.slice(0, 3)) - - let (dx, dy, dz) = dir - let (ux, uy, uz) = up - let (rx, ry, rz) = vector.norm(vector.cross(dir, up)) - - ((rx, dx, ux, 0), (ry, dy, uy, 0), (rz, dz, uz, 0), (0, 0, 0, 1)) -} - -// Return 4x4 rotate x matrix -/// Returns a $4 times 4$ $x$ rotation matrix -/// - angle (angle): The angle to rotate around the $x$ axis -/// -> matrix -#let transform-rotate-x(angle) = { - ((1, 0, 0, 0), (0, cos(angle), -sin(angle), 0), (0, sin(angle), cos(angle), 0), (0, 0, 0, 1)) -} - -// Return 4x4 rotate y matrix -/// Returns a $4 times 4$ $y$ rotation matrix -/// - angle (angle): The angle to rotate around the $y$ axis -/// -> matrix -#let transform-rotate-y(angle) = { - ((cos(angle), 0, -sin(angle), 0), (0, 1, 0, 0), (sin(angle), 0, cos(angle), 0), (0, 0, 0, 1)) -} - -// Return 4x4 rotate z matrix -/// Returns a $4 times 4$ $z$ rotation matrix -/// - angle (angle): The angle to rotate around the $z$ axis -/// -> matrix -#let transform-rotate-z(angle) = { - ((cos(angle), -sin(angle), 0, 0), (sin(angle), cos(angle), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) -} - -// 3D rotation matrix around an arbitrary axis (ax, ay, az). -#let _rotate-axis-angle(ax, ay, az, angle) = { - let c = cos(angle) - let s = sin(angle) - ( - (ax * ax * (1 - c) + c, ax * ay * (1 - c) - az * s, ax * az * (1 - c) + ay * s, 0), - (ay * ax * (1 - c) + az * s, ay * ay * (1 - c) + c, ay * az * (1 - c) - ax * s, 0), - (az * ax * (1 - c) - ay * s, az * ay * (1 - c) + ax * s, az * az * (1 - c) + c, 0), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation matrix from azimuth/elevation/roll. -/// Assumes the viewing convention where $z$ points up and $x$ points toward the viewer. -/// - azimuth (angle): Rotation around z. -/// - elevation (angle): Tilt above the xy plane. -/// - roll (angle): Rotation around the current viewing axis. -/// -> matrix -#let transform-rotate-aer(azimuth, elevation, roll: 0deg) = { - let rotate-z-up = transform-rotate-x(-90deg) - let rotate-azimuth = transform-rotate-z(-90deg - azimuth) - let (ax, ay, az) = (-sin(azimuth), cos(azimuth), 0) - let rotate-elevation = _rotate-axis-angle(ax, ay, az, elevation) - let base = mul-mat(rotate-z-up, rotate-azimuth, rotate-elevation) - - if roll == 0deg { - return base - } - - // Roll around the current viewing axis after azimuth/elevation. - let (vx, vy, vz) = vector.norm(mul4x4-vec3(base, (1, 0, 0), w: 0)) - let rotate-roll = _rotate-axis-angle(vx, vy, vz, roll) - mul-mat(rotate-roll, base) -} - -// Return 4x4 rotate xz matrix -/// Returns a $4 times 4$ $x z$ rotation matrix -/// - x (angle): The angle to rotate around the $x$ axis -/// - z (angle): The angle to rotate around the $z$ axis -/// -> matrix -#let transform-rotate-xz(x, z) = { - ( - (cos(z), sin(z), 0, 0), - (-cos(x) * sin(z), cos(x) * cos(z), -sin(x), 0), - (sin(x) * sin(z), -sin(x) * cos(z), cos(x), 1), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation matrix - yaw-pitch-roll -/// -/// - a (angle): Yaw -/// - b (angle): Pitch -/// - c (angle): Roll -/// -> matrix -#let transform-rotate-ypr(a, b, c) = { - ( - (cos(a) * cos(b), cos(a) * sin(b) * sin(c) - sin(a) * cos(c), cos(a) * sin(b) * cos(c) + sin(a) * sin(c), 0), - (sin(a) * cos(b), sin(a) * sin(b) * sin(c) + cos(a) * cos(c), sin(a) * sin(b) * cos(c) - cos(a) * sin(c), 0), - (-sin(b), cos(b) * sin(c), cos(b) * cos(c), 1), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation matrix - euler angles -/// -/// Calculates the product of the three rotation matrices -/// $R = R_z(z) R_y(y) R_x(x)$ -/// -/// - x (angle): Rotation about x -/// - y (angle): Rotation about y -/// - z (angle): Rotation about z -/// -> matrix -#let transform-rotate-xyz(x, y, z) = { - ( - (cos(y) * cos(z), sin(x) * sin(y) * cos(z) - cos(x) * sin(z), cos(x) * sin(y) * cos(z) + sin(x) * sin(z), 0), - (cos(y) * sin(z), sin(x) * sin(y) * sin(z) + cos(x) * cos(z), cos(x) * sin(y) * sin(z) - sin(x) * cos(z), 0), - (-sin(y), sin(x) * cos(y), cos(x) * cos(y), 0), - (0, 0, 0, 1), - ) -} - /// Multiplies matrices on top of each other. /// - ..matrices (matrix): The matrices to multiply from left to right. /// -> matrix @@ -464,3 +302,167 @@ return inverted } + +/// Returns a $4 times 4$ translation matrix +/// - x (float): The translation in the $x$ direction. +/// - y (float): The translation in the $y$ direction. +/// - z (float): The translation in the $x$ direction. +/// -> matrix +#let transform-translate(x, y, z) = { + ((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z), (0, 0, 0, 1)) +} + +/// Returns a $4 times 4$ x-shear matrix +/// - factor (float): The shear in the $x$ direction. +/// -> matrix +#let transform-shear-x(factor) = { + ((1, factor, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) +} + + +/// Returns a $4 times 4$ z-shear matrix +/// - factor (float): The shear in the $z$ direction. +/// -> matrix +#let transform-shear-z(factor) = { + ((1, 0, factor, 0), (0, 1, -factor, 0), (0, 0, 1, 0), (0, 0, 0, 1)) +} + +/// Returns a $4 times 4$ scale matrix +/// - f (float,array,dictionary): The scale factor(s) of the matrix. An {{array}} of at least 3 {{float}}s sets the x, y and z scale factors. A {{dictionary}} sets the scale in the direction of the corresponding x, y and z keys. A single {{float}} sets the scale for all directions. +/// -> matrix +#let transform-scale(f) = { + let (x, y, z) = if type(f) == array { + vector.as-vec(f, init: (1, 1, 1)) + } else if type(f) == dictionary { + (f.at("x", default: 1), f.at("y", default: 1), f.at("z", default: 1)) + } else { + (f, f, f) + } + return ( + (x, 0, 0, 0), + (0, y, 0, 0), + (0, 0, z, 0), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation xyz matrix for a direction and up vector +/// - dir (vector): idk +/// - up (vector): idk +/// -> matrix +#let transform-rotate-dir(dir, up) = { + dir = vector.norm(dir.slice(0, 3)) + up = vector.norm(up.slice(0, 3)) + + let (dx, dy, dz) = dir + let (ux, uy, uz) = up + let (rx, ry, rz) = vector.norm(vector.cross(dir, up)) + + ((rx, dx, ux, 0), (ry, dy, uy, 0), (rz, dz, uz, 0), (0, 0, 0, 1)) +} + +// Return 4x4 rotate x matrix +/// Returns a $4 times 4$ $x$ rotation matrix +/// - angle (angle): The angle to rotate around the $x$ axis +/// -> matrix +#let transform-rotate-x(angle) = { + ((1, 0, 0, 0), (0, cos(angle), -sin(angle), 0), (0, sin(angle), cos(angle), 0), (0, 0, 0, 1)) +} + +// Return 4x4 rotate y matrix +/// Returns a $4 times 4$ $y$ rotation matrix +/// - angle (angle): The angle to rotate around the $y$ axis +/// -> matrix +#let transform-rotate-y(angle) = { + ((cos(angle), 0, -sin(angle), 0), (0, 1, 0, 0), (sin(angle), 0, cos(angle), 0), (0, 0, 0, 1)) +} + +// Return 4x4 rotate z matrix +/// Returns a $4 times 4$ $z$ rotation matrix +/// - angle (angle): The angle to rotate around the $z$ axis +/// -> matrix +#let transform-rotate-z(angle) = { + ((cos(angle), -sin(angle), 0, 0), (sin(angle), cos(angle), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) +} + +// 3D rotation matrix around an arbitrary axis (ax, ay, az). +#let _rotate-axis-angle(ax, ay, az, angle) = { + let c = cos(angle) + let s = sin(angle) + ( + (ax * ax * (1 - c) + c, ax * ay * (1 - c) - az * s, ax * az * (1 - c) + ay * s, 0), + (ay * ax * (1 - c) + az * s, ay * ay * (1 - c) + c, ay * az * (1 - c) - ax * s, 0), + (az * ax * (1 - c) - ay * s, az * ay * (1 - c) + ax * s, az * az * (1 - c) + c, 0), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation matrix from azimuth/elevation/roll. +/// Assumes the viewing convention where $z$ points up and $x$ points toward the viewer. +/// - azimuth (angle): Rotation around z. +/// - elevation (angle): Tilt above the xy plane. +/// - roll (angle): Rotation around the current viewing axis. +/// -> matrix +#let transform-rotate-aer(azimuth, elevation, roll: 0deg) = { + let rotate-z-up = transform-rotate-x(-90deg) + let rotate-azimuth = transform-rotate-z(-90deg - azimuth) + let (ax, ay, az) = (-sin(azimuth), cos(azimuth), 0) + let rotate-elevation = _rotate-axis-angle(ax, ay, az, elevation) + let base = mul-mat(rotate-z-up, rotate-azimuth, rotate-elevation) + + if roll == 0deg { + return base + } + + // Roll around the current viewing axis after azimuth/elevation. + let (vx, vy, vz) = vector.norm(mul4x4-vec3(base, (1, 0, 0), w: 0)) + let rotate-roll = _rotate-axis-angle(vx, vy, vz, roll) + mul-mat(rotate-roll, base) +} + +// Return 4x4 rotate xz matrix +/// Returns a $4 times 4$ $x z$ rotation matrix +/// - x (angle): The angle to rotate around the $x$ axis +/// - z (angle): The angle to rotate around the $z$ axis +/// -> matrix +#let transform-rotate-xz(x, z) = { + ( + (cos(z), sin(z), 0, 0), + (-cos(x) * sin(z), cos(x) * cos(z), -sin(x), 0), + (sin(x) * sin(z), -sin(x) * cos(z), cos(x), 0), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation matrix - yaw-pitch-roll +/// +/// - a (angle): Yaw +/// - b (angle): Pitch +/// - c (angle): Roll +/// -> matrix +#let transform-rotate-ypr(a, b, c) = { + ( + (cos(a) * cos(b), cos(a) * sin(b) * sin(c) - sin(a) * cos(c), cos(a) * sin(b) * cos(c) + sin(a) * sin(c), 0), + (sin(a) * cos(b), sin(a) * sin(b) * sin(c) + cos(a) * cos(c), sin(a) * sin(b) * cos(c) - cos(a) * sin(c), 0), + (-sin(b), cos(b) * sin(c), cos(b) * cos(c), 0), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation matrix - euler angles +/// +/// Calculates the product of the three rotation matrices +/// $R = R_z(z) R_y(y) R_x(x)$ +/// +/// - x (angle): Rotation about x +/// - y (angle): Rotation about y +/// - z (angle): Rotation about z +/// -> matrix +#let transform-rotate-xyz(x, y, z) = { + ( + (cos(y) * cos(z), sin(x) * sin(y) * cos(z) - cos(x) * sin(z), cos(x) * sin(y) * cos(z) + sin(x) * sin(z), 0), + (cos(y) * sin(z), sin(x) * sin(y) * sin(z) + cos(x) * cos(z), cos(x) * sin(y) * sin(z) - sin(x) * cos(z), 0), + (-sin(y), sin(x) * cos(y), cos(x) * cos(y), 0), + (0, 0, 0, 1), + ) +} diff --git a/src/path-util.typ b/src/path-util.typ index d601a37ca..4e3c2d27a 100644 --- a/src/path-util.typ +++ b/src/path-util.typ @@ -93,19 +93,19 @@ /// `true` iff every other vertex is within `tol` of that z-coordinate. /// /// - path (path): Input path; must be non-empty -/// - tol (float): Absolute z tolerance +/// - eps (float): Absolute z tolerance /// -> array Tuple of the form (z, same-z) -#let same-z-plane(path, tol: 1e-6) = { +#let same-z-plane(path, eps: 1e-6) = { assert( path.len() > 0, message: "Cannot determine z-plane of an empty path", ) - let z0 = path.first().at(0).at(2) + let z0 = path.first().at(0).at(2, default: 0.0) for (origin, _, segments) in path { - if calc.abs(origin.at(2) - z0) > tol { return (z0, false) } + if calc.abs(origin.at(2, default: 0.0) - z0) > eps { return (z0, false) } for (kind, ..args) in segments { for v in args { - if calc.abs(v.at(2) - z0) > tol { return (z0, false) } + if calc.abs(v.at(2, default: 0.0) - z0) > eps { return (z0, false) } } } } diff --git a/src/version.typ b/src/version.typ index da052ac53..f28a4ad5d 100644 --- a/src/version.typ +++ b/src/version.typ @@ -1 +1 @@ -#let version = version(0,5,2) +#let version = version(0,5,3) diff --git a/tests/angle/ref/1.png b/tests/angle/ref/1.png index bf4ecac56..6e9eaa7f7 100644 Binary files a/tests/angle/ref/1.png and b/tests/angle/ref/1.png differ diff --git a/tests/canvas/baseline/ref/1.png b/tests/canvas/baseline/ref/1.png index f026ae3d1..7ad1829a6 100644 Binary files a/tests/canvas/baseline/ref/1.png and b/tests/canvas/baseline/ref/1.png differ diff --git a/tests/canvas/make-ctx/test.typ b/tests/canvas/make-ctx/test.typ new file mode 100644 index 000000000..6e37062eb --- /dev/null +++ b/tests/canvas/make-ctx/test.typ @@ -0,0 +1,9 @@ +#import "/src/lib.typ" as cetz + +assert.ne(cetz.make-ctx(), none) + +assert.eq(cetz.make-ctx().transform, cetz.matrix.ident(4)) +assert.eq(cetz.make-ctx(x: 2, y: 2, z: 2).transform, + ((2, 0, 0, 0), (0, 2, 0, 0), (0, 0, 2, 0), (0, 0, 0, 1))) +assert.eq(cetz.make-ctx(x: (1, 2, 3), y: (2, 3, 4), z: (3, 4, 5)).transform, + ((1, 2, 3, 0), (2, 3, 4, 0), (3, 4, 5, 0), (0, 0, 0, 1))) diff --git a/tests/content/angle/ref/1.png b/tests/content/angle/ref/1.png new file mode 100644 index 000000000..9795ac164 Binary files /dev/null and b/tests/content/angle/ref/1.png differ diff --git a/tests/content/angle/test.typ b/tests/content/angle/test.typ new file mode 100644 index 000000000..9fbb0c7b9 --- /dev/null +++ b/tests/content/angle/test.typ @@ -0,0 +1,17 @@ +#import "/src/lib.typ": * +#import "/tests/helper.typ": * +#set page(width: auto, height: auto) + +// #1032 +#test-case({ + import draw: * + set-style(padding: 5pt) + + line((0, 0), (5, 5), name: "line1") + content("line1.50%", angle: "line1.end", anchor: "south", [Content]) + + rotate(15deg) + + line((5, 0), (10, 5), name: "line2") + content("line2.50%", angle: "line2.end", anchor: "south", [Content]) +}) diff --git a/tests/custom/mark/ref/1.png b/tests/custom/mark/ref/1.png index 11ec3460e..0262b1bc3 100644 Binary files a/tests/custom/mark/ref/1.png and b/tests/custom/mark/ref/1.png differ diff --git a/tests/docs/torus/ref/1.png b/tests/docs/torus/ref/1.png new file mode 100644 index 000000000..d4e08a3a0 Binary files /dev/null and b/tests/docs/torus/ref/1.png differ diff --git a/tests/docs/torus/test.typ b/tests/docs/torus/test.typ new file mode 100644 index 000000000..aa681010b --- /dev/null +++ b/tests/docs/torus/test.typ @@ -0,0 +1,62 @@ +#set page(width: auto, height: auto) +#import "/src/lib.typ" as cetz +#import "/tests/helper.typ": * + +#let torus(pos, angle: 60deg, cap-angle: 5deg, radius: 1, thickness: 0.4, fill: yellow, stroke: black) = { + import cetz.draw: scope, compound-path, circle, arc, hide, intersections, arc-through, merge-path + + let cap-angle = cap-angle * calc.sin(angle) + + scope({ + let radius = (radius, radius * calc.cos(angle)) + let inner-north = pos.at(1) + radius.at(1) - thickness + let show-hole = inner-north > pos.at(1) + + hide({ + circle(pos, radius: cetz.vector.add(radius, (thickness, thickness)), name: "outer") + circle(pos, radius: radius, name: "mid") + }) + + intersections("inner", { + arc-through( + ((name: "outer", anchor: 0deg - cap-angle), 200%, (name: "mid", anchor: 0deg)), + ((name: "outer", anchor: 270deg), 200%, (name: "mid", anchor: 270deg)), + ((name: "outer", anchor: 180deg + cap-angle), 200%, (name: "mid", anchor: 180deg)),) + arc-through( + ((name: "outer", anchor: 180deg - cap-angle), 200%, (name: "mid", anchor: 180deg)), + ((name: "outer", anchor: 90deg), 200%, (name: "mid", anchor: 90deg)), + ((name: "outer", anchor: 0deg + cap-angle), 200%, (name: "mid", anchor: 0deg)),) + }) + + compound-path({ + circle(pos, radius: cetz.vector.add(radius, (thickness, thickness)), name: "outer") + + if show-hole { + if cap-angle > 0deg { + merge-path({ + arc-through("inner.0", + ((name: "outer", anchor: 90deg), 200%, (name: "mid", anchor: 90deg)), + "inner.1") + arc-through("inner.1", + ((name: "outer", anchor: 270deg), 200%, (name: "mid", anchor: 270deg)), + "inner.0") + }, close: true) + } else { + circle(pos, radius: cetz.vector.sub(radius, (thickness, thickness))) + } + } + }, fill-rule: "even-odd", fill: fill, stroke: stroke) + + if cap-angle > 0deg and angle not in (0deg, 90deg, 180deg, 270deg) { + arc-through(((name: "outer", anchor: 0deg - cap-angle), 200%, (name: "mid", anchor: 0deg)), + ((name: "outer", anchor: 270deg), 200%, (name: "mid", anchor: 270deg)), + ((name: "outer", anchor: 180deg + cap-angle), 200%, (name: "mid", anchor: 180deg)), fill: none, stroke: stroke) + } + }) +} + +#cetz.canvas({ + import cetz.draw: * + + torus((0, 0)) +}) diff --git a/tests/mark/anchors/ref/1.png b/tests/mark/anchors/ref/1.png index 1b35f8693..d4757d103 100644 Binary files a/tests/mark/anchors/ref/1.png and b/tests/mark/anchors/ref/1.png differ diff --git a/tests/mark/auto/offset/ref/1.png b/tests/mark/auto/offset/ref/1.png index 43dc826b1..24529b08c 100644 Binary files a/tests/mark/auto/offset/ref/1.png and b/tests/mark/auto/offset/ref/1.png differ diff --git a/tests/mark/offset/ref/1.png b/tests/mark/offset/ref/1.png new file mode 100644 index 000000000..0b727b01f Binary files /dev/null and b/tests/mark/offset/ref/1.png differ diff --git a/tests/mark/offset/test.typ b/tests/mark/offset/test.typ new file mode 100644 index 000000000..6a47450b9 --- /dev/null +++ b/tests/mark/offset/test.typ @@ -0,0 +1,25 @@ +#set page(width: auto, height: auto) +#import "/src/lib.typ": * +#import "/tests/helper.typ": * + +// Bug #1099 - Offsetting "|" introduced a litte gap +#test-case({ + import cetz.draw: * + + line((0, 0), (1, 0), stroke: blue) + line((0, 0), (1, 0), mark: (start: "|", end: "|", offset: 10%, stroke: black.transparentize(50%))) +}) + +#test-case({ + import cetz.draw: * + + line((0, 0), (1, 0), stroke: blue) + line((0, 0), (1, 0), mark: (reverse: true, start: "|", end: "|", offset: 10%, stroke: black.transparentize(50%))) +}) + +#test-case({ + import cetz.draw: * + + line((0, 0), (1, 0), stroke: blue) + line((0, 0), (1, 0), mark: (start: ">>", end: ">>", offset: 10%, stroke: black.transparentize(50%))) +}) diff --git a/tests/matrix/ref/1.png b/tests/matrix/ref/1.png deleted file mode 100644 index 797166170..000000000 Binary files a/tests/matrix/ref/1.png and /dev/null differ diff --git a/tests/matrix/test.typ b/tests/matrix/test.typ index ab10be7b9..2f131c4c0 100644 --- a/tests/matrix/test.typ +++ b/tests/matrix/test.typ @@ -1,17 +1,40 @@ #set page(width: auto, height: auto) #import "/src/lib.typ": matrix -#{ - let m = ( - (3, 2, 1), - (1, 0, 2), - ) - let v = ( - 1, 0, 4 - ) - let r = ( - 7, 9 - ) - - assert(matrix.mul-vec(m, v) == r) +// matrix.ident +#assert.eq(matrix.ident(1), ((1.0,),)) +#assert.eq(matrix.ident(2), ((1.0, 0), (0, 1.0))) +#assert.eq(matrix.ident(3), ((1.0, 0, 0), (0, 1.0, 0), (0, 0, 1.0))) +#assert.eq(matrix.ident(5), ((1.0, 0, 0, 0, 0), (0, 1.0, 0, 0, 0), (0, 0, 1.0, 0, 0), (0, 0, 0, 1.0, 0), (0, 0, 0, 0, 1.0))) + +// matrix.is-identity +#for i in range(1, 8) { + assert(matrix.is-identity(matrix.ident(i))) } +#assert(not matrix.is-identity(((1.1,),))) +#assert(not matrix.is-identity(((1, 0), (1, 2)))) + +// matrix.diag +#assert.eq(matrix.diag(1), ((1,),)) +#assert.eq(matrix.diag(1, 2), ((1, 0), (0, 2))) +#assert.eq(matrix.diag(1, 2, 3), ((1, 0, 0), (0, 2, 0), (0, 0, 3))) + +// matrix.dim +#assert.eq(matrix.dim(((1,),)), (1, 1)) +#assert.eq(matrix.dim(((1, 2),)), (1, 2)) +#assert.eq(matrix.dim(((1,), (2,))), (2, 1)) + +// matrix.column +#assert.eq(matrix.column(matrix.ident(3), 1), (0.0, 1.0, 0.0)) + +// matrix.inverse +#assert.eq(matrix.inverse(matrix.ident(4)), matrix.ident(4)) +#assert.eq(matrix.inverse(((2, 1), (6, 4))), ((2, -1/2), (-3, 1))) + +// matrix.mul-vec +#assert.eq(matrix.mul-vec(matrix.ident(3), (1, 0, 4)), (1, 0, 4)) +#assert.eq(matrix.mul-vec(((3, 2, 1), (1, 0, 2)), (1, 0, 4)), (7, 9)) + +// matrix.mul-mat +#assert.eq(matrix.mul-mat(((3, 2, 1), (1, 0, 2)), ((1, 2), (0, 1), (4, 0))), ((7, 8), (9, 2))) +#assert.eq(matrix.mul-mat(matrix.inverse(((2, 1), (6, 4))), ((2, 1), (6, 4))), matrix.ident(2)) // A A^(-1) = I diff --git a/tests/merge/ref/1.png b/tests/merge/ref/1.png index 7ade42916..929750dc6 100644 Binary files a/tests/merge/ref/1.png and b/tests/merge/ref/1.png differ diff --git a/tests/multiple/marks/ref/1.png b/tests/multiple/marks/ref/1.png index 9b1294e86..cb7c81ee2 100644 Binary files a/tests/multiple/marks/ref/1.png and b/tests/multiple/marks/ref/1.png differ diff --git a/tests/projection/perspective/ref/1.png b/tests/projection/perspective/ref/1.png index 0aef1aec2..1f94979ea 100644 Binary files a/tests/projection/perspective/ref/1.png and b/tests/projection/perspective/ref/1.png differ diff --git a/tests/rect/around/base/ref/1.png b/tests/rect/around/base/ref/1.png index 78aa8902c..bf3e8449b 100644 Binary files a/tests/rect/around/base/ref/1.png and b/tests/rect/around/base/ref/1.png differ diff --git a/tests/style/fold-stroke/ref/1.png b/tests/style/fold-stroke/ref/1.png index 42bef5fc9..4b226ad06 100644 Binary files a/tests/style/fold-stroke/ref/1.png and b/tests/style/fold-stroke/ref/1.png differ diff --git a/tests/transform/transform/ref/1.png b/tests/transform/transform/ref/1.png index fb0a4adfd..3e1d68018 100644 Binary files a/tests/transform/transform/ref/1.png and b/tests/transform/transform/ref/1.png differ diff --git a/tests/transform/transform/test.typ b/tests/transform/transform/test.typ index 50ead3e8b..65094ad96 100644 --- a/tests/transform/transform/test.typ +++ b/tests/transform/transform/test.typ @@ -19,3 +19,10 @@ transform(matrix.transform-scale((1, 0.5, 1))) arrow }) + +#test-case({ + import draw: * + + transform(matrix.transform-rotate-aer(10deg, 20deg, roll: 30deg)) + arrow +}) diff --git a/typst.toml b/typst.toml index 472ef21e7..a5fdbffbd 100644 --- a/typst.toml +++ b/typst.toml @@ -1,6 +1,6 @@ [package] name = "cetz" -version = "0.5.2" +version = "0.5.3" compiler = "0.14.0" repository = "https://github.com/cetz-package/cetz" homepage = "https://cetz-package.github.io/"