Skip to content

Commit ceb577b

Browse files
committed
Find closest point by name
1 parent 297f77b commit ceb577b

File tree

6 files changed

+63
-13
lines changed

6 files changed

+63
-13
lines changed

CHANGES.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ package called `cetz-plot`.
4141
depth ordering and face culling of drawables. Ordering is enabled by default.
4242
- Closed `line` and `merge-path` elements now have a `"centroid"` anchor that
4343
is the calculated centroid of the (non self-intersecting!) shape.
44-
- Added `closest-point` for creating an anchor at the closest point between a
44+
- Added `find-closest-point` for creating an anchor at the closest point between a
4545
reference point and one or more elements.
4646

4747
## Marks

src/bezier.typ

+8-2
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,14 @@
586586
return pts
587587
}
588588

589-
/// Find the closest point on a bezier to a given point
590-
/// by using a binary search along the curve.
589+
/// Find the closest point on a bezier to a given point.
590+
///
591+
/// - pt (vector): Reference point to find the closest point to
592+
/// - s (vector): Bezier start
593+
/// - e (vector): Bezier end
594+
/// - c1 (vector): Bezier control point 1
595+
/// - c2 (vector): Bezier control point 2
596+
/// - max-recursion (int): Max recursion depth
591597
#let cubic-closest-point(pt, s, e, c1, c2, max-recursion: 1) = {
592598
let probe(low, high, depth) = {
593599
let min = calc.inf

src/draw.typ

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#import "draw/grouping.typ": intersections, group, scope, anchor, copy-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, hide, floating
1+
#import "draw/grouping.typ": intersections, group, scope, anchor, copy-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, hide, floating, find-closest-point
22
#import "draw/transformations.typ": set-transform, rotate, translate, scale, set-origin, move-to, set-viewport
33
#import "draw/styling.typ": set-style, fill, stroke, register-mark
44
#import "draw/shapes.typ": circle, circle-through, arc, arc-through, mark, line, grid, content, rect, bezier, bezier-through, catmull, hobby, merge-path

src/draw/grouping.typ

+17-9
Original file line numberDiff line numberDiff line change
@@ -194,24 +194,33 @@
194194
/// creates an anchor. Transformations insides the body are scoped and do
195195
/// not get applied outsides.
196196
///
197-
/// - name (string): Anchor name.
197+
/// - name (str): Anchor name.
198198
/// - reference-point (coordinate): Coordinate to find the closest point to.
199-
/// - body (element): One or more elements to consider. A least one is required. A function that accepts `ctx` and returns elements is also accepted.
200-
#let closest-point(name, reference-point, body) = {
199+
/// - 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.
200+
#let find-closest-point(name, reference-point, body) = {
201201
import "/src/bezier.typ": cubic-closest-point
202202

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

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

211-
let group-ctx = ctx
212-
group-ctx.groups.push(())
213-
let (ctx: group-ctx, drawables, bounds) = process.many(group-ctx, util.resolve-body(ctx, body))
214-
ctx.nodes += group-ctx.nodes
213+
let (sub-ctx, drawables, output-drawables) = if type(body) == str {
214+
let node = ctx.nodes.at(body)
215+
(ctx, node.drawables, false)
216+
} else {
217+
let group-ctx = ctx
218+
group-ctx.groups.push(())
219+
let node = process.many(group-ctx, util.resolve-body(ctx, body))
220+
(node.ctx, node.drawables, true)
221+
}
222+
223+
ctx.nodes += sub-ctx.nodes
215224

216225
let min = calc.inf
217226
let min-pt = none
@@ -268,8 +277,7 @@
268277
ctx: ctx,
269278
name: name,
270279
anchors: anchors,
271-
drawables: drawables,
272-
bounds: bounds
280+
drawables: if output-drawables { drawables } else { () },
273281
)
274282
},)
275283
}

tests/closest-point/ref/1.png

10.4 KB
Loading

tests/closest-point/test.typ

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#set page(width: auto, height: auto)
2+
#import "/tests/helper.typ": *
3+
4+
#test-case({
5+
import cetz.draw: *
6+
7+
group(name: "g", {
8+
rotate(10deg)
9+
rect((-1, -1), (1, 1), radius: .45)
10+
})
11+
12+
for i in range(0, 360, step: 10) {
13+
let pt = (i * 1deg, 2)
14+
15+
find-closest-point("test", pt, {
16+
rotate(10deg)
17+
hide(rect((-1, -1), (1, 1), radius: .45))
18+
})
19+
20+
line(pt, "test")
21+
circle(pt, radius: .1, fill: blue)
22+
}
23+
})
24+
25+
#test-case({
26+
import cetz.draw: *
27+
28+
group(name: "g", {
29+
rotate(10deg)
30+
rect((-1, -1), (1, 1), radius: .45)
31+
})
32+
33+
let pt = (2, 2)
34+
find-closest-point("test", pt, "g")
35+
line("test", pt)
36+
})

0 commit comments

Comments
 (0)