Skip to content

Commit

Permalink
Find closest point by name
Browse files Browse the repository at this point in the history
  • Loading branch information
johannes-wolf committed Aug 20, 2024
1 parent bc95c80 commit d91440f
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package called `cetz-plot`.
depth ordering and face culling of drawables. Ordering is enabled by default.
- Closed `line` and `merge-path` elements now have a `"centroid"` anchor that
is the calculated centroid of the (non self-intersecting!) shape.
- Added `closest-point` for creating an anchor at the closest point between a
- Added `find-closest-point` for creating an anchor at the closest point between a
reference point and one or more elements.

## Marks
Expand Down
10 changes: 8 additions & 2 deletions src/bezier.typ
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,14 @@
return pts
}

/// Find the closest point on a bezier to a given point
/// by using a binary search along the curve.
/// Find the closest point on a bezier to a given point.
///
/// - pt (vector): Reference point to find the closest point to
/// - s (vector): Bezier start
/// - e (vector): Bezier end
/// - c1 (vector): Bezier control point 1
/// - c2 (vector): Bezier control point 2
/// - max-recursion (int): Max recursion depth
#let cubic-closest-point(pt, s, e, c1, c2, max-recursion: 1) = {
let probe(low, high, depth) = {
let min = calc.inf
Expand Down
2 changes: 1 addition & 1 deletion src/draw.typ
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#import "draw/grouping.typ": intersections, group, scope, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks, hide, floating, closest-point
#import "draw/grouping.typ": intersections, group, scope, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks, hide, floating, find-closest-point
#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
Expand Down
26 changes: 17 additions & 9 deletions src/draw/grouping.typ
Original file line number Diff line number Diff line change
Expand Up @@ -195,24 +195,33 @@
/// creates an anchor. Transformations insides the body are scoped and do
/// not get applied outsides.
///
/// - name (string): Anchor name.
/// - name (str): Anchor name.
/// - reference-point (coordinate): Coordinate to find the closest point to.
/// - body (element): One or more elements to consider. A least one is required. A function that accepts `ctx` and returns elements is also accepted.
#let closest-point(name, reference-point, body) = {
/// - body (element,str): One or more elements to consider. A least one is required. A function that accepts `ctx` and returns elements is also accepted. If a string is passed, the existing named element is used.
#let find-closest-point(name, reference-point, body) = {
import "/src/bezier.typ": cubic-closest-point

assert(type(name) == str,
message: "Anchor name must be of type string, got " + repr(name))
assert(type(body) in (array, function, str),
message: "Expected body to be a list of elements, a callback or an elements name")
coordinate.resolve-system(reference-point)

return (ctx => {
let (_, pt) = coordinate.resolve(ctx, reference-point)
pt = util.apply-transform(ctx.transform, pt)

let group-ctx = ctx
group-ctx.groups.push(())
let (ctx: group-ctx, drawables, bounds) = process.many(group-ctx, util.resolve-body(ctx, body))
ctx.nodes += group-ctx.nodes
let (sub-ctx, drawables, output-drawables) = if type(body) == str {
let node = ctx.nodes.at(body)
(ctx, node.drawables, false)
} else {
let group-ctx = ctx
group-ctx.groups.push(())
let node = process.many(group-ctx, util.resolve-body(ctx, body))
(node.ctx, node.drawables, true)
}

ctx.nodes += sub-ctx.nodes

let min = calc.inf
let min-pt = none
Expand Down Expand Up @@ -269,8 +278,7 @@
ctx: ctx,
name: name,
anchors: anchors,
drawables: drawables,
bounds: bounds
drawables: if output-drawables { drawables } else { () },
)
},)
}
Expand Down
Binary file added tests/closest-point/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.
36 changes: 36 additions & 0 deletions tests/closest-point/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#set page(width: auto, height: auto)
#import "/tests/helper.typ": *

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

group(name: "g", {
rotate(10deg)
rect((-1, -1), (1, 1), radius: .45)
})

for i in range(0, 360, step: 10) {
let pt = (i * 1deg, 2)

find-closest-point("test", pt, {
rotate(10deg)
hide(rect((-1, -1), (1, 1), radius: .45))
})

line(pt, "test")
circle(pt, radius: .1, fill: blue)
}
})

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

group(name: "g", {
rotate(10deg)
rect((-1, -1), (1, 1), radius: .45)
})

let pt = (2, 2)
find-closest-point("test", pt, "g")
line("test", pt)
})

0 comments on commit d91440f

Please sign in to comment.