1 use crate::coord
::cartesian
::{Cartesian2d, MeshLine}
;
2 use crate::coord
::ranged1d
::{KeyPointHint, Ranged}
;
3 use crate::coord
::{CoordTranslate, Shift}
;
4 use crate::element
::{CoordMapper, Drawable, PointCollection}
;
5 use crate::style
::text_anchor
::{HPos, Pos, VPos}
;
6 use crate::style
::{Color, SizeDesc, TextStyle}
;
8 /// The abstraction of a drawing area
9 use plotters_backend
::{BackendCoord, DrawingBackend, DrawingErrorKind}
;
11 use std
::borrow
::Borrow
;
12 use std
::cell
::RefCell
;
13 use std
::error
::Error
;
14 use std
::iter
::{once, repeat}
;
18 /// The representation of the rectangle in backend canvas
19 #[derive(Clone, Debug)]
28 /// Split the rectangle into a few smaller rectangles
29 fn split
<'a
, BPI
: IntoIterator
<Item
= &'a
i32> + 'a
>(
33 ) -> impl Iterator
<Item
= Rect
> + 'a
{
34 let (mut x0
, mut y0
) = (self.x0
, self.y0
);
35 let (full_x
, full_y
) = (self.x1
, self.y1
);
38 .chain(once(if vertical { &self.y1 }
else { &self.x1 }
))
40 let x1
= if vertical { full_x }
else { p }
;
41 let y1
= if vertical { p }
else { full_y }
;
42 let ret
= Rect { x0, y0, x1, y1 }
;
54 /// Evenly split the rectangle to a row * col mesh
55 fn split_evenly(&self, (row
, col
): (usize, usize)) -> impl Iterator
<Item
= Rect
> + '_
{
56 fn compute_evenly_split(from
: i32, to
: i32, n
: usize, idx
: usize) -> i32 {
57 let size
= (to
- from
) as usize;
58 from
+ idx
as i32 * (size
/ n
) as i32 + idx
.min(size
% n
) as i32
61 .flat_map(move |x
| repeat(x
).zip(0..col
))
62 .map(move |(ri
, ci
)| Self {
63 y0
: compute_evenly_split(self.y0
, self.y1
, row
, ri
),
64 y1
: compute_evenly_split(self.y0
, self.y1
, row
, ri
+ 1),
65 x0
: compute_evenly_split(self.x0
, self.x1
, col
, ci
),
66 x1
: compute_evenly_split(self.x0
, self.x1
, col
, ci
+ 1),
70 /// Evenly the rectangle into a grid with arbitrary breaks; return a rect iterator.
73 x_breaks
: impl Iterator
<Item
= i32>,
74 y_breaks
: impl Iterator
<Item
= i32>,
75 ) -> impl Iterator
<Item
= Rect
> {
76 let mut xs
= vec
![self.x0
, self.x1
];
77 let mut ys
= vec
![self.y0
, self.y1
];
78 xs
.extend(x_breaks
.map(|v
| v
+ self.x0
));
79 ys
.extend(y_breaks
.map(|v
| v
+ self.y0
));
84 let xsegs
: Vec
<_
> = xs
86 .zip(xs
.iter().skip(1))
87 .map(|(a
, b
)| (*a
, *b
))
90 // Justify: this is actually needed. Because we need to return a iterator that have
91 // static life time, thus we need to copy the value to a buffer and then turn the buffer
93 #[allow(clippy::needless_collect)]
94 let ysegs
: Vec
<_
> = ys
96 .zip(ys
.iter().skip(1))
97 .map(|(a
, b
)| (*a
, *b
))
100 ysegs
.into_iter().flat_map(move |(y0
, y1
)| {
104 .map(move |(x0
, x1
)| Self { x0, y0, x1, y1 }
)
108 /// Make the coordinate in the range of the rectangle
109 pub fn truncate(&self, p
: (i32, i32)) -> (i32, i32) {
110 (p
.0.min(self.x1
).max(self.x0
), p
.1.min(self.y1
).max(self.y0
))
114 /// The abstraction of a drawing area. Plotters uses drawing area as the fundamental abstraction for the
115 /// high level drawing API. The major functionality provided by the drawing area is
116 /// 1. Layout specification - Split the parent drawing area into sub-drawing-areas
117 /// 2. Coordinate Translation - Allows guest coordinate system attached and used for drawing.
118 /// 3. Element based drawing - drawing area provides the environment the element can be drawn onto it.
119 pub struct DrawingArea
<DB
: DrawingBackend
, CT
: CoordTranslate
> {
120 backend
: Rc
<RefCell
<DB
>>,
125 impl<DB
: DrawingBackend
, CT
: CoordTranslate
+ Clone
> Clone
for DrawingArea
<DB
, CT
> {
126 fn clone(&self) -> Self {
128 backend
: self.backend
.clone(),
129 rect
: self.rect
.clone(),
130 coord
: self.coord
.clone(),
135 /// The error description of any drawing area API
137 pub enum DrawingAreaErrorKind
<E
: Error
+ Send
+ Sync
> {
138 /// The error is due to drawing backend failure
139 BackendError(DrawingErrorKind
<E
>),
140 /// We are not able to get the mutable reference of the backend,
141 /// which indicates the drawing backend is current used by other
142 /// drawing operation
144 /// The error caused by invalid layout
148 impl<E
: Error
+ Send
+ Sync
> std
::fmt
::Display
for DrawingAreaErrorKind
<E
> {
149 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
) -> Result
<(), std
::fmt
::Error
> {
151 DrawingAreaErrorKind
::BackendError(e
) => write
!(fmt
, "backend error: {}", e
),
152 DrawingAreaErrorKind
::SharingError
=> {
153 write
!(fmt
, "Multiple backend operation in progress")
155 DrawingAreaErrorKind
::LayoutError
=> write
!(fmt
, "Bad layout"),
160 impl<E
: Error
+ Send
+ Sync
> Error
for DrawingAreaErrorKind
<E
> {}
162 #[allow(type_alias_bounds)]
163 type DrawingAreaError
<T
: DrawingBackend
> = DrawingAreaErrorKind
<T
::ErrorType
>;
165 impl<DB
: DrawingBackend
> From
<DB
> for DrawingArea
<DB
, Shift
> {
166 fn from(backend
: DB
) -> Self {
167 Self::with_rc_cell(Rc
::new(RefCell
::new(backend
)))
171 impl<'a
, DB
: DrawingBackend
> From
<&'a Rc
<RefCell
<DB
>>> for DrawingArea
<DB
, Shift
> {
172 fn from(backend
: &'a Rc
<RefCell
<DB
>>) -> Self {
173 Self::with_rc_cell(backend
.clone())
177 /// A type which can be converted into a root drawing area
178 pub trait IntoDrawingArea
: DrawingBackend
+ Sized
{
179 /// Convert the type into a root drawing area
180 fn into_drawing_area(self) -> DrawingArea
<Self, Shift
>;
183 impl<T
: DrawingBackend
> IntoDrawingArea
for T
{
184 fn into_drawing_area(self) -> DrawingArea
<T
, Shift
> {
189 impl<DB
: DrawingBackend
, X
: Ranged
, Y
: Ranged
> DrawingArea
<DB
, Cartesian2d
<X
, Y
>> {
190 /// Draw the mesh on a area
191 pub fn draw_mesh
<DrawFunc
, YH
: KeyPointHint
, XH
: KeyPointHint
>(
193 mut draw_func
: DrawFunc
,
196 ) -> Result
<(), DrawingAreaErrorKind
<DB
::ErrorType
>>
198 DrawFunc
: FnMut(&mut DB
, MeshLine
<X
, Y
>) -> Result
<(), DrawingErrorKind
<DB
::ErrorType
>>,
200 self.backend_ops(move |b
| {
202 .draw_mesh(y_count_max
, x_count_max
, |line
| draw_func(b
, line
))
206 /// Get the range of X of the guest coordinate for current drawing area
207 pub fn get_x_range(&self) -> Range
<X
::ValueType
> {
208 self.coord
.get_x_range()
211 /// Get the range of Y of the guest coordinate for current drawing area
212 pub fn get_y_range(&self) -> Range
<Y
::ValueType
> {
213 self.coord
.get_y_range()
216 /// Get the range of X of the backend coordinate for current drawing area
217 pub fn get_x_axis_pixel_range(&self) -> Range
<i32> {
218 self.coord
.get_x_axis_pixel_range()
221 /// Get the range of Y of the backend coordinate for current drawing area
222 pub fn get_y_axis_pixel_range(&self) -> Range
<i32> {
223 self.coord
.get_y_axis_pixel_range()
227 impl<DB
: DrawingBackend
, CT
: CoordTranslate
> DrawingArea
<DB
, CT
> {
228 /// Get the left upper conner of this area in the drawing backend
229 pub fn get_base_pixel(&self) -> BackendCoord
{
230 (self.rect
.x0
, self.rect
.y0
)
233 /// Strip the applied coordinate specification and returns a shift-based drawing area
234 pub fn strip_coord_spec(&self) -> DrawingArea
<DB
, Shift
> {
236 rect
: self.rect
.clone(),
237 backend
: self.backend
.clone(),
238 coord
: Shift((self.rect
.x0
, self.rect
.y0
)),
242 /// Strip the applied coordinate specification and returns a drawing area
243 pub fn use_screen_coord(&self) -> DrawingArea
<DB
, Shift
> {
245 rect
: self.rect
.clone(),
246 backend
: self.backend
.clone(),
247 coord
: Shift((0, 0)),
251 /// Get the area dimension in pixel
252 pub fn dim_in_pixel(&self) -> (u32, u32) {
254 (self.rect
.x1
- self.rect
.x0
) as u32,
255 (self.rect
.y1
- self.rect
.y0
) as u32,
259 /// Compute the relative size based on the drawing area's height
260 pub fn relative_to_height(&self, p
: f64) -> f64 {
261 f64::from((self.rect
.y1
- self.rect
.y0
).max(0)) * (p
.min(1.0).max(0.0))
264 /// Compute the relative size based on the drawing area's width
265 pub fn relative_to_width(&self, p
: f64) -> f64 {
266 f64::from((self.rect
.x1
- self.rect
.x0
).max(0)) * (p
.min(1.0).max(0.0))
269 /// Get the pixel range of this area
270 pub fn get_pixel_range(&self) -> (Range
<i32>, Range
<i32>) {
271 (self.rect
.x0
..self.rect
.x1
, self.rect
.y0
..self.rect
.y1
)
274 /// Perform operation on the drawing backend
275 fn backend_ops
<R
, O
: FnOnce(&mut DB
) -> Result
<R
, DrawingErrorKind
<DB
::ErrorType
>>>(
278 ) -> Result
<R
, DrawingAreaError
<DB
>> {
279 if let Ok(mut db
) = self.backend
.try_borrow_mut() {
281 .map_err(DrawingAreaErrorKind
::BackendError
)?
;
282 ops(&mut db
).map_err(DrawingAreaErrorKind
::BackendError
)
284 Err(DrawingAreaErrorKind
::SharingError
)
288 /// Fill the entire drawing area with a color
289 pub fn fill
<ColorType
: Color
>(&self, color
: &ColorType
) -> Result
<(), DrawingAreaError
<DB
>> {
290 self.backend_ops(|backend
| {
292 (self.rect
.x0
, self.rect
.y0
),
293 (self.rect
.x1
, self.rect
.y1
),
294 &color
.to_backend_color(),
300 /// Draw a single pixel
301 pub fn draw_pixel
<ColorType
: Color
>(
305 ) -> Result
<(), DrawingAreaError
<DB
>> {
306 let pos
= self.coord
.translate(&pos
);
307 self.backend_ops(|b
| b
.draw_pixel(pos
, color
.to_backend_color()))
310 /// Present all the pending changes to the backend
311 pub fn present(&self) -> Result
<(), DrawingAreaError
<DB
>> {
312 self.backend_ops(|b
| b
.present())
315 /// Draw an high-level element
316 pub fn draw
<'a
, E
, B
>(&self, element
: &'a E
) -> Result
<(), DrawingAreaError
<DB
>>
319 &'a E
: PointCollection
<'a
, CT
::From
, B
>,
322 let backend_coords
= element
.point_iter().into_iter().map(|p
| {
324 B
::map(&self.coord
, b
, &self.rect
)
326 self.backend_ops(move |b
| element
.draw(backend_coords
, b
, self.dim_in_pixel()))
329 /// Map coordinate to the backend coordinate
330 pub fn map_coordinate(&self, coord
: &CT
::From
) -> BackendCoord
{
331 self.coord
.translate(coord
)
334 /// Estimate the dimension of the text if drawn on this drawing area.
335 /// We can't get this directly from the font, since the drawing backend may or may not
336 /// follows the font configuration. In terminal, the font family will be dropped.
337 /// So the size of the text is drawing area related.
339 /// - `text`: The text we want to estimate
340 /// - `font`: The font spec in which we want to draw the text
341 /// - **return**: The size of the text if drawn on this area
342 pub fn estimate_text_size(
346 ) -> Result
<(u32, u32), DrawingAreaError
<DB
>> {
347 self.backend_ops(move |b
| b
.estimate_text_size(text
, style
))
351 impl<DB
: DrawingBackend
> DrawingArea
<DB
, Shift
> {
352 fn with_rc_cell(backend
: Rc
<RefCell
<DB
>>) -> Self {
353 let (x1
, y1
) = RefCell
::borrow(backend
.borrow()).get_size();
362 coord
: Shift((0, 0)),
366 /// Shrink the region, note all the locations are in guest coordinate
367 pub fn shrink
<A
: SizeDesc
, B
: SizeDesc
, C
: SizeDesc
, D
: SizeDesc
>(
371 ) -> DrawingArea
<DB
, Shift
> {
372 let left_upper
= (left_upper
.0.in_pixels
(&self), left_upper
.1.in_pixels
(&self));
373 let dimension
= (dimension
.0.in_pixels
(&self), dimension
.1.in_pixels
(&self));
374 self.rect
.x0
= self.rect
.x1
.min(self.rect
.x0
+ left_upper
.0);
375 self.rect
.y0
= self.rect
.y1
.min(self.rect
.y0
+ left_upper
.1);
377 self.rect
.x1
= self.rect
.x0
.max(self.rect
.x0
+ dimension
.0);
378 self.rect
.y1
= self.rect
.y0
.max(self.rect
.y0
+ dimension
.1);
380 self.coord
= Shift((self.rect
.x0
, self.rect
.y0
));
385 /// Apply a new coord transformation object and returns a new drawing area
386 pub fn apply_coord_spec
<CT
: CoordTranslate
>(&self, coord_spec
: CT
) -> DrawingArea
<DB
, CT
> {
388 rect
: self.rect
.clone(),
389 backend
: self.backend
.clone(),
394 /// Create a margin for the given drawing area and returns the new drawing area
395 pub fn margin
<ST
: SizeDesc
, SB
: SizeDesc
, SL
: SizeDesc
, SR
: SizeDesc
>(
401 ) -> DrawingArea
<DB
, Shift
> {
402 let left
= left
.in_pixels(self);
403 let right
= right
.in_pixels(self);
404 let top
= top
.in_pixels(self);
405 let bottom
= bottom
.in_pixels(self);
408 x0
: self.rect
.x0
+ left
,
409 y0
: self.rect
.y0
+ top
,
410 x1
: self.rect
.x1
- right
,
411 y1
: self.rect
.y1
- bottom
,
413 backend
: self.backend
.clone(),
414 coord
: Shift((self.rect
.x0
+ left
, self.rect
.y0
+ top
)),
418 /// Split the drawing area vertically
419 pub fn split_vertically
<S
: SizeDesc
>(&self, y
: S
) -> (Self, Self) {
420 let y
= y
.in_pixels(self);
421 let split_point
= [y
+ self.rect
.y0
];
422 let mut ret
= self.rect
.split(split_point
.iter(), true).map(|rect
| Self {
424 backend
: self.backend
.clone(),
425 coord
: Shift((rect
.x0
, rect
.y0
)),
428 (ret
.next().unwrap(), ret
.next().unwrap())
431 /// Split the drawing area horizontally
432 pub fn split_horizontally
<S
: SizeDesc
>(&self, x
: S
) -> (Self, Self) {
433 let x
= x
.in_pixels(self);
434 let split_point
= [x
+ self.rect
.x0
];
435 let mut ret
= self.rect
.split(split_point
.iter(), false).map(|rect
| Self {
437 backend
: self.backend
.clone(),
438 coord
: Shift((rect
.x0
, rect
.y0
)),
441 (ret
.next().unwrap(), ret
.next().unwrap())
444 /// Split the drawing area evenly
445 pub fn split_evenly(&self, (row
, col
): (usize, usize)) -> Vec
<Self> {
447 .split_evenly((row
, col
))
450 backend
: self.backend
.clone(),
451 coord
: Shift((rect
.x0
, rect
.y0
)),
456 /// Split the drawing area into a grid with specified breakpoints on both X axis and Y axis
457 pub fn split_by_breakpoints
<
469 xs
.as_ref().iter().map(|x
| x
.in_pixels(self)),
470 ys
.as_ref().iter().map(|x
| x
.in_pixels(self)),
474 backend
: self.backend
.clone(),
475 coord
: Shift((rect
.x0
, rect
.y0
)),
480 /// Draw a title of the drawing area and return the remaining drawing area
481 pub fn titled
<'a
, S
: Into
<TextStyle
<'a
>>>(
485 ) -> Result
<Self, DrawingAreaError
<DB
>> {
486 let style
= style
.into();
488 let x_padding
= (self.rect
.x1
- self.rect
.x0
) / 2;
490 let (_
, text_h
) = self.estimate_text_size(text
, &style
)?
;
491 let y_padding
= (text_h
/ 2).min(5) as i32;
493 let style
= &style
.pos(Pos
::new(HPos
::Center
, VPos
::Top
));
495 self.backend_ops(|b
| {
499 (self.rect
.x0
+ x_padding
, self.rect
.y0
+ y_padding
),
506 y0
: self.rect
.y0
+ y_padding
* 2 + text_h
as i32,
510 backend
: self.backend
.clone(),
511 coord
: Shift((self.rect
.x0
, self.rect
.y0
+ y_padding
* 2 + text_h
as i32)),
515 /// Draw text on the drawing area
521 ) -> Result
<(), DrawingAreaError
<DB
>> {
522 self.backend_ops(|b
| b
.draw_text(text
, style
, (pos
.0 + self.rect
.x0
, pos
.1 + self.rect
.y0
)))
526 impl<DB
: DrawingBackend
, CT
: CoordTranslate
> DrawingArea
<DB
, CT
> {
527 /// Returns the coordinates by value
528 pub fn into_coord_spec(self) -> CT
{
532 /// Returns the coordinates by reference
533 pub fn as_coord_spec(&self) -> &CT
{
537 /// Returns the coordinates by mutable reference
538 pub fn as_coord_spec_mut(&mut self) -> &mut CT
{
544 mod drawing_area_tests
{
545 use crate::{create_mocked_drawing_area, prelude::*}
;
548 let drawing_area
= create_mocked_drawing_area(1024, 768, |m
| {
549 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
550 assert_eq
!(c
, WHITE
.to_rgba());
552 assert_eq
!(u
, (0, 0));
553 assert_eq
!(d
, (1024, 768));
557 assert_eq
!(b
.num_draw_rect_call
, 1);
558 assert_eq
!(b
.draw_count
, 1);
562 drawing_area
.fill(&WHITE
).expect("Drawing Failure");
566 fn test_split_evenly() {
568 &RED
, &BLUE
, &YELLOW
, &WHITE
, &BLACK
, &MAGENTA
, &CYAN
, &BLUE
, &RED
,
570 let drawing_area
= create_mocked_drawing_area(902, 900, |m
| {
573 let colors
= colors
.clone();
574 m
.check_draw_rect(move |c
, _
, f
, u
, d
| {
575 assert_eq
!(c
, colors
[col
* 3 + row
].to_rgba());
577 assert_eq
!(u
, (300 * row
as i32 + 2.min(row
) as i32, 300 * col
as i32));
581 300 + 300 * row
as i32 + 2.min(row
+ 1) as i32,
582 300 + 300 * col
as i32
589 assert_eq
!(b
.num_draw_rect_call
, 9);
590 assert_eq
!(b
.draw_count
, 9);
595 .split_evenly((3, 3))
599 d
.fill(*c
).expect("Drawing Failure");
604 fn test_split_horizontally() {
605 let drawing_area
= create_mocked_drawing_area(1024, 768, |m
| {
606 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
607 assert_eq
!(c
, RED
.to_rgba());
609 assert_eq
!(u
, (0, 0));
610 assert_eq
!(d
, (345, 768));
613 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
614 assert_eq
!(c
, BLUE
.to_rgba());
616 assert_eq
!(u
, (345, 0));
617 assert_eq
!(d
, (1024, 768));
621 assert_eq
!(b
.num_draw_rect_call
, 2);
622 assert_eq
!(b
.draw_count
, 2);
626 let (left
, right
) = drawing_area
.split_horizontally(345);
627 left
.fill(&RED
).expect("Drawing Error");
628 right
.fill(&BLUE
).expect("Drawing Error");
632 fn test_split_vertically() {
633 let drawing_area
= create_mocked_drawing_area(1024, 768, |m
| {
634 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
635 assert_eq
!(c
, RED
.to_rgba());
637 assert_eq
!(u
, (0, 0));
638 assert_eq
!(d
, (1024, 345));
641 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
642 assert_eq
!(c
, BLUE
.to_rgba());
644 assert_eq
!(u
, (0, 345));
645 assert_eq
!(d
, (1024, 768));
649 assert_eq
!(b
.num_draw_rect_call
, 2);
650 assert_eq
!(b
.draw_count
, 2);
654 let (left
, right
) = drawing_area
.split_vertically(345);
655 left
.fill(&RED
).expect("Drawing Error");
656 right
.fill(&BLUE
).expect("Drawing Error");
660 fn test_split_grid() {
662 &RED
, &BLUE
, &YELLOW
, &WHITE
, &BLACK
, &MAGENTA
, &CYAN
, &BLUE
, &RED
,
664 let breaks
: [i32; 5] = [100, 200, 300, 400, 500];
668 let drawing_area
= create_mocked_drawing_area(1024, 768, |m
| {
671 let get_bp
= |full
, limit
, id
| {
674 } else if id
> limit
{
677 breaks
[id
as usize - 1]
681 let expected_u
= (get_bp(1024, nxb
, col
), get_bp(768, nyb
, row
));
683 (get_bp(1024, nxb
, col
+ 1), get_bp(768, nyb
, row
+ 1));
685 colors
[(row
* (nxb
+ 1) + col
) as usize % colors
.len()];
687 m
.check_draw_rect(move |c
, _
, f
, u
, d
| {
688 assert_eq
!(c
, expected_color
.to_rgba());
690 assert_eq
!(u
, expected_u
);
691 assert_eq
!(d
, expected_d
);
696 m
.drop_check(move |b
| {
697 assert_eq
!(b
.num_draw_rect_call
, ((nxb
+ 1) * (nyb
+ 1)) as u32);
698 assert_eq
!(b
.draw_count
, ((nyb
+ 1) * (nxb
+ 1)) as u32);
702 let result
= drawing_area
703 .split_by_breakpoints(&breaks
[0..nxb
as usize], &breaks
[0..nyb
as usize]);
704 for i
in 0..result
.len() {
706 .fill(colors
[i
% colors
.len()])
707 .expect("Drawing Error");
714 let drawing_area
= create_mocked_drawing_area(1024, 768, |m
| {
715 m
.check_draw_text(|c
, font
, size
, _pos
, text
| {
716 assert_eq
!(c
, BLACK
.to_rgba());
717 assert_eq
!(font
, "serif");
718 assert_eq
!(size
, 30.0);
719 assert_eq
!("This is the title", text
);
721 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
722 assert_eq
!(c
, WHITE
.to_rgba());
726 assert_eq
!(d
, (1024, 768));
729 assert_eq
!(b
.num_draw_text_call
, 1);
730 assert_eq
!(b
.num_draw_rect_call
, 1);
731 assert_eq
!(b
.draw_count
, 2);
736 .titled("This is the title", ("serif", 30))
744 let drawing_area
= create_mocked_drawing_area(1024, 768, |m
| {
745 m
.check_draw_rect(|c
, _
, f
, u
, d
| {
746 assert_eq
!(c
, WHITE
.to_rgba());
748 assert_eq
!(u
, (3, 1));
749 assert_eq
!(d
, (1024 - 4, 768 - 2));
753 assert_eq
!(b
.num_draw_rect_call
, 1);
754 assert_eq
!(b
.draw_count
, 1);
761 .expect("Drawing Failure");
766 let drawing_area
= create_mocked_drawing_area(1024, 768, |_m
| {}
)
767 .apply_coord_spec(Cartesian2d
::<
768 crate::coord
::types
::RangedCoordi32
,
769 crate::coord
::types
::RangedCoordu32
,
770 >::new(-100..100, 0..200, (0..1024, 0..768)));
772 let x_range
= drawing_area
.get_x_range();
773 assert_eq
!(x_range
, -100..100);
775 let y_range
= drawing_area
.get_y_range();
776 assert_eq
!(y_range
, 0..200);
780 fn test_relative_size() {
781 let drawing_area
= create_mocked_drawing_area(1024, 768, |_m
| {}
);
783 assert_eq
!(102.4, drawing_area
.relative_to_width(0.1));
784 assert_eq
!(384.0, drawing_area
.relative_to_height(0.5));
786 assert_eq
!(1024.0, drawing_area
.relative_to_width(1.3));
787 assert_eq
!(768.0, drawing_area
.relative_to_height(1.5));
789 assert_eq
!(0.0, drawing_area
.relative_to_width(-0.2));
790 assert_eq
!(0.0, drawing_area
.relative_to_height(-0.5));
794 fn test_relative_split() {
795 let drawing_area
= create_mocked_drawing_area(1000, 1200, |m
| {
797 m
.check_draw_rect(move |c
, _
, f
, u
, d
| {
802 assert_eq
!(c
, RED
.to_rgba());
803 assert_eq
!(u
, (0, 0));
804 assert_eq
!(d
, (300, 600));
807 assert_eq
!(c
, BLUE
.to_rgba());
808 assert_eq
!(u
, (300, 0));
809 assert_eq
!(d
, (1000, 600));
812 assert_eq
!(c
, GREEN
.to_rgba());
813 assert_eq
!(u
, (0, 600));
814 assert_eq
!(d
, (300, 1200));
817 assert_eq
!(c
, WHITE
.to_rgba());
818 assert_eq
!(u
, (300, 600));
819 assert_eq
!(d
, (1000, 1200));
821 _
=> panic
!("Too many draw rect"),
828 assert_eq
!(b
.num_draw_rect_call
, 4);
829 assert_eq
!(b
.draw_count
, 4);
834 drawing_area
.split_by_breakpoints([(30).percent_width()], [(50).percent_height()]);
836 split
[0].fill(&RED
).unwrap();
837 split
[1].fill(&BLUE
).unwrap();
838 split
[2].fill(&GREEN
).unwrap();
839 split
[3].fill(&WHITE
).unwrap();
843 fn test_relative_shrink() {
844 let drawing_area
= create_mocked_drawing_area(1000, 1200, |m
| {
845 m
.check_draw_rect(move |_
, _
, _
, u
, d
| {
846 assert_eq
!((100, 100), u
);
847 assert_eq
!((300, 700), d
);
851 assert_eq
!(b
.num_draw_rect_call
, 1);
852 assert_eq
!(b
.draw_count
, 1);
855 .shrink(((10).percent_width(), 100), (200, (50).percent_height()));
857 drawing_area
.fill(&RED
).unwrap();