-
-
Notifications
You must be signed in to change notification settings - Fork 346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(widget): add circle widget #159
Conversation
Good idea. Naive is probably fine, though if you care about it you can avoid 7/8s of the trig calls by reflecting at 45 degrees and plotting that point 8 times. (e.g. at (x,y),(y,x),(-y,x),(-x,y),(-x,-y),(-y,-x),(x,-y),(y,-x)). And Bresenham's algorithm is there if you want ever need to avoid the calls altogether and also decrease (or increase for large circles) the number of points actually plotted. One thing missing on this is tests. Would you mind adding some? Unit tests can be as simple as: let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
let canvas = ...
canvas.render(buffer.area, buffer);
let expected = Buffer::with_lines(vec![
" .... "
" . . "
]);
assert_eq(buffer, expected); Also, perhaps add a circle to the demo / canvas examples? |
@joshka I've added a unit test (trickier than you'd think to create a unit-test sized example!) and also added a Circle to the demo app on the map. |
I asked phind.com (basically ChatGPT 4) to apply your PR comment feedback to the algorithm and it come up with the following: impl Shape for Circle {
fn draw(&self, painter: &mut Painter<'_, '_>) {
let mut x = 0;
let mut y = self.radius as i64;
let mut d = 3 - 2 * (self.radius as i64);
while x <= y {
let points = [
(self.x + x as f64, self.y + y as f64),
(self.x + y as f64, self.y + x as f64),
(self.x - x as f64, self.y + y as f64),
(self.x - y as f64, self.y + x as f64),
(self.x + x as f64, self.y - y as f64),
(self.x + y as f64, self.y - x as f64),
(self.x - x as f64, self.y - y as f64),
(self.x - y as f64, self.y - x as f64),
];
for &(x, y) in points.iter() {
if let Some((x, y)) = painter.get_point(x, y) {
painter.paint(x, y, self.color);
}
}
if d < 0 {
d += 4 * x as i64 + 6;
} else {
d += 4 * (x as i64 - y as i64) + 10;
y -= 1;
}
x += 1;
}
}
} I tried it locally and it works, though I thought the circles weren't as round for low res terminal UIs. So I'd say better to stick to the naive algorithm. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - the make it fast part of this is not blocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested, works well. Approved!
Adds a simple
Circle
widget.This was something I needed to show an accuracy radius on the world map and thought it may be useful to upstream. There may be more efficient circle drawing algorithms, especially for low-res terminal UIs, this is just the naive textbook implementation.