Skip to content

Commit

Permalink
mark: Support placing marks post-transform (#493)
Browse files Browse the repository at this point in the history
This allows placing marks _after_ the path got transformed.
Use-Case: drawing marks on scaled paths, drawing marks insides plots
(which use a scaled viewport). The behavior is disabled by default and
can be enabled by setting marks `post-transform` style key to `true`.


Maybe the implementation has too much copied code, but doing the
transform insides `mark.typ` is also not that clean I think.
  • Loading branch information
johannes-wolf authored Feb 20, 2024
1 parent 4b203d1 commit b55f085
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 42 deletions.
11 changes: 8 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@
- Open arcs are no longer modified for anchors, invalid border anchors will panic.
- Grids now actually support border anchors.

## Marks
- Marks can now be placed on a path after that path got transformed. See the new `transform-shape` style key.

## Misc
- The `hide` function now support an additional `bounds:` parameter to enable canvas bounds adjustment for hidden elements

## Libs
### Plot
- The default style of plots changed
- New style keys for enabling/disabling the shared zero tick for "school-book" style plots
- New style keys for specifying the layer of different plot elements (`grid-layer`, `axis-layer`, `background-layer`)

## Misc
- The `hide` function now support an additional `bounds:` parameter to enable canvas bounds adjustment for hidden elements
- Fixed annotation bounds calculation
- Marks insides annotations are now unaffected by the plots canvas scaling by default (see marks new post-transform style key)

# 0.2.0
CeTZ 0.2.0 requires Typst 0.10.0
Expand Down
1 change: 1 addition & 0 deletions manual.typ
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ line(..c)

#doc-style.show-parameter-block("shorten-to", ("integer", "auto", "none"), [Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index.])

#doc-style.show-parameter-block("transform-shape", "bool", [When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed.], default: true)


#pagebreak()
Expand Down
56 changes: 22 additions & 34 deletions src/draw/shapes.typ
Original file line number Diff line number Diff line change
Expand Up @@ -313,19 +313,16 @@
)

if mark_.check-mark(style.mark) {
let (marks, segments) = mark_.place-marks-along-path(ctx, style.mark, drawables.segments)
drawables.segments = segments
drawables = (drawables,) + marks
drawables = mark_.place-marks-along-path(ctx, style.mark, transform, drawables)
} else {
drawables = drawable.apply-transform(transform, drawables)
}

return (
ctx: ctx,
name: name,
anchors: anchors,
drawables: drawable.apply-transform(
transform,
drawables,
)
drawables: drawables,
)
},)
}
Expand Down Expand Up @@ -457,8 +454,8 @@
style.start = none
style.symbol = none

let segments = (path-util.line-segment(pts),)
let (drawables, _) = mark_.place-marks-along-path(ctx, style, segments)
let drawables = drawable.path((path-util.line-segment(pts),))
drawables = mark_.place-marks-along-path(ctx, style, none, drawables, add-path: false)
return (
ctx: ctx,
drawables: drawable.apply-transform(ctx.transform, drawables)
Expand Down Expand Up @@ -568,16 +565,16 @@

// Place marks and adjust segments
if mark_.check-mark(style.mark) {
let (marks, segments) = mark_.place-marks-along-path(ctx, style.mark, drawables.segments)
drawables.segments = segments
drawables = (drawables,) + marks
drawables = mark_.place-marks-along-path(ctx, style.mark, transform, drawables)
} else {
drawables = drawable.apply-transform(transform, drawables)
}

return (
ctx: ctx,
name: name,
anchors: anchors,
drawables: drawable.apply-transform(transform, drawables)
drawables: drawables,
)
},)
}
Expand Down Expand Up @@ -1197,19 +1194,16 @@
)

if mark_.check-mark(style.mark) {
let (marks, segments) = mark_.place-marks-along-path(ctx, style.mark, drawables.segments)
drawables.segments = segments
drawables = (drawables,) + marks
drawables = mark_.place-marks-along-path(ctx, style.mark, transform, drawables)
} else {
drawables = drawable.apply-transform(transform, drawables)
}

return (
ctx: ctx,
name: name,
anchors: anchors,
drawables: drawable.apply-transform(
transform,
drawables
)
drawables: drawables,
)
},
)
Expand Down Expand Up @@ -1305,19 +1299,16 @@
}

if mark_.check-mark(style.mark) {
let (marks, segments) = mark_.place-marks-along-path(ctx, style.mark, drawables.segments)
drawables.segments = segments
drawables = (drawables,) + marks
drawables = mark_.place-marks-along-path(ctx, style.mark, transform, drawables)
} else {
drawables = drawable.apply-transform(transform, drawables)
}

return (
ctx: ctx,
name: name,
anchors: anchors,
drawables: drawable.apply-transform(
transform,
drawables
)
drawables: drawables,
)
},)
}
Expand Down Expand Up @@ -1391,19 +1382,16 @@
}

if mark_.check-mark(style.mark) {
let (marks, segments) = mark_.place-marks-along-path(ctx, style.mark, drawables.segments)
drawables.segments = segments
drawables = (drawables,) + marks
drawables = mark_.place-marks-along-path(ctx, style.mark, transform, drawables)
} else {
drawables = drawable.apply-transform(transform, drawables)
}

return (
ctx: ctx,
name: name,
anchors: anchors,
drawables: drawable.apply-transform(
transform,
drawables
)
drawables: drawables,
)
},)
}
Expand Down
8 changes: 6 additions & 2 deletions src/drawable.typ
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
if drawables.len() == 0 {
return ()
}
if transform == none {
return drawables
}
for drawable in drawables {
assert(type(drawable) != array,
message: "Expected drawable, got array: " + repr(drawable))
if drawable.type == "path" {
drawable.segments = drawable.segments.map(s => {
return (s.at(0),) + util.apply-transform(transform, ..s.slice(1))
drawable.segments = drawable.segments.map(segment => {
let (kind, ..pts) = segment
return (kind,) + util.apply-transform(transform, ..pts)
})
} else if drawable.type == "content" {
drawable.pos = util.apply-transform(transform, drawable.pos)
Expand Down
5 changes: 4 additions & 1 deletion src/lib/plot/annotation.typ
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
#let annotate(body, axes: ("x", "y"), resize: true, padding: none, background: false) = {
((
type: "annotation",
body: body,
body: {
draw.set-style(mark: (transform-shape: false))
body;
},
axes: axes,
resize: resize,
background: background,
Expand Down
23 changes: 21 additions & 2 deletions src/mark.typ
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,18 @@
)
}

#let place-marks-along-path(ctx, style, segments) = {
#let place-marks-along-path(ctx, style, transform, path, add-path: true) = {
let distance = (0, 0)
let snap-to = (none, none)
let drawables = ()

let (path, is-transformed) = if not style.transform-shape and transform != none {
(drawable.apply-transform(transform, path).first(), true)
} else {
(path, false)
}

let segments = path.segments
if style.start != none or style.symbol != none {
let (drawables: start-drawables, distance: start-distance, pos: pt) = place-mark-on-path(
ctx,
Expand Down Expand Up @@ -289,5 +297,16 @@
snap-to: snap-to)
}

return (drawables, segments)
if add-path {
path.segments = segments
drawables.insert(0, path)
}

// If not transformed pre mark placement,
// transform everything after mark placement.
if not is-transformed {
drawables = drawable.apply-transform(transform, drawables)
}

return drawables
}
4 changes: 4 additions & 0 deletions src/styles.typ
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
/// set the default to `0` and to `auto` for the mark you want to
/// shorten the path to. Set to `none` to disable path shortening.
shorten-to: auto,
/// Apply shape transforms for marks. This is not honored per mark, but
/// for all marks on a path. If set to false, marks get placed after the
/// shape they are placed on got transformed.
transform-shape: true,
),
circle: (
radius: auto,
Expand Down
Binary file added tests/mark/shape-transform/ref/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions tests/mark/shape-transform/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#import "/src/lib.typ": *
#import "/tests/helper.typ": *

#test-case({
import cetz.draw: *

set-style(mark: (transform-shape: true))

scale(x: 3)
line((-1,-1), (1,1), mark: (start: "rect", end: "]"))
})

#test-case({
import cetz.draw: *

set-style(mark: (transform-shape: false))

scale(x: 3)
line((-1,-1), (1,1), mark: (start: "rect", end: ">"))
})

#test-case({
import cetz.draw: *

set-style(mark: (transform-shape: false))

rotate(45deg)
line((-1,-1), (1,-1), (1,1), mark: (start: "rect", end: "rect", scale: 3))
})
Binary file modified tests/plot/annotation/ref/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/plot/annotation/test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@
line((calc.pi / 2, 1.1), (rel: (0, .2)), (rel: (2*calc.pi, 0)), (rel: (0, -.2)))
content((calc.pi * 1.5, 1.5), $ lambda $)
})
plot.annotate(padding: .1, {
line((calc.pi / 2,-.1), (calc.pi / 2, .8), mark: (end: "stealth"))
})
})
}))

0 comments on commit b55f085

Please sign in to comment.