Skip to content

Commit

Permalink
shapes: Add arc-through
Browse files Browse the repository at this point in the history
Thanks to Matthew Brown for the implementation.
  • Loading branch information
johannes-wolf committed Nov 20, 2023
1 parent 9bac962 commit 4feddf4
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 1 deletion.
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, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks
#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, mark, line, grid, content, rect, bezier, bezier-through, catmull, hobby, merge-path, shadow
#import "draw/shapes.typ": circle, circle-through, arc, arc-through, mark, line, grid, content, rect, bezier, bezier-through, catmull, hobby, merge-path, shadow
44 changes: 44 additions & 0 deletions src/draw/shapes.typ
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,50 @@
},)
}

#let arc-through(
a,
b,
c,
name: none,
..style,
) = get-ctx(ctx => {
let (ctx, a) = coordinate.resolve(ctx, a)
let (ctx, b) = coordinate.resolve(ctx, b)
let (ctx, c) = coordinate.resolve(ctx, c)

let center = 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 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 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
}

return arc(a, start: start, delta: delta, radius: radius,
anchor: "arc-start", name: name, ..style)
})

#let mark(from, to, ..style) = {
assert.eq(
style.pos(),
Expand Down
Binary file added tests/arc/arc-through/ref.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions tests/arc/arc-through/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#set page(width: auto, height: auto)
#import "/src/lib.typ": *

#let show-points(..pts) = {
import draw: *
for pt in pts.pos() {
circle(pt, radius: .1)
}
}

#let test(a, b, c) = {
import draw: *
group({
anchor("center", (0,0,0))
show-points(a, b, c)
arc-through(a, b, c)
}, name: "g", anchor: "west", padding: .1)
set-origin("g.east")
}

#box(stroke: 2pt + red, canvas(length: 1cm, {
import 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))
}))

#box(stroke: 2pt + red, canvas(length: 1cm, {
import draw: *

for a in range(36, 360 + 36, step: 36) {
let a = a * 1deg
test((1,0),
(calc.cos(a / 2), calc.sin(a / 2)),
(calc.cos(a), calc.sin(a)))
}
}))

#box(stroke: 2pt + red, canvas(length: 1cm, {
import draw: *

for d in range(0, 8 + 1) {
let d = (d - 2) / 5
test((0,0), (1,d), (2,.5))
}
}))

#box(stroke: 2pt + red, canvas(length: 1cm, {
import draw: *

// The style radius must not influence the
// arc radius!
set-style(radius: 5)
set-style(arc: (radius: 5))
test((0,0), (1, 1), (2, 0))
}))

0 comments on commit 4feddf4

Please sign in to comment.