diff --git a/src/lib/angle.typ b/src/lib/angle.typ index 3c3e4357d..3384631e2 100644 --- a/src/lib/angle.typ +++ b/src/lib/angle.typ @@ -1,9 +1,9 @@ -#import "../drawable.typ" -#import "../styles.typ" -#import "../vector.typ" -#import "../util.typ" -#import "../coordinate.typ" -#import "../anchor.typ" as anchor_ +#import "/src/drawable.typ" +#import "/src/styles.typ" +#import "/src/vector.typ" +#import "/src/util.typ" +#import "/src/coordinate.typ" +#import "/src/anchor.typ" as anchor_ #import "/src/draw.typ" // Angle default-style @@ -63,7 +63,8 @@ ..style ) = draw.group(name: name, ctx => { let style = styles.resolve(ctx.style, merge: style.named(), base: default-style, root: "angle") - let (ctx, origin, a, b) = coordinate.resolve(ctx, origin, a, b) + let (ctx, origin) = coordinate.resolve(ctx, origin) + let (ctx, a, b) = coordinate.resolve(ctx, a, b, update: false) assert(origin.at(2) == a.at(2) and a.at(2) == b.at(2), message: "Angle z coordinates of all three points must be equal") @@ -132,3 +133,83 @@ draw.content(label-pt, label) } }) + +/// Draw a right angle between `a` and `b` through origin `origin` +/// +/// #example(``` +/// line((0,0), (1,1.5), name: "a") +/// line((0,0), (2,-1), name: "b") +/// +/// // Draw an angle between the two lines +/// cetz.angle.right-angle("a.start", "a.end", "b.end", +/// radius: 1.5) +/// ```) +/// +/// *Style Root:* `angle` +/// +/// *Style Keys:* +/// #show-parameter-block("radius", ("number"), [ +/// The radius of the angles arc. If of type `ratio`, it is relative to the smaller distance of either origin to a or origin to b.], default: .5) +/// #show-parameter-block("label-radius", ("number", "ratio"), [ +/// The radius of the angles label origin. If of type `ratio`, it is relative to the distance between `origin` and the angle corner.], default: 50%) +/// +/// *Anchors* +/// / `"a"`: Point a +/// / `"b"`: Point b +/// / `"origin"`: Origin +/// / `"corner"`: Angle corner +/// / `"label"`: Label center +/// +/// - origin (coordinate): Angle origin +/// - a (coordinate): Coordinate of side `a`, containing an angle between `origin` and `b`. +/// - b (coordinate): Coordinate of side `b`, containing an angle between `origin` and `a`. +/// - label (none,content): Draw a label at the angles "label" anchor. +/// - name (none,string): Element name, used for querying anchors. +/// - ..style (style): Style key-value pairs. +#let right-angle( + origin, + a, + b, + label: "•", + name: none, + ..style +) = draw.group(name: name, ctx => { + let style = styles.resolve(ctx.style, merge: style.named(), base: default-style, root: "angle") + let (ctx, origin) = coordinate.resolve(ctx, origin) + let (ctx, a, b) = coordinate.resolve(ctx, a, b, update: false) + let vo = origin; let va = a; let vb = b + + // Radius can be relative to the min-distance between origin-a and origin-b + if type(style.radius) == ratio { + style.radius = style.radius * calc.min(vector.dist(vo, va), vector.dist(vo, vb)) / 100% + } + let (r, _) = util.resolve-radius(style.radius).map(util.resolve-number.with(ctx)) + + let va = vector.add(vo, vector.scale(vector.norm(vector.sub(va, vo)), r)) + let vb = vector.add(vo, vector.scale(vector.norm(vector.sub(vb, vo)), r)) + let angle-b = vector.angle2(vo, vb) + let vm = vector.add(va, (calc.cos(angle-b) * r, calc.sin(angle-b) * r, 0)) + + // Label radius can be relative to the distance between origin and the + // angle corner + if type(style.label-radius) == ratio { + style.label-radius = style.label-radius * vector.dist(vm, vo) / 100% + } + let (ra, _) = util.resolve-radius(style.label-radius).map(util.resolve-number.with(ctx)) + + if style.fill != none { + draw.line(vo, va, vm, vb, close: true, stroke: none, fill: style.fill) + } + draw.line(va, vm, vb, ..style, fill: none) + + let label-pt = vector.scale(vector.norm(vector.sub(vm, vo)), ra) + if label != none { + draw.content(label-pt, label) + } + + draw.anchor("a", a) + draw.anchor("b", b) + draw.anchor("origin", origin) + draw.anchor("corner", vm) + draw.anchor("label", label-pt) +}) diff --git a/tests/angle/right-angle/ref.png b/tests/angle/right-angle/ref.png new file mode 100644 index 000000000..4c5d78ab8 Binary files /dev/null and b/tests/angle/right-angle/ref.png differ diff --git a/tests/angle/right-angle/test.typ b/tests/angle/right-angle/test.typ new file mode 100644 index 000000000..510052337 --- /dev/null +++ b/tests/angle/right-angle/test.typ @@ -0,0 +1,64 @@ +#set page(width: auto, height: auto) +#import "/src/lib.typ": * +#import "/tests/helper.typ": * + +#test-case({ + import draw: * + import angle: right-angle + + for a in range(0, 360, step: 36) { + a *= 1deg + translate((1.5, 0, 0)) + group({ + let (o, a, b) = ((0,0), (calc.cos(a), calc.sin(a)), (calc.cos(a+90deg), calc.sin(a+90deg))) + line(a, o, b) + right-angle(o, a, b) + }) + } +}) + +#test-case({ + import draw: * + import angle: right-angle + + for a in range(0, 360, step: 36) { + a *= 1deg + translate((1.5, 0, 0)) + group({ + let (o, a, b) = ((0,0), (calc.cos(a), calc.sin(a)), (calc.cos(a+45deg), calc.sin(a+45deg))) + line(a, o, b) + right-angle(o, a, b) + }) + } +}) + +#test-case({ + import draw: * + import angle: right-angle + + for a in range(0, 360, step: 36) { + a *= 1deg + translate((1.5, 0, 0)) + group({ + let (o, a, b) = ((0,0), (calc.cos(a), calc.sin(a)), (calc.cos(a+120deg), calc.sin(a+120deg))) + line(a, o, b) + right-angle(o, a, b) + }) + } +}) + +#test-case({ + import draw: * + import angle: right-angle, angle + + scale(3) + let (o, a, b) = ((0,0), (0,1), (1,0)) + line(a, o, b) + right-angle(o, a, b, name: "angle") + for-each-anchor("angle", n => { + if n in ("a", "b", "origin", "corner", "label") { + circle("angle." + n, stroke: blue, radius: .1) + content("angle." + n, [#n]) + } + }) +})