diff --git a/CHANGES.md b/CHANGES.md index 70375a3e4..5954aa518 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ CeTZ 0.2.0 requires Typst 0.10.0 - Added element `arc-through` to draw an arc through three points - Added Hobby curves (`hobby`) in addition to catmull (thanks to @Enivex) - Added `radius` style to `rect` for drawing rounded rects +- Added `hide` function for hiding elements ### Plot - Added `plot.add-contour(..)` for plotting contour plots diff --git a/src/canvas.typ b/src/canvas.typ index adf835edf..f9809024a 100644 --- a/src/canvas.typ +++ b/src/canvas.typ @@ -61,6 +61,9 @@ return [] } + // Filter hidden drawables + drawables = drawables.filter(d => not d.hidden) + // Order draw commands by z-index drawables = drawables.sorted(key: (cmd) => { return cmd.at("z-index", default: 0) diff --git a/src/draw.typ b/src/draw.typ index 9927d9845..495f17c74 100644 --- a/src/draw.typ +++ b/src/draw.typ @@ -1,4 +1,4 @@ -#import "draw/grouping.typ": intersections, group, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks +#import "draw/grouping.typ": intersections, group, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks, hide #import "draw/transformations.typ": set-transform, rotate, translate, scale, set-origin, move-to, set-viewport #import "draw/styling.typ": set-style, fill, stroke #import "draw/shapes.typ": circle, circle-through, arc, arc-through, mark, line, grid, content, rect, bezier, bezier-through, catmull, hobby, merge-path diff --git a/src/draw/grouping.typ b/src/draw/grouping.typ index 160fd741d..07406dc16 100644 --- a/src/draw/grouping.typ +++ b/src/draw/grouping.typ @@ -13,6 +13,42 @@ #import "transformations.typ": move-to +/// Hides an element. +/// +/// Hidden elements are not drawn to the canvas, +/// are ignored when calculating bounding boxes and discarded by `merge-path`. All +/// other behaviours remain the same as a non-hidden element. +/// +/// #example(``` +/// set-style(radius: .5) +/// intersections("i", { +/// circle((0,0), name: "a") +/// circle((1,2), name: "b") +/// // Use a hidden line to find the border intersections +/// hide(line("a.center", "b.center")) +/// }) +/// line("i.0", "i.1") +/// ```) +/// +/// - body (element): One or more elements to hide +#let hide(body) = { + if type(body) == array { + return body.map(f => { + (ctx) => { + let element = f(ctx) + if "drawables" in element { + element.drawables = element.drawables.map(d => { + d.hidden = true + return d + }) + } + return element + } + }) + } + return body +} + /// Calculates the intersections between multiple paths and creates one anchor /// per intersection point. /// @@ -44,6 +80,8 @@ /// }) /// ```) /// +/// You can calculate intersections with hidden elements by using @@hide(). +/// /// - name (string): Name to prepend to the generated anchors. /// - ..elements (elements,string): Elements and/or element names to calculate intersections with. /// Elements referred to by name are (unlike elements passed) not drawn by the intersections function! diff --git a/src/draw/shapes.typ b/src/draw/shapes.typ index 23d4fa66e..9700c5de0 100644 --- a/src/draw/shapes.typ +++ b/src/draw/shapes.typ @@ -1564,6 +1564,7 @@ } } for drawable in r.drawables { + if drawable.hidden { continue } assert.eq(drawable.type, "path") segments += drawable.segments } diff --git a/src/drawable.typ b/src/drawable.typ index bf5d7f909..99bfc9f9d 100644 --- a/src/drawable.typ +++ b/src/drawable.typ @@ -41,7 +41,8 @@ close: close, segments: segments, fill: fill, - stroke: stroke + stroke: stroke, + hidden: false ) } @@ -52,6 +53,7 @@ width: width, height: height, body: body, + hidden: false, ) } diff --git a/src/process.typ b/src/process.typ index 7513afda9..cb911a05a 100644 --- a/src/process.typ +++ b/src/process.typ @@ -15,15 +15,17 @@ element.drawables = (element.drawables,) } for drawable in element.drawables { - bounds = aabb.aabb( - if drawable.type == "path" { - path-util.bounds(drawable.segments) - } else if drawable.type == "content" { - let (x, y, _, w, h,) = drawable.pos + (drawable.width, drawable.height) - ((x + w / 2, y - h / 2, 0), (x - w / 2, y + h / 2, 0)) - }, - init: bounds - ) + if not drawable.hidden { + bounds = aabb.aabb( + if drawable.type == "path" { + path-util.bounds(drawable.segments) + } else if drawable.type == "content" { + let (x, y, _, w, h,) = drawable.pos + (drawable.width, drawable.height) + ((x + w / 2, y - h / 2, 0), (x - w / 2, y + h / 2, 0)) + }, + init: bounds + ) + } } } if "name" in element and type(element.name) == "string" and "anchors" in element { diff --git a/tests/hide/ref.png b/tests/hide/ref.png new file mode 100644 index 000000000..7aa78f337 Binary files /dev/null and b/tests/hide/ref.png differ diff --git a/tests/hide/test.typ b/tests/hide/test.typ new file mode 100644 index 000000000..b4eaa0fc7 --- /dev/null +++ b/tests/hide/test.typ @@ -0,0 +1,37 @@ +#set page(width: auto, height: auto) +#import "/src/lib.typ": * + +#box(stroke: 2pt + red, canvas({ + import draw: * + rect((0,0), (5,5)) + + // Hide a circle + hide(circle((6,6))) + + // Hide content + hide(content((-6,-6), [Hidden])) + + // Hide multiple elements + hide({ + rect((0,0), (1,1)) + rect((1,1), (2,2)) + rect((2,2), (3,3)) + }) + + // Use hidden anchor + hide(line((0,0), (2.5, 2.5), name: "line")) + content("line.end", [Hidden anchor]) +})) + +#box(stroke: 2pt + red, canvas({ + import draw: * + + merge-path({ + arc((0,0), start: 0deg, stop: 180deg) + hide({ + // This gets ignored + line((), (rel: (-5,0), update: false)) + }) + line((), (rel: (1, -1))) + }, close: true) +}))