1use image::{ImageError, RgbImage};
2use std::ops::Range;
3
4use crate::math::{Bounds, Point};
5use super::color::{BLACK, Color, GREY, WHITE};
6use super::plot_frame::PlotFrame;
7use super::shapes::{Circle, Orientation};
8
9pub struct Plot {
46 pub width: i64, pub height: i64, pub frame: PlotFrame,
52 bg_color: Color,
53 canvas_color: Color,
54 canvas_bounds: Bounds<i64>,
55 frame_color: Option<Color>,
56 image: image::RgbImage,
57}
58
59impl Plot {
60
61 pub fn new(width: u32, height: u32) -> Plot {
63 let mut p = Plot {
64 bg_color: WHITE,
65 canvas_color: GREY,
66 width: width as i64,
67 height: height as i64,
68 canvas_bounds: (0..(width as i64), 0..(height as i64)).into(),
69 frame_color: Some(BLACK),
70 frame: PlotFrame::new(
71 (0..(width as i64), 0..(height as i64)).into(),
72 (0.0..1.0, 0.0..1.0).into()),
73 image: RgbImage::new(width, height),
74 }
75 .with_drawing_bounds(0.05..0.95, 0.05..0.95);
76 p.clear();
77 p
78 }
79
80 pub fn clear(&mut self) {
82 let x_bounds = self.canvas_bounds.x.clone();
83 let y_bounds = self.canvas_bounds.y.clone();
84 for y in 0..self.height {
85 for x in 0..self.width {
86 if x_bounds.contains(&x) && y_bounds.contains(&y) {
87 self.image.put_pixel(x as u32, y as u32, self.canvas_color);
88 } else {
89 self.image.put_pixel(x as u32, y as u32, self.bg_color);
90 }
91 }
92 }
93 if let Some(color) = self.frame_color {
94 for y in y_bounds.clone() {
95 self.image.put_pixel(x_bounds.start as u32, y as u32, color);
96 self.image.put_pixel(x_bounds.end as u32, y as u32, color);
97 }
98 for x in x_bounds.clone() {
99 self.image.put_pixel(x as u32, y_bounds.start as u32, color);
100 self.image.put_pixel(x as u32, y_bounds.end as u32, color);
101 }
102 }
103 }
104
105 pub fn draw_line<P>(&mut self, point1: P, point2: P, color: Color)
107 where P: Into<(f64, f64)> {
108 let p1 = self.frame.pt_to_px(point1.into());
109 let p2 = self.frame.pt_to_px(point2.into());
110 let dx = p2.0 - p1.0;
111 let dy = p2.1 - p1.1;
112
113 if dx.abs() > dy.abs() {
114 if dx > 0 {
115 for x in p1.0..p2.0 {
116 let y = p1.1 + dy * (x - p1.0) / dx;
117 self.put_pixel_safe(x, y, color);
118 }
119 } else {
120 for x in p2.0..p1.0 {
121 let y = p1.1 + dy * (x - p1.0) / dx;
122 self.put_pixel_safe(x, y, color);
123 }
124 }
125 } else {
126 if dy > 0 {
127 for y in p1.1..p2.1 {
128 let x = p1.0 + dx * (y - p1.1) / dy;
129 self.put_pixel_safe(x, y, color);
130 }
131 } else {
132 for y in p2.1..p1.1 {
133 let x = p1.0 + dx * (y - p1.1) / dy;
134 self.put_pixel_safe(x, y, color);
135 }
136 }
137 }
138 self.put_pixel_safe(p2.0, p2.1, color);
139 }
140
141 pub fn draw_pixel_line(&mut self, p1: (f64, f64), p2: (f64, f64), color: Color) {
143 let dx = p2.0 - p1.0;
144 let dy = p2.1 - p1.1;
145
146 if dx.abs() > dy.abs() {
147 if dx > 0.0 {
148 let px_x_1 = p1.0.round() as i64;
149 let px_x_2 = p2.0.round() as i64;
150 for x in px_x_1..px_x_2 {
151 let y = p1.1 + dy * (x as f64 - p1.0) / dx;
152 self.put_pixel_safe(x, y.round() as i64, color);
153 }
154 } else {
155 let px_x_1 = p1.0.round() as i64;
156 let px_x_2 = p2.0.round() as i64;
157 for x in px_x_2..px_x_1 {
158 let y = p1.1 + dy * (x as f64 - p1.0) / dx;
159 self.put_pixel_safe(x, y.round() as i64, color);
160 }
161 }
162 } else {
163 if dy > 0.0 {
164 let px_y_1 = p1.1.round() as i64;
165 let px_y_2 = p2.1.round() as i64;
166 for y in px_y_1..px_y_2 {
167 let x = p1.0 + dx * (y as f64 - p1.1) / dy;
168 self.put_pixel_safe(x.round() as i64, y, color);
169 }
170 } else {
171 let px_y_1 = p1.1.round() as i64;
172 let px_y_2 = p2.1.round() as i64;
173 for y in px_y_2..px_y_1 {
174 let x = p1.0 + dx * (y as f64 - p1.1) / dy;
175 self.put_pixel_safe(x.round() as i64, y, color);
176 }
177 }
178 }
179 self.put_pixel_safe(p2.0.round() as i64, p2.1.round() as i64, BLACK);
180 }
181
182 pub fn draw_orientations(&mut self, orientation: &Orientation, positions: &[Point], angles: &[f64]) {
184 for i in 0..positions.len() {
185 orientation.draw(self, positions[i], angles[i]);
186 }
187 }
188
189 pub fn draw_circles(&mut self, circle: &Circle, positions: &[Point]) {
191 for i in 0..positions.len() {
192 circle.draw(self, positions[i]);
193 }
194 }
195
196 pub fn save(&self, filename: &str) -> Result<(), ImageError> {
198 self.image.save(filename)
199 }
200
201 pub fn put_pixel_safe(&mut self, x: i64, y: i64, c: Color) {
203 if self.canvas_bounds.x.contains(&x) && self.canvas_bounds.y.contains(&y) {
204 self.image.put_pixel(x as u32, y as u32, c)
205 }
206 }
207
208 pub fn with_drawing_bounds<R: Into<Range<f64>>>(mut self, x_bounds: R, y_bounds: R) -> Self {
215 let w = self.width as f64;
216 let h = self.height as f64;
217 let x = x_bounds.into();
218 let y = y_bounds.into();
219 self.canvas_bounds.x = ((x.start * w + 0.5) as i64)..((x.end * w + 0.5) as i64);
220 self.canvas_bounds.y = ((y.start * h + 0.5) as i64)..((y.end * h + 0.5) as i64);
221 self.frame.set_drawing_bounds(self.canvas_bounds.x.clone(), self.canvas_bounds.y.clone());
222 self
223 }
224
225 pub fn with_plotting_range<R: Into<Range<f64>>>(mut self, x_range: R, y_range: R) -> Self {
230 self.frame.set_plotting_range(x_range, y_range);
231 self
232 }
233
234 pub fn with_bg_color(mut self, color: Color) -> Self {
236 self.bg_color = color;
237 self
238 }
239
240 pub fn with_canvas_color(mut self, color: Color) -> Self {
242 self.canvas_color = color;
243 self
244 }
245
246 pub fn with_frame_color(mut self, color: Option<Color>) -> Self {
248 self.frame_color = color;
249 self
250 }
251
252}