├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── CHANGES.md ├── LICENSE ├── README.md ├── cetz-core ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── docs ├── advanced │ ├── advanced.md │ ├── context.mdx │ ├── custom-types.mdx │ ├── draw-functions.mdx │ └── transformations.mdx ├── api │ ├── draw-functions │ │ ├── draw-functions.md │ │ ├── grouping │ │ │ ├── anchor.mdx │ │ │ ├── copy-anchors.mdx │ │ │ ├── floating.mdx │ │ │ ├── for-each-anchor.mdx │ │ │ ├── get-ctx.mdx │ │ │ ├── group.mdx │ │ │ ├── grouping.md │ │ │ ├── hide.mdx │ │ │ ├── intersections.mdx │ │ │ ├── on-layer.mdx │ │ │ ├── scope.mdx │ │ │ └── set-ctx.mdx │ │ ├── projections │ │ │ ├── on-xy.mdx │ │ │ ├── on-xz.mdx │ │ │ ├── on-yz.mdx │ │ │ ├── ortho.mdx │ │ │ └── projections.md │ │ ├── shapes │ │ │ ├── arc.mdx │ │ │ ├── bezier.mdx │ │ │ ├── catmull.mdx │ │ │ ├── circle.mdx │ │ │ ├── content.mdx │ │ │ ├── grid.mdx │ │ │ ├── hobby.mdx │ │ │ ├── line.mdx │ │ │ ├── mark.mdx │ │ │ ├── merge-path.mdx │ │ │ ├── polygon.mdx │ │ │ ├── rect.mdx │ │ │ └── shapes.md │ │ ├── styling │ │ │ ├── fill.mdx │ │ │ ├── set-style.mdx │ │ │ ├── stroke.mdx │ │ │ └── styling.md │ │ └── transformations │ │ │ ├── move-to.mdx │ │ │ ├── rotate.mdx │ │ │ ├── scale.mdx │ │ │ ├── set-origin.mdx │ │ │ ├── set-transform.mdx │ │ │ ├── set-viewport.mdx │ │ │ ├── transformations.md │ │ │ └── translate.mdx │ ├── internal │ │ ├── aabb.mdx │ │ ├── anchor.mdx │ │ ├── bezier.mdx │ │ ├── canvas.mdx │ │ ├── complex.mdx │ │ ├── coordinate.mdx │ │ ├── drawable.mdx │ │ ├── hobby.mdx │ │ ├── internal.md │ │ ├── intersection.mdx │ │ ├── mark.mdx │ │ ├── matrix.mdx │ │ ├── path-util.mdx │ │ ├── process.mdx │ │ ├── styles.mdx │ │ ├── util.mdx │ │ └── vector.mdx │ ├── libraries │ │ ├── angle │ │ │ ├── angle.mdx │ │ │ ├── index.md │ │ │ └── right-angle.mdx │ │ ├── decorations │ │ │ ├── braces │ │ │ │ ├── brace.mdx │ │ │ │ ├── flat-brace.mdx │ │ │ │ └── index.md │ │ │ ├── index.md │ │ │ └── path │ │ │ │ ├── coil.mdx │ │ │ │ ├── index.mdx │ │ │ │ ├── wave.mdx │ │ │ │ └── zigzag.mdx │ │ ├── index.md │ │ ├── palette │ │ │ ├── index.mdx │ │ │ └── new.mdx │ │ └── tree │ │ │ ├── index.md │ │ │ └── tree.mdx │ ├── overview.md │ └── sidebar.js ├── basics │ ├── anchors.mdx │ ├── basics.md │ ├── canvas.md │ ├── coordinate-systems.mdx │ ├── custom-types.mdx │ ├── marks.mdx │ └── styling.mdx ├── custom-types.js ├── getting-started.mdx ├── internals │ └── internals.md ├── libraries │ ├── libraries.md │ ├── plot.mdx │ └── tree.mdx ├── overview.md ├── sidebar.js └── tutorials │ └── karl.mdx ├── gallery ├── karls-picture.png ├── karls-picture.typ ├── paciolis.png ├── paciolis.typ ├── periodic-table.png ├── periodic-table.typ ├── plate-capacitor.png ├── plate-capacitor.typ ├── tree.png ├── tree.typ ├── waves.png └── waves.typ ├── justfile ├── requirements.typ ├── src ├── aabb.typ ├── anchor.typ ├── bezier.typ ├── canvas.typ ├── complex.typ ├── coordinate.typ ├── deps.typ ├── draw.typ ├── draw │ ├── grouping.typ │ ├── projection.typ │ ├── shapes.typ │ ├── styling.typ │ ├── transformations.typ │ └── util.typ ├── drawable.typ ├── hobby.typ ├── intersection.typ ├── lib.typ ├── lib │ ├── angle.typ │ ├── decorations.typ │ ├── decorations │ │ ├── brace.typ │ │ └── path.typ │ ├── palette.typ │ └── tree.typ ├── mark-shapes.typ ├── mark.typ ├── matrix.typ ├── path-util.typ ├── polygon.typ ├── process.typ ├── sorting.typ ├── styles.typ ├── util.typ ├── vector.typ └── version.typ ├── tests ├── .gitignore ├── anchor-centroid │ ├── ref │ │ └── 1.png │ └── test.typ ├── angle │ ├── ref │ │ └── 1.png │ └── test.typ ├── arc-previous-position │ ├── ref │ │ └── 1.png │ └── test.typ ├── arc-through │ ├── ref │ │ └── 1.png │ └── test.typ ├── arc │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── arrows │ ├── ref │ │ └── 1.png │ └── test.typ ├── bezier-shortening │ ├── ref │ │ └── 1.png │ └── test.typ ├── bezier-through │ ├── ref │ │ └── 1.png │ └── test.typ ├── bezier │ ├── ref │ │ └── 1.png │ └── test.typ ├── bounds │ ├── ref │ │ └── 1.png │ └── test.typ ├── canvas-baseline │ └── test.typ ├── catmul │ ├── ref │ │ └── 1.png │ └── test.typ ├── circle-through │ ├── ref │ │ └── 1.png │ └── test.typ ├── circle │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-anchors │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-intersection │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-padding │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-rotation │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-rtl │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-span │ ├── ref │ │ └── 1.png │ └── test.typ ├── content-transform │ ├── ref │ │ └── 1.png │ └── test.typ ├── content │ ├── ref │ │ └── 1.png │ └── test.typ ├── coordinate-lerp │ ├── ref │ │ └── 1.png │ └── test.typ ├── coordinate │ └── custom │ │ ├── ref │ │ └── 1.png │ │ └── test.typ ├── copy-anchor │ ├── ref │ │ └── 1.png │ └── test.typ ├── cube │ ├── ref │ │ └── 1.png │ └── test.typ ├── custom-mark │ ├── ref │ │ └── 1.png │ └── test.typ ├── decorations │ ├── ref │ │ └── 1.png │ └── test.typ ├── element-anchors │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── empty-group │ ├── ref │ │ └── 1.png │ └── test.typ ├── empty │ ├── ref │ │ └── 1.png │ └── test.typ ├── floating │ ├── ref │ │ └── 1.png │ └── test.typ ├── gradient │ ├── ref │ │ └── 1.png │ └── test.typ ├── grid │ ├── ref │ │ └── 1.png │ └── test.typ ├── group-anchors │ ├── ref │ │ └── 1.png │ └── test.typ ├── group-nested-anchors │ ├── ref │ │ └── 1.png │ └── test.typ ├── group-none │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── group-padding │ ├── ref │ │ └── 1.png │ └── test.typ ├── group-transform │ ├── ref │ │ └── 1.png │ └── test.typ ├── group-translate │ ├── ref │ │ └── 1.png │ └── test.typ ├── helper.typ ├── hide │ ├── ref │ │ └── 1.png │ └── test.typ ├── hobby │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── image │ ├── image.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── intersection │ ├── ref │ │ └── 1.png │ └── test.typ ├── layer │ ├── ref │ │ └── 1.png │ └── test.typ ├── line-element-element-intersection │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── line-fill-rule │ ├── ref │ │ └── 1.png │ └── test.typ ├── local-anchor │ ├── ref │ │ └── 1.png │ └── test.typ ├── mark-anchors │ ├── ref │ │ └── 1.png │ └── test.typ ├── mark-auto-offset │ ├── ref │ │ └── 1.png │ └── test.typ ├── mark-position │ ├── ref │ │ └── 1.png │ └── test.typ ├── mark-shape-transform │ ├── ref │ │ └── 1.png │ └── test.typ ├── mark-single │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── mark-z-axis │ ├── ref │ │ └── 1.png │ └── test.typ ├── matrix │ ├── ref │ │ └── 1.png │ └── test.typ ├── merge │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── multiple-marks │ ├── ref │ │ └── 1.png │ └── test.typ ├── padding │ ├── ref │ │ └── 1.png │ └── test.typ ├── palette │ ├── ref │ │ └── 1.png │ └── test.typ ├── path-anchors │ ├── ref.png │ ├── ref │ │ └── 1.png │ └── test.typ ├── path-decoration │ ├── ref │ │ └── 1.png │ └── test.typ ├── polygon │ ├── ref │ │ └── 1.png │ └── test.typ ├── primitives │ ├── ref │ │ └── 1.png │ └── test.typ ├── projection-default │ ├── ref │ │ └── 1.png │ └── test.typ ├── projection-ortho │ ├── ref │ │ └── 1.png │ └── test.typ ├── rect-rounded │ ├── ref │ │ └── 1.png │ └── test.typ ├── rect │ ├── ref │ │ └── 1.png │ └── test.typ ├── relative-length │ ├── ref │ │ └── 1.png │ └── test.typ ├── relative-no-update │ ├── ref │ │ └── 1.png │ └── test.typ ├── right-angle │ ├── ref │ │ └── 1.png │ └── test.typ ├── ring │ ├── ref │ │ └── 1.png │ └── test.typ ├── root-anchor │ ├── ref │ │ └── 1.png │ └── test.typ ├── rotate-around │ ├── ref │ │ └── 1.png │ └── test.typ ├── rotation │ ├── ref │ │ └── 1.png │ └── test.typ ├── set-get-ctx │ ├── ref │ │ └── 1.png │ └── test.typ ├── style │ ├── ref │ │ └── 1.png │ └── test.typ ├── transform-precission │ ├── ref │ │ └── 1.png │ └── test.typ ├── translation │ ├── ref │ │ └── 1.png │ └── test.typ ├── tree │ ├── ref │ │ └── 1.png │ └── test.typ └── viewport │ ├── ref │ └── 1.png │ └── test.typ └── typst.toml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | 9 | [Makefile] 10 | indent_style = tab -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [johannes-wolf,fenjalien] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - master 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | ci: 13 | runs-on: ubuntu-latest 14 | if: github.event.pull_request.draft == false 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | - name: Install wasm32 target 20 | run: rustup target add wasm32-unknown-unknown 21 | - name: Install just from crates.io 22 | uses: baptiste0928/cargo-install@v3 23 | with: 24 | crate: just 25 | - name: Install tytanic 26 | uses: baptiste0928/cargo-install@v3 27 | with: 28 | crate: tytanic 29 | git: https://github.com/tingerrr/tytanic.git 30 | - uses: typst-community/setup-typst@v3 31 | with: 32 | typst-version: '0.13.1' 33 | cache-dependency-path: src/deps.typ 34 | - run: | 35 | typst --version 36 | tt --version 37 | just install @local 38 | just install @preview 39 | just test 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs/_generated 2 | tmp* 3 | **/target/ 4 | cetz-core/cetz_core.wasm 5 | **/.rustc_info.json 6 | 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "common"] 2 | path = common 3 | url = https://github.com/cetz-package/common.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CeTZ 2 | 3 | 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/). 4 | 5 | ## Examples 6 | 7 | 8 | 9 | 14 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 |
Karl's PicturePacioli's construction of the icosahedronWaves
32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 |
Tree LayoutPeriodic Table of ElementsPlate Capacitor
53 | 54 | *Click on the example image to jump to the code.* 55 | 56 | You can explore an example gallery of scientific diagrams at [janosh.github.io/diagrams](https://janosh.github.io/diagrams). 57 | 58 | ## Usage 59 | 60 | For information, see the [online manual](https://cetz-package.github.io/docs). 61 | 62 | To use this package, simply add the following code to your document: 63 | 64 | ```typ 65 | #import "@preview/cetz:0.3.4" 66 | 67 | #cetz.canvas({ 68 | import cetz.draw: * 69 | // Your drawing code goes here 70 | }) 71 | ``` 72 | 73 | ## CeTZ Libraries 74 | 75 | - [cetz-plot - Plotting and Charts Library](https://github.com/cetz-package/cetz-plot) 76 | - [cetz-venn - Simple two- or three-set Venn diagrams](https://github.com/cetz-package/cetz-venn) 77 | 78 | ## Installing 79 | 80 | To install the CeTZ package under [your local typst package dir](https://github.com/typst/packages?tab=readme-ov-file#local-packages) you can use the `install` script from the repository. 81 | 82 | ```bash 83 | just install 84 | ``` 85 | 86 | The installed version can be imported by prefixing the package name with `@local`. 87 | 88 | ```typ 89 | #import "@local/cetz:0.3.4" 90 | 91 | #cetz.canvas({ 92 | import cetz.draw: * 93 | // Your drawing code goes here 94 | }) 95 | ``` 96 | 97 | ### Just 98 | 99 | This project uses [just](https://github.com/casey/just), a handy command runner. 100 | 101 | You can run all commands without having `just` installed, just have a look into the `justfile`. 102 | To install `just` on your system, use your systems package manager. On Windows, [Cargo](https://doc.rust-lang.org/cargo/) (`cargo install just`), [Chocolatey](https://chocolatey.org/) (`choco install just`) and [some other sources](https://just.systems/man/en/chapter_4.html) can be used. You need to run it from a `sh` compatible shell on Windows (e.g git-bash). 103 | 104 | ## Testing 105 | 106 | This package comes with some unit tests under the `tests` directory. 107 | To run all tests you can run the `just test` target. You need to have 108 | [`tytanic`](https://github.com/tingerrr/tytanic/) in your `PATH`: `cargo install tytanic`. 109 | 110 | ## Projects using CeTZ 111 | - [conchord](https://github.com/sitandr/conchord) Package for writing lyrics with chords that generates fretboard diagrams using CeTZ. 112 | - [finite](https://github.com/jneug/typst-finite) Finite is a Typst package for rendering finite automata. 113 | - [fletcher](https://github.com/Jollywatt/typst-fletcher) Package for drawing commutative diagrams and figures with arrows. 114 | - [chronos](https://git.kb28.ch/HEL/chronos) Package for drawing sequence diagrams. 115 | - [circuiteria](https://git.kb28.ch/HEL/circuiteria) Package for drawing circuits. 116 | - [rivet](https://git.kb28.ch/HEL/rivet-typst) Package for drawing instruction / register diagrams. 117 | - [plotsy-3d](https://github.com/misskacie/plotsy-3d) Package for rendering 3D objects & plots. 118 | -------------------------------------------------------------------------------- /cetz-core/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "cetz-core" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "ciborium", 10 | "serde", 11 | "wasm-minimal-protocol", 12 | ] 13 | 14 | [[package]] 15 | name = "cfg-if" 16 | version = "1.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 19 | 20 | [[package]] 21 | name = "ciborium" 22 | version = "0.2.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 25 | dependencies = [ 26 | "ciborium-io", 27 | "ciborium-ll", 28 | "serde", 29 | ] 30 | 31 | [[package]] 32 | name = "ciborium-io" 33 | version = "0.2.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 36 | 37 | [[package]] 38 | name = "ciborium-ll" 39 | version = "0.2.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 42 | dependencies = [ 43 | "ciborium-io", 44 | "half", 45 | ] 46 | 47 | [[package]] 48 | name = "crunchy" 49 | version = "0.2.3" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" 52 | 53 | [[package]] 54 | name = "half" 55 | version = "2.5.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" 58 | dependencies = [ 59 | "cfg-if", 60 | "crunchy", 61 | ] 62 | 63 | [[package]] 64 | name = "proc-macro2" 65 | version = "1.0.94" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 68 | dependencies = [ 69 | "unicode-ident", 70 | ] 71 | 72 | [[package]] 73 | name = "quote" 74 | version = "1.0.40" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 77 | dependencies = [ 78 | "proc-macro2", 79 | ] 80 | 81 | [[package]] 82 | name = "serde" 83 | version = "1.0.219" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 86 | dependencies = [ 87 | "serde_derive", 88 | ] 89 | 90 | [[package]] 91 | name = "serde_derive" 92 | version = "1.0.219" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 95 | dependencies = [ 96 | "proc-macro2", 97 | "quote", 98 | "syn", 99 | ] 100 | 101 | [[package]] 102 | name = "syn" 103 | version = "2.0.100" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 106 | dependencies = [ 107 | "proc-macro2", 108 | "quote", 109 | "unicode-ident", 110 | ] 111 | 112 | [[package]] 113 | name = "unicode-ident" 114 | version = "1.0.18" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 117 | 118 | [[package]] 119 | name = "venial" 120 | version = "0.5.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "61584a325b16f97b5b25fcc852eb9550843a251057a5e3e5992d2376f3df4bb2" 123 | dependencies = [ 124 | "proc-macro2", 125 | "quote", 126 | ] 127 | 128 | [[package]] 129 | name = "wasm-minimal-protocol" 130 | version = "0.1.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "264a7e0acbdd292aca03fee87eaea5a07647394c8985b19be27e950bde57930a" 133 | dependencies = [ 134 | "proc-macro2", 135 | "quote", 136 | "venial", 137 | ] 138 | -------------------------------------------------------------------------------- /cetz-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cetz-core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | # To avoid accidentally publishing on crates.io. 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | ciborium = "0.2.1" 13 | serde = "1.0.219" 14 | wasm-minimal-protocol = "0.1" 15 | 16 | [profile.release] 17 | lto = true 18 | strip = true 19 | opt-level = 'z' 20 | codegen-units = 1 21 | panic = "abort" 22 | -------------------------------------------------------------------------------- /docs/advanced/advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced 2 | 3 | {{int}} 4 | -------------------------------------------------------------------------------- /docs/advanced/context.mdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/docs/advanced/context.mdx -------------------------------------------------------------------------------- /docs/advanced/custom-types.mdx: -------------------------------------------------------------------------------- 1 | # Custom Types 2 | 3 | ## context 4 | 5 | A {{dictionary}} that holds the internal state of the canvas such as the element dictionary, the current transformation matrix, group and canvas unit length. 6 | The following fields are considered stable: 7 | - length (length): Length of one canvas unit as typst length 8 | - transform (matrix): Current 4x4 transformation matrix 9 | - background (none,color,gradient,tiling): The canvas' background 10 | - debug (bool): True if the canvas' debug flag is set 11 | - shared-state (dictionary): State that is not scoped by `group` or `scope` elements and can be used to share canvas-global state 12 | 13 | ## element 14 | 15 | A function that, when called with a context, returns some data that effects the canvas. Can also be an array of this type. 16 | -------------------------------------------------------------------------------- /docs/advanced/draw-functions.mdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/docs/advanced/draw-functions.mdx -------------------------------------------------------------------------------- /docs/advanced/transformations.mdx: -------------------------------------------------------------------------------- 1 | The default transformation matrix of the canvas is set to: 2 | /// $mat(1, 0,-0.5, 0; 3 | /// 0,-1, 0.5, 0; 4 | /// 0, 0, 0, 0; 5 | /// 0, 0, 0, 1)$ 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/draw-functions.md: -------------------------------------------------------------------------------- 1 | # Draw Functions 2 | 3 | Draw functions are the functions you import from the `draw` module and call within a `canvas` body. Depending on how and where you call them they can cause stuff to be drawn to the canvas or modify what is already on the canvas. 4 | 5 | - [shapes](./shapes) For drawing shapes on the canvas. 6 | - [grouping](./grouping) For grouping other draw functions together. 7 | - [styling](./styling) For changing the styling of other draw functions. 8 | - [transformations](./transformations) For transforming the points on the canvas. 9 | - [projection](./projections) 10 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/anchor.mdx: -------------------------------------------------------------------------------- 1 | import Anchor from "@site/cetz/docs/_generated/draw/grouping/anchor.mdx"; 2 | 3 | # anchor 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/copy-anchors.mdx: -------------------------------------------------------------------------------- 1 | import CopyAnchors from "@site/cetz/docs/_generated/draw/grouping/copy-anchors.mdx"; 2 | 3 | # copy-anchors 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/floating.mdx: -------------------------------------------------------------------------------- 1 | import Floating from "@site/cetz/docs/_generated/draw/grouping/floating.mdx"; 2 | 3 | # floating 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/for-each-anchor.mdx: -------------------------------------------------------------------------------- 1 | import ForEachAnchor from "@site/cetz/docs/_generated/draw/grouping/for-each-anchor.mdx"; 2 | 3 | # for-each-anchor 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/get-ctx.mdx: -------------------------------------------------------------------------------- 1 | import GetCtx from "@site/cetz/docs/_generated/draw/grouping/get-ctx.mdx"; 2 | 3 | # get-ctx 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/group.mdx: -------------------------------------------------------------------------------- 1 | import Group from "@site/cetz/docs/_generated/draw/grouping/group.mdx"; 2 | 3 | # group 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/grouping.md: -------------------------------------------------------------------------------- 1 | # Grouping 2 | 3 | Draw functions that allow collecting other draw functions together. 4 | 5 | - [hide](./hide) Hides an element. 6 | - [intersections](./intersections) Calculates the intersections between multiple elements. 7 | - [group](./group) Groups one or more elements together. 8 | - [anchor](./anchor) Creates a new anchor for the current group. 9 | - [copy-anchors](./copy-anchors) Copies multiple anchors from one element into the current group. 10 | - [set-ctx](./set-ctx) Allows modifying the current canvas context. 11 | - [get-ctx](./get-ctx) Allows the reading of the current canvas context. 12 | - [for-each-anchor](./for-each-anchor) Iterates throguh all named anchors of an element with a callback. 13 | - [on-layer](./on-layer) Places elements on a specific layer. 14 | - [floating](./floating) Places an element without affecting bounding boxes. 15 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/hide.mdx: -------------------------------------------------------------------------------- 1 | import Hide from "@site/cetz/docs/_generated/draw/grouping/hide.mdx"; 2 | 3 | # hide 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/intersections.mdx: -------------------------------------------------------------------------------- 1 | import Intersections from "@site/cetz/docs/_generated/draw/grouping/intersections.mdx"; 2 | 3 | # intersections 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/on-layer.mdx: -------------------------------------------------------------------------------- 1 | import OnLayer from "@site/cetz/docs/_generated/draw/grouping/on-layer.mdx"; 2 | 3 | # on-layer 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/scope.mdx: -------------------------------------------------------------------------------- 1 | import Scope from "@site/cetz/docs/_generated/draw/grouping/scope.mdx"; 2 | 3 | # scope 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/grouping/set-ctx.mdx: -------------------------------------------------------------------------------- 1 | import SetCtx from "@site/cetz/docs/_generated/draw/grouping/set-ctx.mdx"; 2 | 3 | # set-ctx 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/projections/on-xy.mdx: -------------------------------------------------------------------------------- 1 | import OnXY from "@site/cetz/docs/_generated/draw/projection/on-xy.mdx"; 2 | 3 | # on-xy 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/projections/on-xz.mdx: -------------------------------------------------------------------------------- 1 | import OnXZ from "@site/cetz/docs/_generated/draw/projection/on-xz.mdx"; 2 | 3 | # on-xz 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/projections/on-yz.mdx: -------------------------------------------------------------------------------- 1 | import OnYZ from "@site/cetz/docs/_generated/draw/projection/on-yz.mdx"; 2 | 3 | # on-yz 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/projections/ortho.mdx: -------------------------------------------------------------------------------- 1 | import Ortho from "@site/cetz/docs/_generated/draw/projection/ortho.mdx"; 2 | 3 | # ortho 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/projections/projections.md: -------------------------------------------------------------------------------- 1 | # Projection 2 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/arc.mdx: -------------------------------------------------------------------------------- 1 | import Arc from "@site/cetz/docs/_generated/draw/shapes/arc.mdx"; 2 | import ArcThrough from "@site/cetz/docs/_generated/draw/shapes/arc-through.mdx"; 3 | 4 | # arc 5 | 6 | 7 | 8 | ## arc-through 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/bezier.mdx: -------------------------------------------------------------------------------- 1 | import Bezier from "@site/cetz/docs/_generated/draw/shapes/bezier.mdx"; 2 | import BezierThrough from "@site/cetz/docs/_generated/draw/shapes/bezier-through.mdx"; 3 | 4 | # bezier 5 | 6 | 7 | 8 | ## bezier-through 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/catmull.mdx: -------------------------------------------------------------------------------- 1 | import Catmull from "@site/cetz/docs/_generated/draw/shapes/catmull.mdx"; 2 | 3 | # catmull 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/circle.mdx: -------------------------------------------------------------------------------- 1 | import Circle from "@site/cetz/docs/_generated/draw/shapes/circle.mdx"; 2 | import CircleThrough from "@site/cetz/docs/_generated/draw/shapes/circle-through.mdx"; 3 | 4 | # circle 5 | 6 | 7 | 8 | ## circle-through 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/content.mdx: -------------------------------------------------------------------------------- 1 | import Content from "@site/cetz/docs/_generated/draw/shapes/content.mdx"; 2 | 3 | # content 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/grid.mdx: -------------------------------------------------------------------------------- 1 | import Grid from "@site/cetz/docs/_generated/draw/shapes/grid.mdx"; 2 | 3 | # grid 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/hobby.mdx: -------------------------------------------------------------------------------- 1 | import Hobby from "@site/cetz/docs/_generated/draw/shapes/hobby.mdx"; 2 | 3 | # hobby 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/line.mdx: -------------------------------------------------------------------------------- 1 | import Line from "@site/cetz/docs/_generated/draw/shapes/line.mdx"; 2 | 3 | # line 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/mark.mdx: -------------------------------------------------------------------------------- 1 | import Mark from "@site/cetz/docs/_generated/draw/shapes/mark.mdx"; 2 | 3 | # mark 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/merge-path.mdx: -------------------------------------------------------------------------------- 1 | import MergePath from "@site/cetz/docs/_generated/draw/shapes/merge-path.mdx"; 2 | 3 | # merge-path 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/polygon.mdx: -------------------------------------------------------------------------------- 1 | import Polygon from "@site/cetz/docs/_generated/draw/shapes/polygon.mdx"; 2 | 3 | # polygon 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/rect.mdx: -------------------------------------------------------------------------------- 1 | import Rect from "@site/cetz/docs/_generated/draw/shapes/rect.mdx"; 2 | 3 | # rect 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/shapes/shapes.md: -------------------------------------------------------------------------------- 1 | # Shapes 2 | 3 | Draw functions that draw shapes onto the canvas, be that lines, circles or curves. 4 | 5 | - [circle](./circle) Draws a circle at a point. 6 | - [arc](./arc) Draws an arc between two points. 7 | - [mark](./mark) Draws a mark at a point in a direction. 8 | - [line](./line) Draws a line between two points, or a line strip between 3 or more points. 9 | - [polygon](./polygon) Draws a regular polygon. 10 | - [grid](./grid) Draws a grid. 11 | - [content](./content) Places some content on the canvas. 12 | - [rect](./rect) Draws a rectangle between two points. 13 | - [bezier](./bezier) Draws a bezier with control points. 14 | - [hobby](./hobby) Draws a hobby curve. 15 | - [catmull](./catmull) Draws a Catmull-Rom curve. 16 | - [merge-path](./merge-path) Merges the paths of other draw functions into one continuous path. 17 | -------------------------------------------------------------------------------- /docs/api/draw-functions/styling/fill.mdx: -------------------------------------------------------------------------------- 1 | import Fill from "@site/cetz/docs/_generated/draw/styling/fill.mdx"; 2 | 3 | # fill 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/styling/set-style.mdx: -------------------------------------------------------------------------------- 1 | import SetStyle from "@site/cetz/docs/_generated/draw/styling/set-style.mdx"; 2 | 3 | # set-style 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/styling/stroke.mdx: -------------------------------------------------------------------------------- 1 | import Stroke from "@site/cetz/docs/_generated/draw/styling/stroke.mdx"; 2 | 3 | # stroke 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/styling/styling.md: -------------------------------------------------------------------------------- 1 | # Styling 2 | 3 | Draw functions that change how other elements are drawn. 4 | 5 | - [set-style](./set-style) Sets the styling for elements. 6 | - [fill](./fill) Shorthand to set the fill of elements. 7 | - [stroke](./stroke) Shorthand to set the stroke of elements. -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/move-to.mdx: -------------------------------------------------------------------------------- 1 | import MoveTo from "@site/cetz/docs/_generated/draw/transformations/move-to.mdx"; 2 | 3 | # move-to 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/rotate.mdx: -------------------------------------------------------------------------------- 1 | import Rotate from "@site/cetz/docs/_generated/draw/transformations/rotate.mdx"; 2 | 3 | # rotate 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/scale.mdx: -------------------------------------------------------------------------------- 1 | import Scale from "@site/cetz/docs/_generated/draw/transformations/scale.mdx"; 2 | 3 | # scale 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/set-origin.mdx: -------------------------------------------------------------------------------- 1 | import SetOrigin from "@site/cetz/docs/_generated/draw/transformations/set-origin.mdx"; 2 | 3 | # set-origin 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/set-transform.mdx: -------------------------------------------------------------------------------- 1 | import SetTransform from "@site/cetz/docs/_generated/draw/transformations/set-transform.mdx"; 2 | 3 | # set-transform 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/set-viewport.mdx: -------------------------------------------------------------------------------- 1 | import SetViewport from "@site/cetz/docs/_generated/draw/transformations/set-viewport.mdx"; 2 | 3 | # set-viewport 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/transformations.md: -------------------------------------------------------------------------------- 1 | # Transformations 2 | 3 | All transformation functions push a transformation matrix onto the current transform stack. To apply transformations scoped use the [`group`](../grouping/group.mdx) draw function. 4 | 5 | Transformation matrices get multiplied in the following order: 6 | 7 | $$ 8 | M_{\text{world}} = M_\text{world} \cdot M_\text{local} 9 | $$ 10 | -------------------------------------------------------------------------------- /docs/api/draw-functions/transformations/translate.mdx: -------------------------------------------------------------------------------- 1 | import Translate from "@site/cetz/docs/_generated/draw/transformations/translate.mdx"; 2 | 3 | # translate 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/aabb.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Aabb 3 | --- 4 | 5 | # {{aabb}} 6 | 7 | An Axis Aligned Bounding Box, or AABB for short. 8 | 9 | They take the form of a dictionary with the following keys: 10 | 11 | - low vector: Min. bounds vector 12 | - high vector: Max. bounds vector 13 | 14 | The following functions are in the `aabb` module. 15 | 16 | import Combined from "@site/cetz/docs/_generated/aabb/-combined.mdx"; 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/api/internal/anchor.mdx: -------------------------------------------------------------------------------- 1 | # Anchor 2 | 3 | Functions to aid in anchor creation when defining custom elements. 4 | 5 | import Combined from "@site/cetz/docs/_generated/anchor/-combined.mdx"; 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/api/internal/bezier.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bezier 3 | --- 4 | 5 | # {{bezier}} 6 | 7 | An array of three or four vectors, the start point, the endpoint, the first control point and optionally a second control point if the bezier is cubic. 8 | 9 | The following functions are in the `bezier` module. 10 | 11 | import Combined from "@site/cetz/docs/_generated/bezier/-combined.mdx"; 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/api/internal/canvas.mdx: -------------------------------------------------------------------------------- 1 | # canvas 2 | 3 | import Combined from "@site/cetz/docs/_generated/canvas/canvas.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/complex.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Complex 3 | --- 4 | 5 | # {{complex}} 6 | 7 | A complex number that takes the form of an {{array}} of two {{float}}s. The first element is the real number and the second represents the imaginary. 8 | 9 | import Combined from "@site/cetz/docs/_generated/complex/-combined.mdx"; 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/api/internal/coordinate.mdx: -------------------------------------------------------------------------------- 1 | # Coordinate 2 | 3 | Functions to aid in resolving coordinates. 4 | 5 | import Combined from "@site/cetz/docs/_generated/coordinate/-combined.mdx"; 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/api/internal/drawable.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Drawable 3 | --- 4 | 5 | # {{drawable}} 6 | 7 | A dictionary that contains path or content data that will actually get drawn by the canvas. Can also be an array of this type. 8 | 9 | import Combined from "@site/cetz/docs/_generated/drawable/-combined.mdx"; 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/api/internal/hobby.mdx: -------------------------------------------------------------------------------- 1 | # Hobby 2 | 3 | import Combined from "@site/cetz/docs/_generated/hobby/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/internal.md: -------------------------------------------------------------------------------- 1 | # Internal 2 | -------------------------------------------------------------------------------- /docs/api/internal/intersection.mdx: -------------------------------------------------------------------------------- 1 | # Intersection 2 | 3 | import Combined from "@site/cetz/docs/_generated/intersection/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/mark.mdx: -------------------------------------------------------------------------------- 1 | # Mark 2 | 3 | import Combined from "@site/cetz/docs/_generated/mark/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/matrix.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Matrix 3 | --- 4 | 5 | # {{matrix}} 6 | 7 | An array of arrays of floats that represent a matrix. Can be any size but transformation matrices are 4x4. 8 | 9 | import Combined from "@site/cetz/docs/_generated/matrix/-combined.mdx"; 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/api/internal/path-util.mdx: -------------------------------------------------------------------------------- 1 | # Path Util 2 | 3 | import Combined from "@site/cetz/docs/_generated/path-util/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/process.mdx: -------------------------------------------------------------------------------- 1 | # Process 2 | 3 | import Combined from "@site/cetz/docs/_generated/process/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/styles.mdx: -------------------------------------------------------------------------------- 1 | # Styles 2 | 3 | import Combined from "@site/cetz/docs/_generated/styles/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/util.mdx: -------------------------------------------------------------------------------- 1 | # Util 2 | 3 | import Combined from "@site/cetz/docs/_generated/util/-combined.mdx"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/internal/vector.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vector 3 | --- 4 | 5 | # {{vector}} 6 | 7 | An array of any number of floats. 8 | 9 | import Combined from "@site/cetz/docs/_generated/vector/-combined.mdx"; 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/api/libraries/angle/angle.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /api/libraries/angle/angle 3 | --- 4 | 5 | import Angle from "@site/cetz/docs/_generated/lib/angle/angle.mdx"; 6 | 7 | # Angle 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/api/libraries/angle/index.md: -------------------------------------------------------------------------------- 1 | # Angle 2 | 3 | Provides helper functions to draw angles and right angles. 4 | -------------------------------------------------------------------------------- /docs/api/libraries/angle/right-angle.mdx: -------------------------------------------------------------------------------- 1 | import RightAngle from "@site/cetz/docs/_generated/lib/angle/right-angle.mdx"; 2 | 3 | # Right Angle 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/braces/brace.mdx: -------------------------------------------------------------------------------- 1 | import Brace from "@site/cetz/docs/_generated/lib/decorations/brace/brace.mdx"; 2 | 3 | # Brace 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/braces/flat-brace.mdx: -------------------------------------------------------------------------------- 1 | import FlatBrace from "@site/cetz/docs/_generated/lib/decorations/brace/flat-brace.mdx"; 2 | 3 | # Flat Brace 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/braces/index.md: -------------------------------------------------------------------------------- 1 | # Braces 2 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/index.md: -------------------------------------------------------------------------------- 1 | # Decorations 2 | 3 | Various pre-made shapes and path modifications. 4 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/path/coil.mdx: -------------------------------------------------------------------------------- 1 | import Coil from "@site/cetz/docs/_generated/lib/decorations/path/coil.mdx"; 2 | 3 | # Coil 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/path/index.mdx: -------------------------------------------------------------------------------- 1 | # Path Decorations 2 | 3 | Path decorations are elements that accept a path as input and generate one or more shapes that follow that path. 4 | 5 | All path decoration functions support the following style keys: 6 | 7 | 8 | Absolute or relative start of the decoration on the path. 9 | 10 | 11 | 12 | Absolute or relative end of the decoration on the path. 13 | 14 | 15 | 16 | If set to `"LINE"`, generate lines between the path's start/end and the 17 | decoration's start/end if the path is *not closed*. 18 | 19 | 20 | 21 | Width or thickness of the decoration. 22 | 23 | 24 | 25 | The number of repetitions/phases to generate. This key is ignored if 26 | `segment-length` is not none 27 | 28 | 29 | 30 | Length of one repetion/phase of the decoration. 31 | 32 | 33 | 34 | Alignment of the decoration on the path *if `segment-length` is set* and the 35 | decoration does not fill up the full range between start and stop. Can be one 36 | of `"START"`, `"MID"`, `"END`. 37 | 38 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/path/wave.mdx: -------------------------------------------------------------------------------- 1 | import Wave from "@site/cetz/docs/_generated/lib/decorations/path/wave.mdx"; 2 | 3 | # Wave 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/decorations/path/zigzag.mdx: -------------------------------------------------------------------------------- 1 | import Zigzag from "@site/cetz/docs/_generated/lib/decorations/path/zigzag.mdx"; 2 | 3 | # Zigzag 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/index.md: -------------------------------------------------------------------------------- 1 | # Libraries 2 | 3 | Functions of CeTZ's libraries. 4 | -------------------------------------------------------------------------------- /docs/api/libraries/palette/index.mdx: -------------------------------------------------------------------------------- 1 | # Palette 2 | 3 | A palette is a function of the form `index => style` that takes an index, that can be any int and returns a canvas style dictionary. If passed the string `"len"` it must return the length of its unique styles. An example use for palette functions is the plot library, which can use palettes to apply different styles per plot. 4 | 5 | ## Predefined Palettes 6 | -------------------------------------------------------------------------------- /docs/api/libraries/palette/new.mdx: -------------------------------------------------------------------------------- 1 | import New from "@site/cetz/docs/_generated/lib/palette/new.mdx"; 2 | 3 | # New 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/api/libraries/tree/index.md: -------------------------------------------------------------------------------- 1 | # Tree 2 | 3 | The tree library allows the drawing diagrams with simple tree layout algorithms. 4 | -------------------------------------------------------------------------------- /docs/api/libraries/tree/tree.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /api/libraries/tree/tree 3 | --- 4 | 5 | import Tree from "@site/cetz/docs/_generated/lib/tree/tree.mdx"; 6 | 7 | # Tree 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/api/overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This section is a reference for all functions in the CeTZ package and its libraries. 4 | -------------------------------------------------------------------------------- /docs/basics/anchors.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Anchors 3 | --- 4 | 5 | import Type from "@site/src/components/Type"; 6 | 7 | You can refer to a position relative to an element by using its anchors. Anchors come in three different variations but can all be used in two ways. 8 | 9 | The first is by using the `anchor` argument on an element. When given, the element will be translated such that the given anchor will be where the given position is. This is supported by all elements that have the `anchor` argument. 10 | 11 | ```typc example 12 | // Draw a circle and place its "west" anchor at the origin. 13 | circle((0,0), anchor: "west") 14 | 15 | // Draw a smaller red circle at the origin. 16 | fill(red) 17 | stroke(none) 18 | circle((0,0), radius: 0.3) 19 | ``` 20 | 21 | The second is by using [anchor coordinates](/basics/coordinate-systems#anchor). You must first give the element a name by passing a string to its `name` argument, you can then use its anchors to place other elements. Note that this is only available for elements that have a `name` argument. 22 | 23 | ```typc example 24 | // Name the circle 25 | circle((0,0), name: "circle") 26 | 27 | // Draw a smaller red circle at "circle"'s east anchor 28 | fill(red) 29 | stroke(none) 30 | circle("circle.east", radius: 0.3) 31 | ``` 32 | 33 | ## Named 34 | 35 | Named anchors are normally unique to the type of element, such as a bezier curve's control points. Other border and path anchors specify their own named anchors that are available to all elements that support border or path anchors. 36 | 37 | Elements that have an `anchor` argument also have a "default" named anchor. You can use it by just giving the element's name without an anchor. 38 | 39 | ## Border 40 | 41 | A border anchor refers to a point on the element's border where a ray is cast from the element's center at a given angle and hits the border. 42 | 43 | They are given as angles where `0deg` is towards the right and `90deg` is up. 44 | 45 | Border anchors also specify named compass directions such as "north", "north-east", etc. Border anchors also specify a "center" named anchor which is where the ray cast originates from. 46 | 47 | ```typc example 48 | circle((0, 0), name: "circle", radius: 1) 49 | 50 | set-style(content: (frame: "rect", stroke: none, fill: white, padding: .1)) 51 | content((name: "circle", anchor: 0deg), [0deg], anchor: "west") 52 | content((name: "circle", anchor: 160deg), [160deg], anchor: "south-east") 53 | content("circle.north", [North], anchor: "south") 54 | content("circle.south-east", [South East], anchor: "north-west") 55 | content("circle.south-west", [South West], anchor: "north-east") 56 | ``` 57 | 58 | ## Path 59 | 60 | A path anchor refers to a point along the path of an element. They can be given as either a number for an absolute distance along the path, or a ratio for a relative distance along the path. 61 | 62 | Path anchors also specify three anchors "start", "mid" and "end". 63 | 64 | ```typc example 65 | line((0,0), (10, 1), name: "line") 66 | 67 | set-style(content: (frame: "rect", stroke: none, fill: white, padding: .1)) 68 | content("line.start", [0%, 0, "start"], anchor: "east") 69 | content("line.mid", [50%, "mid"]) 70 | content("line.end", [100%, "end"], anchor: "west") 71 | 72 | content((name: "line", anchor: 75%), [75%]) 73 | content((name: "line", anchor: 50pt), [50pt]) 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/basics/basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | # Basics 5 | 6 | The following chapters are about the basic and core concepts of CeTZ. They are recommended reading for basic usage. 7 | -------------------------------------------------------------------------------- /docs/basics/canvas.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Canvas 3 | --- 4 | 5 | The [`canvas`](/api/internal/canvas) function is what handles all of the logic and processing in order to produce drawings. It's usually called with a code block `{...}` as argument. The content of the curly braces is the _body_ of the canvas. Import all the draw functions you need at the top of the body: 6 | 7 | ```typ 8 | #cetz.canvas({ 9 | import cetz.draw: * 10 | 11 | }) 12 | ``` 13 | 14 | You can now call the draw functions within the body and they'll produce some graphics! Typst will evaluate the code block and pass the result to the `canvas` function for rendering. 15 | 16 | The canvas does not have typical `width` and `height` parameters. Instead its size will grow and shrink to fit the drawn graphic. 17 | 18 | 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! 19 | -------------------------------------------------------------------------------- /docs/basics/custom-types.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | import Type from "@site/src/components/Type"; 5 | 6 | # Custom Types 7 | 8 | Many CeTZ functions expect data in certain formats which we will call types. Note that these are actually made up of Typst primitives. 9 | 10 | ## coordinate 11 | 12 | A position on the canvas specified by any coordinate system. See [Coordinate Systems](/basics/coordinate-systems). 13 | 14 | ## number 15 | 16 | Any of float, int or length. 17 | 18 | ## style 19 | 20 | Represents options passed to draw functions that affect how elements are drawn. They are normally taken in the form of named arguments to the draw functions or sometimes can be a dictionary for a single argument. 21 | 22 | -------------------------------------------------------------------------------- /docs/basics/styling.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Styling 3 | --- 4 | 5 | 6 | import Parameter from "@site/src/components/Parameter"; 7 | import Type from "@site/src/components/Type"; 8 | 9 | You can style draw elements by passing the relevant named arguments to their draw functions. All elements that draw something have stroke and fill styling unless said otherwise. 10 | 11 | 12 | How to fill the drawn element. 13 | 14 | 19 | How to stroke the border or the path of the draw element. [See Typst's line 20 | documentation for more 21 | details.](https://typst.app/docs/reference/visualize/line/#parameters-stroke) 22 | 23 | 24 | How to fill self-intersecting paths. Can be "non-zero" or "even-odd". 25 | [See Typst's path documentation for more details.](https://typst.app/docs/reference/visualize/path/#parameters-fill-rule) 26 | 27 | 28 | 29 | 30 | ```typc example 31 | // Draws a red circle with a blue border 32 | circle((0, 0), fill: red, stroke: blue) 33 | 34 | // Draws a green line 35 | line((0, 0), (1, 1), stroke: green) 36 | ``` 37 | 38 | Instead of having to specify the same styling for each time you want to draw an element, you can use the [`set-style`](/api/draw-functions/styling/set-style) function to change the style for all elements after it, like a Typst `set` rule. You can still pass styling to a draw function to override what has been set with `set-style`. You can also use the [`fill`](/api/draw-functions/styling/fill) and [`stroke`](/api/draw-functions/styling/stroke) functions as a shorthand to set the fill and stroke respectively. 39 | 40 | ```typc example 41 | // Draws an empty square with a black border 42 | rect((-1, -1), (1, 1)) 43 | 44 | // Sets the global style to have a fill of red and a stroke of blue 45 | set-style(stroke: blue, fill: red) 46 | circle((0,0)) 47 | 48 | // Draws a green line despite the global stroke being blue 49 | line((), (1,1), stroke: green) 50 | ``` 51 | 52 | When using a dictionary for a style, it is important to note that they update each other instead of overriding the entire option like a non-dictionary value would. For example, if the stroke is set to `(paint: red, thickness: 5pt)` and you pass `(paint: blue)`, the stroke would become `(paint: blue, thickness: 5pt)`. 53 | 54 | ```typc example 55 | // Sets the stroke to red with a thickness of 5pt 56 | set-style(stroke: (paint: red, thickness: 5pt)) 57 | 58 | // Draws a line with the global stroke 59 | line((0,0), (1,0)) 60 | 61 | // Draws a blue line with a thickness of 5pt because dictionaries update the style 62 | line((0,0), (1,1), stroke: (paint: blue)) 63 | 64 | // Draws a yellow line with a thickness of 1pt because other values override the style 65 | line((0,0), (0,1), stroke: yellow) 66 | ``` 67 | 68 | You can also specify styling for each type of element. Note that dictionary values will still update with its global value, the full hierarchy is `function > element type > global`. When the value of a style is auto, it will become exactly its parent style. 69 | 70 | ```typc example 71 | set-style( 72 | // Global fill and stroke 73 | fill: green, 74 | stroke: (thickness: 5pt), 75 | // Stroke and fill for only rectangles 76 | rect: (stroke: (dash: "dashed"), fill: blue), 77 | ) 78 | rect((0,0), (1,1)) 79 | circle((2.5, 0.5)) 80 | rect((4, 0), (5, 1), stroke: (thickness: 1pt)) 81 | ``` 82 | -------------------------------------------------------------------------------- /docs/custom-types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | number: { link: "/docs/basics/custom-types#number", class: "num" }, 3 | coordinate: { link: "/docs/basics/custom-types#coordinate" }, 4 | style: { link: "/docs/basics/custom-types#style" }, 5 | context: { link: "/docs/advanced/custom-types#context" }, 6 | vector: { link: "/docs/api/internal/vector" }, 7 | matrix: { link: "/docs/api/internal/matrix" }, 8 | element: {link: "/docs/advanced/custom-types#element"}, 9 | }; 10 | -------------------------------------------------------------------------------- /docs/getting-started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | sidebar_position: 1 4 | --- 5 | 6 | ## Usage 7 | 8 | This is the minimal starting point in a `.typ` file: 9 | 10 | ```typ 11 | #import "@preview/cetz:0.3.4" 12 | #cetz.canvas({ 13 | import cetz.draw: * 14 | ... 15 | }) 16 | ``` 17 | 18 | Note that draw functions are imported inside the scope of the `canvas` block. This is recommended as some draw functions override Typst's functions such as `line`. 19 | 20 | ## Examples 21 | 22 | From this point on only the code inside the `canvas` block will be shown in examples unless specified otherwise. 23 | 24 | ```typc example 25 | circle((0, 0)) 26 | line((0, 0), (2, 1)) 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/internals/internals.md: -------------------------------------------------------------------------------- 1 | # Internals 2 | 3 | CeTZ is made up of several parts which can be daunting and complicated to figure out. Here we plan to detail how CeTZ actually works and explain the ideas. 4 | 5 | This is not required reading for typical usage! 6 | -------------------------------------------------------------------------------- /docs/libraries/libraries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Libraries 3 | --- 4 | 5 | CeTZ provides more specialised and focused functions in order to draw plots, charts, angles etc. They have been separated from the `draw` module into separate libraries for the sake of organisation and clarity. 6 | 7 | We are planning to move them into their own packages. We don't know when but most likely once the current Typst package handling system gets improved. 8 | -------------------------------------------------------------------------------- /docs/libraries/plot.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Plot 3 | --- 4 | 5 | import Type from "@site/src/components/Type"; 6 | 7 | The plot library allows the plotting of data. 8 | 9 | ## Types 10 | 11 | The following types are commonly used by functions of this library 12 | 13 | ### domain 14 | 15 | A tuple representing a mathematical function's domain as a closed interval. Example domains are `(0, 1)` for $[0,1]$ or `(-calc.pi, calc.pi)` for $[-\pi, \pi]$. 16 | 17 | ### axes 18 | 19 | A tuple of axis names. Functions that take axes will use those axes as their `x` and `y` axis for plotting. To rotate a plot, you can simply swap its axes such as `("y", "x")`. 20 | 21 | ## Usage 22 | 23 | ... 24 | -------------------------------------------------------------------------------- /docs/libraries/tree.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tree 3 | --- 4 | 5 | import Type from "@site/src/components/Type"; 6 | 7 | The tree library allows the drawing of diagrams with simple tree layout algorithms. 8 | 9 | ## Nodes 10 | 11 | A tree node is an array consisting of the node's value at index 0 followed by its child nodes. For the default `draw-node` function, the value (the first item) of a node must be of type content. 12 | 13 | Example of a list of nodes: 14 | 15 | ```typc example 16 | cetz.tree.tree( 17 | ( 18 | [A], 19 | ( 20 | [B], 21 | ( 22 | [C], 23 | ([D],) 24 | ) 25 | ) 26 | ), 27 | direction: "right" 28 | ) 29 | ``` 30 | 31 | Example of a tree of nodes: 32 | 33 | ```typc example 34 | cetz.tree.tree( 35 | ( 36 | [A], 37 | ( 38 | [B], 39 | [C] 40 | ), 41 | ( 42 | [D], 43 | [E] 44 | ) 45 | ), 46 | direction: "right" 47 | ) 48 | ``` 49 | 50 | ## Drawing and Styling Tree Nodes 51 | 52 | The `tree()` function takes an optional `draw-node:` and `draw-edge:` callback function that can be used to customice node and edge drawing. 53 | 54 | The `draw-node` function must take the current node and its parents node anchor as arguments and return one or more elements. 55 | 56 | For drawing edges between nodes, the `draw-edge` function must take two node anchors and the target node as arguments and return one or more elements. 57 | 58 | ```typc example 59 | import cetz.tree 60 | let data = ([\*], ([A], [A.A], [A.B]), ([B], [B.A])) 61 | tree.tree( 62 | data, 63 | direction: "right", 64 | draw-node: (node, ..) => { 65 | circle((), radius: .35, fill: blue, stroke: none) 66 | content((), text(white, [#node.content])) 67 | }, 68 | draw-edge: (from, to, ..) => { 69 | let (a, b) = (from + ".center", to + ".center") 70 | line((a, .4, b), (b, .4, a)) 71 | } 72 | ) 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: / 3 | sidebar_position: 0 4 | --- 5 | 6 | # Overview 7 | 8 | CeTZ, ein Typst Zeichenpaket, is a drawing package for [Typst](https://typst.app/). Its API is similar to Processing but with relative coordinates and anchors from 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! 9 | 10 | These docs are a work in progress! Please submit issues for parts that don't make sense or need improving :) 11 | 12 | We are also still trying to find a logo for CeTZ so if you have any ideas please let us know through the Typst discord server. 13 | -------------------------------------------------------------------------------- /docs/sidebar.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 2 | export default [ 3 | "overview", 4 | "getting-started", 5 | { 6 | type: "category", 7 | label: "Basics", 8 | link: { 9 | type: "doc", 10 | id: "basics/basics", 11 | }, 12 | items: [ 13 | "basics/custom-types", 14 | "basics/canvas", 15 | "basics/styling", 16 | "basics/coordinate-systems", 17 | "basics/anchors", 18 | "basics/marks", 19 | ], 20 | }, 21 | { 22 | type: "category", 23 | label: "Libraries", 24 | link: { 25 | type: "doc", 26 | id: "libraries/libraries", 27 | }, 28 | items: ["libraries/tree"], 29 | }, 30 | { 31 | type: "category", 32 | label: "Tutorials", 33 | link: { 34 | type: "generated-index", 35 | title: "Tutorials", 36 | }, 37 | items: ["tutorials/karl"], 38 | }, 39 | { 40 | type: "category", 41 | label: "Advanced", 42 | link: { 43 | type: "doc", 44 | id: "advanced/advanced", 45 | }, 46 | items: ["advanced/custom-types"], 47 | }, 48 | ]; 49 | -------------------------------------------------------------------------------- /gallery/karls-picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/gallery/karls-picture.png -------------------------------------------------------------------------------- /gallery/karls-picture.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/cetz:0.3.4" 2 | #set page(width: auto, height: auto, margin: .5cm) 3 | 4 | #show math.equation: block.with(fill: white, inset: 1pt) 5 | 6 | #cetz.canvas(length: 3cm, { 7 | import cetz.draw: * 8 | 9 | set-style( 10 | mark: (fill: black, scale: 2), 11 | stroke: (thickness: 0.4pt, cap: "round"), 12 | angle: ( 13 | radius: 0.3, 14 | label-radius: .22, 15 | fill: green.lighten(80%), 16 | stroke: (paint: green.darken(50%)) 17 | ), 18 | content: (padding: 1pt) 19 | ) 20 | 21 | grid((-1.5, -1.5), (1.4, 1.4), step: 0.5, stroke: gray + 0.2pt) 22 | 23 | circle((0,0), radius: 1) 24 | 25 | line((-1.5, 0), (1.5, 0), mark: (end: "stealth")) 26 | content((), $ x $, anchor: "west") 27 | line((0, -1.5), (0, 1.5), mark: (end: "stealth")) 28 | content((), $ y $, anchor: "south") 29 | 30 | for (x, ct) in ((-1, $ -1 $), (-0.5, $ -1/2 $), (1, $ 1 $)) { 31 | line((x, 3pt), (x, -3pt)) 32 | content((), anchor: "north", ct) 33 | } 34 | 35 | for (y, ct) in ((-1, $ -1 $), (-0.5, $ -1/2 $), (0.5, $ 1/2 $), (1, $ 1 $)) { 36 | line((3pt, y), (-3pt, y)) 37 | content((), anchor: "east", ct) 38 | } 39 | 40 | // Draw the green angle 41 | cetz.angle.angle((0,0), (1,0), (1, calc.tan(30deg)), 42 | label: text(green, [#sym.alpha])) 43 | 44 | line((0,0), (1, calc.tan(30deg))) 45 | 46 | set-style(stroke: (thickness: 1.2pt)) 47 | 48 | line((30deg, 1), ((), "|-", (0,0)), stroke: (paint: red), name: "sin") 49 | content(("sin.start", 50%, "sin.end"), text(red)[$ sin alpha $]) 50 | line("sin.end", (0,0), stroke: (paint: blue), name: "cos") 51 | content(("cos.start", 50%, "cos.end"), text(blue)[$ cos alpha $], anchor: "north") 52 | line((1, 0), (1, calc.tan(30deg)), name: "tan", stroke: (paint: orange)) 53 | content("tan.end", $ text(#orange, tan alpha) = text(#red, sin alpha) / text(#blue, cos alpha) $, anchor: "west") 54 | }) 55 | -------------------------------------------------------------------------------- /gallery/paciolis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/gallery/paciolis.png -------------------------------------------------------------------------------- /gallery/paciolis.typ: -------------------------------------------------------------------------------- 1 | // Example by @samuelireson 2 | #import "@preview/cetz:0.3.4": canvas, draw, tree 3 | 4 | #set page(width: auto, height: auto, margin: .5cm) 5 | 6 | #canvas(length: 2cm, { 7 | import draw: * 8 | let phi = (1 + calc.sqrt(5)) / 2 9 | 10 | ortho({ 11 | hide({ 12 | line( 13 | (-phi, -1, 0), (-phi, 1, 0), (phi, 1, 0), (phi, -1, 0), close: true, name: "xy", 14 | ) 15 | line( 16 | (-1, 0, -phi), (1, 0, -phi), (1, 0, phi), (-1, 0, phi), close: true, name: "xz", 17 | ) 18 | line( 19 | (0, -phi, -1), (0, -phi, 1), (0, phi, 1), (0, phi, -1), close: true, name: "yz", 20 | ) 21 | }) 22 | 23 | intersections("a", "yz", "xy") 24 | intersections("b", "xz", "yz") 25 | intersections("c", "xy", "xz") 26 | 27 | set-style(stroke: (thickness: 0.5pt, cap: "round", join: "round")) 28 | line((0, 0, 0), "c.1", (phi, 1, 0), (phi, -1, 0), "c.3") 29 | line("c.0", (-phi, 1, 0), "a.2") 30 | line((0, 0, 0), "b.1", (1, 0, phi), (-1, 0, phi), "b.3") 31 | line("b.0", (1, 0, -phi), "c.2") 32 | line((0, 0, 0), "a.1", (0, phi, 1), (0, phi, -1), "a.3") 33 | line("a.0", (0, -phi, 1), "b.2") 34 | 35 | anchor("A", (0, phi, 1)) 36 | content("A", [$A$], anchor: "north", padding: .1) 37 | anchor("B", (-1, 0, phi)) 38 | content("B", [$B$], anchor: "south", padding: .1) 39 | anchor("C", (1, 0, phi)) 40 | content("C", [$C$], anchor: "south", padding: .1) 41 | line("A", "B", stroke: (dash: "dashed")) 42 | line("A", "C", stroke: (dash: "dashed")) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /gallery/periodic-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/gallery/periodic-table.png -------------------------------------------------------------------------------- /gallery/plate-capacitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/gallery/plate-capacitor.png -------------------------------------------------------------------------------- /gallery/plate-capacitor.typ: -------------------------------------------------------------------------------- 1 | // Copied from https://github.com/janosh/tikz/blob/87754ea/assets/plate-capacitor/plate-capacitor.typ 2 | 3 | #import "@preview/cetz:0.3.4": canvas, draw 4 | #import draw: line, rect, content, bezier, group, anchor 5 | 6 | #set page(width: auto, height: auto, margin: 5pt) 7 | 8 | // Constants 9 | #let height = 5 10 | #let width = 4 11 | #let plate-width = 0.5 12 | #let diel-width = 0.16 * width 13 | #let n-field-lines = 7 14 | #let n-charges = 7 15 | 16 | // Colors 17 | #let e-color = rgb("#e67300") 18 | #let plus-color = rgb("#cc2200").transparentize(20%) 19 | #let minus-color = rgb("#0044cc").transparentize(20%) 20 | 21 | // Helper function to draw a capacitor plate with charges 22 | #let plate(x, is-anode: true) = { 23 | let color = if is-anode { plus-color } else { minus-color } 24 | let fill-base = if is-anode { rgb("#f29797") } else { rgb("#9fc2f6") } 25 | let sign = if is-anode { $+$ } else { $-$ } 26 | 27 | // Draw plate with gradient fill 28 | rect( 29 | (x, 0), 30 | (x + plate-width, height), 31 | stroke: (paint: color, thickness: .7pt), 32 | fill: gradient.linear(fill-base.lighten(50%), fill-base, angle: 90deg), 33 | ) 34 | // Draw charge label 35 | content( 36 | (x + plate-width / 2, height + 0.1), 37 | text(fill: color)[$sign Q_"C"$], 38 | anchor: "south", 39 | ) 40 | // Draw charges 41 | for ii in range(n-charges) { 42 | let y = ii * height / n-charges + 0.325 43 | content((x + plate-width / 2, y), text(fill: color)[$sign$]) 44 | } 45 | } 46 | 47 | // Helper function to draw a dipole 48 | #let dipole(x, y, ..style) = group({ 49 | let plus-grad = gradient.linear( 50 | angle: 90deg, 51 | minus-color.lighten(30%), 52 | minus-color.darken(30%), 53 | ) 54 | let minus-grad = gradient.linear( 55 | angle: 90deg, 56 | plus-color.lighten(30%), 57 | plus-color.darken(30%), 58 | ) 59 | rect(x, ((x, "|-", y), 50%, y), fill: plus-grad, radius: (west: .5), name: "minus", ..style) 60 | rect(y, ((x, "-|", y), 50%, x), fill: minus-grad, radius: (east: .5), name: "plus", ..style) 61 | content("plus", [+]) 62 | content("minus", [--]) 63 | }) 64 | 65 | #canvas({ 66 | // Dielectric slab 67 | rect( 68 | (diel-width, -0.03 * height), 69 | (width - diel-width, 1.08 * height), 70 | stroke: e-color, 71 | fill: rgb("#fff8f0"), // very light orange 72 | ) 73 | content((width / 2, 1.15 * height), text(fill: e-color)[$arrow(E)$]) 74 | content((width * 0.8, 1.09 * height), text(fill: minus-color)[$+Q_"surf"$], anchor: "south") 75 | content((1.3 * diel-width, 1.09 * height), text(fill: plus-color)[$-Q_"surf"$], anchor: "south") 76 | 77 | // Electric field lines 78 | for ii in range(n-field-lines) { 79 | let y = (ii + 0.42) * height / n-field-lines 80 | line( 81 | (-plate-width, y), 82 | (width + plate-width, y), 83 | stroke: (paint: e-color, thickness: 1.2pt), 84 | mark: (pos: 0.5, end: "stealth", fill: e-color), 85 | ) 86 | } 87 | 88 | // Draw plates 89 | plate(width, is-anode: false) // Left plate (cathode) 90 | plate(-plate-width, is-anode: true) // Right plate (anode) 91 | 92 | // Dipoles 93 | for x in (0.3, 0.5, 0.7) { 94 | for ii in range(n-field-lines) { 95 | let y = (ii + 0.94) * height / n-field-lines 96 | dipole((x * width - 0.3, y - 0.12), (x * width + 0.3, y + 0.12), stroke: 0.5pt) 97 | } 98 | } 99 | }) 100 | -------------------------------------------------------------------------------- /gallery/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/gallery/tree.png -------------------------------------------------------------------------------- /gallery/tree.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/cetz:0.3.4": canvas, draw, tree 2 | 3 | #set page(width: auto, height: auto, margin: .5cm) 4 | 5 | #let data = ( 6 | [A], ([B], [C], [D]), ([E], [F]) 7 | ) 8 | 9 | #canvas({ 10 | import draw: * 11 | 12 | set-style(content: (padding: .2), 13 | fill: gray.lighten(70%), 14 | stroke: gray.lighten(70%)) 15 | 16 | tree.tree(data, spread: 2.5, grow: 1.5, draw-node: (node, ..) => { 17 | circle((), radius: .45, stroke: none) 18 | content((), node.content) 19 | }, draw-edge: (from, to, ..) => { 20 | line((a: from, number: .6, b: to), 21 | (a: to, number: .6, b: from), mark: (end: ">")) 22 | }, name: "tree") 23 | 24 | // Draw a "custom" connection between two nodes 25 | let (a, b) = ("tree.0-0-1", "tree.0-1-0",) 26 | line((a, .6, b), (b, .6, a), mark: (end: ">", start: ">")) 27 | }) 28 | -------------------------------------------------------------------------------- /gallery/waves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/gallery/waves.png -------------------------------------------------------------------------------- /gallery/waves.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/cetz:0.3.4": canvas, draw, vector, matrix 2 | 3 | #set page(width: auto, height: auto, margin: .5cm) 4 | 5 | #canvas({ 6 | import draw: * 7 | 8 | ortho(y: -30deg, x: 30deg, { 9 | on-xz({ 10 | grid((0,-2), (8,2), stroke: gray + .5pt) 11 | }) 12 | 13 | // Draw a sine wave on the xy plane 14 | let wave(amplitude: 1, fill: none, phases: 2, scale: 8, samples: 100) = { 15 | line(..(for x in range(0, samples + 1) { 16 | let x = x / samples 17 | let p = (2 * phases * calc.pi) * x 18 | ((x * scale, calc.sin(p) * amplitude),) 19 | }), fill: fill) 20 | 21 | let subdivs = 8 22 | for phase in range(0, phases) { 23 | let x = phase / phases 24 | for div in range(1, subdivs + 1) { 25 | let p = 2 * calc.pi * (div / subdivs) 26 | let y = calc.sin(p) * amplitude 27 | let x = x * scale + div / subdivs * scale / phases 28 | line((x, 0), (x, y), stroke: rgb(0, 0, 0, 150) + .5pt) 29 | } 30 | } 31 | } 32 | 33 | on-xy({ 34 | wave(amplitude: 1.6, fill: rgb(0, 0, 255, 50)) 35 | }) 36 | on-xz({ 37 | wave(amplitude: 1, fill: rgb(255, 0, 0, 50)) 38 | }) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # Local Variables: 2 | # mode: makefile 3 | # End: 4 | gallery_dir := "./gallery" 5 | 6 | build: 7 | cd cetz-core; \ 8 | cargo build --release \ 9 | --target wasm32-unknown-unknown; \ 10 | cp target/wasm32-unknown-unknown/release/cetz_core.wasm cetz_core.wasm 11 | 12 | package target *options: 13 | ./common/scripts/package "{{target}}" {{options}} 14 | 15 | install target="@local": build 16 | ./common/scripts/package "{{target}}" 17 | 18 | test *filter: build 19 | tt run {{filter}} 20 | 21 | update-test *filter: 22 | tt update {{filter}} 23 | 24 | gallery: 25 | for f in "{{gallery_dir}}"/*.typ; do typst c "$f" "${f/typ/png}"; done 26 | -------------------------------------------------------------------------------- /requirements.typ: -------------------------------------------------------------------------------- 1 | #import "/src/deps.typ" 2 | #import "@preview/tidy:0.1.0" 3 | #import "@preview/tidy:0.2.0" 4 | #import "@preview/t4t:0.3.2" 5 | -------------------------------------------------------------------------------- /src/aabb.typ: -------------------------------------------------------------------------------- 1 | #import "vector.typ" 2 | #let cetz-core = plugin("../cetz-core/cetz_core.wasm") 3 | 4 | /// Compute an axis aligned bounding box (aabb) for a list of vectors. 5 | /// 6 | /// - pts (array): List of vectors. 7 | /// - init (aabb): Initial aabb 8 | /// -> aabb 9 | #let aabb(pts, init: none) = { 10 | let args = (pts: pts, init: init) 11 | let encoded = cbor.encode(args) 12 | let bounds = cbor(cetz-core.aabb_func(encoded)) 13 | return bounds 14 | } 15 | 16 | /// Get the mid-point of an AABB as vector. 17 | /// 18 | /// - bounds (aabb): The AABB to get the mid-point of. 19 | /// -> vector 20 | #let mid(bounds) = { 21 | return vector.scale(vector.add(bounds.low, bounds.high), .5) 22 | } 23 | 24 | /// Get the size of an aabb as vector. This is a vector from the aabb's low to high. 25 | /// 26 | /// - bounds (aabb): The aabb to get the size of. 27 | /// -> vector 28 | #let size(bounds) = { 29 | return vector.sub(bounds.high, bounds.low) 30 | } 31 | 32 | /// Pad AABB with padding from dictionary with keys top, left, right and bottom. 33 | /// 34 | /// - bounds (aabb): The AABB to pad. 35 | /// - padding (none, dictionary): Padding values 36 | /// 37 | /// -> aabb 38 | #let padded(bounds, padding) = { 39 | if padding != none { 40 | bounds.low.at(0) -= padding.at("left", default: 0) 41 | bounds.low.at(1) -= padding.at("top", default: 0) 42 | bounds.high.at(0) += padding.at("right", default: 0) 43 | bounds.high.at(1) += padding.at("bottom", default: 0) 44 | } 45 | return bounds 46 | } 47 | -------------------------------------------------------------------------------- /src/complex.typ: -------------------------------------------------------------------------------- 1 | /// Returns the real part of a complex number. 2 | /// - V (complex): A complex number. 3 | /// -> float 4 | #let re(V) = V.at(0) 5 | 6 | /// Returns the imaginary part of a complex number. 7 | /// - V (complex): A complex number. 8 | /// -> float 9 | #let im(V) = V.at(1) 10 | 11 | 12 | /// Multiplies two complex numbers together and returns the result $V W$. 13 | /// - V (complex): The complex number on the left hand side. 14 | /// - W (complex): The complex number on the right hand side. 15 | #let mul(V, W) = (re(V) * re(W) - im(V) * im(W), im(V) * re(W) + re(V) * im(W)) 16 | 17 | /// Calculates the conjugate of a complex number. 18 | /// - V (complex): A complex number. 19 | /// -> complex 20 | #let conj(V) = (re(V),-im(V)) 21 | 22 | // TODO: check what "in R^2" means. 23 | /// Calculates the dot product of two complex numbers in R^2 $V \cdot W$. 24 | /// - V (complex): The complex number on the left hand side. 25 | /// - W (complex): The complex number on the right hand side. 26 | /// -> float 27 | #let dot(V,W) = re(mul(V,conj(W))) 28 | 29 | /// Calculates the squared normal of a complex number. 30 | /// - V (complex): The complex number. 31 | /// -> float 32 | #let normsq(V) = dot(V,V) 33 | 34 | 35 | /// Calculates the normal of a complex number 36 | /// - V (complex): The complex number. 37 | /// -> float 38 | #let norm(V) = calc.sqrt(normsq(V)) 39 | 40 | /// Multiplies a complex number by a scale factor. 41 | /// - V (complex): The complex number to scale. 42 | /// - t (float): The scale factor. 43 | /// -> complex 44 | #let scale(V,t) = mul(V,(t,0)) 45 | 46 | /// Returns a unit vector in the direction of a complex number. 47 | /// - V (complex): The complex number. 48 | /// -> vector 49 | #let unit(V) = scale(V, 1/norm(V)) 50 | 51 | /// Inverts a complex number. 52 | /// - V (complex): The complex number 53 | /// -> complex 54 | #let inv(V) = scale(conj(V), 1/normsq(V)) 55 | 56 | /// Divides two complex numbers. 57 | /// - V (complex): The complex number of the numerator. 58 | /// - W (complex): The complex number of the denominator. 59 | /// -> complex 60 | #let div(V,W) = mul(V,inv(W)) 61 | 62 | /// Adds two complex numbers together. 63 | /// - V (complex): The complex number on the left hand side. 64 | /// - W (complex): The complex number on the right hand side. 65 | /// -> complex 66 | #let add(V,W) = (re(V) + re(W),im(V) + im(W)) 67 | 68 | /// Subtracts two complex numbers together. 69 | /// - V (complex): The complex number on the left hand side. 70 | /// - W (complex): The complex number on the right hand side. 71 | /// -> complex 72 | #let sub(V,W) = (re(V) - re(W),im(V) - im(W)) 73 | 74 | /// Calculates the argument of a complex number. 75 | /// - V (complex): The complex number. 76 | #let arg(V) = calc.atan2(..V) / 1rad 77 | 78 | /// Get the signed angle of two complex numbers from V to W. 79 | /// - V (complex): A complex number. 80 | /// - W (complex): A complex number. 81 | #let ang(V,W) = arg(div(W,V)) 82 | 83 | // exp(i*a) 84 | #let expi(a) = (calc.cos(a),calc.sin(a)) 85 | 86 | // Rotate by angle a 87 | #let rot(v,a) = mul(v,expi(a)) 88 | -------------------------------------------------------------------------------- /src/deps.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/oxifmt:0.2.1" 2 | -------------------------------------------------------------------------------- /src/draw.typ: -------------------------------------------------------------------------------- 1 | #import "draw/grouping.typ": intersections, group, scope, anchor, copy-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, hide, floating 2 | #import "draw/transformations.typ": set-transform, rotate, translate, scale, set-origin, move-to, set-viewport 3 | #import "draw/styling.typ": set-style, fill, stroke, register-mark 4 | #import "draw/shapes.typ": circle, circle-through, arc, arc-through, mark, line, grid, content, rect, bezier, bezier-through, catmull, hobby, merge-path, polygon, multi-path 5 | #import "draw/projection.typ": ortho, on-xy, on-xz, on-yz 6 | #import "draw/util.typ": assert-version, register-coordinate-resolver 7 | -------------------------------------------------------------------------------- /src/draw/styling.typ: -------------------------------------------------------------------------------- 1 | #import "/src/util.typ" 2 | 3 | /// Set current style 4 | /// 5 | /// - ..style (style): Style key-value pairs 6 | #let set-style(..style) = { 7 | assert.eq( 8 | style.pos().len(), 9 | 0, 10 | message: "set-style takes no positional arguments", 11 | ) 12 | 13 | (ctx => { 14 | ctx.style = util.merge-dictionary(ctx.style, style.named()) 15 | 16 | return (ctx: ctx) 17 | },) 18 | } 19 | 20 | /// Set current fill style 21 | /// 22 | /// Shorthand for `set-style(fill: )` 23 | /// 24 | /// - fill (paint): Fill style 25 | #let fill(fill) = set-style(fill: fill) 26 | 27 | /// Set current stroke style 28 | /// 29 | /// Shorthand for `set-style(stroke: )` 30 | /// 31 | /// - stroke (stroke): Stroke style 32 | #let stroke(stroke) = set-style(stroke: stroke) 33 | 34 | /// Register a custom mark to the canvas 35 | /// 36 | /// The mark should contain both anchors called **tip** and **base** that are used to determine the marks orientation. If unset both default to `(0, 0)`. 37 | /// An anchor named **center** is used as center of the mark, if present. Otherwise the mid between **tip** and **base** is used. 38 | /// 39 | /// ```typc example 40 | /// register-mark(":)", style => { 41 | /// circle((0,0), radius: .5, fill: yellow) 42 | /// arc((0,0), start: 180deg + 30deg, delta: 180deg - 60deg, anchor: "origin", radius: .3) 43 | /// circle((-0.15, 0.15), radius: .1, fill: white) 44 | /// circle((-0.10, 0.10), radius: .025, fill: black) 45 | /// circle(( 0.15, 0.15), radius: .1, fill: white) 46 | /// circle(( 0.20, 0.10), radius: .025, fill: black) 47 | /// 48 | /// anchor("tip", ( 0.5, 0)) 49 | /// anchor("base", (-0.5, 0)) 50 | /// }) 51 | /// 52 | /// line((0,0), (3,0), mark: (end: ":)")) 53 | /// ``` 54 | /// 55 | /// - symbol (str): Mark name 56 | /// - mnemonic (none,str): Mark short name 57 | /// - body (function): Mark drawing callback, receiving the mark style as argument and returning elements. Format `(styles) => elements`. 58 | #let register-mark(symbol, body, mnemonic: none) = { 59 | assert(type(symbol) == str) 60 | assert(type(body) == function) 61 | 62 | (ctx => { 63 | ctx.marks.marks.insert(symbol, body) 64 | if type(mnemonic) == str and mnemonic.len() > 0 { 65 | ctx.marks.mnemonics.insert(mnemonic, symbol) 66 | } 67 | return (ctx: ctx) 68 | },) 69 | } 70 | -------------------------------------------------------------------------------- /src/draw/util.typ: -------------------------------------------------------------------------------- 1 | /// Assert that the cetz version of the canvas matches the given version (range). 2 | /// 3 | /// min (version): Minimum version (current >= min) 4 | /// max (none, version): First unsupported version (current < max) 5 | /// hint (string): Name of the function/module this assert is called from 6 | #let assert-version(min, max: none, hint: "") = { 7 | if hint != "" { hint = " by " + hint } 8 | (ctx => { 9 | /* Default to 2.0.0, as this is the first version that had elements as single functions. */ 10 | let v = ctx.at("version", default: version(0,2,0)) 11 | assert(min <= v, 12 | message: "CeTZ canvas version is " + str(v) + ", but the minimum required version" + hint + " is " + str(min)) 13 | if max != none { 14 | assert(max > v, 15 | message: "CeTZ canvas version is " + str(v) + ", but the maximum supported version" + hint + " is " + str(min)) 16 | } 17 | 18 | return (ctx: ctx) 19 | },) 20 | } 21 | 22 | /// Push a custom coordinate resolve function to the list of coordinate 23 | /// resolvers. This resolver is scoped to the current context scope! 24 | /// 25 | /// A coordinate resolver must be a function of the format `(context, coordinate) => coordinate`. And must _always_ return a valid coordinate or panic, in case of an error. 26 | /// 27 | /// If multiple resolvers are registered, coordinates get passed through all 28 | /// resolvers in reverse registering order. All coordinates get paased to cetz' 29 | /// default coordinate resolvers. 30 | /// 31 | /// ```typc example 32 | /// register-coordinate-resolver((ctx, c) => { 33 | /// if type(c) == dictionary and "log" in c { 34 | /// c = c.log.map(n => calc.log(n, base: 10)) 35 | /// } 36 | /// return c 37 | /// }) 38 | /// 39 | /// circle((log: (10, 0)), radius: .25) 40 | /// circle((log: (100, 0)), radius: .25) 41 | /// circle((log: (1000, 0)), radius: .25) 42 | /// ``` 43 | /// 44 | /// - resolver (function): The resolver function, taking a context and a single coordinate and returning a single coordinate 45 | #let register-coordinate-resolver(resolver) = { 46 | assert.eq(type(resolver), function, 47 | message: "Coordinate resolver must be of type function (ctx, coordinate) => coordinate.") 48 | 49 | return (ctx => { 50 | if type(ctx.resolve-coordinate) == array { 51 | ctx.resolve-coordinate.push(resolver) 52 | } else { 53 | ctx.resolve-coordinate = (resolver,) 54 | } 55 | 56 | return (ctx: ctx) 57 | },) 58 | } 59 | -------------------------------------------------------------------------------- /src/intersection.typ: -------------------------------------------------------------------------------- 1 | #import "vector.typ" 2 | #import "util.typ" 3 | 4 | /// Checks for a line-line intersection between the given points and returns its position, otherwise {{none}}. 5 | /// 6 | /// - a (vector): Line 1 point 1 7 | /// - b (vector): Line 1 point 2 8 | /// - c (vector): Line 2 point 1 9 | /// - d (vector): Line 2 point 2 10 | /// - ray (bool): When `true`, intersections will be found for the whole line instead of inbetween the given points. 11 | /// -> vector,none 12 | #let line-line(a, b, c, d, ray: false) = { 13 | let lli8(x1, y1, x2, y2, x3, y3, x4, y4) = { 14 | let nx = (x1*y2 - y1*x2)*(x3 - x4)-(x1 - x2)*(x3*y4 - y3*x4) 15 | let ny = (x1*y2 - y1*x2)*(y3 - y4)-(y1 - y2)*(x3*y4 - y3*x4) 16 | let d = (x1 - x2)*(y3 - y4)-(y1 - y2)*(x3 - x4) 17 | if d == 0 { 18 | return none 19 | } 20 | return (nx / d, ny / d, 0) 21 | } 22 | let pt = lli8(a.at(0), a.at(1), b.at(0), b.at(1), 23 | c.at(0), c.at(1), d.at(0), d.at(1)) 24 | if pt != none { 25 | let on-line(pt, a, b) = { 26 | let (x, y, ..) = pt 27 | let epsilon = util.float-epsilon 28 | let mx = calc.min(a.at(0), b.at(0)) - epsilon 29 | let my = calc.min(a.at(1), b.at(1)) - epsilon 30 | let Mx = calc.max(a.at(0), b.at(0)) + epsilon 31 | let My = calc.max(a.at(1), b.at(1)) + epsilon 32 | return mx <= x and Mx >= x and my <= y and My >= y 33 | } 34 | if ray or (on-line(pt, a, b) and on-line(pt, c, d)) { 35 | return pt 36 | } 37 | } 38 | } 39 | 40 | /// Finds the intersections of a line and cubic bezier. 41 | /// 42 | /// - s (vector): Bezier start point 43 | /// - e (vector): Bezier end point 44 | /// - c1 (vector): Bezier control point 1 45 | /// - c2 (vector): Bezier control point 2 46 | /// - la (vector): Line start point 47 | /// - lb (vector): Line end point 48 | /// - ray (bool): When `true`, intersections will be found for the whole line instead of inbetween the given points. 49 | /// -> array 50 | #let line-cubic(la, lb, s, e, c1, c2) = { 51 | import "/src/bezier.typ": line-cubic-intersections as line-cubic 52 | return line-cubic(la, lb, s, e, c1, c2) 53 | } 54 | 55 | /// Finds the intersections of a line and path in 2D. The path should be given as a {{drawable}} of type `path`. 56 | /// 57 | /// - la (vector): Line start 58 | /// - lb (vector): Line end 59 | /// - path (drawable): The path. 60 | /// -> array 61 | #let line-path(la, lb, path) = { 62 | let pts = () 63 | 64 | for ((start, closed, segments)) in path.at("segments", default: ()) { 65 | let origin = start 66 | for ((kind, ..args)) in segments { 67 | if kind == "l" { 68 | let pt = line-line(la, lb, origin, args.last()) 69 | if pt != none { 70 | pts.push(pt) 71 | } 72 | } else if kind == "c" { 73 | let (c1, c2, e) = args 74 | pts += line-cubic(la, lb, origin, e, c1, c2) 75 | } 76 | 77 | origin = args.last() 78 | } 79 | 80 | if closed { 81 | let pt = line-line(la, lb, origin, start) 82 | if pt != none { 83 | pts.push(pt) 84 | } 85 | } 86 | } 87 | 88 | return pts 89 | } 90 | 91 | /// Finds the intersections between two path {{drawable}}s in 2D. 92 | /// 93 | /// - a (path): Path a 94 | /// - b (path): Path b 95 | /// - samples (int): Number of samples to use for bezier curves 96 | /// -> array 97 | #let path-path(a, b, samples: 8) = { 98 | import "bezier.typ": cubic-point 99 | 100 | let pts = () 101 | 102 | for ((start, closed, segments)) in a.at("segments", default: ()) { 103 | let origin = start 104 | for ((kind, ..args)) in segments { 105 | if kind == "l" { 106 | pts += line-path(origin, args.last(), b) 107 | } else if kind == "c" { 108 | let (c1, c2, e) = args 109 | let line-strip = range(samples + 1).map(t => { 110 | cubic-point(origin, e, c1, c2, t / samples) 111 | }) 112 | 113 | for i in range(1, line-strip.len()) { 114 | pts += line-path(line-strip.at(i - 1), line-strip.at(i), b) 115 | } 116 | } 117 | 118 | origin = args.last() 119 | } 120 | 121 | if closed { 122 | pts += line-path(origin, start, b) 123 | } 124 | } 125 | return pts 126 | } 127 | -------------------------------------------------------------------------------- /src/lib.typ: -------------------------------------------------------------------------------- 1 | #import "version.typ": version 2 | 3 | #import "canvas.typ": canvas 4 | #import "draw.typ" 5 | 6 | // Expose utilities 7 | #import "vector.typ" 8 | #import "matrix.typ" 9 | #import "styles.typ" 10 | #import "coordinate.typ" 11 | #import "intersection.typ" 12 | #import "drawable.typ" 13 | #import "process.typ" 14 | #import "util.typ" 15 | #import "path-util.typ" 16 | #import "mark.typ" 17 | #import "mark-shapes.typ" 18 | #import "sorting.typ" 19 | 20 | // Libraries 21 | #import "lib/palette.typ" 22 | #import "lib/angle.typ" 23 | #import "lib/tree.typ" 24 | #import "lib/decorations.typ" 25 | -------------------------------------------------------------------------------- /src/lib/decorations.typ: -------------------------------------------------------------------------------- 1 | #import "decorations/brace.typ": brace, brace-default-style, flat-brace, flat-brace-default-style 2 | #import "decorations/path.typ": zigzag, wave, coil, square 3 | -------------------------------------------------------------------------------- /src/lib/palette.typ: -------------------------------------------------------------------------------- 1 | #let base-style = (stroke: (paint: black), fill: none) 2 | 3 | /// Create a new palette based on a base style 4 | /// 5 | /// ```typc example 6 | /// let p = cetz.palette.new(colors: (red, blue, green)) 7 | /// for i in range(0, p("len")) { 8 | /// set-style(..p(i)) 9 | /// circle((0,0), radius: .5) 10 | /// set-origin((1.1, 0)) 11 | /// } 12 | /// ``` 13 | /// 14 | /// The functions returned by this function have the following named arguments: 15 | /// - fill (bool) = true: If true, the returned fill color is one of the colors from the `colors` list, otherwise the base styles fill is used. 16 | /// - stroke (bool) = false: If true, the returned stroke color is one of the colors from the `colors` list, otherwise the base styles stroke color is used. 17 | /// 18 | /// You can use a palette for stroking via: `red.with(stroke: true)`. 19 | /// 20 | /// - base (style): Style dictionary to use as base style for the styles generated per color 21 | /// - colors (none, array): List of colors the returned palette should return styles with. 22 | /// - dash (none, array): List of stroke dash patterns the returned palette should return styles with. 23 | /// -> function 24 | #let new(base: base-style, colors: (), dash: ()) = { 25 | if not "stroke" in base { base.stroke = (paint: black, thickness: 1pt, dash: "solid") } 26 | if not "fill" in base { base.fill = none } 27 | 28 | let color-n = colors.len() 29 | let pattern-n = dash.len() 30 | return (index, fill: true, stroke: false) => { 31 | if index == "len" { return calc.max(color-n, pattern-n, 1) } 32 | 33 | let style = base 34 | if pattern-n > 0 { 35 | style.stroke.dash = dash.at(calc.rem(index, pattern-n)) 36 | } 37 | if color-n > 0 { 38 | if stroke { 39 | style.stroke.paint = colors.at(calc.rem(index, color-n)) 40 | } 41 | if fill { 42 | style.fill = colors.at(calc.rem(index, color-n)) 43 | } 44 | } 45 | return style 46 | } 47 | } 48 | 49 | // Predefined color themes 50 | #let tango-colors = ( 51 | "edd400", "f57900", "c17d11", 52 | "73d216", "3465a4", "75507b", 53 | "cc0000", "d3d7cf", "555753").map(rgb) 54 | #let tango-light-colors = ( 55 | "fce94f", "fcaf3e", "e9b96e", 56 | "8ae234", "729fcf", "ad7fa8", 57 | "ef2929", "eeeeec", "888a85").map(rgb) 58 | #let tango-dark-colors = ( 59 | "c4a000", "ce5c00", "8f5902", 60 | "4e9a06", "204a87", "5c3566", 61 | "a40000", "babdb6", "2e3436").map(rgb) 62 | #let rainbow-colors = ( 63 | "#9400D4", "#4B0082", "#0000FF", 64 | "#00FF00", "#FFFF00", "#FF7F00", 65 | "#FF0000").map(rgb) 66 | 67 | #let red-colors = ( 68 | "#FFCCCC", "#FF9999", "#FF6666", 69 | "#FF3333", "#CC0000").map(rgb) 70 | #let orange-colors = ( 71 | "#FFE5CC", "#FFCC99", "#FFB266", 72 | "#FF9933", "#FF8000").map(rgb) 73 | #let light-green-colors = ( 74 | "#E5FFCC", "#CCFF99", "#B2FF66", 75 | "#99FF33", "#72E300", "#66CC00", 76 | "#55A800", "#478F00", "#3A7300", 77 | "#326300").map(rgb) 78 | #let dark-green-colors = ( 79 | "#80E874", "#5DD45D", "#3CC23C", 80 | "#009900", "#006E00").map(rgb) 81 | #let turquoise-colors = ( 82 | "#C0FFD3", "#99FFCC", "#66FFB2", 83 | "#33FF99", "#4BD691").map(rgb) 84 | #let cyan-colors = ( 85 | "#CCFFFF", "#99FFFF", "#66FFFF", 86 | "#00F3F3", "#00DADA").map(rgb) 87 | #let blue-colors = ( 88 | "#BABAFF", "#9999FF", "#6666FF", 89 | "#3333FF", "#0000CC").map(rgb) 90 | #let indigo-colors = ( 91 | "#BABAFF", "#9999FF", "#6666FF", 92 | "#3333FF", "#0000CC").map(rgb) 93 | #let purple-colors = ( 94 | "#E0C2FF", "#CC99FF", "#B266FF", 95 | "#9933FF", "#7F00FF").map(rgb) 96 | #let magenta-colors = ( 97 | "#FFD4FF", "#FF99FF", "#FF66FF", 98 | "#F331F3", "#DA00DA").map(rgb) 99 | #let pink-colors = ( 100 | "#FFCCE5", "#FF99CC", "#FF66B2", 101 | "#FF3399", "#F20C7F", "#DB006B", 102 | "#C30061", "#99004C", "#800040", 103 | "#660033").map(rgb) 104 | 105 | 106 | // Predefined palettes 107 | #let gray = new(colors: range(90, 40, step: -12).map(v => luma(v * 1%))) 108 | 109 | #let red = new(colors: red-colors) 110 | #let orange = new(colors: orange-colors) 111 | #let light-green = new(colors: light-green-colors) 112 | #let dark-green = new(colors: dark-green-colors) 113 | #let turquoise = new(colors: turquoise-colors) 114 | #let cyan = new(colors: cyan-colors) 115 | #let blue = new(colors: blue-colors) 116 | #let indigo = new(colors: indigo-colors) 117 | #let purple = new(colors: purple-colors) 118 | #let magenta = new(colors: magenta-colors) 119 | #let pink = new(colors: pink-colors) 120 | 121 | #let rainbow = new(colors: rainbow-colors) 122 | 123 | #let tango = new(colors: tango-colors) 124 | #let tango-light = new(colors: tango-light-colors) 125 | #let tango-dark = new(colors: tango-dark-colors) 126 | -------------------------------------------------------------------------------- /src/polygon.typ: -------------------------------------------------------------------------------- 1 | #import "/src/vector.typ" 2 | 3 | /// Returns a list of polygon points from 4 | /// a list of segments. 5 | /// 6 | /// Cubic segments get linearized by sampling. 7 | /// 8 | /// - segment (array): List of segments 9 | /// - samples (int): Number of samples 10 | /// -> array 11 | #let from-subpath(subpath, samples: 10) = { 12 | import "/src/bezier.typ": cubic-point 13 | let (origin, _, segments) = subpath 14 | 15 | let poly = (origin,) 16 | for ((kind, ..args)) in segments { 17 | if kind == "c" { 18 | let (c1, c2, e) = args 19 | poly += range(0, samples).map(t => { 20 | cubic-point(poly.last(), e, c1, c2, t / (samples - 1)) 21 | }) 22 | } else { 23 | poly += args 24 | } 25 | } 26 | return poly 27 | } 28 | 29 | /// Computes the signed area of a 2D polygon. 30 | /// 31 | /// The formula used is the following: 32 | /// $ 1/2 \sum_{i}=0^{n-1} x_i*y_i+1 - x_i+1*y_i $ 33 | /// 34 | /// - points (array): List of Vectors of dimension >= 2 35 | /// -> float 36 | #let signed-area(points) = { 37 | let a = 0 38 | let n = points.len() 39 | let (cx, cy) = (0, 0) 40 | for i in range(0, n) { 41 | let (x0, y0, ..) = points.at(i) 42 | let (x1, y1, ..) = points.at(calc.rem(i + 1, n)) 43 | cx += (x0 + x1) * (x0 * y1 - x1 * y0) 44 | cy += (y0 + y1) * (x0 * y1 - x1 * y0) 45 | a += x0 * y1 - x1 * y0 46 | } 47 | return .5 * a 48 | } 49 | 50 | /// Returns the winding order of a 2D polygon 51 | /// by using it's signed area. 52 | /// 53 | /// Returns either "ccw" (counter clock-wise) or "cw" (clock-wise) or none. 54 | /// 55 | /// - point (array): List of polygon points 56 | /// -> str,none 57 | #let winding-order(points) = { 58 | let area = signed-area(points) 59 | if area > 0 { 60 | "cw" 61 | } else if area < 0 { 62 | "ccw" 63 | } else { 64 | none 65 | } 66 | } 67 | 68 | // Calculate triangle centroid 69 | #let triangle-centroid(points) = { 70 | assert.eq(points.len(), 3) 71 | 72 | let (mx, my, mz) = (0, 0, 0) 73 | for p in points { 74 | let (x, y, z) = p 75 | mx += x 76 | my += y 77 | mz += z 78 | } 79 | return (mx / 3, my / 3, mz / 3) 80 | } 81 | 82 | // Calculate the centroid of a line, triangle or simple polygon 83 | // Formulas: 84 | // https://en.wikipedia.org/wiki/Centroid 85 | #let simple-centroid(points) = { 86 | return if points.len() <= 1 { 87 | none 88 | } else if points.len() == 2 { 89 | vector.lerp(..points, .5) 90 | } else if points.len() == 3 { 91 | triangle-centroid(points) 92 | } else if points.len() >= 3 { 93 | // Skip polygons with multiple z values 94 | let z = points.first().at(2, default: 0) 95 | if points.any(p => p.at(2) != z) { 96 | return none 97 | } 98 | 99 | let a = 0 100 | let n = points.len() 101 | let (cx, cy) = (0, 0) 102 | for i in range(0, n) { 103 | let (x0, y0, ..) = points.at(i) 104 | let (x1, y1, ..) = points.at(calc.rem(i + 1, n)) 105 | cx += (x0 + x1) * (x0 * y1 - x1 * y0) 106 | cy += (y0 + y1) * (x0 * y1 - x1 * y0) 107 | a += x0 * y1 - x1 * y0 108 | } 109 | return (cx/(3*a), cy/(3*a), z) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/process.typ: -------------------------------------------------------------------------------- 1 | #import "util.typ" 2 | #import "path-util.typ" 3 | #import "aabb.typ" 4 | #import "drawable.typ" 5 | #import "vector.typ" 6 | 7 | 8 | /// Processes an element's function to get its drawables and bounds. Returns a {{dictionary}} with the key-values: `ctx` The modified context object, `bounds` The {{aabb}} of the element's drawables, `drawables` An {{array}} of the element's {{drawable}}s. 9 | /// 10 | /// - ctx (ctx): The current context object. 11 | /// - element-func (function): A function that when passed {{ctx}}, it should return an element dictionary. 12 | #let element(ctx, element-func) = { 13 | let bounds = none 14 | let element 15 | let anchors = (:) 16 | 17 | (ctx, ..element,) = element-func(ctx) 18 | if "drawables" in element { 19 | if type(element.drawables) == dictionary { 20 | element.drawables = (element.drawables,) 21 | } 22 | for drawable in element.drawables { 23 | if drawable.bounds { 24 | bounds = aabb.aabb( 25 | if drawable.type == "path" { 26 | path-util.bounds(drawable.segments) 27 | } else if drawable.type == "content" { 28 | let (x, y, _, w, h,) = drawable.pos + (drawable.width, drawable.height) 29 | ((x + w / 2, y - h / 2, 0.0), (x - w / 2, y + h / 2, 0.0)) 30 | }, 31 | init: bounds 32 | ) 33 | } 34 | } 35 | } 36 | 37 | let name = element.at("name", default: none) 38 | if name != none { 39 | assert.eq(type(name), str, 40 | message: "Element name must be a string") 41 | assert(not name.contains("."), 42 | message: "Invalid name for element '" + element.name + "'; name must not contain '.'") 43 | 44 | if "anchors" in element { 45 | ctx.nodes.insert(name, element) 46 | if ctx.groups.len() > 0 { 47 | ctx.groups.last().push(name) 48 | } 49 | } 50 | } 51 | 52 | if ctx.debug and bounds != none { 53 | element.drawables.push(drawable.path( 54 | ((bounds.low, true, ( 55 | ("l", (bounds.high.at(0), bounds.low.at(1), 0)), 56 | ("l", bounds.high), 57 | ("l", (bounds.low.at(0), bounds.high.at(1), 0)))),), 58 | stroke: red, 59 | close: true 60 | )) 61 | } 62 | 63 | return ( 64 | ctx: ctx, 65 | bounds: bounds, 66 | drawables: element.at("drawables", default: ()), 67 | ) 68 | } 69 | 70 | /// Runs the `element` function for a list of element functions and aggregates the results. 71 | /// - ctx (ctx): The current context object. 72 | /// - body (array): The array of element functions to process. 73 | /// -> dictionary 74 | #let many(ctx, body) = { 75 | let drawables = () 76 | let bounds = none 77 | 78 | for el in body { 79 | let r = element(ctx, el) 80 | if r != none { 81 | if r.bounds != none { 82 | let pts = (r.bounds.low, r.bounds.high,) 83 | bounds = aabb.aabb(pts, init: bounds) 84 | } 85 | ctx = r.ctx 86 | drawables += r.drawables 87 | } 88 | } 89 | return (ctx: ctx, bounds: bounds, drawables: drawables) 90 | } 91 | -------------------------------------------------------------------------------- /src/sorting.typ: -------------------------------------------------------------------------------- 1 | #import "/src/vector.typ" 2 | #import "/src/util.typ" 3 | 4 | /// Sort list of points by distance to a 5 | /// reference point. 6 | /// 7 | /// - points (array): List of points to sort 8 | /// - reference (vec): Reference point 9 | /// -> List of points 10 | #let points-by-distance(ctx, points, reference: (0, 0, 0)) = { 11 | let reference = util.apply-transform(ctx.transform, reference) 12 | return points.sorted(key: pt => { 13 | vector.dist(pt, reference) 14 | }) 15 | } 16 | 17 | /// Sort list of 2D points by angle to a 18 | /// reference 2D point in CCW order. 19 | /// Z component is ignored. 20 | /// 21 | /// - points (array): List of points to sort 22 | /// - reference (vec): Reference point 23 | /// -> List of points 24 | #let points-by-angle(ctx, points, reference: (0, 0, 0)) = { 25 | let (rx, ry, ..) = util.apply-transform(ctx.transform, reference) 26 | return points.sorted(key: ((px, py, ..)) => { 27 | 360deg - calc.atan2(rx - px, ry - py) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /src/version.typ: -------------------------------------------------------------------------------- 1 | #let version = version(0,3,4) 2 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | **/out/* 2 | **/diff/* 3 | *.pdf 4 | -------------------------------------------------------------------------------- /tests/anchor-centroid/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/anchor-centroid/ref/1.png -------------------------------------------------------------------------------- /tests/anchor-centroid/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let x(pos) = { 6 | import draw: * 7 | group({ 8 | set-origin(pos) 9 | scale(.1) 10 | stroke(red) 11 | line((-1, -1), (1, 1)) 12 | line((-1, 1), (1, -1)) 13 | }) 14 | } 15 | 16 | #test-case({ 17 | import draw: * 18 | 19 | line((-1, -1), (1, 1), name: "elem", close: true) 20 | x("elem.centroid") 21 | }) 22 | 23 | #test-case({ 24 | import draw: * 25 | 26 | line((-1, -1), (+1, -1), (0, 1), name: "elem", close: true) 27 | x("elem.centroid") 28 | }) 29 | 30 | #test-case({ 31 | import draw: * 32 | 33 | line((-1, -1), (+1, -1), (+1, +1), (-1, +1), name: "elem", close: true) 34 | x("elem.centroid") 35 | }) 36 | 37 | #test-case({ 38 | import draw: * 39 | 40 | line((-1, -1), (+1, -1), (+1, +0), (+0, +0), 41 | (+0, +1), (-1, +1), name: "elem", close: true) 42 | x("elem.centroid") 43 | }) 44 | 45 | #test-case({ 46 | import draw: * 47 | 48 | merge-path(name: "elem", close: true, { 49 | arc((0,0), start: 0deg, stop: 90deg) 50 | line((), (rel: (-1, 0)), (rel: (0, -1))) 51 | }) 52 | x("elem.centroid") 53 | }) 54 | 55 | #test-case({ 56 | import draw: * 57 | 58 | merge-path(name: "elem", close: true, { 59 | // Circle is in merge-path! 60 | circle(()) 61 | }) 62 | x("elem.centroid") 63 | }) 64 | -------------------------------------------------------------------------------- /tests/angle/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/angle/ref/1.png -------------------------------------------------------------------------------- /tests/angle/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let angles = range(0, 360, step: 36).map(v => v * 1deg) 6 | 7 | #test-case(a => { 8 | import draw: * 9 | import angle: angle 10 | 11 | let (o, a, b) = ((0,0), (1,0), (calc.cos(a), calc.sin(a))) 12 | line(a, o, b) 13 | angle(o, a, b, label: $alpha$) 14 | }, args: angles) 15 | 16 | #test-case(a => { 17 | import draw: * 18 | import angle: angle 19 | 20 | let (o, a, b) = ((0,0), (1,0), (calc.cos(a), calc.sin(a))) 21 | line(a, o, b) 22 | angle(o, a, b, label: $alpha$, direction: "cw") 23 | }, args: angles) 24 | 25 | #test-case(a => { 26 | import draw: * 27 | import angle: angle 28 | 29 | let (o, a, b) = ( 30 | (0, 0), 31 | (calc.cos(a+90deg), calc.sin(a+90deg)), (calc.cos(a), calc.sin(a)) 32 | ) 33 | 34 | line(a, o, b) 35 | angle(o, a, b, label: $alpha$, direction: "cw") 36 | }, args: angles) 37 | 38 | #test-case(a => { 39 | import draw: * 40 | import angle: angle 41 | 42 | let (o, a, b) = ( 43 | (0, 0), 44 | (calc.cos(a+90deg), calc.sin(a+90deg)), (calc.cos(a), calc.sin(a)) 45 | ) 46 | 47 | line(a, o, b) 48 | angle(o, a, b, label: $alpha$, direction: "ccw") 49 | }, args: angles) 50 | 51 | 52 | #test-case({ 53 | import draw: * 54 | import angle: angle 55 | 56 | let (a, b, c) = ((-1, 1), (0, 0), (1, 2)) 57 | 58 | line(a, b, c) 59 | set-style(angle: (stroke: red, label-radius: 1)) 60 | angle(b, c, a, mark: (start: ">", end: ">"), label: $omega$) 61 | 62 | translate((2,0,0)) 63 | 64 | line(a, b, c) 65 | set-style(stroke: blue) 66 | set-style(angle: (stroke: auto, radius: 1, label-radius: .5)) 67 | angle(b, c, a, mark: (start: "|", end: ">"), 68 | direction: "cw", label: $alpha$, name: "alpha") 69 | 70 | set-style(stroke: black) 71 | circle("alpha.origin", radius: .15) 72 | circle("alpha.label", radius: .25) 73 | circle("alpha.start", radius: .25) 74 | circle("alpha.end", radius: .25) 75 | }) 76 | 77 | #test-case({ 78 | import draw: * 79 | import angle: * 80 | 81 | angle((0,0), (1,0), (0,1), mark: (end: ">")) 82 | }) 83 | 84 | #test-case({ 85 | import draw: * 86 | import angle: * 87 | 88 | angle((0,0), (1,0), (0,1), mark: (end: ">"), direction: "cw") 89 | }) 90 | 91 | #test-case({ 92 | import draw: * 93 | import angle: * 94 | 95 | angle((0,0), (1,0), (0,1), radius: .5cm) 96 | angle((0,0), (1,0), (0,1), radius: 75%, stroke: blue) 97 | angle((0,0), (1,0), (0,1), radius: 1, stroke: green) 98 | }) 99 | -------------------------------------------------------------------------------- /tests/arc-previous-position/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/arc-previous-position/ref/1.png -------------------------------------------------------------------------------- /tests/arc-previous-position/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | arc((0,0), start: 0deg, stop: 180deg) 8 | circle((), radius: .1, fill: blue) 9 | })) 10 | 11 | #box(stroke: 2pt + red, canvas({ 12 | import draw: * 13 | 14 | arc((0,0), start: 180deg, stop: 0deg) 15 | circle((), radius: .1, fill: blue) 16 | })) 17 | 18 | #box(stroke: 2pt + red, canvas({ 19 | import draw: * 20 | 21 | arc((0,0), start: 180deg, stop: 0deg, update-position: false) 22 | circle((), radius: .1, fill: blue) 23 | })) 24 | -------------------------------------------------------------------------------- /tests/arc-through/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/arc-through/ref/1.png -------------------------------------------------------------------------------- /tests/arc-through/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let show-points(..pts) = { 5 | import draw: * 6 | for pt in pts.pos() { 7 | circle(pt, radius: .1) 8 | } 9 | } 10 | 11 | #let test(a, b, c) = { 12 | import draw: * 13 | group({ 14 | anchor("default", (0,0,0)) 15 | show-points(a, b, c) 16 | arc-through(a, b, c) 17 | }, name: "g", anchor: "west", padding: .1) 18 | set-origin("g.east") 19 | } 20 | 21 | #box(stroke: 2pt + red, canvas(length: 1cm, { 22 | import draw: * 23 | 24 | test((0,0), (1, 1), (2, 0)) 25 | test((0,0), (1,-1), (2, 0)) 26 | test((0,1), (1, 0), (0,-1)) 27 | test((0,1), (-1,0), (0,-1)) 28 | })) 29 | 30 | #box(stroke: 2pt + red, canvas(length: 1cm, { 31 | import draw: * 32 | 33 | for a in range(36, 360 + 36, step: 36) { 34 | let a = a * 1deg 35 | test((1,0), 36 | (calc.cos(a / 2), calc.sin(a / 2)), 37 | (calc.cos(a), calc.sin(a))) 38 | } 39 | })) 40 | 41 | #box(stroke: 2pt + red, canvas(length: 1cm, { 42 | import draw: * 43 | 44 | for d in range(0, 8 + 1) { 45 | let d = (d - 2) / 5 46 | test((0,0), (1,d), (2,.5)) 47 | } 48 | })) 49 | 50 | #box(stroke: 2pt + red, canvas(length: 1cm, { 51 | import draw: * 52 | 53 | // The style radius must not influence the 54 | // arc radius! 55 | set-style(radius: 5) 56 | set-style(arc: (radius: 5)) 57 | test((0,0), (1, 1), (2, 0)) 58 | })) 59 | 60 | #box(stroke: 2pt + red, canvas(length: 1cm, { 61 | import draw: * 62 | 63 | arc-through((0,0), (1,1), (2,0)) 64 | arc-through((0,0,1), (1,1,1), (2,0,1), stroke: blue) 65 | })) 66 | -------------------------------------------------------------------------------- /tests/arc/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/arc/ref.png -------------------------------------------------------------------------------- /tests/arc/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/arc/ref/1.png -------------------------------------------------------------------------------- /tests/arc/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas(length: 1cm, { 5 | import draw: * 6 | 7 | for r in ((1,1), (0.5,1), (1,0.5)) { 8 | translate((2.5, 0, 0)) 9 | group({ 10 | for a in range(0, 360 + 45, step: 45) { 11 | if a == 0 { 12 | a = 1 13 | } 14 | a *= 1deg 15 | circle((0,0), radius: r, stroke: red) 16 | arc((0,0), start: 45deg, delta: a, radius: r, name: "a", 17 | anchor: "origin", mode: "PIE", fill: blue) 18 | 19 | circle("a.arc-start", fill: green, radius: .1) 20 | circle("a.arc-end", fill: red, radius: .1) 21 | translate((0, 2.5, 0)) 22 | } 23 | }) 24 | } 25 | })) 26 | 27 | #box(stroke: 2pt + red, canvas(length: 1cm, { 28 | import draw: * 29 | 30 | arc((0,0), start: 45deg, delta: 90deg) 31 | arc((0,0,1), start: 45deg, delta: 90deg, stroke: blue) 32 | })) 33 | -------------------------------------------------------------------------------- /tests/arrows/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/arrows/ref/1.png -------------------------------------------------------------------------------- /tests/arrows/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | 8 | let next(mark) = { 9 | line((), (rel: (1, 0)), mark: mark) 10 | move-to((rel: (-1, .25))) 11 | } 12 | 13 | set-style(fill: blue, mark: (fill: auto)) 14 | rotate(90deg) 15 | 16 | let marks = (">", "<", "|", "<>", "o") 17 | for m in marks { 18 | next((end: m)) 19 | } 20 | 21 | for m in marks { 22 | next((start: m)) 23 | } 24 | 25 | fill(none) 26 | 27 | let marks = (">", "<") 28 | for m in marks { 29 | next((end: m)) 30 | } 31 | 32 | for m in marks { 33 | next((start: m)) 34 | } 35 | } ) 36 | 37 | #test-case({ 38 | import draw: * 39 | 40 | line((0,0), (9,0), stroke: blue + 1pt) 41 | line((0,0), (9,0), stroke: green + .1pt) 42 | line((0,-1), (9,-1), stroke: blue + 1pt) 43 | line((0,-1), (9,-1), stroke: green + .1pt) 44 | 45 | set-style(mark: (stroke: (paint: green, miter-limit: 50), 46 | fill: red)) 47 | 48 | for x in range(0, 18) { 49 | line((x * .5, -1), (x * .5, 0), mark: (start: ">", end: ">", 50 | width: (x / 50 + .05))) 51 | } 52 | }) 53 | 54 | #test-case({ 55 | import draw: * 56 | 57 | line((0,0), (9,0), stroke: blue + 1pt) 58 | line((0,0), (9,0), stroke: green + .1pt) 59 | line((0,-1), (9,-1), stroke: blue + 1pt) 60 | line((0,-1), (9,-1), stroke: green + .1pt) 61 | 62 | set-style(mark: (stroke: (paint: green, miter-limit: 50), 63 | fill: red)) 64 | 65 | for x in range(0, 18) { 66 | line((x * .5, -1), (x * .5, 0), mark: (start: "<", end: "<", 67 | width: (x / 50 + .05))) 68 | } 69 | }) 70 | 71 | #test-case({ 72 | import draw: * 73 | 74 | line((0,0), (9,0), stroke: blue + 1pt) 75 | line((0,0), (9,0), stroke: green + .1pt) 76 | line((0,-1), (9,-1), stroke: blue + 1pt) 77 | line((0,-1), (9,-1), stroke: green + .1pt) 78 | 79 | set-style(mark: (stroke: (paint: green, miter-limit: 50, join: "round"), 80 | fill: red)) 81 | 82 | for x in range(0, 18) { 83 | line((x * .5, -1), (x * .5, 0), mark: (start: "<", end: ">", 84 | width: (x / 50 + .05))) 85 | } 86 | }) 87 | 88 | #test-case({ 89 | import draw: * 90 | 91 | line((0,0), (9,0), stroke: blue + 1pt) 92 | line((0,0), (9,0), stroke: green + .1pt) 93 | line((0,-1), (9,-1), stroke: blue + 1pt) 94 | line((0,-1), (9,-1), stroke: green + .1pt) 95 | 96 | set-style(mark: (stroke: (paint: green, miter-limit: 50, join: "bevel"), 97 | fill: red)) 98 | 99 | for x in range(0, 18) { 100 | line((x * .5, -1), (x * .5, 0), mark: (start: "<", end: ">", 101 | width: (x / 50 + .05))) 102 | } 103 | }) 104 | 105 | // Issue #830 106 | #test-case({ 107 | import draw: * 108 | line((0, 0), (1, 0), mark: (start: "stealth")) 109 | line((0, -1), (1, -1), mark: (end: "stealth")) 110 | line((0, -2), (1, -2), mark: (start: "stealth", end: "stealth")) 111 | line((0, -3), (1, -3), mark: (symbol: "stealth",)) 112 | }) 113 | -------------------------------------------------------------------------------- /tests/bezier-shortening/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/bezier-shortening/ref/1.png -------------------------------------------------------------------------------- /tests/bezier-shortening/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/src/bezier.typ": cubic-shorten, cubic-point, cubic-arclen 4 | 5 | #let test(d, curve: ((0,0), (3,0), (1,2), (2,-8))) = { 6 | import draw: * 7 | 8 | bezier(..curve, name: "o", stroke: 3pt + blue) 9 | 10 | let short = cubic-shorten(..curve, d, samples: 505) 11 | bezier(..short, stroke: 3pt + black, name: "s") 12 | 13 | let o-len = cubic-arclen(..curve) 14 | let s-len = cubic-arclen(..short) 15 | content((4,0), [#calc.round(o-len - s-len, digits: 2)]) 16 | } 17 | 18 | #for d in (0, .1, .25, .5, 1, 2, 3) { 19 | block(stroke: 2pt + red, canvas(length: .5cm, { 20 | test(-d) 21 | })) 22 | block(stroke: 2pt + red, canvas(length: .5cm, { 23 | test(+d) 24 | })) 25 | } 26 | -------------------------------------------------------------------------------- /tests/bezier-through/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/bezier-through/ref/1.png -------------------------------------------------------------------------------- /tests/bezier-through/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let bezier-through(a, b, c, ..args) = { 6 | import cetz.draw: * 7 | line(a, b, c, stroke: gray) 8 | for pt in (a, b, c) { 9 | circle(pt, radius: .1cm, fill: gray, stroke: none) 10 | } 11 | 12 | cetz.draw.bezier-through(a, b, c, ..args) 13 | } 14 | 15 | #test-case(points => { 16 | bezier-through(..points) 17 | }, args: ( 18 | ((0,0), (0, 0), (2,0)), 19 | ((0,0), (2, 0), (2,0)), 20 | ((0,0), (1, 0), (2,0)), 21 | ((0,0), (1, 1), (2,0)), 22 | ((0,0), (1,-1), (2,0)), 23 | ((0,0), (1, 1), (2,2)), 24 | ((0,0), (1, 2), (2,2)), 25 | ((0,0), (1, 0), (2,2)), 26 | ((0,0), (3, 0), (1,0)), 27 | ((0,0), (-3,0), (1,0)), 28 | )) 29 | 30 | #test-case({ 31 | import draw: * 32 | 33 | merge-path(close: true, { 34 | bezier-through((-1, 0), (-calc.cos(45deg), calc.sin(45deg)), (0, 1)) 35 | bezier-through((0, 1), (calc.cos(45deg), calc.sin(45deg)), (1, 0)) 36 | bezier-through((1, 0), (calc.cos(45deg), -calc.sin(45deg)), (0, -1)) 37 | bezier-through((0, -1), (-calc.cos(45deg), -calc.sin(45deg)), (-1, 0)) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/bezier/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/bezier/ref/1.png -------------------------------------------------------------------------------- /tests/bezier/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | /* Make sure the current position is set to the curves end point [236] */ 5 | #block(stroke: 2pt + red, canvas(length: .5cm, { 6 | import draw: * 7 | 8 | set-style(radius: .1) 9 | circle((), fill: green) 10 | bezier((0,0), (3, 0), (1,1), (2,-1)) 11 | circle((), fill: red) 12 | })) 13 | -------------------------------------------------------------------------------- /tests/bounds/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/bounds/ref/1.png -------------------------------------------------------------------------------- /tests/bounds/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | group(name: "g", { 8 | scale(.25) 9 | rotate(-37deg) 10 | bezier((0,0), (0, 10), (1,-10), (-5,20)) 11 | }) 12 | on-layer(-1, { 13 | rect("g.south-west", "g.north-east", stroke: .5pt + green) 14 | }) 15 | })) 16 | 17 | #box(stroke: 2pt + red, canvas({ 18 | import draw: * 19 | 20 | group(name: "g", { 21 | arc((0,0), start: 45deg, stop: 360deg - 45deg) 22 | }) 23 | on-layer(-1, { 24 | rect("g.south-west", "g.north-east", stroke: .5pt + green) 25 | }) 26 | })) 27 | 28 | #box(stroke: 2pt + red, canvas({ 29 | import draw: * 30 | 31 | let pts = ((-1.414213562373095, 0), 32 | (-1.4142135623730954, -1.414213562373095), 33 | (-1.8047378541243648, -0.3905242917512698), 34 | (-1.8047378541243653, -1.023689270621825)) 35 | group(name: "g", { 36 | bezier(..pts) 37 | }) 38 | 39 | on-layer(-1, { 40 | rect("g.south-west", "g.north-east", stroke: .5pt + green) 41 | }) 42 | })) 43 | -------------------------------------------------------------------------------- /tests/canvas-baseline/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | This is an example #canvas(baseline: "text.base", stroke: red, { 5 | import draw: * 6 | 7 | content((0,0), [A letter: g], name: "text") 8 | hobby("text.north-west", (rel: (0, 1), to: ("text.north-east", 50%, "text.north-west")), "text.north-east", mark: (start: "|", end: ">")) 9 | }) with an inline canvas! 10 | 11 | 12 | This is an example #canvas(baseline: (0, 0), stroke: red, { 13 | import draw: * 14 | 15 | content((0,0), [Picture \ Picture \ Picture]) 16 | }) with an inline canvas! 17 | 18 | By default #canvas(stroke: red, { 19 | import draw: * 20 | 21 | content((0,0), [Picture]) 22 | }) is a "block level" element. 23 | -------------------------------------------------------------------------------- /tests/catmul/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/catmul/ref/1.png -------------------------------------------------------------------------------- /tests/catmul/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | catmull((0,0), (1,0)) 7 | })) 8 | 9 | #box(stroke: 2pt + red, canvas({ 10 | import draw: * 11 | catmull((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2), k: .3) 12 | })) 13 | 14 | #box(stroke: 2pt + red, canvas({ 15 | import draw: * 16 | catmull((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2)) 17 | })) 18 | 19 | #box(stroke: 2pt + red, canvas({ 20 | import draw: * 21 | catmull((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2), tension: .7) 22 | })) 23 | 24 | #box(stroke: 2pt + red, canvas({ 25 | import draw: * 26 | catmull((0,-1), (1,1), (2,0), tension: .45, close: true, fill: blue) 27 | })) 28 | -------------------------------------------------------------------------------- /tests/circle-through/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/circle-through/ref/1.png -------------------------------------------------------------------------------- /tests/circle-through/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas(length: .5cm, { 5 | import draw: * 6 | 7 | let (a, b, c) = ((0,0), (2,-.5), (1,1)) 8 | line(a, b, c, close: true, stroke: gray) 9 | circle-through(a, b, c, name: "c") 10 | circle("c.center", radius: .05, fill: red) 11 | })) 12 | -------------------------------------------------------------------------------- /tests/circle/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/circle/ref/1.png -------------------------------------------------------------------------------- /tests/circle/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case(radius => { 6 | import draw: * 7 | 8 | circle((0, 0), radius: radius, name: "c") 9 | point("c.center", "M") 10 | }, args: (1, 1cm, (1, .5), (1cm, .5), (.5, 1), (.5, 1cm))) 11 | 12 | #test-case(outer => { 13 | import draw: * 14 | 15 | let center = (1, 1) 16 | circle(center, outer) 17 | move-to(center) 18 | point(outer, "O") 19 | point(center, "M") 20 | }, args: ((2, 1), (rel: (1, 0)), (rel: (1, 1)))) 21 | 22 | #test-case({ 23 | import draw: * 24 | 25 | for z in range(-1, 2) { 26 | circle((0,0,z)) 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /tests/content-anchors/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-anchors/ref/1.png -------------------------------------------------------------------------------- /tests/content-anchors/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | #import draw: content, rotate, scale, translate 5 | 6 | #test-case({ 7 | import draw: * 8 | content((0, 0), text(size: 40pt)[Yogurt], padding: (rest: 1, top: 2), frame: "rect", name: "content") 9 | line("content.base-west", "content.base-east", stroke: green) 10 | for-each-anchor("content", name => { 11 | content((), text(size: 6pt)[#name], frame: "rect", 12 | fill: white, stroke: none) 13 | }) 14 | }) 15 | 16 | #for a in ("center", "north", "south", "east", "west", "north-east", "north-west", "south-east", "south-west", "mid", "base") { 17 | test-case({ 18 | cross((0,0)) 19 | content((0,0), [#a], anchor: a) 20 | }) 21 | 22 | test-case({ 23 | cross((0,0)) 24 | rotate(45deg) 25 | content((0,0), [#a (rotate)], anchor: a) 26 | }) 27 | 28 | test-case({ 29 | cross((0,0)) 30 | translate((1,1)) 31 | cross((0,0)) 32 | content((0,0), [#a (translate)], anchor: a) 33 | }) 34 | 35 | test-case({ 36 | cross((0,0)) 37 | scale(2) 38 | cross((0,0)) 39 | content((0,0), [#a (scale)], anchor: a) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /tests/content-intersection/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-intersection/ref/1.png -------------------------------------------------------------------------------- /tests/content-intersection/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | set-style(fill: gray, stroke: gray) 8 | 9 | intersections("i", { 10 | content((0,0), [Text]) 11 | on-layer(-1, { 12 | line((1,1), (-1,-1)) 13 | bezier((-1,0), (1,0), (-.5,.5), (.5,-.5), fill: none) 14 | }) 15 | }) 16 | on-layer(-1, { 17 | for-each-anchor("i", n => { 18 | circle("i." + n, radius: .05) 19 | }) 20 | }) 21 | }) 22 | 23 | #test-case({ 24 | import draw: * 25 | 26 | draw.content(name: "test1", frame: "rect", (0,0))[Test 1] 27 | draw.content(name: "test2", frame: "rect", (-1,-1))[Test 2] 28 | draw.line("test1", "test2") 29 | }) 30 | 31 | #test-case({ 32 | let thide = hide 33 | import draw: * 34 | 35 | draw.content(name: "test1", (0,0))[Test 1] 36 | draw.content((-1,-1))[#box(stroke: 1pt + red, thide[Test 2])] 37 | draw.content(name: "test2", frame: "rect", anchor: "north-east", (-1,-1))[Test 2] 38 | draw.line("test1", "test2") 39 | }) 40 | 41 | #test-case({ 42 | let thide = hide 43 | import draw: * 44 | 45 | draw.content(name: "test1", (0,0))[Test 1] 46 | draw.content((-1,-1))[#box(stroke: 1pt + red, thide[Test 2])] 47 | draw.content(name: "test2", frame: "rect", anchor: "south", (-1,-1))[Test 2] 48 | draw.line("test1", "test2") 49 | }) 50 | 51 | #test-case({ 52 | import draw:* 53 | content((-1,0), [Text], name: "a") 54 | content((+1,0), [Text], name: "b") 55 | line("a", "b") 56 | }) 57 | -------------------------------------------------------------------------------- /tests/content-padding/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-padding/ref/1.png -------------------------------------------------------------------------------- /tests/content-padding/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | // All Sides 6 | #test-case({ 7 | import draw: * 8 | content((0,0), [This is a test.], padding: .5, frame: "rect") 9 | }) 10 | 11 | // Array Syntax 12 | #test-case({ 13 | import draw: * 14 | // Y, X 15 | content((0,0), [This is a test.], padding: (.5, 0), frame: "rect") 16 | content((0,1), [This is a test.], padding: (0, .5), frame: "rect") 17 | // Top, Y, Bottom 18 | content((0,2), [This is a test.], padding: (.5, 0, 0), frame: "rect") 19 | content((0,3), [This is a test.], padding: (0, 0, .5), frame: "rect") 20 | // Top, Right, Bottom, Left 21 | content((0,4), [This is a test.], padding: (.5, 0, 0, 0), frame: "rect") 22 | content((0,5), [This is a test.], padding: (0, .5, 0, 0), frame: "rect") 23 | content((0,6), [This is a test.], padding: (0, 0, .5, 0), frame: "rect") 24 | content((0,7), [This is a test.], padding: (0, 0, 0, .5), frame: "rect") 25 | }) 26 | 27 | // Dictionary Syntax 28 | #test-case({ 29 | import draw: * 30 | content((0,0), [This is a test.], padding: (left: .5), frame: "rect") 31 | content((0,1), [This is a test.], padding: (right: .5), frame: "rect") 32 | content((0,2), [This is a test.], padding: (top: .5), frame: "rect") 33 | content((0,3), [This is a test.], padding: (bottom: .5), frame: "rect") 34 | content((0,4), [This is a test.], padding: (:), frame: "rect") 35 | }) 36 | -------------------------------------------------------------------------------- /tests/content-rotation/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-rotation/ref/1.png -------------------------------------------------------------------------------- /tests/content-rotation/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | // Test aabb 6 | #for angle in (0deg, 30deg, -30deg, 45deg, -45deg, 90deg, -90deg) { 7 | test-case({ 8 | import draw: * 9 | content((), [Content], angle: angle) 10 | }) 11 | } 12 | 13 | // Rotate to coordinate 14 | #for angle in (0deg, 30deg, -30deg, 45deg, -45deg, 90deg, -90deg) { 15 | test-case({ 16 | import draw: * 17 | let pt = (angle, 2) 18 | line((0,0), pt) 19 | content((0,0), [Content], angle: pt) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /tests/content-rtl/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-rtl/ref.png -------------------------------------------------------------------------------- /tests/content-rtl/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-rtl/ref/1.png -------------------------------------------------------------------------------- /tests/content-rtl/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #set text(dir: rtl) 6 | 7 | #test-case({ 8 | import draw: * 9 | 10 | line((0,0), (1,0), mark: (end: ">")) 11 | content((0,1), [This is an example of RTL.]) 12 | line((0,0), (45deg, 1), mark: (end: ">")) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/content-span/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-span/ref/1.png -------------------------------------------------------------------------------- /tests/content-span/test.typ: -------------------------------------------------------------------------------- 1 | #import "/src/lib.typ": * 2 | #import "/tests/helper.typ": * 3 | #set page(width: auto, height: auto) 4 | 5 | #test-case({ 6 | import draw: * 7 | content((0,0), (+1,+1), align(center + horizon)[tr], frame: "rect") 8 | content((0,0), (-1,-1), align(center + horizon)[bl], frame: "rect") 9 | content((0,0), (-1,+1), align(center + horizon)[tl], frame: "rect") 10 | content((0,0), (+1,-1), align(center + horizon)[br], frame: "rect") 11 | }) 12 | -------------------------------------------------------------------------------- /tests/content-transform/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content-transform/ref/1.png -------------------------------------------------------------------------------- /tests/content-transform/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let test() = { 6 | import draw: * 7 | content((0,0), [This is a test.], padding: .1, name: "e") 8 | for-each-anchor("e", n => { 9 | circle("e." + n, radius: .05) 10 | }) 11 | } 12 | 13 | #test-case({ 14 | import draw: * 15 | cross((0,0)) 16 | scale(2.5) 17 | cross((0,0)) 18 | test() 19 | }) 20 | 21 | #test-case({ 22 | import draw: * 23 | cross((0,0)) 24 | rotate(45deg) 25 | cross((0,0)) 26 | test() 27 | }) 28 | 29 | #test-case({ 30 | import draw: * 31 | cross((0,0)) 32 | translate((1,2,1)) 33 | cross((0,0)) 34 | test() 35 | }) 36 | -------------------------------------------------------------------------------- /tests/content/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/content/ref/1.png -------------------------------------------------------------------------------- /tests/content/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | content((0,0), [This is a test.]) 8 | }) 9 | 10 | #test-case({ 11 | import draw: * 12 | content((0,0), auto, [This is a test.], frame: "rect") 13 | }) 14 | 15 | #test-case({ 16 | import draw: * 17 | content((0,0), (1.5,1), [This is a test.], frame: "rect") 18 | }) 19 | 20 | #test-case({ 21 | import draw: * 22 | content((0,0), (1,1.5), [This is a test.], frame: "rect") 23 | }) 24 | 25 | #test-case({ 26 | import draw: * 27 | content((0,0), auto, angle: 45deg, [This is a test.], frame: "rect") 28 | }) 29 | 30 | #test-case({ 31 | import draw: * 32 | content((0,0), (1,1.5), angle: 45deg, [This is a test.], frame: "rect") 33 | }) 34 | 35 | #test-case({ 36 | import draw: * 37 | content((0,0), (1.5,1), angle: 45deg, [This is a test.], frame: "rect") 38 | }) 39 | 40 | #test-case({ 41 | import draw: * 42 | content((0,0), auto, angle: 0deg, [This is a test.], frame: "circle") 43 | }) 44 | 45 | #test-case({ 46 | import draw: * 47 | content((0,0), auto, angle: 45deg, [This is a test.], frame: "circle") 48 | }) 49 | 50 | #test-case({ 51 | import draw: * 52 | content((0,0), (1,1.5), angle: 45deg, [This is a test.], frame: "circle") 53 | }) 54 | 55 | #test-case({ 56 | import draw: * 57 | set-style(content: (frame: "circle", stroke: 3pt), fill: blue) 58 | content((0,0), (1,1), angle: 15deg, 59 | text(white, align(center+horizon)[With style!])) 60 | }) 61 | 62 | #test-case({ 63 | import draw: * 64 | circle((0,0), name: "c") 65 | content((0,0), angle: "c.north-east", [Text]) 66 | }) 67 | 68 | // Test the z coordinate is respected 69 | #test-case({ 70 | import draw: * 71 | content((0, 0,-1), [Z=-1]) 72 | content((0, 0, 0), [Z=0]) 73 | content((0, 0, 1), [Z=1]) 74 | }) 75 | 76 | // Test inline math measuring 77 | #test-case({ 78 | import draw: * 79 | content((0, 0), $x$) 80 | }) 81 | 82 | #test-case({ 83 | import draw: * 84 | 85 | let elements = ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l") 86 | 87 | line((1, 4), (13, 4), (13, 3), (1, 3), close: true) 88 | 89 | for i in range(1, 13) { 90 | line((i, 4), (i, 3)) 91 | content((i + 0.5, 3.5), text(bottom-edge:"descender", top-edge: "ascender")[#elements.at(i - 1)]) 92 | } 93 | }) 94 | 95 | #test-case({ 96 | import draw:* 97 | content((-1,0), [Text], name: "a") 98 | content((+1,0), [Text], name: "b") 99 | line("a", "b") 100 | }) 101 | 102 | // #792 Scaling 103 | #test-case({ 104 | import draw:* 105 | scale(2) 106 | set-style(content: (auto-scale: false)) 107 | content((0,0), [Text]) 108 | set-style(content: (auto-scale: true)) 109 | content((0,1), [Text]) 110 | }) 111 | 112 | // Compiler crash 113 | #test-case({ 114 | import draw: * 115 | rect((-1,-1), (1,1)) 116 | content((0,0), [Test], padding: -1, frame: "rect") 117 | }) 118 | -------------------------------------------------------------------------------- /tests/coordinate-lerp/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/coordinate-lerp/ref/1.png -------------------------------------------------------------------------------- /tests/coordinate-lerp/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | grid((-2,-1), (7,1), stroke: gray) 8 | 9 | let a = (1,0) 10 | let b = (4,0) 11 | 12 | set-style(circle: (radius: .1)) 13 | for i in (-50%, 0%, 50%, 100%, 150%) { 14 | let pt = (a, i, b) 15 | circle(pt) 16 | content(pt, repr(i), anchor: "north", padding: (top: .5)) 17 | } 18 | }) 19 | 20 | #test-case({ 21 | import draw: * 22 | grid((-2,-1), (7,1), stroke: gray) 23 | 24 | let a = (1,0) 25 | let b = (4,0) 26 | 27 | set-style(circle: (radius: .1)) 28 | for i in (-1.5, 0, 1.5, 3, 4.5) { 29 | let pt = (a, i, b) 30 | circle(pt) 31 | content(pt, repr(i), anchor: "north", padding: (top: .5)) 32 | } 33 | }) 34 | 35 | 36 | #test-case({ 37 | import draw: * 38 | grid((-2,-1), (7,1), stroke: gray) 39 | 40 | let a = (1,0) 41 | let b = (4,0) 42 | 43 | set-style(circle: (radius: .1)) 44 | for i in (-1.5cm, 0cm, 1.5cm, 3cm, 4.5cm) { 45 | let pt = (a, i, b) 46 | circle(pt) 47 | content(pt, repr(i), anchor: "north", padding: (top: .5)) 48 | } 49 | }) 50 | 51 | #test-case({ 52 | import draw: * 53 | grid((0,-3), (5,4), stroke: gray) 54 | 55 | let a = (1,0) 56 | let b = (4,0) 57 | 58 | set-style(circle: (radius: .1)) 59 | circle(a, fill: red) 60 | circle(b, fill: green) 61 | 62 | for i in (-50%, 0%, 50%, 100%) { 63 | let pt = (a, i, 90deg, b) 64 | circle(pt) 65 | content(pt, repr(i), anchor: "north", padding: (top: .5)) 66 | } 67 | }) 68 | 69 | #test-case({ 70 | import draw: * 71 | grid((0,-3), (3,4), stroke: gray) 72 | 73 | let a = (1,0) 74 | let b = (2,0) 75 | 76 | set-style(circle: (radius: .1)) 77 | circle(a, fill: red) 78 | circle(b, fill: green) 79 | 80 | for i in (-2, 0, 1.5, 3) { 81 | let pt = (a, i, 90deg, b) 82 | circle(pt) 83 | content(pt, repr(i), anchor: "north", padding: (top: .5)) 84 | } 85 | }) 86 | -------------------------------------------------------------------------------- /tests/coordinate/custom/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/coordinate/custom/ref/1.png -------------------------------------------------------------------------------- /tests/coordinate/custom/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | grid((-2,-1), (7,1), stroke: gray) 8 | 9 | let log-resolver(ctx, coordinate) = { 10 | if type(coordinate) == dictionary and "log" in coordinate { 11 | coordinate = coordinate.log 12 | coordinate = coordinate.map(n => calc.log(calc.max(n, util.float-epsilon), base: 10)) 13 | } 14 | 15 | return coordinate 16 | } 17 | 18 | register-coordinate-resolver(log-resolver) 19 | 20 | set-style(circle: (radius: .1)) 21 | for i in (.1, 1, 10, 100, 1000, 10000) { 22 | let pt = (log: (i * 1, 1)) 23 | circle(pt) 24 | content(pt, repr(i), anchor: "north", padding: (top: .5)) 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /tests/copy-anchor/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/copy-anchor/ref/1.png -------------------------------------------------------------------------------- /tests/copy-anchor/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas(length: 1cm, { 5 | import draw: * 6 | 7 | group(name: "b", { 8 | group(name: "a", { 9 | rect((-1,-1), (1,1)) 10 | anchor("my-anchor", (0,0)) 11 | }) 12 | 13 | copy-anchors("a") 14 | circle("my-anchor", radius: .1, stroke: red) 15 | }) 16 | 17 | circle("b.my-anchor", radius: .2, stroke: blue) 18 | })) 19 | 20 | #box(stroke: 2pt + red, canvas(length: 1cm, { 21 | import draw: * 22 | 23 | group(name: "a", { 24 | group(name: "b", { 25 | line((), (1,1), name: "l") 26 | }) 27 | copy-anchors("b") 28 | circle("l.end") 29 | }) 30 | 31 | circle("a.l.start") 32 | })) -------------------------------------------------------------------------------- /tests/cube/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/cube/ref/1.png -------------------------------------------------------------------------------- /tests/cube/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | line((0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), close: true) 8 | line((0, 0, 1), (0, 0, 0)) 9 | line((1, 0, 1), (1, 0, 0)) 10 | line((1, 1, 1), (1, 1, 0)) 11 | line((0, 1, 1), (0, 1, 0)) 12 | line((0, 0), (1, 0), (1, 1), (0, 1), close: true) 13 | })) 14 | -------------------------------------------------------------------------------- /tests/custom-mark/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/custom-mark/ref/1.png -------------------------------------------------------------------------------- /tests/custom-mark/test.typ: -------------------------------------------------------------------------------- 1 | #import "/src/lib.typ": * 2 | #import "/tests/helper.typ": * 3 | #set page(width: auto, height: auto) 4 | 5 | #let register-face() = { 6 | import draw: * 7 | 8 | register-mark("face", style => { 9 | circle((0,0), radius: .5, fill: yellow) 10 | arc((0,0), start: 180deg + 30deg, delta: 180deg - 60deg, anchor: "origin", radius: .3) 11 | circle((-.15, +.15), radius: .1, fill: white) 12 | circle((-.10, +.10), radius: .025, fill: black) 13 | circle((+.15, +.15), radius: .1, fill: white) 14 | circle((+.20, +.10), radius: .025, fill: black) 15 | 16 | anchor("tip", (+.5, 0)) 17 | anchor("base", (-.5, 0)) 18 | }, mnemonic: ":)") 19 | } 20 | 21 | #test-case({ 22 | import draw: * 23 | 24 | register-face() 25 | catmull((-3, 0), (-1,1), (1,-1), (3,0), mark: (end: "face", start: (symbol: ":)", flip: true, reverse: true), )) 26 | }) 27 | 28 | #test-case({ 29 | import draw: * 30 | 31 | line((0,-1), (0,1), stroke: green) 32 | 33 | register-face() 34 | mark((0,0), (+1,0), symbol: ":)", slant: 50%, anchor: "center") 35 | }) 36 | 37 | #test-case({ 38 | import draw: * 39 | 40 | register-face() 41 | line((0,0), (3,0), mark: (end: (":)", ":)", ":)"), sep: -.3)) 42 | }) 43 | -------------------------------------------------------------------------------- /tests/decorations/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/decorations/ref/1.png -------------------------------------------------------------------------------- /tests/decorations/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | decorations.brace((-1,0), (1,0), stroke: blue, amplitude: 1, thickness: 50%, pointiness: 25%, outer-thickness: .1) 7 | }) 8 | 9 | #test-case(args => { 10 | decorations.brace((-1,-1), (1,1), ..args) 11 | }, args: ((:), (flip: true))) 12 | 13 | #test-case(args => { 14 | decorations.brace((-1,0), (1,0), ..args) 15 | }, args: ( 16 | (amplitude: .3), 17 | (pointiness: 0%), 18 | (pointiness: 50%), 19 | (pointiness: 100%), 20 | )) 21 | 22 | #test-case(args => { 23 | decorations.flat-brace((-1,-1), (1,1), ..args) 24 | }, args: ((:), (flip: true))) 25 | 26 | #test-case(args => { 27 | decorations.flat-brace((-1,0), (1,0), ..args) 28 | }, args: ( 29 | (amplitude: .7), 30 | (curves: (.5, 0, 0, 0), outer-curves: 1), 31 | (aspect: .3), 32 | )) 33 | 34 | // Bug #577 35 | #test-case(args => { 36 | decorations.brace((-1,0), (1,0), ..args, pointiness: 100%) 37 | }, args: ( 38 | (amplitude: 0.5), 39 | (amplitude: 1), 40 | )) 41 | 42 | #test-case(args => { 43 | decorations.brace((-1,0), (1,0), ..args, pointiness: 0%) 44 | }, args: ( 45 | (amplitude: 0.5), 46 | (amplitude: 1), 47 | )) 48 | 49 | // Bug #687 50 | #test-case(args => { 51 | decorations.flat-brace((-1,-1), (1,1), ..args, name: "brace") 52 | draw.circle("brace.content", radius: 0.1); 53 | }, args: ( 54 | (flip: false), 55 | (flip: true), 56 | )) 57 | 58 | #test-case(args => { 59 | decorations.brace((-1,-1), (1,1), ..args, name: "brace") 60 | draw.circle("brace.content", radius: 0.1); 61 | }, args: ( 62 | (flip: false), 63 | (flip: true), 64 | )) 65 | 66 | #test-case({ 67 | decorations.flat-brace((0,0), (1,0), stroke: blue, fill: gray) 68 | decorations.brace((0,1), (1,1), stroke: blue, fill: gray, taper: false) 69 | }) 70 | -------------------------------------------------------------------------------- /tests/element-anchors/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/element-anchors/ref.png -------------------------------------------------------------------------------- /tests/element-anchors/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/element-anchors/ref/1.png -------------------------------------------------------------------------------- /tests/element-anchors/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let display(body, ..args) = { 5 | import draw: * 6 | (body)(..args, name: "elem"); 7 | get-ctx(ctx => { 8 | for-each-anchor( 9 | "elem", 10 | exclude: if args.named().at("mode", default: none) == "OPEN" { 11 | ("east", "north-east", "north", "south", "south-east") 12 | } else {()}, 13 | o => { 14 | let n = "elem." + o 15 | 16 | group({ 17 | rotate(45deg) 18 | set-style(stroke: blue) 19 | line((rel: (-.1, 0), to: n), (rel: (.2, 0))) 20 | line((rel: (0, -.1), to: n), (rel: (0, .2))) 21 | }) 22 | 23 | let (_, nv) = coordinate.resolve(ctx, n) 24 | let (x, y, ..) = nv 25 | let anchor = ( 26 | (if y < 0 { "north" } else if y > 0 { "south" },) + (if x < 0 { "east" } else if x > 0 { "west" },) 27 | ).filter(p => p != none).join("-") 28 | if anchor == none { 29 | anchor = "south" 30 | } 31 | 32 | content(n, text(8pt, o), anchor: anchor, padding: .2) 33 | } 34 | ) 35 | }) 36 | } 37 | 38 | #box(stroke: 2pt + red, canvas(length: 1cm, { 39 | import draw: * 40 | display(line, (0,0), (2,1), (4,0)) 41 | })) 42 | 43 | #box(stroke: 2pt + red, canvas(length: 1cm, { 44 | import draw: * 45 | display(circle, ()) 46 | })) 47 | 48 | #box(stroke: 2pt + red, canvas(length: 1cm, { 49 | import draw: * 50 | display(circle-through, (-1,0), (0,1), (1,0)) 51 | })) 52 | 53 | #box(stroke: 2pt + red, canvas(length: 1cm, { 54 | import draw: * 55 | display(arc, (0,0), start: 225deg, stop: 135deg, radius: 5, mode: "OPEN") 56 | })) 57 | 58 | #box(stroke: 2pt + red, canvas(length: 1cm, { 59 | import draw: * 60 | display(arc, (0,0), start: 225deg, stop: 135deg, radius: 5, mode: "PIE") 61 | })) 62 | 63 | #box(stroke: 2pt + red, canvas(length: 1cm, { 64 | import draw: * 65 | display(arc, (0,0), start: 225deg, stop: 135deg, radius: 5, mode: "CLOSE") 66 | })) 67 | 68 | #box(stroke: 2pt + red, canvas(length: 1cm, { 69 | import draw: * 70 | display(line, (-1,0), (0,1), (1,0)) 71 | })) 72 | 73 | #box(stroke: 2pt + red, canvas(length: 1cm, { 74 | import draw: * 75 | display(grid, (-1,-1), (1,1), step: 1) 76 | })) 77 | 78 | #box(stroke: 2pt + red, canvas(length: 1cm, { 79 | import draw: * 80 | display(content, (), text(2cm)[Text]) 81 | })) 82 | 83 | #box(stroke: 2pt + red, canvas(length: 1cm, { 84 | import draw: * 85 | display(rect, (-1,-1), (1,1)) 86 | })) 87 | 88 | #box(stroke: 2pt + red, canvas(length: 1cm, { 89 | import draw: * 90 | display(rect, (-1,-1), (1,1), radius: .5) 91 | })) 92 | 93 | #box(stroke: 2pt + red, canvas(length: 1cm, { 94 | import draw: * 95 | display(bezier, (-2,0), (2,0), (-1,1), (1,-1)) 96 | })) 97 | 98 | #box(stroke: 2pt + red, canvas(length: 1cm, { 99 | import draw: * 100 | display(bezier, (-1,-1), (1,-1), (0,2)) 101 | })) 102 | 103 | #box(stroke: 2pt + red, canvas(length: 1cm, { 104 | import draw: * 105 | display(bezier-through, (-1,-1), (0,1), (1,-1)) 106 | })) 107 | 108 | #box(stroke: 2pt + red, canvas(length: 1cm, { 109 | import draw: * 110 | display(catmull, (-2,0), (-1,-1), (0,1), (1,-1), (2,0)) 111 | })) 112 | -------------------------------------------------------------------------------- /tests/empty-group/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/empty-group/ref/1.png -------------------------------------------------------------------------------- /tests/empty-group/test.typ: -------------------------------------------------------------------------------- 1 | #import "/src/lib.typ": * 2 | #import "/tests/helper.typ": * 3 | 4 | #test-case({ 5 | import draw: * 6 | 7 | group(none) 8 | group(ctx => none) 9 | }) 10 | 11 | #test-case({ 12 | import draw: * 13 | group({ 14 | group(name: "group-1", none) 15 | copy-anchors("group-1") 16 | }) 17 | group({ 18 | group(name: "group-2", { 19 | anchor("default", (0,1)) 20 | }) 21 | copy-anchors("group-2") 22 | }) 23 | }) -------------------------------------------------------------------------------- /tests/empty/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/empty/ref/1.png -------------------------------------------------------------------------------- /tests/empty/test.typ: -------------------------------------------------------------------------------- 1 | // Empty test file 2 | -------------------------------------------------------------------------------- /tests/floating/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/floating/ref/1.png -------------------------------------------------------------------------------- /tests/floating/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | rect((0,0), (5,5)) 8 | 9 | // Floating circle should not affect bounds 10 | floating(circle((6,6))) 11 | 12 | // Floating content 13 | floating(content((2.5, 6), [Floating content])) 14 | 15 | // Multiple floating elements 16 | floating({ 17 | rect((-1,3), (0,2)) 18 | rect((0,2), (1,1)) 19 | }) 20 | 21 | // Styles apply to floating elementss 22 | set-style(stroke: green) 23 | 24 | // Use floating anchor 25 | floating(circle((5,2), name: "floating-circle")) 26 | line("floating-circle", (0,0)) 27 | }) 28 | 29 | // The example used in `floating` docstring 30 | #test-case({ 31 | import draw: * 32 | 33 | group({ 34 | circle((0,0)) 35 | content((0,2), [Non-floating]) 36 | floating(content((2,0), [Floating])) 37 | }, name: "bounds") 38 | 39 | set-style(stroke: red) 40 | rect("bounds.north-west", "bounds.south-east") 41 | }) -------------------------------------------------------------------------------- /tests/gradient/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/gradient/ref/1.png -------------------------------------------------------------------------------- /tests/gradient/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | 8 | fill(gradient.linear(red, blue)) 9 | 10 | rect((2,2), (4,4)) 11 | circle((0,0)) 12 | line((0,2), (1,3), (0, 4), (-3, 3), close: true) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/grid/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/grid/ref/1.png -------------------------------------------------------------------------------- /tests/grid/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | grid((0,0), (1,1), step: .1) 8 | 9 | translate((0, 1.5)) 10 | grid((0,0), (1,1), step: .5) 11 | 12 | translate((0, 1.5)) 13 | grid((0,0), (1,1), step: (x: .2, y: .5)) 14 | })) 15 | -------------------------------------------------------------------------------- /tests/group-anchors/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-anchors/ref/1.png -------------------------------------------------------------------------------- /tests/group-anchors/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import cetz.draw: * 7 | 8 | set-style(circle: (stroke: none, fill: gray)) 9 | 10 | circle((0, 0), radius: .1cm, fill: blue) 11 | content((0, 0), [(0,0)], anchor: "north", padding: .1) 12 | 13 | group(name: "group", { 14 | anchor("default", (0, 0)) 15 | rect((-1, 0), (2, 2)) 16 | }, anchor: "north") 17 | 18 | on-layer(-1, { 19 | for-each-anchor("group", name => { 20 | circle("group." + name, radius: .1cm, fill: gray, stroke: none) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/group-nested-anchors/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-nested-anchors/ref/1.png -------------------------------------------------------------------------------- /tests/group-nested-anchors/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | group(name: "g", { 8 | group(name: "inner-1", { 9 | group(name: "inner-2", { 10 | rect((-1,-1), (1,1)) 11 | anchor("p1", (1,1)) 12 | }) 13 | copy-anchors("inner-2") 14 | anchor("p2", (-1,-1)) 15 | }) 16 | copy-anchors("inner-1") 17 | }) 18 | 19 | circle("g.p1", fill: blue) 20 | circle("g.p2", fill: red) 21 | })) 22 | 23 | #box(stroke: 2pt + red, canvas({ 24 | import draw: * 25 | 26 | group(name: "parent", { 27 | content((0,0), [Content], name: "content") 28 | group(name: "child", { 29 | circle((1,-2), fill: blue, name: "circle") 30 | }) 31 | }) 32 | 33 | rect("parent.content.south-west", 34 | "parent.content.north-east", stroke: green) 35 | rect( 36 | "parent.child.circle.-45deg", 37 | "parent.child.circle.135deg", 38 | stroke: green 39 | ) 40 | })) -------------------------------------------------------------------------------- /tests/group-none/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-none/ref.png -------------------------------------------------------------------------------- /tests/group-none/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-none/ref/1.png -------------------------------------------------------------------------------- /tests/group-none/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | 8 | group({}) 9 | }) 10 | -------------------------------------------------------------------------------- /tests/group-padding/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-padding/ref/1.png -------------------------------------------------------------------------------- /tests/group-padding/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let show-group(padding: none) = { 5 | draw.group(padding: padding, { 6 | draw.rect((0,0), (1,1), fill: blue) 7 | }, name: "g") 8 | draw.rect("g.south-west", "g.north-east") 9 | } 10 | 11 | #box(stroke: 2pt + red, canvas({ 12 | import draw: * 13 | show-group() 14 | })) 15 | 16 | #box(stroke: 2pt + red, canvas({ 17 | import draw: * 18 | show-group(padding: 1) 19 | })) 20 | 21 | #box(stroke: 2pt + red, canvas({ 22 | import draw: * 23 | show-group(padding: (0, 1)) 24 | })) 25 | 26 | #box(stroke: 2pt + red, canvas({ 27 | import draw: * 28 | show-group(padding: (1, 0)) 29 | })) 30 | 31 | #box(stroke: 2pt + red, canvas({ 32 | import draw: * 33 | show-group(padding: (top: 1)) 34 | })) 35 | 36 | #box(stroke: 2pt + red, canvas({ 37 | import draw: * 38 | show-group(padding: (bottom: 1)) 39 | })) 40 | 41 | #box(stroke: 2pt + red, canvas({ 42 | import draw: * 43 | show-group(padding: (left: 1)) 44 | })) 45 | 46 | #box(stroke: 2pt + red, canvas({ 47 | import draw: * 48 | show-group(padding: (right: 1)) 49 | })) 50 | -------------------------------------------------------------------------------- /tests/group-transform/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-transform/ref/1.png -------------------------------------------------------------------------------- /tests/group-transform/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let show-group(body: ()) = { 5 | draw.group({ 6 | body + draw.rect((-2,-2), (2,2)) 7 | }, name: "g") 8 | draw.for-each-anchor("g", n => { 9 | draw.content("g."+n, n) 10 | }) 11 | } 12 | 13 | #box(stroke: 2pt + red, canvas({ 14 | import draw: * 15 | 16 | translate((1,1,1)) 17 | show-group() 18 | })) 19 | 20 | #box(stroke: 2pt + red, canvas({ 21 | import draw: * 22 | 23 | scale(x: 2, y: 50%) 24 | show-group() 25 | })) 26 | 27 | #box(stroke: 2pt + red, canvas({ 28 | import draw: * 29 | 30 | rotate(30deg) 31 | show-group() 32 | })) 33 | 34 | #box(stroke: 2pt + red, canvas({ 35 | import draw: * 36 | 37 | show-group(body: translate((1,1,1))) 38 | })) 39 | 40 | #box(stroke: 2pt + red, canvas({ 41 | import draw: * 42 | 43 | show-group(body: scale(x: 200%, y: .5)) 44 | })) 45 | 46 | #box(stroke: 2pt + red, canvas({ 47 | import draw: * 48 | 49 | show-group(body: rotate(30deg)) 50 | })) 51 | -------------------------------------------------------------------------------- /tests/group-translate/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/group-translate/ref/1.png -------------------------------------------------------------------------------- /tests/group-translate/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let cross(pos: (0,0)) = { 5 | draw.line((rel: (-.2,0), to: pos), 6 | (rel: (.4,0)), stroke: blue + 2pt) 7 | draw.line((rel: (0,-.2), to: pos), 8 | (rel: (0,.4)), stroke: blue + 2pt) 9 | } 10 | 11 | /* Test the translation of grouped elements 12 | via the group anchor. */ 13 | #box(stroke: 2pt + red, canvas({ 14 | import draw: * 15 | 16 | cross() 17 | set-style(content: (padding: 1)) 18 | group({ 19 | content((0,0), [Center]) 20 | }) 21 | group({ 22 | content((0,0), [North]) 23 | }, anchor: "north") 24 | group({ 25 | content((0,0), [South]) 26 | }, anchor: "south") 27 | group({ 28 | content((0,0), [West]) 29 | }, anchor: "west") 30 | group({ 31 | content((0,0), [East]) 32 | }, anchor: "east") 33 | group({ 34 | content((0,0), [North-West]) 35 | }, anchor: "north-west") 36 | group({ 37 | content((0,0), [North-East]) 38 | }, anchor: "north-east") 39 | group({ 40 | content((0,0), [South-West]) 41 | }, anchor: "south-west") 42 | group({ 43 | content((0,0), [South-East]) 44 | }, anchor: "south-east") 45 | group({ 46 | anchor("custom", (0,-2)) 47 | content((0,0), [Custom]) 48 | }, anchor: "custom") 49 | })) 50 | -------------------------------------------------------------------------------- /tests/helper.typ: -------------------------------------------------------------------------------- 1 | #import "/src/lib.typ" as cetz 2 | 3 | /// Draw a point + label 4 | #let point(pt, name) = { 5 | cetz.draw.circle(pt, radius: .05cm, fill: black, stroke: none, name: "pt") 6 | cetz.draw.content((rel: (.2cm, 0), to: "pt"), [#name], anchor: "west") 7 | } 8 | 9 | /// Draw a cross at position pt 10 | #let cross(pt, size: .25, ..style) = { 11 | import cetz.draw: * 12 | let len = size / 2 13 | line((rel: (-len,0), to: pt), 14 | (rel: (len, 0), to: pt), stroke: green, ..style) 15 | line((rel: (0,-len), to: pt), 16 | (rel: (0, len), to: pt), stroke: green, ..style) 17 | } 18 | 19 | /// Test case canvas surrounded by a red border 20 | #let test-case(body, ..canvas-args, args: none) = { 21 | if type(body) != function { 22 | body = _ => { body } 23 | args = (none,) 24 | } else { 25 | assert(type(args) == array and args.len() > 0, 26 | message: "Function body requires args set!") 27 | } 28 | 29 | for arg in args { 30 | block(stroke: 2pt + red, 31 | cetz.canvas(..canvas-args, { 32 | body(arg) 33 | }) 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/hide/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/hide/ref/1.png -------------------------------------------------------------------------------- /tests/hide/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | rect((0,0), (5,5)) 8 | 9 | // Hide a circle 10 | hide(circle((6,6))) 11 | 12 | // Hide content 13 | hide(content((-6,-6), [Hidden])) 14 | 15 | // Hide multiple elements 16 | hide({ 17 | rect((0,0), (1,1)) 18 | rect((1,1), (2,2)) 19 | rect((2,2), (3,3)) 20 | }) 21 | 22 | // Use hidden anchor 23 | hide(line((0,0), (2.5, 2.5), name: "line")) 24 | content("line.end", [Hidden anchor]) 25 | }) 26 | 27 | #test-case({ 28 | import draw: * 29 | 30 | merge-path({ 31 | arc((0,0), start: 0deg, stop: 180deg) 32 | hide({ 33 | // This gets ignored 34 | line((), (rel: (-5,0), update: false)) 35 | }) 36 | line((), (rel: (1, -1))) 37 | }, close: true) 38 | }) 39 | 40 | #test-case({ 41 | import draw: * 42 | 43 | hide(line((-1,-1), (1,1)), bounds: true) 44 | }) 45 | -------------------------------------------------------------------------------- /tests/hobby/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/hobby/ref.png -------------------------------------------------------------------------------- /tests/hobby/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/hobby/ref/1.png -------------------------------------------------------------------------------- /tests/hobby/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | hobby((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2), omega: 0) 8 | }) 9 | 10 | #test-case({ 11 | import draw: * 12 | hobby((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2), omega: .5) 13 | }) 14 | 15 | #test-case({ 16 | import draw: * 17 | hobby((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2), omega: 1) 18 | }) 19 | 20 | #test-case({ 21 | import draw: * 22 | hobby((0,-1), (1,1), (2,0), (3,1), (4,0), (5,2), close: true, fill: blue) 23 | }) 24 | 25 | // Two points, not closed 26 | #test-case({ 27 | import draw: * 28 | hobby((0,0), (1,1)) 29 | }) 30 | 31 | // Two points, closed 32 | #test-case({ 33 | import draw: * 34 | hobby((0,0), (1,1), close: true) 35 | }) 36 | -------------------------------------------------------------------------------- /tests/image/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/image/image.png -------------------------------------------------------------------------------- /tests/image/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/image/ref/1.png -------------------------------------------------------------------------------- /tests/image/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | content( 8 | (0,0), 9 | image("image.png", width: 2cm), 10 | anchor: "north-west", 11 | name: "i" 12 | ) 13 | 14 | set-style(radius: .1, fill: blue) 15 | for-each-anchor("i", anchor => { 16 | circle("i." + anchor) 17 | }) 18 | 19 | fill(red); 20 | circle(("i.north-west", 75%, "i.north-east")) 21 | })) 22 | -------------------------------------------------------------------------------- /tests/intersection/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/intersection/ref/1.png -------------------------------------------------------------------------------- /tests/intersection/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let test(body) = canvas(length: 1cm, { 6 | import draw: * 7 | 8 | group({ 9 | intersections("i", { 10 | body 11 | }) 12 | for-each-anchor("i", (name) => { 13 | circle("i."+name, radius: .1, fill: red) 14 | }) 15 | }) 16 | }) 17 | 18 | #box(stroke: 2pt + red, { 19 | import draw: * 20 | test({ 21 | line((-1,-1), (1,1)) 22 | }) 23 | test({ 24 | line((-1,-1), (1,1)) 25 | line((-1,1), (1,-1)) 26 | }) 27 | test({ 28 | line((-1,.5),(1,.5)) 29 | line((-1,-1), (1,1)) 30 | line((-1,1), (1,-1)) 31 | }) 32 | test({ 33 | circle((0,0)) 34 | line((-1,-1), (1,1)) 35 | }) 36 | test({ 37 | circle((0,0)) 38 | line((-1,.5),(1,.5)) 39 | }) 40 | test({ 41 | bezier-through((-1,0), (0,1), (1,0)) 42 | line((-1,.5),(1,.5)) 43 | }) 44 | test({ 45 | bezier-through((-1,0), (0,.5), (1,0)) 46 | circle((0,0), radius: .8) 47 | }) 48 | test({ 49 | bezier-through((-1,0), (0,.5), (1,0)) 50 | bezier-through((-1,.5), (0,-.5), (1,.5)) 51 | }) 52 | test({ 53 | bezier((-1,-1), (1,1), (-.5,2), (.5,-2)) 54 | bezier((-1,1), (1,-1), (-.5,-2), (.5,2)) 55 | }) 56 | test({ 57 | grid((0,0), (2,2), step: 1) 58 | }) 59 | test({ 60 | rect((0,0), (2,2)) 61 | rotate(45deg) 62 | line((0,0), (calc.sqrt(2*calc.pow(2,2)),0)) 63 | }) 64 | test({ 65 | // The marks must not generate intersections with the line! 66 | line((0,0), (2,2), mark: (start: ">", end: ">")) 67 | }) 68 | }) 69 | 70 | #test-case({ 71 | import draw: * 72 | 73 | intersections("i", { 74 | content((0, 0), [This is\ Text!], frame: "circle", name: "a") 75 | content((2, 1), [Hello!], frame: "circle", name: "b") 76 | // Invisible intersection line 77 | line("a.default", "b.default", stroke: none) 78 | }) 79 | line("i.0", "i.1", mark: (end: ">")) 80 | }) 81 | 82 | #test-case({ 83 | import draw: * 84 | 85 | circle((0,0), name: "a") 86 | rect((0,0), (2,2), name: "b") 87 | intersections("i", "a", "b", { 88 | line((-1,-1), (1,1)) 89 | }) 90 | for-each-anchor("i", (name) => { 91 | circle("i."+name, radius: .1, fill: red) 92 | }) 93 | }) 94 | 95 | #test-case(fn => { 96 | import draw: * 97 | 98 | let c = circle((1,1), name: "a", radius: 1.25) 99 | let r = rect((0,0), (2,2), name: "b") 100 | intersections("i", r, c, sort: fn) 101 | 102 | for-each-anchor("i", (name) => { 103 | content((), [#name], frame: "circle", fill: white) 104 | }) 105 | }, args: ( 106 | none, 107 | sorting.points-by-angle.with(reference: (1, 1)), 108 | sorting.points-by-distance.with(reference: (0, 0.1)), 109 | )) 110 | -------------------------------------------------------------------------------- /tests/layer/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/layer/ref/1.png -------------------------------------------------------------------------------- /tests/layer/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | 8 | circle((0,0), fill: red, stroke: none) 9 | on-layer(0, { 10 | circle((1, 0), fill: green, stroke: none) 11 | }) 12 | on-layer(1, { 13 | circle((2, 0), fill: blue, stroke: none) 14 | }) 15 | }) 16 | 17 | #test-case({ 18 | import draw: * 19 | 20 | on-layer(2, { 21 | circle((2, 0), fill: blue, stroke: none) 22 | }) 23 | on-layer(1, { 24 | circle((1, 0), fill: green, stroke: none) 25 | }) 26 | circle((0,0), fill: red, stroke: none) 27 | }) 28 | 29 | // Test nested layers 30 | #test-case({ 31 | import draw: * 32 | 33 | on-layer(1, { 34 | circle((1, 0), fill: green, stroke: none, name: "c2") 35 | on-layer(0, { 36 | circle((0,0), fill: red, stroke: none) 37 | }) 38 | }) 39 | 40 | on-layer(1, { 41 | content("c2.center", [Green]) 42 | }) 43 | content((0,0), [Red]) 44 | }) 45 | 46 | #test-case({ 47 | import draw: * 48 | on-layer(1, none) 49 | on-layer(1, ctx => none) 50 | }) 51 | -------------------------------------------------------------------------------- /tests/line-element-element-intersection/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/line-element-element-intersection/ref.png -------------------------------------------------------------------------------- /tests/line-element-element-intersection/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/line-element-element-intersection/ref/1.png -------------------------------------------------------------------------------- /tests/line-element-element-intersection/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let test(a, b, ..line-args) = { 5 | import draw: * 6 | 7 | a; b; 8 | line("a", "b", ..line-args) 9 | } 10 | 11 | #box(stroke: 2pt + red, canvas({ 12 | import draw: * 13 | 14 | test(rect((0,-.5), (rel: (1,1)), name: "a"), 15 | circle((3,0), name: "b")) 16 | })) 17 | 18 | #box(stroke: 2pt + red, canvas({ 19 | import draw: * 20 | 21 | test(rect((0,-1), (rel: (1,1)), name: "a"), 22 | circle((2,1), name: "b")) 23 | })) 24 | 25 | #box(stroke: 2pt + red, canvas({ 26 | import draw: * 27 | 28 | test(rect((0,-2), (rel: (1,1)), name: "a"), 29 | circle((2,2), name: "b")) 30 | })) 31 | 32 | #box(stroke: 2pt + red, canvas({ 33 | import draw: * 34 | 35 | test(rect((0,0), (rel: (1,1)), name: "a"), 36 | rect((0,0), (rel: (1,1)), name: "b")) 37 | })) 38 | 39 | #box(stroke: 2pt + red, canvas({ 40 | import draw: * 41 | 42 | set-style(content: (padding: .1)) 43 | test(content((0,0), [Text], name: "a"), 44 | content((1,1), [Text], name: "b")) 45 | })) 46 | 47 | #box(stroke: 2pt + red, canvas({ 48 | import draw: * 49 | 50 | set-style(content: (padding: .1)) 51 | test(content((0,0), [Text], frame: "circle", name: "a"), 52 | content((1,1), [Text], frame: "circle", name: "b")) 53 | })) 54 | 55 | #box(stroke: 2pt + red, canvas({ 56 | import draw: * 57 | 58 | set-style(content: (padding: .1)) 59 | test(rect((0,0), (rel: (1,1)), name: "a"), 60 | group({ 61 | line((2,2), (3,1), (rel: (0,2)), (rel: (-.1, -1.6)), close: true) 62 | anchor("default", (5,3)) 63 | }, name: "b")) 64 | })) 65 | -------------------------------------------------------------------------------- /tests/line-fill-rule/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/line-fill-rule/ref/1.png -------------------------------------------------------------------------------- /tests/line-fill-rule/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | 3 | #import "/src/lib.typ": * 4 | #import "/tests/helper.typ": * 5 | 6 | #test-case({ 7 | import draw: * 8 | 9 | line((25pt, 0pt), 10 | (10pt, 50pt), 11 | (50pt, 20pt), 12 | (0pt, 20pt), 13 | (40pt, 50pt), close: true, fill: blue, fill-rule: "non-zero") 14 | }) 15 | 16 | #test-case({ 17 | import draw: * 18 | 19 | line((25pt, 0pt), 20 | (10pt, 50pt), 21 | (50pt, 20pt), 22 | (0pt, 20pt), 23 | (40pt, 50pt), close: true, fill: blue, fill-rule: "even-odd") 24 | }) 25 | -------------------------------------------------------------------------------- /tests/local-anchor/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/local-anchor/ref/1.png -------------------------------------------------------------------------------- /tests/local-anchor/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | circle((0,0), radius: 0.5) 8 | arc((0, 1), start: -90deg, stop: 90deg, name: "c", anchor: "arc-start") 9 | stroke(blue) 10 | circle("c.arc-start", radius: 0.1) 11 | })) 12 | -------------------------------------------------------------------------------- /tests/mark-anchors/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-anchors/ref/1.png -------------------------------------------------------------------------------- /tests/mark-anchors/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case(args => { 6 | import draw: * 7 | 8 | line((0, -1), (0, 1)) 9 | mark((0, 0), (args.dir,0), symbol: "stealth", anchor: args.anchor) 10 | }, args: ( 11 | (dir: +1, anchor: "tip"), 12 | (dir: +1, anchor: "center"), 13 | (dir: +1, anchor: "base"), 14 | (dir: -1, anchor: "tip"), 15 | (dir: -1, anchor: "center"), 16 | (dir: -1, anchor: "base"), 17 | )) 18 | 19 | #test-case(args => { 20 | import draw: * 21 | 22 | line((0, -1), (0, 1), stroke: green) 23 | line((-1, 0), (0,0), mark: (end: "stealth", anchor: args.anchor)) 24 | }, args: ( 25 | (anchor: "tip"), 26 | (anchor: "center"), 27 | (anchor: "base"), 28 | )) 29 | 30 | #test-case(args => { 31 | import draw: * 32 | 33 | set-style(mark: (stroke: blue)) 34 | line((0,-1.2), (0,+1.2), stroke: green) 35 | mark((0, 0), (-1, 0), symbol: args.symbol, anchor: "tip") 36 | line((-1, 2), (3, 2), mark: (start: args.symbol, end: args.symbol, harpoon: true)) 37 | }, args: ( 38 | (symbol: ">"), 39 | (symbol: "stealth"), 40 | (symbol: "|"), 41 | (symbol: "o"), 42 | (symbol: "]"), 43 | (symbol: "<>"), 44 | (symbol: "[]"), 45 | (symbol: "hook"), 46 | (symbol: "straight"), 47 | (symbol: "barbed"), 48 | (symbol: "+"), 49 | (symbol: "x"), 50 | (symbol: ">"), 51 | )) 52 | 53 | #test-case({ 54 | import draw: * 55 | 56 | anchor("a", (-1,0)) 57 | anchor("b", (+1,0)) 58 | 59 | line("a", "b") 60 | mark("a", "b", symbol: "<", fill: black, anchor: "center") 61 | mark("b", "a", symbol: "<", fill: black, anchor: "center") 62 | 63 | anchor("c", (-1,-1)) 64 | anchor("d", (+1,-1)) 65 | 66 | line("c", "d") 67 | mark("c", "d", symbol: ">", fill: black, anchor: "center") 68 | mark("d", "c", symbol: ">", fill: black, anchor: "center") 69 | }) 70 | -------------------------------------------------------------------------------- /tests/mark-auto-offset/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-auto-offset/ref/1.png -------------------------------------------------------------------------------- /tests/mark-auto-offset/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #for w in range(1, 10) { 5 | let w = w / 10 6 | let l = 1 7 | box(stroke: 2pt + red, canvas({ 8 | import draw: * 9 | 10 | for x in range(0, 8) { 11 | let width = (x + 1) * 1pt 12 | let x = x * 2 13 | 14 | set-style(stroke: width, mark: (width: w, length: 1, scale: .4, stroke: width)) 15 | line((x,0), (x,3), mark: (end: ">", start: ">")) 16 | 17 | line((x - .5,3), (x + .5,3), stroke: .5pt + green) 18 | line((x - .5,0), (x + .5,0), stroke: .5pt + green) 19 | } 20 | for x in range(0, 8) { 21 | let width = (x + 1) * 1pt 22 | let x = x * 2 + 2 * 8 23 | 24 | set-style(stroke: width, mark: (width: w, length: 1, scale: .4, stroke: width + blue)) 25 | line((x,0), (x,3), mark: (end: "<", start: "<")) 26 | 27 | line((x - .5,3), (x + .5,3), stroke: .5pt + green) 28 | line((x - .5,0), (x + .5,0), stroke: .5pt + green) 29 | } 30 | })) 31 | par([]) 32 | } 33 | 34 | #box(stroke: 2pt + red, canvas({ 35 | import draw: * 36 | rect((1,-1), (2,2)) 37 | rect((-2,-1), (-1,2)) 38 | bezier((-1,-.5), (1,1), (0,-.5), (0,1), 39 | mark: (start: ">", end: ">", fill: blue, stroke: blue, flex: false)) 40 | })) 41 | 42 | #box(stroke: 2pt + red, canvas({ 43 | import draw: * 44 | rect((1,-1), (2,2)) 45 | rect((-2,-1), (-1,2)) 46 | bezier((-1,-.5), (1,1), (0,-.5), (0,1), 47 | mark: (start: ">", end: ">", fill: blue, stroke: blue, flex: true)) 48 | })) 49 | 50 | #box(stroke: 2pt + red, canvas({ 51 | import draw: * 52 | rect((1,-1), (2,2)) 53 | rect((-2,-1), (-1,2)) 54 | bezier((-1,-.5), (1,1), (0,-.5), (0,1), 55 | mark: (start: "|", end: "o", fill: blue, stroke: blue)) 56 | })) 57 | 58 | #box(stroke: 2pt + red, canvas({ 59 | import draw: * 60 | rect((1,-1), (2,2)) 61 | rect((-2,-1), (-1,2)) 62 | catmull((-1,-.5), (0,-.5), (0,1), (1,1), 63 | mark: (start: ">", end: ">", fill: blue, stroke: blue)) 64 | })) 65 | 66 | #box(stroke: 2pt + red, canvas({ 67 | import draw: * 68 | rect((1,-1), (2,2)) 69 | rect((-2,-1), (-1,2)) 70 | hobby((-1,-.5), (0,-.5), (0,1), (1,1), 71 | mark: (start: ">", end: ">", fill: blue, stroke: blue)) 72 | })) 73 | -------------------------------------------------------------------------------- /tests/mark-position/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-position/ref/1.png -------------------------------------------------------------------------------- /tests/mark-position/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | // No Position 6 | #test-case({ 7 | import draw: * 8 | set-style(mark: (fill: white)) 9 | line((-1,0), (1,0), mark: (start: ">", end: ">")) 10 | }) 11 | 12 | // Absolute Position 13 | #test-case({ 14 | import draw: * 15 | set-style(mark: (fill: white)) 16 | line((-1,1), (1,1), mark: (start: ">", end: ">", pos: .25, shorten-to: none)) 17 | }) 18 | 19 | 20 | // Relative Offset 21 | #test-case({ 22 | import draw: * 23 | set-style(mark: (fill: white)) 24 | line((-1,0), (1,0), mark: (start: ">", end: ">", pos: 25%, shorten-to: none)) 25 | line((-1,1), (1,1), mark: (end: ">", pos: 50%, shorten-to: none)) 26 | line((-1,2), (1,2), mark: (start: ">", pos: 50%, shorten-to: none)) 27 | line((-1,3), (1,3), mark: (start: (">", (symbol: "|", pos: 50%, anchor: "center")), end: ">", shorten-to: 0)) 28 | }) 29 | -------------------------------------------------------------------------------- /tests/mark-shape-transform/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-shape-transform/ref/1.png -------------------------------------------------------------------------------- /tests/mark-shape-transform/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import cetz.draw: * 7 | 8 | set-style(mark: (transform-shape: true)) 9 | 10 | scale(x: 3) 11 | line((-1,-1), (1,1), mark: (start: "rect", end: "]")) 12 | }) 13 | 14 | #test-case({ 15 | import cetz.draw: * 16 | 17 | set-style(mark: (transform-shape: false)) 18 | 19 | scale(x: 3) 20 | line((-1,-1), (1,1), mark: (start: "rect", end: ">")) 21 | }) 22 | 23 | #test-case({ 24 | import cetz.draw: * 25 | 26 | set-style(mark: (transform-shape: false)) 27 | 28 | rotate(45deg) 29 | line((-1,-1), (1,-1), (1,1), mark: (start: "rect", end: "rect", scale: 3)) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/mark-single/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-single/ref.png -------------------------------------------------------------------------------- /tests/mark-single/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-single/ref/1.png -------------------------------------------------------------------------------- /tests/mark-single/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | // No Position 6 | #test-case({ 7 | import draw: * 8 | 9 | // Left 10 | mark((0,0), (-1,0), symbol: ">", scale: 3, fill: blue) 11 | 12 | // Up 13 | mark((0,0), (0,1), symbol: ">", scale: 3, fill: green) 14 | 15 | // Down 16 | mark((0,0), (0,-1), symbol: ">", scale: 3, fill: red) 17 | 18 | // Right 19 | mark((0,0), (1,0), symbol: ">", scale: 3, fill: yellow) 20 | }) 21 | 22 | // Angle 23 | #test-case({ 24 | import draw: * 25 | 26 | mark((0,0), 0deg, symbol: ">", scale: 3, fill: blue) 27 | mark((0,0), 90deg, symbol: ">", scale: 3, fill: green) 28 | mark((0,0), 180deg, symbol: ">", scale: 3, fill: red) 29 | mark((0,0), 270deg, symbol: ">", scale: 3, fill: yellow) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/mark-z-axis/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/mark-z-axis/ref/1.png -------------------------------------------------------------------------------- /tests/mark-z-axis/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | line((-1,0), (1,0), mark: (start: ">", end: ">")) 8 | line((0,-1), (0,1), mark: (start: ">", end: ">")) 9 | line((0,0,-1), (0,0,1), mark: (start: ">", end: ">", 10 | scale: 1)) 11 | })) 12 | -------------------------------------------------------------------------------- /tests/matrix/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/matrix/ref/1.png -------------------------------------------------------------------------------- /tests/matrix/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": matrix 3 | 4 | #{ 5 | let m = ( 6 | (3, 2, 1), 7 | (1, 0, 2), 8 | ) 9 | let v = ( 10 | 1, 0, 4 11 | ) 12 | let r = ( 13 | 7, 9 14 | ) 15 | 16 | assert(matrix.mul-vec(m, v) == r) 17 | } 18 | -------------------------------------------------------------------------------- /tests/merge/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/merge/ref.png -------------------------------------------------------------------------------- /tests/merge/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/merge/ref/1.png -------------------------------------------------------------------------------- /tests/merge/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | 8 | // Merge lines 9 | fill(red) 10 | merge-path({ 11 | line((0,0), (1,2)) 12 | line((), (2,0)) 13 | line((), (0,0)) 14 | }, close: true) 15 | 16 | translate((0, -1)) 17 | 18 | // Merge bezier paths 19 | fill(blue) 20 | merge-path({ 21 | bezier((0,0), (2,0), (1, 1)) 22 | bezier((2, -1), (0, -1), (.5, -2), (1.5, 0)) 23 | }, close: true) 24 | 25 | translate((0, -2)) 26 | 27 | // Merge different paths 28 | fill(green) 29 | merge-path({ 30 | line((0,0), (1,0), (2,-1)) 31 | arc((), start: 0deg, stop: -130deg, name: "arc") 32 | bezier("arc.arc-end", (0,0), (0, -1), (2, -2)) 33 | }) 34 | }) 35 | 36 | #test-case({ 37 | import draw: * 38 | 39 | rotate(45deg) 40 | merge-path({ 41 | line((0,0), (1,0), (2,-1)) 42 | arc((), start: 0deg, stop: -130deg, name: "arc") 43 | bezier("arc.arc-end", (0,0), (0, -1), (2, -2)) 44 | }, name: "p", fill: yellow) 45 | 46 | for i in range(0, 110, step: 10) { 47 | circle((name: "p", anchor: 1% * i), radius: .1, fill: white) 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /tests/multiple-marks/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/multiple-marks/ref/1.png -------------------------------------------------------------------------------- /tests/multiple-marks/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let l = ("|", "o", ">") 6 | 7 | #test-case({ 8 | import draw: * 9 | 10 | line((-1, -1), (1, 1), mark: (start: l, end: l)) 11 | }) 12 | 13 | #test-case({ 14 | import draw: * 15 | 16 | bezier((-1, -1), (1, 1), (-1, 1), (1, -1), mark: (start: l, end: l)) 17 | }) 18 | 19 | #test-case({ 20 | import draw: * 21 | 22 | arc((0, 0), start: 0deg, stop: 180deg, anchor: "center", mark: (start: l, end: l)) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/padding/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/padding/ref/1.png -------------------------------------------------------------------------------- /tests/padding/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case(padding: 1, background: gray, { 6 | import draw: * 7 | circle(()) 8 | }) 9 | 10 | #test-case(padding: (top: 1, left: 2), background: gray, { 11 | import draw: * 12 | scale(x: -1, y: -.5) 13 | circle(()) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/palette/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/palette/ref/1.png -------------------------------------------------------------------------------- /tests/palette/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas(length: 1cm, { 5 | import draw: * 6 | 7 | let p = palette.pink 8 | for i in range(0, p("len")) { 9 | set-style(..p(i)) 10 | rect((0,0), (1,1)) 11 | set-origin((1,0)) 12 | } 13 | })) 14 | 15 | #box(stroke: 2pt + red, canvas(length: 1cm, { 16 | import draw: * 17 | 18 | let p = palette.new( 19 | base: (stroke: (paint: none, dash: "dashed")), 20 | colors: (red, green, blue), 21 | dash: ("solid", "dashed", "dotted")) 22 | for i in range(0, p("len")) { 23 | set-style(..p(i, stroke: true, fill: false)) 24 | circle((.5,.5), radius: .5) 25 | set-origin((1,0)) 26 | } 27 | })) 28 | -------------------------------------------------------------------------------- /tests/path-anchors/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/path-anchors/ref.png -------------------------------------------------------------------------------- /tests/path-anchors/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/path-anchors/ref/1.png -------------------------------------------------------------------------------- /tests/path-anchors/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let display(body, ..args, angle: false) = { 5 | import draw: * 6 | 7 | // Fixed distance 8 | (body)(..args, name: "elem"); 9 | for i in (0, .5, 1) { 10 | circle((name: "elem", anchor: i), radius: .1) 11 | } 12 | 13 | set-origin((3,0)) 14 | 15 | (body)(..args, name: "elem"); 16 | for i in (0%, 25%, 50%, 75%, 100%) { 17 | circle((name: "elem", anchor: i), radius: .1) 18 | } 19 | 20 | if angle { 21 | set-origin((3,0)) 22 | 23 | (body)(..args, name: "elem"); 24 | let angles = if args.named().at("mode", default: "") == "OPEN" { 25 | (170deg, 180deg) 26 | } else { 27 | (0deg, 45deg, 90deg, 170deg, 180deg) 28 | } 29 | for i in angles { 30 | circle((name: "elem", anchor: i), radius: .1) 31 | } 32 | } 33 | } 34 | 35 | #box(stroke: 2pt + red, canvas(length: 1cm, { 36 | import draw: * 37 | display(line, (0,0), (.75,1), (1.25,-1), (2,0)) 38 | })) 39 | 40 | #box(stroke: 2pt + red, canvas(length: 1cm, { 41 | import draw: * 42 | display(circle, (0,0), angle: true) 43 | })) 44 | 45 | #box(stroke: 2pt + red, canvas(length: 1cm, { 46 | import draw: * 47 | display(circle-through, (-1,0), (0,1), (1,0), angle: true) 48 | })) 49 | 50 | #box(stroke: 2pt + red, canvas(length: 1cm, { 51 | import draw: * 52 | display(arc, (0,0), start: 225deg, stop: 135deg, radius: 2, mode: "OPEN", angle: true) 53 | })) 54 | 55 | #box(stroke: 2pt + red, canvas(length: 1cm, { 56 | import draw: * 57 | display(arc, (0,0), start: 225deg, stop: 135deg, radius: 2, mode: "PIE", angle: true) 58 | })) 59 | 60 | #box(stroke: 2pt + red, canvas(length: 1cm, { 61 | import draw: * 62 | display(arc, (0,0), start: 225deg, stop: 135deg, radius: 2, mode: "CLOSE", angle: true) 63 | })) 64 | 65 | #box(stroke: 2pt + red, canvas(length: 1cm, { 66 | import draw: * 67 | display(line, (-1,0), (0,1), (1,0)) 68 | })) 69 | 70 | /* 71 | #box(stroke: 2pt + red, canvas(length: 1cm, { 72 | import draw: * 73 | display(content, (), text(2cm)[Text]) 74 | })) 75 | */ 76 | 77 | #box(stroke: 2pt + red, canvas(length: 1cm, { 78 | import draw: * 79 | display(rect, (-1,-1), (1,1), angle: true) 80 | })) 81 | 82 | #box(stroke: 2pt + red, canvas(length: 1cm, { 83 | import draw: * 84 | display(rect, (-1,-1), (1,1), radius: .5) 85 | })) 86 | 87 | #box(stroke: 2pt + red, canvas(length: 1cm, { 88 | import draw: * 89 | display(bezier, (-2,0), (2,0), (-1,1), (1,-1)) 90 | })) 91 | 92 | #box(stroke: 2pt + red, canvas(length: 1cm, { 93 | import draw: * 94 | display(bezier, (-1,-1), (1,-1), (0,2)) 95 | })) 96 | 97 | #box(stroke: 2pt + red, canvas(length: 1cm, { 98 | import draw: * 99 | display(bezier-through, (-1,-1), (0,1), (1,-1)) 100 | })) 101 | 102 | #box(stroke: 2pt + red, canvas(length: 1cm, { 103 | import draw: * 104 | display(catmull, (-2,0), (-1,-1), (0,1), (1,-1), (2,0)) 105 | })) 106 | 107 | #box(stroke: 2pt + red, canvas(length: 1cm, { 108 | import draw: * 109 | display(group, { 110 | circle((0,0), radius: .5) 111 | circle((1,1), radius: .7) 112 | }, angle: true) 113 | })) 114 | 115 | #box(stroke: 2pt + red, canvas(length: 1cm, { 116 | import draw: * 117 | 118 | rotate(10deg) 119 | rect((-1,-1), (1,1), name: "a") 120 | for i in (0, 1, 2, 3, 4, 5, 6, 7) { 121 | circle((name: "a", anchor: i), radius: .1) 122 | } 123 | 124 | set-origin((3,0)) 125 | 126 | rect((-1,-1), (1,1), name: "a") 127 | for i in (0%, 10%, 20%, 30%, 40%, 50%) { 128 | circle((name: "a", anchor: i), radius: .1) 129 | } 130 | 131 | set-origin((3, 0)) 132 | 133 | rect((-1,-1), (1,1), name: "a") 134 | for i in range(0, 360, step: 36) { 135 | let i = i * 1deg 136 | circle((name: "a", anchor: i), radius: .1) 137 | } 138 | })) 139 | -------------------------------------------------------------------------------- /tests/path-decoration/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/path-decoration/ref/1.png -------------------------------------------------------------------------------- /tests/path-decoration/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #import decorations: zigzag, coil, wave, square 6 | 7 | #let all-fns = (zigzag, coil, wave, square) 8 | 9 | #test-case(fn => { 10 | import draw: * 11 | 12 | fn(line((0,0), (4,0))) 13 | fn(line((0,1), (4,1)), amplitude: .5) 14 | fn(line((0,2), (4,2)), amplitude: t => { 1 - .5 * t / 50% }) 15 | fn(line((0,3), (4,3)), amplitude: (0, .5, 1)) 16 | }, args: all-fns) 17 | 18 | #test-case(fn => { 19 | import draw: * 20 | fn(hobby((0,0), (4,0), (6,2))) 21 | }, args: all-fns) 22 | 23 | #box(stroke: 2pt + red, canvas(length: 1cm, { 24 | import draw: * 25 | 26 | set-style(radius: .9) 27 | coil(circle((0,0)), amplitude: .2, segments: 30, factor: 100%) 28 | coil(circle((0,2)), amplitude: .2, segments: 30, factor: 120%, stroke: blue) 29 | coil(circle((0,4)), amplitude: .2, segments: 30, factor: 150%, stroke: red) 30 | })) 31 | 32 | #box(stroke: 2pt + red, canvas(length: 1cm, { 33 | import draw: * 34 | 35 | set-style(radius: .9) 36 | wave(circle((0,0)), amplitude: .2, segments: 20, tension: .3) 37 | wave(circle((0,2)), amplitude: .2, segments: 20, tension: .5, stroke: blue) 38 | wave(circle((0,4)), amplitude: .2, segments: 20, tension: 1, stroke: red) 39 | })) 40 | 41 | #box(stroke: 2pt + red, canvas(length: 1cm, { 42 | import draw: * 43 | 44 | set-style(radius: .9) 45 | square(circle((0,2)), amplitude: .2, segments: 20) 46 | })) 47 | 48 | #test-case(fn => { 49 | import draw: * 50 | 51 | fn(line((0,0), (3,0)), start: 10%, stop: 90%, amplitude: .5) 52 | fn(line((0,1), (3,1)), start: 1, stop: 2, amplitude: .5) 53 | }, args: all-fns) 54 | 55 | #test-case(fn => { 56 | import draw: * 57 | 58 | fn(line((0,0,-1), (0,0,1)), start: 10%, stop: 90%) 59 | }, args: all-fns) 60 | 61 | #test-case(factor => { 62 | import draw: * 63 | square(line((0,0), (3,0)), factor: factor) 64 | }, args: (25%, 50%, 75%)) 65 | 66 | #test-case({ 67 | import draw: * 68 | 69 | // Keep the fixed amplitude 70 | for i in range(0, 6) { 71 | wave(line((0,i), (3,i)), start: 10%, stop: 1 + i / 5, 72 | segment-length: .22, amplitude: .8) 73 | } 74 | }) 75 | 76 | #test-case(fn => { 77 | import draw: * 78 | 79 | // Amplitudes of type length 80 | fn(line((0,0), (4,0)), amplitude: 0.25) 81 | fn(line((0,1), (4,1)), amplitude: 2.5mm) 82 | fn(line((0,2), (4,2)), amplitude: t => 1em*calc.sin(float(t)*calc.pi)) 83 | fn(line((0,3), (4,3)), amplitude: (5mm, 0, 2mm, 0)) 84 | }, args: all-fns) 85 | 86 | // Bug #736: Waves with a single segment 87 | // have are sharp on the second peak. 88 | #test-case({ 89 | import draw: * 90 | 91 | wave(line((0,0), (4,0)), segments: 1) 92 | }) 93 | -------------------------------------------------------------------------------- /tests/polygon/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/polygon/ref/1.png -------------------------------------------------------------------------------- /tests/polygon/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import cetz.draw: * 7 | 8 | polygon((0, 0), 5, radius: 1, name: "poly") 9 | 10 | for-each-anchor("poly", name => { 11 | if name.starts-with(regex("(corner|edge)")) { 12 | circle((), fill: gray, radius: .1) 13 | } 14 | }) 15 | }) 16 | 17 | #test-case(sides => { 18 | import cetz.draw: * 19 | 20 | polygon((0, 0), sides, radius: 1, angle: 90deg) 21 | }, args: (3, 4, 5, 6)) 22 | 23 | #test-case({ 24 | import cetz.draw: * 25 | 26 | set-style(polygon: (radius: 1, fill: blue, stroke: red + 4pt)) 27 | polygon((0, 0), 6) 28 | }) 29 | 30 | #test-case({ 31 | import cetz.draw: * 32 | 33 | polygon((0, 0), 6, radius: 1, 34 | fill: red, stroke: blue + 4pt) 35 | }) 36 | 37 | #test-case({ 38 | import cetz.draw: * 39 | 40 | polygon((0, 0), 5, name: "p1") 41 | polygon((2, 2), 3, name: "p2") 42 | line("p1", "p2") 43 | }) 44 | 45 | #test-case({ 46 | import cetz.draw: * 47 | 48 | polygon((0, 0), 3, radius: 3cm) 49 | }) 50 | -------------------------------------------------------------------------------- /tests/primitives/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/primitives/ref/1.png -------------------------------------------------------------------------------- /tests/primitives/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | line((0, 0), (1, 0)) 8 | rect((0, 1), (1, 2)) 9 | circle((.5, 3.5), radius: .5) 10 | arc((1, 4.5), start: 0deg, stop: 90deg, radius: .5) 11 | bezier((0, 6), (1, 6), (.5, 5)) 12 | bezier((0, 7), (1, 7), (.25, 6), (.75, 8)) 13 | })) 14 | -------------------------------------------------------------------------------- /tests/projection-default/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/projection-default/ref/1.png -------------------------------------------------------------------------------- /tests/projection-default/test.typ: -------------------------------------------------------------------------------- 1 | #import "/src/lib.typ" as cetz 2 | #import "/tests/helper.typ": * 3 | #set page(width: auto, height: auto) 4 | 5 | // Positive direction 6 | #test-case({ 7 | import cetz.draw: * 8 | 9 | set-style(mark: (transform-shape: false)) 10 | line((0,0,0), (1,0,0), mark: (end: ">")) 11 | line((0,0,0), (0,1,0), mark: (end: ">")) 12 | line((0,0,0), (0,0,1), mark: (end: ">")) 13 | }) 14 | 15 | // Negative direction 16 | #test-case({ 17 | import cetz.draw: * 18 | 19 | set-style(mark: (transform-shape: false)) 20 | line((0,0,0), (-1,0,0), mark: (end: ">")) 21 | line((0,0,0), (0,-1,0), mark: (end: ">")) 22 | line((0,0,0), (0,0,-1), mark: (end: ">")) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/projection-ortho/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/projection-ortho/ref/1.png -------------------------------------------------------------------------------- /tests/projection-ortho/test.typ: -------------------------------------------------------------------------------- 1 | #import "/src/lib.typ": * 2 | #import "/tests/helper.typ": * 3 | #set page(width: auto, height: auto) 4 | 5 | #let axes(l) = { 6 | import draw: * 7 | 8 | set-style(mark: (end: ">")) 9 | 10 | on-layer(-1, { 11 | line((-l,0), (l,0), stroke: red, name: "x") 12 | content((rel: ((name: "x", anchor: 50%), .5, "x.end"), to: "x.end"), text(red, $x$)) 13 | 14 | line((0,-l), (0,l), stroke: blue, name: "y") 15 | content((rel: ((name: "y", anchor: 50%), .5, "y.end"), to: "y.end"), text(blue, $y$)) 16 | 17 | line((0,0,-l), (0,0,l), stroke: green, name: "z", mark: (z-up: (1,0,0))) 18 | content((rel: ((name: "z", anchor: 50%), .5, "z.end"), to: "z.end"), text(green, $z$)) 19 | }) 20 | } 21 | 22 | #let checkerboard() = { 23 | import draw: * 24 | for x in range(0, 3) { 25 | for y in range(0, 3) { 26 | rect((x,y),(rel: (1,1)), 27 | fill: if calc.rem(x + y, 2) != 0 { black } else { white }) 28 | } 29 | } 30 | } 31 | 32 | #test-case({ 33 | import draw: * 34 | ortho(reset-transform: false, { 35 | line((-1, 0), (1, 0), mark: (end: ">")) 36 | }) 37 | }) 38 | 39 | #test-case({ 40 | import draw: * 41 | ortho({ 42 | axes(4) 43 | checkerboard() // Same as on-xy 44 | }) 45 | }) 46 | 47 | #test-case({ 48 | import draw: * 49 | ortho({ 50 | axes(4) 51 | on-xy({ 52 | checkerboard() 53 | }) 54 | }) 55 | }) 56 | 57 | #test-case({ 58 | import draw: * 59 | ortho({ 60 | axes(4) 61 | on-xz({ 62 | checkerboard() 63 | }) 64 | }) 65 | }) 66 | 67 | #test-case({ 68 | import draw: * 69 | ortho({ 70 | axes(4) 71 | on-yz({ 72 | checkerboard() 73 | }) 74 | }) 75 | }) 76 | 77 | #test-case({ 78 | import draw: * 79 | ortho(sorted: true, { 80 | axes(4) 81 | on-yz(x: -1, { 82 | checkerboard() 83 | }) 84 | on-xy(z: -1, { 85 | checkerboard() 86 | }) 87 | on-xz(y: -1, { 88 | checkerboard() 89 | }) 90 | }) 91 | }) 92 | 93 | // Ordering 94 | #test-case({ 95 | import draw: * 96 | ortho(sorted: true, { 97 | scope({ translate((0, 0, +1)); rect((-1, -1), (1, 1), fill: blue) }) 98 | scope({ translate((0, 0, 0)); rect((-1, -1), (1, 1), fill: red) }) 99 | scope({ translate((0, 0, -1)); rect((-1, -1), (1, 1), fill: green) }) 100 | }) 101 | }) 102 | 103 | // Fully visible 104 | #test-case({ 105 | import draw: * 106 | ortho(x: 0deg, y: 0deg, cull-face: "cw", { 107 | rect((-1, -1), (1, 1)) 108 | circle((0,0)) 109 | }) 110 | }) 111 | 112 | // Nothing visible 113 | #test-case({ 114 | import draw: * 115 | ortho(x: 0deg, y: 0deg, cull-face: "cw", { 116 | line((-1, -1), (1, -1), (1, 1), (-1, 1), close: true) 117 | rotate(y: 120deg) 118 | line((-1,-1), (1,-1), (0,1), close: true) 119 | }) 120 | }) 121 | 122 | // Face order of library shapes 123 | #test-case({ 124 | import draw: * 125 | ortho(cull-face: "cw", { 126 | rect((-1, -1), (1, 1), radius: .5) 127 | }) 128 | }) 129 | 130 | #test-case({ 131 | import draw: * 132 | ortho(cull-face: "cw", { 133 | circle((0,0)) 134 | }) 135 | }) 136 | 137 | #test-case({ 138 | import draw: * 139 | ortho(cull-face: "cw", { 140 | arc((0,0), start: 0deg, stop: 270deg, mode: "PIE") 141 | }) 142 | }) 143 | 144 | #test-case({ 145 | import draw: * 146 | ortho(cull-face: "cw", { 147 | content((0,0), [Text]) 148 | }) 149 | }) 150 | -------------------------------------------------------------------------------- /tests/rect-rounded/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/rect-rounded/ref/1.png -------------------------------------------------------------------------------- /tests/rect-rounded/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #let test(..args) = { 5 | import draw: * 6 | rect(..args, name: "r") 7 | for-each-anchor("r", n => { 8 | circle("r." + n, radius: .05, fill: gray) 9 | }) 10 | } 11 | 12 | #box(stroke: 2pt + red, canvas({ 13 | import draw: * 14 | 15 | test((-1,-1), (1,1)) 16 | })) 17 | 18 | #box(stroke: 2pt + red, canvas({ 19 | import draw: * 20 | 21 | test((-1,-1), (1,1), radius: 0) 22 | set-origin((2.5, 0)) 23 | test((-1,-1), (1,1), radius: .5) 24 | set-origin((2.5, 0)) 25 | test((-1,-1), (1,1), radius: 1) 26 | })) 27 | 28 | #box(stroke: 2pt + red, canvas({ 29 | import draw: * 30 | 31 | test((-1,-1), (1,1), radius: (north: 1)) 32 | set-origin((2.5, 0)) 33 | test((-1,-1), (1,1), radius: (east: 1)) 34 | set-origin((2.5, 0)) 35 | test((-1,-1), (1,1), radius: (south: 1)) 36 | set-origin((2.5, 0)) 37 | test((-1,-1), (1,1), radius: (west: 1)) 38 | })) 39 | 40 | #box(stroke: 2pt + red, canvas({ 41 | import draw: * 42 | 43 | test((-1,-1), (1,1), radius: (north-west: 1)) 44 | set-origin((2.5, 0)) 45 | test((-1,-1), (1,1), radius: (north-east: 1)) 46 | set-origin((2.5, 0)) 47 | test((-1,-1), (1,1), radius: (south-east: 1)) 48 | set-origin((2.5, 0)) 49 | test((-1,-1), (1,1), radius: (south-west: 1)) 50 | })) 51 | 52 | #box(stroke: 2pt + red, canvas({ 53 | import draw: * 54 | 55 | test((-1,-1), (1,1), radius: (north-west: 1, north-east: .5, 56 | south-west: .25, rest: 0.1)) 57 | })) 58 | 59 | // Use ratio values 60 | #box(stroke: 2pt + red, canvas({ 61 | import draw: * 62 | 63 | test((-1,-1), (3,1), radius: (north-west: 50%, north-east: 25%, 64 | south-west: 10%, rest: 0)) 65 | })) 66 | 67 | // Use different x & y radii 68 | #box(stroke: 2pt + red, canvas({ 69 | import draw: * 70 | 71 | test((-1,-1), (3,1), radius: (north-west: (50%, .2), south-east: (50%, .2))) 72 | })) 73 | 74 | // Use fixed length values 75 | #box(stroke: 2pt + red, canvas({ 76 | import draw: * 77 | 78 | test((-1,-1), (3,1), radius: .5cm) 79 | })) 80 | -------------------------------------------------------------------------------- /tests/rect/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/rect/ref/1.png -------------------------------------------------------------------------------- /tests/rect/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | rect((1,1), (0,0), name: "r") 8 | circle("r.center", radius: .1) 9 | circle("r.north", fill: red, radius: .1) 10 | circle("r.south", fill: green, radius: .1) 11 | circle("r.west", fill: blue, radius: .1) 12 | circle("r.east", fill: yellow, radius: .1) 13 | })) 14 | -------------------------------------------------------------------------------- /tests/relative-length/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/relative-length/ref/1.png -------------------------------------------------------------------------------- /tests/relative-length/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #set text(10pt) 5 | #box(stroke: 2pt + red, canvas(length: 1em, { 6 | import draw: * 7 | 8 | content((0,0), [M]) 9 | content((1,0), [M]) 10 | content((0,1), [M]) 11 | })) 12 | 13 | #set text(20pt) 14 | #box(stroke: 2pt + red, canvas(length: 1em, { 15 | import draw: * 16 | 17 | content((0,0), [M]) 18 | content((1,0), [M]) 19 | content((0,1), [M]) 20 | })) 21 | -------------------------------------------------------------------------------- /tests/relative-no-update/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/relative-no-update/ref/1.png -------------------------------------------------------------------------------- /tests/relative-no-update/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | circle((0, 0), stroke: blue) 7 | circle((rel: (0, -1), update: false), stroke: red) 8 | circle((rel: (0, -2)), stroke: green) 9 | })) 10 | -------------------------------------------------------------------------------- /tests/right-angle/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/right-angle/ref/1.png -------------------------------------------------------------------------------- /tests/right-angle/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let angles = (0, 90, 180, 270, 45, 135).map(v => v * 1deg) 6 | 7 | #test-case(a => { 8 | import draw: * 9 | import angle: right-angle 10 | 11 | let (o, a, b) = ( 12 | (0,0), 13 | ((0, 0), 100%, a, (1, 0)), 14 | ((0, 0), 100%, a + 90deg, (1, 0)), 15 | ) 16 | 17 | line(a, o, b) 18 | right-angle(o, a, b) 19 | }, args: angles) 20 | 21 | #test-case(a => { 22 | import draw: * 23 | import angle: right-angle 24 | 25 | let (o, a, b) = ( 26 | (0,0), 27 | ((0, 0), 100%, a, (1, 0)), 28 | ((0, 0), 100%, a + 45deg, (1, 0)), 29 | ) 30 | 31 | line(a, o, b) 32 | right-angle(o, a, b) 33 | }, args: angles) 34 | 35 | #test-case(a => { 36 | import draw: * 37 | import angle: right-angle 38 | 39 | let (o, a, b) = ( 40 | (0,0), 41 | ((0, 0), 100%, a, (1, 0)), 42 | ((0, 0), 100%, a + 135deg, (1, 0)), 43 | ) 44 | 45 | line(a, o, b) 46 | right-angle(o, a, b) 47 | }, args: angles) 48 | 49 | #test-case({ 50 | import draw: * 51 | import angle: right-angle 52 | 53 | let (o, a, b) = ((0,0), (0,1), (1,0)) 54 | line(a, o, b) 55 | right-angle(o, a, b, name: "angle") 56 | for-each-anchor("angle", n => { 57 | if n in ("a", "b", "origin", "corner", "label") { 58 | circle("angle." + n, stroke: blue, radius: .1) 59 | } 60 | }) 61 | }) 62 | 63 | // Bug #571 64 | #test-case({ 65 | import draw: * 66 | import angle: right-angle 67 | 68 | let (o, a, b) = ((0,3), (0,4), (1,3)) 69 | line(a, o, b) 70 | right-angle(o, a, b, name: "angle") 71 | }) 72 | -------------------------------------------------------------------------------- /tests/ring/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/ring/ref/1.png -------------------------------------------------------------------------------- /tests/ring/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | let ring(start, end, radius) = merge-path({ 8 | arc((0, 0), start: start, stop: end, radius: radius, 9 | anchor: "origin", name: "outer") 10 | 11 | arc("outer.origin", start: end, delta: -(end - start), radius: radius - .2, 12 | anchor: "origin", name: "inner") 13 | 14 | // line("outer.end", "inner.start") 15 | }, close: true) 16 | 17 | stroke(black) 18 | fill(blue) 19 | for i in range(0, 6) { 20 | ring((i+1) * 40deg, (i+1) * 40deg + 120deg, 2 - i * .3) 21 | } 22 | })) 23 | -------------------------------------------------------------------------------- /tests/root-anchor/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/root-anchor/ref/1.png -------------------------------------------------------------------------------- /tests/root-anchor/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import draw: * 7 | 8 | anchor("test", (1, 1)) 9 | circle((1,1)) 10 | 11 | translate((3,1)) 12 | circle("test", radius: .5, stroke: green) 13 | circle((1,1), radius: .5, stroke: red) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/rotate-around/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/rotate-around/ref/1.png -------------------------------------------------------------------------------- /tests/rotate-around/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let draw-shapes() = { 6 | import draw: * 7 | 8 | rect((-2,-2), (-1,-1)) 9 | circle((-1.5,1.5), radius: .5) 10 | line((1,1), (1.5,2), (2,1), close: true) 11 | } 12 | 13 | #test-case({ 14 | import draw: * 15 | grid((-3,-3), (3,3)) 16 | 17 | set-style(fill: gray) 18 | draw-shapes() 19 | 20 | rotate(45deg, origin: (0,0)) 21 | set-style(fill: blue) 22 | draw-shapes() 23 | }) 24 | 25 | #test-case({ 26 | import draw: * 27 | grid((-3,-3), (3,5)) 28 | 29 | set-style(fill: gray) 30 | draw-shapes() 31 | 32 | rotate(45deg, origin: (-1.5,1.5)) 33 | set-style(fill: blue) 34 | draw-shapes() 35 | }) 36 | 37 | #test-case({ 38 | import draw: * 39 | grid((-5,-5), (1,1)) 40 | 41 | scale(2, origin: (-2,-2)) 42 | set-style(fill: blue) 43 | rect((-3,-3), (rel: (1,1))) 44 | rect((-2,-3), (rel: (1,1))) 45 | rect((-2,-2), (rel: (1,1))) 46 | rect((-3,-2), (rel: (1,1))) 47 | }) 48 | 49 | #test-case({ 50 | import draw: * 51 | grid((-4,-4), (2,2)) 52 | 53 | set-style(fill: gray) 54 | rect((-3,-3), (rel: (2,2))) 55 | 56 | rotate(45deg, origin: (-2,-2)) 57 | scale(.5, origin: (-2,-2)) 58 | set-style(fill: blue) 59 | rect((-3,-3), (rel: (2,2))) 60 | }) 61 | 62 | #test-case({ 63 | import draw: * 64 | grid((-4,-4), (2,2)) 65 | 66 | set-transform(none) 67 | scale(y: -1) 68 | 69 | set-style(fill: gray) 70 | rect((-3,-3), (rel: (2,2))) 71 | 72 | rotate(x: 60deg, y: 45deg, origin: (-2,-2)) 73 | set-style(fill: blue) 74 | rect((-3,-3), (rel: (2,2))) 75 | }) 76 | -------------------------------------------------------------------------------- /tests/rotation/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/rotation/ref/1.png -------------------------------------------------------------------------------- /tests/rotation/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | group(name: "g", { 8 | translate((-.5, .5, 0)) 9 | 10 | // CCW 11 | rotate(30deg) 12 | 13 | rect((0, 0), (1, 1), name: "r") 14 | anchor("1", "r.north-west") 15 | anchor("2", "r.north-east") 16 | anchor("3", "r.south-west") 17 | anchor("4", "r.south-east") 18 | }) 19 | 20 | stroke(green) 21 | circle("g.1", radius: .1) 22 | circle("g.2", radius: .1) 23 | circle("g.3", radius: .1) 24 | circle("g.4", radius: .1) 25 | })) 26 | 27 | #let draw-xyz() = { 28 | import draw: * 29 | line((-1,0), (1,0), stroke: red) 30 | line((0,-1), (0,1), stroke: blue) 31 | line((0,0,-1), (0,0,1), stroke: green) 32 | } 33 | 34 | #box(stroke: 2pt + red, canvas({ 35 | import draw: * 36 | 37 | set-transform(none) 38 | rotate(z: 45deg) 39 | draw-xyz() 40 | })) 41 | #box(stroke: 2pt + red, canvas({ 42 | import draw: * 43 | 44 | set-transform(none) 45 | rotate(x: 45deg) 46 | draw-xyz() 47 | })) 48 | #box(stroke: 2pt + red, canvas({ 49 | import draw: * 50 | 51 | set-transform(none) 52 | rotate(y: 45deg) 53 | draw-xyz() 54 | })) 55 | 56 | #box(stroke: 2pt + red, canvas({ 57 | import draw: * 58 | 59 | set-transform(none) 60 | rotate(yaw: 45deg) 61 | draw-xyz() 62 | })) 63 | #box(stroke: 2pt + red, canvas({ 64 | import draw: * 65 | 66 | set-transform(none) 67 | rotate(pitch: 45deg) 68 | draw-xyz() 69 | })) 70 | #box(stroke: 2pt + red, canvas({ 71 | import draw: * 72 | 73 | set-transform(none) 74 | rotate(roll: 45deg) 75 | draw-xyz() 76 | })) 77 | -------------------------------------------------------------------------------- /tests/set-get-ctx/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/set-get-ctx/ref/1.png -------------------------------------------------------------------------------- /tests/set-get-ctx/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas(length: 1cm, { 5 | import draw: * 6 | set-ctx(ctx => { 7 | ctx.my-custom-attribute = "123" 8 | return ctx 9 | }) 10 | 11 | get-ctx(ctx => { 12 | set-style(stroke: green) 13 | content((0, 0), ctx.my-custom-attribute, frame: "rect") 14 | }) 15 | 16 | // Note that the set-style is _not_ scoped! 17 | circle((0,0)) 18 | })) 19 | -------------------------------------------------------------------------------- /tests/style/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/style/ref/1.png -------------------------------------------------------------------------------- /tests/style/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | let next(body) = { 8 | translate((0,-.2,0)) 9 | group(body) 10 | } 11 | 12 | next({ 13 | line((0,0), (1,0)) 14 | }) 15 | next({ 16 | set-style(stroke: blue) 17 | line((0,0), (1,0)) 18 | }) 19 | next({ 20 | line((0,0), (1,0), stroke: blue) 21 | }) 22 | next({ 23 | // Blue arrow 24 | set-style(stroke: blue) 25 | line((0,0), (1,0), mark: (end: ">")) 26 | }) 27 | next({ 28 | // Blue arrow 29 | line((0,0), (1,0), mark: (end: ">"), stroke: blue) 30 | }) 31 | next({ 32 | // Blue + Green arrow head 33 | line((0,0), (1,0), mark: (end: ">", stroke: green), stroke: blue) 34 | }) 35 | next({ 36 | // Blue + Yellow arrow head 37 | set-style(mark: (stroke: yellow)) 38 | line((0,0), (1,0), mark: (end: ">"), stroke: blue) 39 | }) 40 | next({ 41 | // Blue + Green arrow head 42 | set-style(mark: (stroke: yellow), stroke: red) 43 | line((0,0), (1,0), mark: (end: ">", stroke: green), stroke: blue) 44 | }) 45 | next({ 46 | // Blue + Yellow/Green arrow head 47 | set-style(mark: (stroke: yellow, fill: auto), stroke: blue, fill: blue) 48 | line((0,0), (1,0), mark: (end: ">"), fill: green, stroke: green) 49 | }) 50 | next({ 51 | // Blue arrow 52 | set-style(stroke: red) 53 | line((0,0), (1,0), mark: (end: ">"), stroke: blue) 54 | }) 55 | })) 56 | 57 | -------------------------------------------------------------------------------- /tests/transform-precission/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/transform-precission/ref/1.png -------------------------------------------------------------------------------- /tests/transform-precission/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #test-case({ 6 | import cetz.draw: * 7 | for i in range(0, 90 + 1) { 8 | rotate(22deg) 9 | translate((0,1)) 10 | rotate(-25deg) 11 | translate((0,-1)) 12 | scale(y: -1) 13 | } 14 | 15 | // With rounding errors, the line and decoration 16 | // won't be at the same location. 17 | line((-1,0), (1,0), stroke: red) 18 | 19 | cetz.decorations.wave(line((-1,0), (1,0), stroke: green)) 20 | }) 21 | 22 | // #580 23 | #test-case({ 24 | import cetz.draw: * 25 | for i in range(0, 360, step: 3) { 26 | let th = 1deg * i 27 | set-ctx(ctx => { 28 | ctx.transform = ((calc.cos(th), -calc.sin(th), 0, 0), 29 | (-calc.sin(th), -calc.cos(th), 0, 0), 30 | (0, 0, 1, 0), 31 | (0, 0, 0, 1),) 32 | return ctx 33 | }) 34 | 35 | circle((0deg, 4), radius: 0.1, name: "X", fill: luma(200)) 36 | line("X", (rel: (1,0))) 37 | } 38 | }) 39 | -------------------------------------------------------------------------------- /tests/translation/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/translation/ref/1.png -------------------------------------------------------------------------------- /tests/translation/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | group(name: "g", { 8 | translate((-1.5, .5, 0)) 9 | 10 | rect((0, 0), (1, 1)) 11 | anchor("tl", (0, 0)) 12 | anchor("tr", (1, 0)) 13 | anchor("bl", (0, 1)) 14 | anchor("br", (1, 1)) 15 | }) 16 | 17 | group({ 18 | line((-2, 0), (2, 0)) 19 | line((0, -2), (0, 2)) 20 | }) 21 | 22 | stroke(green) 23 | circle("g.tl", radius: .1) 24 | circle("g.tr", radius: .1) 25 | circle("g.bl", radius: .1) 26 | circle("g.br", radius: .1) 27 | })) 28 | 29 | #box(stroke: 2pt + red, canvas({ 30 | import draw: * 31 | 32 | rect((0, 0), (1, 1), name: "a", fill: blue) 33 | content("a.center", [A]) 34 | 35 | // The translation must not get scaled to 2, 36 | // the rects have to touch at the edge. 37 | group({ 38 | translate((0, 1)) 39 | scale(2) 40 | rect((0, 0), (1, 1), name: "b", fill: green) 41 | content("b.center", [B]) 42 | }) 43 | 44 | // Translation should get scaled if multiplied post 45 | // scaling. 46 | group({ 47 | scale(2) 48 | translate((.5, 0), pre: false) 49 | rect((0, 0), (.5, .5), name: "c", fill: red) 50 | content("c.center", [C]) 51 | }) 52 | })) 53 | -------------------------------------------------------------------------------- /tests/tree/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/tree/ref/1.png -------------------------------------------------------------------------------- /tests/tree/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | #import "/tests/helper.typ": * 4 | 5 | #let data = ( 6 | [A], ([B], [C], [D]), ([E], [F]) 7 | ) 8 | 9 | #test-case({ 10 | import draw: * 11 | import tree: * 12 | 13 | set-style( 14 | mark: (fill: auto), 15 | content: (padding: .2), 16 | fill: gray.lighten(70%), 17 | stroke: gray.lighten(70%)) 18 | 19 | tree(data, spread: 2.5, grow: 2, draw-node: (node, ..) => { 20 | content((), node.content, frame: "circle") 21 | }, draw-edge: (from, to, ..) => { 22 | line(from, to, mark: (start: "stealth", end: "stealth")) 23 | }, name: "tree") 24 | 25 | // Draw a "custom" connection between two nodes 26 | let (a, b) = ("tree.0-0-1", "tree.0-1-0",) 27 | line((a, .6, b), (b, .6, a), mark: (end: ">", start: ">")) 28 | }) 29 | 30 | #for position in ("begin", "center", "end") { 31 | test-case({ 32 | cetz.draw.set-style(content: (frame: "rect", padding: .1)) 33 | cetz.tree.tree(data, parent-position: position) 34 | }) 35 | h(.1cm) 36 | } 37 | 38 | #for direction in ("down", "up", "left", "right") { 39 | test-case({ 40 | cetz.draw.set-style(content: (frame: "rect", padding: .1)) 41 | cetz.tree.tree(data, direction: direction) 42 | }) 43 | h(.1cm) 44 | } 45 | 46 | #test-case(edge-layer => { 47 | import cetz.draw: * 48 | cetz.tree.tree(data, edge-layer: edge-layer, draw-node: (node, ..) => { 49 | circle((), radius: .3, fill: white) 50 | content((), node.content) 51 | }, draw-edge: (from, to, ..) => { 52 | line((anchor: "center", name: from), 53 | (anchor: "center", name: to), stroke: red + 2pt) 54 | }) 55 | }, args: (0, 1)) 56 | -------------------------------------------------------------------------------- /tests/viewport/ref/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cetz-package/cetz/6528b5316306c1fcda33637584a24b9e66b5a2cc/tests/viewport/ref/1.png -------------------------------------------------------------------------------- /tests/viewport/test.typ: -------------------------------------------------------------------------------- 1 | #set page(width: auto, height: auto) 2 | #import "/src/lib.typ": * 3 | 4 | #box(stroke: 2pt + red, canvas({ 5 | import draw: * 6 | 7 | let vp(from, to, bounds: (1,1,1)) = { 8 | group(name: "r", { 9 | rect(from, to) 10 | anchor("from", from) 11 | anchor("to", to) 12 | }) 13 | group({ 14 | set-viewport("r.from", "r.to", bounds: bounds) 15 | content((0,0), [A]) 16 | content((1,0), [B]) 17 | content((1,1), [C]) 18 | content((0,1), [D]) 19 | }) 20 | } 21 | 22 | // Mark (0,0) 23 | line((-1,0),(1,0), stroke: blue) 24 | line((0,-1),(0,1), stroke: blue) 25 | 26 | group({ 27 | translate(x: -1) 28 | rotate(45deg) 29 | rect((1,1), (4,4)) 30 | set-viewport((1,1), (4,4)) 31 | for i in range(0, 4) { 32 | for j in range(0, 4) { 33 | circle((i / 3, j / 3), radius: .1, fill: (red, green, blue).at(calc.rem(i+j, 3))) 34 | } 35 | } 36 | }) 37 | 38 | group({ 39 | translate((-2.5,-2.5)) 40 | vp((2,2), (3,3)) 41 | }) 42 | 43 | vp((4,8), (1,5), bounds: (1,1,0)) // Mirrored edges 44 | vp((2,6), (3,7), bounds: (2,2,0)) // Non 1 bounds 45 | })) 46 | -------------------------------------------------------------------------------- /typst.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cetz" 3 | version = "0.3.4" 4 | compiler = "0.13.0" 5 | repository = "https://github.com/cetz-package/cetz" 6 | homepage = "https://cetz-package.github.io/" 7 | entrypoint = "src/lib.typ" 8 | authors = [ 9 | "Johannes Wolf ", 10 | "fenjalien " 11 | ] 12 | categories = [ "visualization" ] 13 | license = "LGPL-3.0-or-later" 14 | description = "Drawing with Typst made easy, providing an API inspired by TikZ and Processing. Includes modules for plotting, charts and tree layout." 15 | keywords = [ "draw", "canvas", "tree" ] 16 | exclude = [ "/gallery/*", "manual.pdf", "manual.typ" ] 17 | --------------------------------------------------------------------------------