From 82c074e1620398aba7728e1401bbf3b2a1c252d2 Mon Sep 17 00:00:00 2001 From: johannes-wolf Date: Wed, 22 Nov 2023 21:46:27 +0100 Subject: [PATCH] shapes: Add arc-through documentation --- src/draw/shapes.typ | 54 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/draw/shapes.typ b/src/draw/shapes.typ index 89201c3bd..01eed9855 100644 --- a/src/draw/shapes.typ +++ b/src/draw/shapes.typ @@ -304,6 +304,29 @@ },) } +/// Draw an arc that passes through three points a, b and c. +/// +/// Note that all three points must not lay on a straight line, otherwise +/// the function fails. +/// +/// #example(``` +/// arc-through((0,0), (1,0), (2,0)) +/// ```) +/// +/// *Style Root* `arc` \ +/// *Style Keys* \ +/// For style keys, see `arc`. +/// +/// *Anchors* \ +/// For anchors see `arc`. +/// +/// - a (coordinate): Start position of the arc +/// - b (coordinate): Position the arc passes through +/// - c (coordinate): End position of the arc +/// - name (none,string): The arc elements node name that, if set can be used to query anchors +/// - ..style (style): Style key value pairs. The function `arc-through` uses +/// all keys that `arc` uses, but `radius`, as this is determinded by the +/// three input points. #let arc-through( a, b, @@ -311,20 +334,22 @@ 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 (ctx, a, b, c) = coordinate.resolve(ctx, a, b, c) assert(a.at(2) == b.at(2) and b.at(2) == c.at(2), message: "The z coordinate of all points must be equal, but is: " + repr((a, b, c).map(v => v.at(2)))) + // Calculate the circle center from three points or fails if all + // three points are on one straight line. 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! - } + + // Find the start and inner angle between a-center-c + let start = vector.angle2(center, a) let delta = vector.angle(a, center, c) + // Returns a negative number if pt is left of the line a-b, + // if pt is right to a-b, a positive number is returned, + // otherwise zero. let side-on-line(a, b, pt) = { let (x1, y1, ..) = a let (x2, y2, ..) = b @@ -332,11 +357,24 @@ return (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1) } + // Center & b b is left, + // are left center not + // + // +-b-+ +-b-+ + // / \ / \ + // | C | --a-------c-- + // \ / \ C / + // ---a---c--- +---+ + // + // If b and C are on the same side of a-c, the arcs radius is >= 180deg, + // otherwise the radius is < 180deg. 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 + // the arcs delta must be > 180deg. Note, that delta is + // the inner angle between a-center-c, so we need to calculate + // the outer angle by subtracting from 360deg. if center-is-left == b-is-left { delta = 360deg - delta }