Closed johannes-wolf closed 9 months ago
We could allow passing 3 positional arguments in the current arc
function? When 3 are dectected it draws an arc through them. We could extend this to circle-through
I think overloading existing functions too much only brings complexity with no real benefit. I would favor having the extra *-through
functions.
what would be best is allowing dot access notation on the element function. for example circle(...)
and circle.through(...)
. But this can't be done until typst structs or custom elements are implemented. So I guess *-through
would be okay... In terms of complexity though, they could be implemented by transforming the three points into a radius and position and calling the primative element function.
Yes, if the dot syntax becomes available we can switch to that.
Would you two be okay with it if I had a go at implementing this? I know you're in the middle of a rework, so I feel like I should ask before making a PR.
I've already gotten the basics working using what CetZ provides out of the box. I would just need to convert it to the same style that the other functions use, where they return that fancy dictionary with custom-anchors
and whatnot.
#let arc-through(a, b, c, ..args) = cetz.draw.get-ctx(ctx => {
import cetz.vector
import cetz.coordinate
let a = coordinate.resolve(ctx, a)
let b = coordinate.resolve(ctx, b)
let c = coordinate.resolve(ctx, c)
let center = coordinate.util.calculate-circle-center-3pt(a, b, c)
let radius = vector.dist(center, a)
let start = {
let (x, y, ..) = vector.sub(a, center)
calc.atan2(x, y) // Typst's atan2 is (x,y) order!!
}
let delta = vector.angle(a, center, c)
cetz.draw.arc(a, ..args, start: start, delta: delta, radius: radius, anchor: "start")
})
Let me know what you think. I'll hold off if it should wait until the rework is complete.
Related to arcs/curves, I'd also like to take a stab at #239 at some point, if that isn't going to be part of the rework, as well... It just so happens that my latest Algorithms assignment fits really well with drawing curved arrows 😅
Thank you for the work! There are some edge cases where your implementation did not work correctly. We have to check on which side of a-c
, b
and center
lay and swap the delta if needed. I hope this version works as expected.
#import "@preview/cetz:0.1.2"
#set page(width: auto, height: auto)
#let side-on-line(a, b, pt) = {
let (x1, y1, ..) = a
let (x2, y2, ..) = b
let (x, y, ..) = pt
return (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1)
}
#let arc-through(a, b, c, ..args) = cetz.draw.get-ctx(ctx => {
import cetz.vector
import cetz.coordinate
let a = coordinate.resolve(ctx, a)
let b = coordinate.resolve(ctx, b)
let c = coordinate.resolve(ctx, c)
let center = coordinate.util.calculate-circle-center-3pt(a, b, c)
let radius = vector.dist(center, a)
let start = {
let (x, y, ..) = vector.sub(a, center)
calc.atan2(x, y) // Typst's atan2 is (x,y) order!!
}
let delta = vector.angle(a, center, c)
let center-is-left = side-on-line(a, c, center) < 0
let b-is-left = side-on-line(a, c, b) < 0
// If the center and point b are on the same side of a-c,
// the arcs delta must be > 180deg
if center-is-left == b-is-left {
delta = 360deg - delta
}
// If b is left of a-c, swap a-c to c-a by using a negative delta
if b-is-left {
delta *= -1
}
cetz.draw.content(center, [#delta])
cetz.draw.arc(a, ..args, start: start, delta: delta, radius: radius, anchor: "start")
})
#let test(a, b, c) = {
import cetz.draw: *
group({
anchor("default", (0,0))
circle(a, radius: .1, fill: green)
circle(b, radius: .1, fill: yellow)
circle(c, radius: .1, fill: red)
arc-through(a, b, c, name: "a")
}, name: "g", anchor: "left")
on-layer(-1, rect("g.bottom-left", "g.top-right", stroke: none, fill: gray.lighten(80%)))
set-origin((rel: (1,0), to: "g.right"))
}
#cetz.canvas({
import cetz.draw: *
test((0,0), (1,1), (2,0))
test((0,0), (1,-1), (2,0))
test((0,-1), (-1,0), (0,1))
test((0,-1), (1,0), (0,1))
test((0,0), (1,3), (4,0))
test((0,0), (1,3), (4,3))
test((1,0), (2,3), (0,3))
test((0,0), (1,-3), (4,0))
test((0,0), (1,-3), (4,3))
})
You can create a PR, but I would wait to merge it until the rework has been merged.
That function should connect two points with an arc touching the third point (see circle-through).