Skip to content
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

Implement fill for circles and rectangles #2

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description = "Ratatui widget to draw a plotters chart"
plotters-backend = "0.3.5"
ratatui = "0.26.0"
thiserror = "1.0.50"
plotters = {version = "0.3.5", optional = true, default-features = false}
plotters = {version = "0.3.5", optional = true, default-features = true}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this?

log = "0.4.20"

[features]
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ A ratatui widget for drawing a plotters chart.
<img src="examples/mandelbrot.png" width=200px />
<img src="examples/normal-dist.png" width=200px />
<img src="examples/3d-plot.png" width=200px />

## Tests

When running tests please note the following constraints.

The tests *require* human interaction. An image is displayed in the upper section of the terminal, with a yes/no question in the lower section. Inspect the image, and respond to the question with 'y' or 'n' as appropriate.
Copy link
Owner

@SOF3 SOF3 Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm, these are called examples not tests. Tests should be runnable in CI.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to look at how ratatui tests its own widgets,

Copy link
Author

@ebardie ebardie Feb 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I humbly disagree - these are tests not examples. I didn't like them needing a human to perform the pass/fail assessment, but I found them more helpful than having no tests :)

Thank you for the pointer to Ratatui's test capabilities. I'll rework and resubmit at some point.


Because the tests require a `ratatui` display loop, they **MUST** be run single threaded `--test-threads=1`, e.g. `cargo test -- --test-threads=1`.
4 changes: 4 additions & 0 deletions examples/boilerplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ pub fn main_boilerplate(

Ok(())
}

#[warn(dead_code)]
fn main() {}

72 changes: 50 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod widget;
#[cfg(feature = "widget")]
pub use widget::*;

pub const CHAR_PIXEL_SIZE: u32 = 4;
pub const CHAR_PIXEL_SIZE: (u32, u32) = (2, 4);

pub struct RatatuiBackend<'a, 'b> {
pub canvas: &'a mut canvas::Context<'b>,
Expand Down Expand Up @@ -41,7 +41,7 @@ impl<'a, 'b> DrawingBackend for RatatuiBackend<'a, 'b> {
mut coord: BackendCoord,
) -> Result {
let width = text.chars().count();
coord.0 -= (width as u32 * CHAR_PIXEL_SIZE / 2) as i32;
coord.0 -= (width as u32 * CHAR_PIXEL_SIZE.0 / 2) as i32;

let (x, y) = backend_to_canvas_coords(coord, self.size);
self.canvas.print(x, y, text::Line::styled(text.to_string(), convert_style(style)));
Expand All @@ -66,15 +66,27 @@ impl<'a, 'b> DrawingBackend for RatatuiBackend<'a, 'b> {
coord: BackendCoord,
radius: u32,
style: &S,
_fill: bool,
fill: bool,
) -> std::result::Result<(), DrawingErrorKind<Self::ErrorType>> {
let (x, y) = backend_to_canvas_coords(coord, self.size);
self.canvas.draw(&canvas::Circle {
x,
y,
radius: radius.into(),
color: convert_color(style.color()),
});
if fill {
let radius = radius as i32;
for dy in -radius..=radius {
let half_width = ((radius.pow(2) - dy.pow(2)) as f64).sqrt() as i32;
self.draw_line(
(coord.0 - half_width, coord.1 + dy),
(coord.0 + half_width, coord.1 + dy),
style,
)?;
}
} else {
self.canvas.draw(&canvas::Circle {
x,
y,
radius: radius.into(),
color: convert_color(style.color()),
});
}
Ok(())
}

Expand All @@ -83,18 +95,34 @@ impl<'a, 'b> DrawingBackend for RatatuiBackend<'a, 'b> {
coord1: BackendCoord,
coord2: BackendCoord,
style: &S,
_fill: bool,
fill: bool,
) -> std::result::Result<(), DrawingErrorKind<Self::ErrorType>> {
let (x1, y1) = backend_to_canvas_coords(coord1, self.size);
let (x2, y2) = backend_to_canvas_coords(coord2, self.size);

self.canvas.draw(&canvas::Rectangle {
x: x1.min(x2),
y: y1.min(y2),
width: (x2 - x1).abs(),
height: (y2 - y1).abs(),
color: convert_color(style.color()),
});
if fill {
let color = convert_color(style.color());

let (start, stop) = (
(coord1.0.min(coord2.0), coord1.1.min(coord2.1)),
(coord1.0.max(coord2.0), coord1.1.max(coord2.1)),
);

for x in start.0..=stop.0 {
let (x1, y1) = backend_to_canvas_coords((x, start.1), self.size);
let (x2, y2) = backend_to_canvas_coords((x, stop.1), self.size);

self.canvas.draw(&canvas::Line::new(x1, y1, x2, y2, color));
}
} else {
let (x1, y1) = backend_to_canvas_coords(coord1, self.size);
let (x2, y2) = backend_to_canvas_coords(coord2, self.size);

self.canvas.draw(&canvas::Rectangle {
x: x1.min(x2),
y: y1.min(y2),
width: (x2 - x1).abs(),
height: (y2 - y1).abs(),
color: convert_color(style.color()),
});
}
Ok(())
}

Expand All @@ -103,12 +131,12 @@ impl<'a, 'b> DrawingBackend for RatatuiBackend<'a, 'b> {
text: &str,
_style: &TStyle,
) -> std::result::Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
Ok((text.chars().count() as u32 * CHAR_PIXEL_SIZE, CHAR_PIXEL_SIZE))
Ok((text.chars().count() as u32 * CHAR_PIXEL_SIZE.0, CHAR_PIXEL_SIZE.1))
}
}

fn rect_to_size(rect: layout::Rect) -> (u32, u32) {
(u32::from(rect.width) * CHAR_PIXEL_SIZE, u32::from(rect.height) * CHAR_PIXEL_SIZE)
(u32::from(rect.width) * CHAR_PIXEL_SIZE.0, u32::from(rect.height) * CHAR_PIXEL_SIZE.1)
}

fn backend_to_canvas_coords((x, y): BackendCoord, rect: layout::Rect) -> (f64, f64) {
Expand Down
Loading
Loading