1use crate::coord::cartesian::{Cartesian2d, MeshLine};
2use crate::coord::ranged1d::{KeyPointHint, Ranged};
3use crate::coord::{CoordTranslate, Shift};
4use crate::element::{CoordMapper, Drawable, PointCollection};
5use crate::style::text_anchor::{HPos, Pos, VPos};
6use crate::style::{Color, SizeDesc, TextStyle};
7
8use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
10
11use std::borrow::Borrow;
12use std::cell::RefCell;
13use std::error::Error;
14use std::iter::{once, repeat};
15use std::ops::Range;
16use std::rc::Rc;
17
18#[derive(Clone, Debug)]
20pub struct Rect {
21 x0: i32,
22 y0: i32,
23 x1: i32,
24 y1: i32,
25}
26
27impl Rect {
28 fn split<'a, BPI: IntoIterator<Item = &'a i32> + 'a>(
30 &'a self,
31 break_points: BPI,
32 vertical: bool,
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);
36 break_points
37 .into_iter()
38 .chain(once(if vertical { &self.y1 } else { &self.x1 }))
39 .map(move |&p| {
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 };
43
44 if vertical {
45 y0 = y1
46 } else {
47 x0 = x1;
48 }
49
50 ret
51 })
52 }
53
54 fn split_evenly<'a>(&'a self, (row, col): (usize, usize)) -> impl Iterator<Item = Rect> + 'a {
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
59 }
60 (0..row)
61 .map(move |x| repeat(x).zip(0..col))
62 .flatten()
63 .map(move |(ri, ci)| Self {
64 y0: compute_evenly_split(self.y0, self.y1, row, ri),
65 y1: compute_evenly_split(self.y0, self.y1, row, ri + 1),
66 x0: compute_evenly_split(self.x0, self.x1, col, ci),
67 x1: compute_evenly_split(self.x0, self.x1, col, ci + 1),
68 })
69 }
70
71 fn split_grid(
72 &self,
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));
80
81 xs.sort();
82 ys.sort();
83
84 let xsegs: Vec<_> = xs
85 .iter()
86 .zip(xs.iter().skip(1))
87 .map(|(a, b)| (*a, *b))
88 .collect();
89 let ysegs: Vec<_> = ys
90 .iter()
91 .zip(ys.iter().skip(1))
92 .map(|(a, b)| (*a, *b))
93 .collect();
94
95 ysegs
96 .into_iter()
97 .map(move |(y0, y1)| {
98 xsegs
99 .clone()
100 .into_iter()
101 .map(move |(x0, x1)| Self { x0, y0, x1, y1 })
102 })
103 .flatten()
104 }
105
106 pub fn truncate(&self, p: (i32, i32)) -> (i32, i32) {
108 (p.0.min(self.x1).max(self.x0), p.1.min(self.y1).max(self.y0))
109 }
110}
111
112pub struct DrawingArea<DB: DrawingBackend, CT: CoordTranslate> {
118 backend: Rc<RefCell<DB>>,
119 rect: Rect,
120 coord: CT,
121}
122
123impl<DB: DrawingBackend, CT: CoordTranslate + Clone> Clone for DrawingArea<DB, CT> {
124 fn clone(&self) -> Self {
125 Self {
126 backend: self.backend.clone(),
127 rect: self.rect.clone(),
128 coord: self.coord.clone(),
129 }
130 }
131}
132
133#[derive(Debug)]
135pub enum DrawingAreaErrorKind<E: Error + Send + Sync> {
136 BackendError(DrawingErrorKind<E>),
138 SharingError,
142 LayoutError,
144}
145
146impl<E: Error + Send + Sync> std::fmt::Display for DrawingAreaErrorKind<E> {
147 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
148 match self {
149 DrawingAreaErrorKind::BackendError(e) => write!(fmt, "backend error: {}", e),
150 DrawingAreaErrorKind::SharingError => {
151 write!(fmt, "Multiple backend operation in progress")
152 }
153 DrawingAreaErrorKind::LayoutError => write!(fmt, "Bad layout"),
154 }
155 }
156}
157
158impl<E: Error + Send + Sync> Error for DrawingAreaErrorKind<E> {}
159
160#[allow(type_alias_bounds)]
161type DrawingAreaError<T: DrawingBackend> = DrawingAreaErrorKind<T::ErrorType>;
162
163impl<DB: DrawingBackend> From<DB> for DrawingArea<DB, Shift> {
164 fn from(backend: DB) -> Self {
165 Self::with_rc_cell(Rc::new(RefCell::new(backend)))
166 }
167}
168
169impl<'a, DB: DrawingBackend> From<&'a Rc<RefCell<DB>>> for DrawingArea<DB, Shift> {
170 fn from(backend: &'a Rc<RefCell<DB>>) -> Self {
171 Self::with_rc_cell(backend.clone())
172 }
173}
174
175pub trait IntoDrawingArea: DrawingBackend + Sized {
177 fn into_drawing_area(self) -> DrawingArea<Self, Shift>;
179}
180
181impl<T: DrawingBackend> IntoDrawingArea for T {
182 fn into_drawing_area(self) -> DrawingArea<T, Shift> {
183 self.into()
184 }
185}
186
187impl<DB: DrawingBackend, X: Ranged, Y: Ranged> DrawingArea<DB, Cartesian2d<X, Y>> {
188 pub fn draw_mesh<DrawFunc, YH: KeyPointHint, XH: KeyPointHint>(
190 &self,
191 mut draw_func: DrawFunc,
192 y_count_max: YH,
193 x_count_max: XH,
194 ) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>
195 where
196 DrawFunc: FnMut(&mut DB, MeshLine<X, Y>) -> Result<(), DrawingErrorKind<DB::ErrorType>>,
197 {
198 self.backend_ops(move |b| {
199 self.coord
200 .draw_mesh(y_count_max, x_count_max, |line| draw_func(b, line))
201 })
202 }
203
204 pub fn get_x_range(&self) -> Range<X::ValueType> {
206 self.coord.get_x_range()
207 }
208
209 pub fn get_y_range(&self) -> Range<Y::ValueType> {
211 self.coord.get_y_range()
212 }
213
214 pub fn get_x_axis_pixel_range(&self) -> Range<i32> {
215 self.coord.get_x_axis_pixel_range()
216 }
217
218 pub fn get_y_axis_pixel_range(&self) -> Range<i32> {
219 self.coord.get_y_axis_pixel_range()
220 }
221}
222
223impl<DB: DrawingBackend, CT: CoordTranslate> DrawingArea<DB, CT> {
224 pub fn get_base_pixel(&self) -> BackendCoord {
226 (self.rect.x0, self.rect.y0)
227 }
228
229 pub fn strip_coord_spec(&self) -> DrawingArea<DB, Shift> {
231 DrawingArea {
232 rect: self.rect.clone(),
233 backend: self.backend.clone(),
234 coord: Shift((self.rect.x0, self.rect.y0)),
235 }
236 }
237
238 pub fn use_screen_coord(&self) -> DrawingArea<DB, Shift> {
239 DrawingArea {
240 rect: self.rect.clone(),
241 backend: self.backend.clone(),
242 coord: Shift((0, 0)),
243 }
244 }
245
246 pub fn dim_in_pixel(&self) -> (u32, u32) {
248 (
249 (self.rect.x1 - self.rect.x0) as u32,
250 (self.rect.y1 - self.rect.y0) as u32,
251 )
252 }
253
254 pub fn relative_to_height(&self, p: f64) -> f64 {
256 f64::from((self.rect.y1 - self.rect.y0).max(0)) * (p.min(1.0).max(0.0))
257 }
258
259 pub fn relative_to_width(&self, p: f64) -> f64 {
261 f64::from((self.rect.x1 - self.rect.x0).max(0)) * (p.min(1.0).max(0.0))
262 }
263
264 pub fn get_pixel_range(&self) -> (Range<i32>, Range<i32>) {
266 (self.rect.x0..self.rect.x1, self.rect.y0..self.rect.y1)
267 }
268
269 fn backend_ops<R, O: FnOnce(&mut DB) -> Result<R, DrawingErrorKind<DB::ErrorType>>>(
271 &self,
272 ops: O,
273 ) -> Result<R, DrawingAreaError<DB>> {
274 if let Ok(mut db) = self.backend.try_borrow_mut() {
275 db.ensure_prepared()
276 .map_err(DrawingAreaErrorKind::BackendError)?;
277 ops(&mut db).map_err(DrawingAreaErrorKind::BackendError)
278 } else {
279 Err(DrawingAreaErrorKind::SharingError)
280 }
281 }
282
283 pub fn fill<ColorType: Color>(&self, color: &ColorType) -> Result<(), DrawingAreaError<DB>> {
285 self.backend_ops(|backend| {
286 backend.draw_rect(
287 (self.rect.x0, self.rect.y0),
288 (self.rect.x1 - 1, self.rect.y1 - 1),
289 &color.to_backend_color(),
290 true,
291 )
292 })
293 }
294
295 pub fn draw_pixel<ColorType: Color>(
297 &self,
298 pos: CT::From,
299 color: &ColorType,
300 ) -> Result<(), DrawingAreaError<DB>> {
301 let pos = self.coord.translate(&pos);
302 self.backend_ops(|b| b.draw_pixel(pos, color.to_backend_color()))
303 }
304
305 pub fn present(&self) -> Result<(), DrawingAreaError<DB>> {
307 self.backend_ops(|b| b.present())
308 }
309
310 pub fn draw<'a, E, B>(&self, element: &'a E) -> Result<(), DrawingAreaError<DB>>
312 where
313 B: CoordMapper,
314 &'a E: PointCollection<'a, CT::From, B>,
315 E: Drawable<DB, B>,
316 {
317 let backend_coords = element.point_iter().into_iter().map(|p| {
318 let b = p.borrow();
319 B::map(&self.coord, b, &self.rect)
320 });
321 self.backend_ops(move |b| element.draw(backend_coords, b, self.dim_in_pixel()))
322 }
323
324 pub fn map_coordinate(&self, coord: &CT::From) -> BackendCoord {
326 self.coord.translate(coord)
327 }
328
329 pub fn estimate_text_size(
338 &self,
339 text: &str,
340 style: &TextStyle,
341 ) -> Result<(u32, u32), DrawingAreaError<DB>> {
342 self.backend_ops(move |b| b.estimate_text_size(text, style))
343 }
344}
345
346impl<DB: DrawingBackend> DrawingArea<DB, Shift> {
347 fn with_rc_cell(backend: Rc<RefCell<DB>>) -> Self {
348 let (x1, y1) = RefCell::borrow(backend.borrow()).get_size();
349 Self {
350 rect: Rect {
351 x0: 0,
352 y0: 0,
353 x1: x1 as i32,
354 y1: y1 as i32,
355 },
356 backend,
357 coord: Shift((0, 0)),
358 }
359 }
360
361 pub fn shrink<A: SizeDesc, B: SizeDesc, C: SizeDesc, D: SizeDesc>(
363 mut self,
364 left_upper: (A, B),
365 dimension: (C, D),
366 ) -> DrawingArea<DB, Shift> {
367 let left_upper = (left_upper.0.in_pixels(&self), left_upper.1.in_pixels(&self));
368 let dimension = (dimension.0.in_pixels(&self), dimension.1.in_pixels(&self));
369 self.rect.x0 = self.rect.x1.min(self.rect.x0 + left_upper.0);
370 self.rect.y0 = self.rect.y1.min(self.rect.y0 + left_upper.1);
371
372 self.rect.x1 = self.rect.x0.max(self.rect.x0 + dimension.0);
373 self.rect.y1 = self.rect.y0.max(self.rect.y0 + dimension.1);
374
375 self.coord = Shift((self.rect.x0, self.rect.y0));
376
377 self
378 }
379
380 pub fn apply_coord_spec<CT: CoordTranslate>(&self, coord_spec: CT) -> DrawingArea<DB, CT> {
382 DrawingArea {
383 rect: self.rect.clone(),
384 backend: self.backend.clone(),
385 coord: coord_spec,
386 }
387 }
388
389 pub fn margin<ST: SizeDesc, SB: SizeDesc, SL: SizeDesc, SR: SizeDesc>(
391 &self,
392 top: ST,
393 bottom: SB,
394 left: SL,
395 right: SR,
396 ) -> DrawingArea<DB, Shift> {
397 let left = left.in_pixels(self);
398 let right = right.in_pixels(self);
399 let top = top.in_pixels(self);
400 let bottom = bottom.in_pixels(self);
401 DrawingArea {
402 rect: Rect {
403 x0: self.rect.x0 + left,
404 y0: self.rect.y0 + top,
405 x1: self.rect.x1 - right,
406 y1: self.rect.y1 - bottom,
407 },
408 backend: self.backend.clone(),
409 coord: Shift((self.rect.x0 + left, self.rect.y0 + top)),
410 }
411 }
412
413 pub fn split_vertically<S: SizeDesc>(&self, y: S) -> (Self, Self) {
415 let y = y.in_pixels(self);
416 let split_point = [y + self.rect.y0];
417 let mut ret = self.rect.split(split_point.iter(), true).map(|rect| Self {
418 rect: rect.clone(),
419 backend: self.backend.clone(),
420 coord: Shift((rect.x0, rect.y0)),
421 });
422
423 (ret.next().unwrap(), ret.next().unwrap())
424 }
425
426 pub fn split_horizontally<S: SizeDesc>(&self, x: S) -> (Self, Self) {
428 let x = x.in_pixels(self);
429 let split_point = [x + self.rect.x0];
430 let mut ret = self.rect.split(split_point.iter(), false).map(|rect| Self {
431 rect: rect.clone(),
432 backend: self.backend.clone(),
433 coord: Shift((rect.x0, rect.y0)),
434 });
435
436 (ret.next().unwrap(), ret.next().unwrap())
437 }
438
439 pub fn split_evenly(&self, (row, col): (usize, usize)) -> Vec<Self> {
441 self.rect
442 .split_evenly((row, col))
443 .map(|rect| Self {
444 rect: rect.clone(),
445 backend: self.backend.clone(),
446 coord: Shift((rect.x0, rect.y0)),
447 })
448 .collect()
449 }
450
451 pub fn split_by_breakpoints<
453 XSize: SizeDesc,
454 YSize: SizeDesc,
455 XS: AsRef<[XSize]>,
456 YS: AsRef<[YSize]>,
457 >(
458 &self,
459 xs: XS,
460 ys: YS,
461 ) -> Vec<Self> {
462 self.rect
463 .split_grid(
464 xs.as_ref().iter().map(|x| x.in_pixels(self)),
465 ys.as_ref().iter().map(|x| x.in_pixels(self)),
466 )
467 .map(|rect| Self {
468 rect: rect.clone(),
469 backend: self.backend.clone(),
470 coord: Shift((rect.x0, rect.y0)),
471 })
472 .collect()
473 }
474
475 pub fn titled<'a, S: Into<TextStyle<'a>>>(
477 &self,
478 text: &str,
479 style: S,
480 ) -> Result<Self, DrawingAreaError<DB>> {
481 let style = style.into();
482
483 let x_padding = (self.rect.x1 - self.rect.x0) / 2;
484
485 let (_, text_h) = self.estimate_text_size(text, &style)?;
486 let y_padding = (text_h / 2).min(5) as i32;
487
488 let style = &style.pos(Pos::new(HPos::Center, VPos::Top));
489
490 self.backend_ops(|b| {
491 b.draw_text(
492 text,
493 style,
494 (self.rect.x0 + x_padding, self.rect.y0 + y_padding),
495 )
496 })?;
497
498 Ok(Self {
499 rect: Rect {
500 x0: self.rect.x0,
501 y0: self.rect.y0 + y_padding * 2 + text_h as i32,
502 x1: self.rect.x1,
503 y1: self.rect.y1,
504 },
505 backend: self.backend.clone(),
506 coord: Shift((self.rect.x0, self.rect.y0 + y_padding * 2 + text_h as i32)),
507 })
508 }
509
510 pub fn draw_text(
512 &self,
513 text: &str,
514 style: &TextStyle,
515 pos: BackendCoord,
516 ) -> Result<(), DrawingAreaError<DB>> {
517 self.backend_ops(|b| b.draw_text(text, style, (pos.0 + self.rect.x0, pos.1 + self.rect.y0)))
518 }
519}
520
521impl<DB: DrawingBackend, CT: CoordTranslate> DrawingArea<DB, CT> {
522 pub fn into_coord_spec(self) -> CT {
523 self.coord
524 }
525
526 pub fn as_coord_spec(&self) -> &CT {
527 &self.coord
528 }
529
530 pub fn as_coord_spec_mut(&mut self) -> &mut CT {
531 &mut self.coord
532 }
533}
534
535#[cfg(test)]
536mod drawing_area_tests {
537 use crate::{create_mocked_drawing_area, prelude::*};
538 #[test]
539 fn test_filling() {
540 let drawing_area = create_mocked_drawing_area(1024, 768, |m| {
541 m.check_draw_rect(|c, _, f, u, d| {
542 assert_eq!(c, WHITE.to_rgba());
543 assert_eq!(f, true);
544 assert_eq!(u, (0, 0));
545 assert_eq!(d, (1023, 767));
546 });
547
548 m.drop_check(|b| {
549 assert_eq!(b.num_draw_rect_call, 1);
550 assert_eq!(b.draw_count, 1);
551 });
552 });
553
554 drawing_area.fill(&WHITE).expect("Drawing Failure");
555 }
556
557 #[test]
558 fn test_split_evenly() {
559 let colors = vec![
560 &RED, &BLUE, &YELLOW, &WHITE, &BLACK, &MAGENTA, &CYAN, &BLUE, &RED,
561 ];
562 let drawing_area = create_mocked_drawing_area(902, 900, |m| {
563 for col in 0..3 {
564 for row in 0..3 {
565 let colors = colors.clone();
566 m.check_draw_rect(move |c, _, f, u, d| {
567 assert_eq!(c, colors[col * 3 + row].to_rgba());
568 assert_eq!(f, true);
569 assert_eq!(u, (300 * row as i32 + 2.min(row) as i32, 300 * col as i32));
570 assert_eq!(
571 d,
572 (
573 300 + 300 * row as i32 + 2.min(row + 1) as i32 - 1,
574 300 + 300 * col as i32 - 1
575 )
576 );
577 });
578 }
579 }
580 m.drop_check(|b| {
581 assert_eq!(b.num_draw_rect_call, 9);
582 assert_eq!(b.draw_count, 9);
583 });
584 });
585
586 drawing_area
587 .split_evenly((3, 3))
588 .iter_mut()
589 .zip(colors.iter())
590 .for_each(|(d, c)| {
591 d.fill(*c).expect("Drawing Failure");
592 });
593 }
594
595 #[test]
596 fn test_split_horizontally() {
597 let drawing_area = create_mocked_drawing_area(1024, 768, |m| {
598 m.check_draw_rect(|c, _, f, u, d| {
599 assert_eq!(c, RED.to_rgba());
600 assert_eq!(f, true);
601 assert_eq!(u, (0, 0));
602 assert_eq!(d, (345 - 1, 768 - 1));
603 });
604
605 m.check_draw_rect(|c, _, f, u, d| {
606 assert_eq!(c, BLUE.to_rgba());
607 assert_eq!(f, true);
608 assert_eq!(u, (345, 0));
609 assert_eq!(d, (1024 - 1, 768 - 1));
610 });
611
612 m.drop_check(|b| {
613 assert_eq!(b.num_draw_rect_call, 2);
614 assert_eq!(b.draw_count, 2);
615 });
616 });
617
618 let (left, right) = drawing_area.split_horizontally(345);
619 left.fill(&RED).expect("Drawing Error");
620 right.fill(&BLUE).expect("Drawing Error");
621 }
622
623 #[test]
624 fn test_split_vertically() {
625 let drawing_area = create_mocked_drawing_area(1024, 768, |m| {
626 m.check_draw_rect(|c, _, f, u, d| {
627 assert_eq!(c, RED.to_rgba());
628 assert_eq!(f, true);
629 assert_eq!(u, (0, 0));
630 assert_eq!(d, (1024 - 1, 345 - 1));
631 });
632
633 m.check_draw_rect(|c, _, f, u, d| {
634 assert_eq!(c, BLUE.to_rgba());
635 assert_eq!(f, true);
636 assert_eq!(u, (0, 345));
637 assert_eq!(d, (1024 - 1, 768 - 1));
638 });
639
640 m.drop_check(|b| {
641 assert_eq!(b.num_draw_rect_call, 2);
642 assert_eq!(b.draw_count, 2);
643 });
644 });
645
646 let (left, right) = drawing_area.split_vertically(345);
647 left.fill(&RED).expect("Drawing Error");
648 right.fill(&BLUE).expect("Drawing Error");
649 }
650
651 #[test]
652 fn test_split_grid() {
653 let colors = vec![
654 &RED, &BLUE, &YELLOW, &WHITE, &BLACK, &MAGENTA, &CYAN, &BLUE, &RED,
655 ];
656 let breaks: [i32; 5] = [100, 200, 300, 400, 500];
657
658 for nxb in 0..=5 {
659 for nyb in 0..=5 {
660 let drawing_area = create_mocked_drawing_area(1024, 768, |m| {
661 for row in 0..=nyb {
662 for col in 0..=nxb {
663 let get_bp = |full, limit, id| {
664 (if id == 0 {
665 0
666 } else if id > limit {
667 full
668 } else {
669 breaks[id as usize - 1]
670 }) as i32
671 };
672
673 let expected_u = (get_bp(1024, nxb, col), get_bp(768, nyb, row));
674 let expected_d = (
675 get_bp(1024, nxb, col + 1) - 1,
676 get_bp(768, nyb, row + 1) - 1,
677 );
678 let expected_color =
679 colors[(row * (nxb + 1) + col) as usize % colors.len()];
680
681 m.check_draw_rect(move |c, _, f, u, d| {
682 assert_eq!(c, expected_color.to_rgba());
683 assert_eq!(f, true);
684 assert_eq!(u, expected_u);
685 assert_eq!(d, expected_d);
686 });
687 }
688 }
689
690 m.drop_check(move |b| {
691 assert_eq!(b.num_draw_rect_call, ((nxb + 1) * (nyb + 1)) as u32);
692 assert_eq!(b.draw_count, ((nyb + 1) * (nxb + 1)) as u32);
693 });
694 });
695
696 let result = drawing_area
697 .split_by_breakpoints(&breaks[0..nxb as usize], &breaks[0..nyb as usize]);
698 for i in 0..result.len() {
699 result[i]
700 .fill(colors[i % colors.len()])
701 .expect("Drawing Error");
702 }
703 }
704 }
705 }
706 #[test]
707 fn test_titled() {
708 let drawing_area = create_mocked_drawing_area(1024, 768, |m| {
709 m.check_draw_text(|c, font, size, _pos, text| {
710 assert_eq!(c, BLACK.to_rgba());
711 assert_eq!(font, "serif");
712 assert_eq!(size, 30.0);
713 assert_eq!("This is the title", text);
714 });
715 m.check_draw_rect(|c, _, f, u, d| {
716 assert_eq!(c, WHITE.to_rgba());
717 assert_eq!(f, true);
718 assert_eq!(u.0, 0);
719 assert!(u.1 > 0);
720 assert_eq!(d, (1024 - 1, 768 - 1));
721 });
722 m.drop_check(|b| {
723 assert_eq!(b.num_draw_text_call, 1);
724 assert_eq!(b.num_draw_rect_call, 1);
725 assert_eq!(b.draw_count, 2);
726 });
727 });
728
729 drawing_area
730 .titled("This is the title", ("serif", 30))
731 .unwrap()
732 .fill(&WHITE)
733 .unwrap();
734 }
735
736 #[test]
737 fn test_margin() {
738 let drawing_area = create_mocked_drawing_area(1024, 768, |m| {
739 m.check_draw_rect(|c, _, f, u, d| {
740 assert_eq!(c, WHITE.to_rgba());
741 assert_eq!(f, true);
742 assert_eq!(u, (3, 1));
743 assert_eq!(d, (1024 - 4 - 1, 768 - 2 - 1));
744 });
745
746 m.drop_check(|b| {
747 assert_eq!(b.num_draw_rect_call, 1);
748 assert_eq!(b.draw_count, 1);
749 });
750 });
751
752 drawing_area
753 .margin(1, 2, 3, 4)
754 .fill(&WHITE)
755 .expect("Drawing Failure");
756 }
757
758 #[test]
759 fn test_ranges() {
760 let drawing_area = create_mocked_drawing_area(1024, 768, |_m| {})
761 .apply_coord_spec(Cartesian2d::<
762 crate::coord::types::RangedCoordi32,
763 crate::coord::types::RangedCoordu32,
764 >::new(-100..100, 0..200, (0..1024, 0..768)));
765
766 let x_range = drawing_area.get_x_range();
767 assert_eq!(x_range, -100..100);
768
769 let y_range = drawing_area.get_y_range();
770 assert_eq!(y_range, 0..200);
771 }
772
773 #[test]
774 fn test_relative_size() {
775 let drawing_area = create_mocked_drawing_area(1024, 768, |_m| {});
776
777 assert_eq!(102.4, drawing_area.relative_to_width(0.1));
778 assert_eq!(384.0, drawing_area.relative_to_height(0.5));
779
780 assert_eq!(1024.0, drawing_area.relative_to_width(1.3));
781 assert_eq!(768.0, drawing_area.relative_to_height(1.5));
782
783 assert_eq!(0.0, drawing_area.relative_to_width(-0.2));
784 assert_eq!(0.0, drawing_area.relative_to_height(-0.5));
785 }
786
787 #[test]
788 fn test_relative_split() {
789 let drawing_area = create_mocked_drawing_area(1000, 1200, |m| {
790 let mut counter = 0;
791 m.check_draw_rect(move |c, _, f, u, d| {
792 assert_eq!(f, true);
793
794 match counter {
795 0 => {
796 assert_eq!(c, RED.to_rgba());
797 assert_eq!(u, (0, 0));
798 assert_eq!(d, (300 - 1, 600 - 1));
799 }
800 1 => {
801 assert_eq!(c, BLUE.to_rgba());
802 assert_eq!(u, (300, 0));
803 assert_eq!(d, (1000 - 1, 600 - 1));
804 }
805 2 => {
806 assert_eq!(c, GREEN.to_rgba());
807 assert_eq!(u, (0, 600));
808 assert_eq!(d, (300 - 1, 1200 - 1));
809 }
810 3 => {
811 assert_eq!(c, WHITE.to_rgba());
812 assert_eq!(u, (300, 600));
813 assert_eq!(d, (1000 - 1, 1200 - 1));
814 }
815 _ => panic!("Too many draw rect"),
816 }
817
818 counter += 1;
819 });
820
821 m.drop_check(|b| {
822 assert_eq!(b.num_draw_rect_call, 4);
823 assert_eq!(b.draw_count, 4);
824 });
825 });
826
827 let split =
828 drawing_area.split_by_breakpoints([(30).percent_width()], [(50).percent_height()]);
829
830 split[0].fill(&RED).unwrap();
831 split[1].fill(&BLUE).unwrap();
832 split[2].fill(&GREEN).unwrap();
833 split[3].fill(&WHITE).unwrap();
834 }
835
836 #[test]
837 fn test_relative_shrink() {
838 let drawing_area = create_mocked_drawing_area(1000, 1200, |m| {
839 m.check_draw_rect(move |_, _, _, u, d| {
840 assert_eq!((100, 100), u);
841 assert_eq!((300 - 1, 700 - 1), d);
842 });
843
844 m.drop_check(|b| {
845 assert_eq!(b.num_draw_rect_call, 1);
846 assert_eq!(b.draw_count, 1);
847 });
848 })
849 .shrink(((10).percent_width(), 100), (200, (50).percent_height()));
850
851 drawing_area.fill(&RED).unwrap();
852 }
853}