Skip to content

Commit 65d856d

Browse files
committed
circle: Support two point circle construction
1 parent ae0ebfb commit 65d856d

File tree

4 files changed

+54
-34
lines changed

4 files changed

+54
-34
lines changed

src/draw/shapes.typ

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@
2727
/// circle((0,0))
2828
/// // Draws an ellipse
2929
/// circle((0,-2), radius: (0.75, 0.5))
30+
/// // Draws a circle at (0, 2) through point (1, 3)
31+
/// circle((0,2), (rel: (1,1)))
3032
/// ```
3133
///
32-
/// - position (coordinate): The position to place the circle on.
34+
/// - ..points-style (coordinate, style): The position to place the circle on.
35+
/// If given two coordinates, the distance between them is used as radius.
36+
/// If given a single coordinate, the radius can be set via the `radius` (style)
37+
/// argument.
3338
/// - name (none,str):
3439
/// - anchor (none, str):
35-
/// - ..style (style):
3640
///
3741
/// ### Styling
3842
/// *Root*: `circle`
@@ -42,20 +46,33 @@
4246
/// ### Anchors
4347
/// Supports border and path anchors. The `"center"` anchor is the default.
4448
///
45-
#let circle(position, name: none, anchor: none, ..style) = {
46-
// No extra positional arguments from the style sink
47-
assert.eq(
48-
style.pos(),
49-
(),
50-
message: "Unexpected positional arguments: " + repr(style.pos()),
51-
)
52-
let style = style.named()
49+
#let circle(..points-style, name: none, anchor: none) = {
50+
let style = points-style.named()
51+
let points = points-style.pos()
52+
assert(points.len() in (1, 2),
53+
message: "circle expects one or two points, got " + repr(points))
54+
assert(points.len() != 2 or "radius" not in style,
55+
message: "unexpected radius for circle constructed by two points")
5356

5457
(ctx => {
55-
let (ctx, pos) = coordinate.resolve(ctx, position)
58+
let (center, outer) = if points.len() == 1 {
59+
(points.at(0), none)
60+
} else {
61+
points
62+
}
63+
64+
let (ctx, center) = coordinate.resolve(ctx, center)
5665
let style = styles.resolve(ctx.style, merge: style, root: "circle")
57-
let (rx, ry) = util.resolve-radius(style.radius).map(util.resolve-number.with(ctx))
58-
let (cx, cy, cz) = pos
66+
67+
// If we got two points, use the second one to calculate
68+
// the radius.
69+
let (rx, ry) = if outer != none {
70+
(ctx, outer) = coordinate.resolve(ctx, outer, update: false)
71+
(vector.dist(center, outer),) * 2
72+
} else {
73+
util.resolve-radius(style.radius).map(util.resolve-number.with(ctx))
74+
}
75+
let (cx, cy, cz) = center
5976

6077
let drawables = drawable.ellipse(
6178
cx, cy, cz,
@@ -65,7 +82,7 @@
6582
)
6683

6784
let (transform, anchors) = anchor_.setup(
68-
(_) => pos,
85+
(_) => center,
6986
("center",),
7087
default: "center",
7188
name: name,

tests/circle/ref/1.png

-2.65 KB
Loading

tests/circle/test.typ

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
11
#set page(width: auto, height: auto)
22
#import "/src/lib.typ": *
3+
#import "/tests/helper.typ": *
34

4-
#box(stroke: 2pt + red, canvas(length: .5cm, {
5-
import draw: *
5+
#test-case(radius => {
6+
import draw: *
67

7-
set-style(radius: (4, .5), stroke: none)
8-
for r in range(0, 6) {
9-
group({
10-
translate((4.5, 4.5))
11-
rotate(r * 30deg)
8+
circle((0, 0), radius: radius, name: "c")
9+
point("c.center", "M")
10+
}, args: (1, 1cm, (1, .5), (1cm, .5), (.5, 1), (.5, 1cm)))
1211

13-
circle((0,0), fill: (red, green, blue, yellow).at(calc.rem(r, 4)))
14-
})
15-
}
12+
#test-case(outer => {
13+
import draw: *
1614

17-
set-style(radius: 0.45, stroke: black, fill: none)
18-
for x in range(0, 10) {
19-
for y in range(0, 10) {
20-
circle((x, y))
21-
}
22-
}
23-
}))
15+
let center = (1, 1)
16+
circle(center, outer)
17+
move-to(center)
18+
point(outer, "O")
19+
point(center, "M")
20+
}, args: ((2, 1), (rel: (1, 0)), (rel: (1, 1))))
2421

25-
#box(stroke: 2pt + red, canvas(length: .5cm, {
22+
#test-case({
2623
import draw: *
2724

28-
for z in range(-2, 2) {
25+
for z in range(-1, 2) {
2926
circle((0,0,z))
3027
}
31-
}))
28+
})

tests/helper.typ

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#import "/src/lib.typ" as cetz
22

3+
/// Draw a point + label
4+
#let point(pt, name) = {
5+
cetz.draw.circle(pt, radius: .05cm, fill: black, stroke: none, name: "pt")
6+
cetz.draw.content((rel: (.2cm, 0), to: "pt"), [#name], anchor: "west")
7+
}
8+
39
/// Draw a cross at position pt
410
#let cross(pt, size: .25, ..style) = {
511
import cetz.draw: *

0 commit comments

Comments
 (0)