diff options
-rw-r--r-- | src/app.rs | 128 | ||||
-rw-r--r-- | src/consts.rs | 14 | ||||
-rw-r--r-- | src/main.rs | 1 |
3 files changed, 76 insertions, 67 deletions
@@ -1,9 +1,11 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | bitmap::{Axis, MapPoint, Pixmap}, | 2 | bitmap::{MapPoint, Pixmap}, |
3 | consts::{colors::*, FONT_PATH}, | ||
4 | symmetry::Symmetry, | ||
3 | undo::{ModifyRecord, OpKind, Operation, UndoStack}, | 5 | undo::{ModifyRecord, OpKind, Operation, UndoStack}, |
4 | }; | 6 | }; |
5 | 7 | ||
6 | use std::convert::From; | 8 | use std::convert::{From, TryFrom}; |
7 | 9 | ||
8 | use sdl2::{ | 10 | use sdl2::{ |
9 | event::Event, | 11 | event::Event, |
@@ -11,7 +13,7 @@ use sdl2::{ | |||
11 | mouse::MouseButton, | 13 | mouse::MouseButton, |
12 | pixels::Color, | 14 | pixels::Color, |
13 | rect::{Point, Rect}, | 15 | rect::{Point, Rect}, |
14 | render::{Canvas, Texture}, | 16 | render::Canvas, |
15 | ttf::Sdl2TtfContext, | 17 | ttf::Sdl2TtfContext, |
16 | video::Window, | 18 | video::Window, |
17 | Sdl, | 19 | Sdl, |
@@ -23,8 +25,6 @@ macro_rules! quick_rect( | |||
23 | ) | 25 | ) |
24 | ); | 26 | ); |
25 | 27 | ||
26 | use crate::consts::{BLACK, FONT_PATH, GRID_COLOR, WHITE}; | ||
27 | |||
28 | pub struct AppState<'ctx> { | 28 | pub struct AppState<'ctx> { |
29 | active_color: bool, | 29 | active_color: bool, |
30 | brush_size: u8, | 30 | brush_size: u8, |
@@ -47,12 +47,6 @@ struct Grid { | |||
47 | color: Color, | 47 | color: Color, |
48 | } | 48 | } |
49 | 49 | ||
50 | #[derive(Debug, Default, Copy, Clone)] | ||
51 | struct Symmetry { | ||
52 | x: Option<u32>, | ||
53 | y: Option<u32>, | ||
54 | } | ||
55 | |||
56 | impl Grid { | 50 | impl Grid { |
57 | fn new() -> Self { | 51 | fn new() -> Self { |
58 | Self { | 52 | Self { |
@@ -133,25 +127,6 @@ impl<'ctx> AppState<'ctx> { | |||
133 | } | 127 | } |
134 | } | 128 | } |
135 | 129 | ||
136 | fn apply_symmetry(&self, figure: &[MapPoint]) -> Vec<MapPoint> { | ||
137 | let Symmetry { x, y } = self.symmetry; | ||
138 | match (x, y) { | ||
139 | (None, None) => vec![], | ||
140 | (Some(line), None) => self.pixmap.mirror_figure(figure, line, Axis::X), | ||
141 | (None, Some(line)) => self.pixmap.mirror_figure(figure, line, Axis::Y), | ||
142 | (Some(x), Some(y)) => { | ||
143 | let along_x = self.pixmap.mirror_figure(figure, x, Axis::X); | ||
144 | let along_y = self.pixmap.mirror_figure(figure, y, Axis::Y); | ||
145 | let reflected = self.pixmap.reflect_figure(figure, (x, y).into()); | ||
146 | along_x | ||
147 | .into_iter() | ||
148 | .chain(along_y) | ||
149 | .chain(reflected) | ||
150 | .collect() | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | fn paint_point<P: Into<Point>>( | 130 | fn paint_point<P: Into<Point>>( |
156 | &mut self, | 131 | &mut self, |
157 | center: P, | 132 | center: P, |
@@ -159,9 +134,12 @@ impl<'ctx> AppState<'ctx> { | |||
159 | ) -> Result<Vec<ModifyRecord>, ()> { | 134 | ) -> Result<Vec<ModifyRecord>, ()> { |
160 | let radius = self.brush_size as u32; | 135 | let radius = self.brush_size as u32; |
161 | let center = self.idx_at_coord(center).ok_or(())?; | 136 | let center = self.idx_at_coord(center).ok_or(())?; |
137 | |||
138 | let circle = self.pixmap.get_circle(center, radius, true); | ||
139 | let sym_circle = self.symmetry.apply(&circle); | ||
140 | |||
162 | let mut modify_record = vec![]; | 141 | let mut modify_record = vec![]; |
163 | let circle = self.pixmap.get_circle(center, radius); | 142 | |
164 | let sym_circle = self.apply_symmetry(&circle); | ||
165 | for point in circle.into_iter().chain(sym_circle) { | 143 | for point in circle.into_iter().chain(sym_circle) { |
166 | let old_val = self.pixmap.set(point, val); | 144 | let old_val = self.pixmap.set(point, val); |
167 | modify_record.push(ModifyRecord::new(point, old_val, val)); | 145 | modify_record.push(ModifyRecord::new(point, old_val, val)); |
@@ -169,15 +147,21 @@ impl<'ctx> AppState<'ctx> { | |||
169 | Ok(modify_record) | 147 | Ok(modify_record) |
170 | } | 148 | } |
171 | 149 | ||
172 | fn paint_line<P: Into<Point>>(&mut self, start: P, end: P) -> Result<Vec<ModifyRecord>, ()> { | 150 | fn paint_line<P: Into<Point>>( |
151 | &mut self, | ||
152 | start: P, | ||
153 | end: P, | ||
154 | val: bool, | ||
155 | ) -> Result<Vec<ModifyRecord>, ()> { | ||
173 | let start = self.idx_at_coord(start).ok_or(())?; | 156 | let start = self.idx_at_coord(start).ok_or(())?; |
174 | let end = self.idx_at_coord(end).ok_or(())?; | 157 | let end = self.idx_at_coord(end).ok_or(())?; |
158 | |||
175 | let line = self.pixmap.get_line(start, end); | 159 | let line = self.pixmap.get_line(start, end); |
176 | let sym_line = self.apply_symmetry(&line); | 160 | let sym_line = self.symmetry.apply(&line); |
161 | |||
177 | let mut line_modify_record = vec![]; | 162 | let mut line_modify_record = vec![]; |
178 | let val = self.active_color; | ||
179 | for point in line.into_iter().chain(sym_line) { | 163 | for point in line.into_iter().chain(sym_line) { |
180 | let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32); | 164 | let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32, true); |
181 | for c in circle_around_point { | 165 | for c in circle_around_point { |
182 | let old_val = self.pixmap.set(c, val); | 166 | let old_val = self.pixmap.set(c, val); |
183 | line_modify_record.push(ModifyRecord::new(c, old_val, val)); | 167 | line_modify_record.push(ModifyRecord::new(c, old_val, val)); |
@@ -283,30 +267,15 @@ impl<'ctx> AppState<'ctx> { | |||
283 | status_height | 267 | status_height |
284 | )) | 268 | )) |
285 | .unwrap(); | 269 | .unwrap(); |
286 | let symmetry_symbol = match self.symmetry { | ||
287 | Symmetry { x: None, y: None } => "", | ||
288 | Symmetry { | ||
289 | x: Some(_), | ||
290 | y: None, | ||
291 | } => "-", | ||
292 | Symmetry { | ||
293 | x: None, | ||
294 | y: Some(_), | ||
295 | } => "|", | ||
296 | Symmetry { | ||
297 | x: Some(_), | ||
298 | y: Some(_), | ||
299 | } => "+", | ||
300 | }; | ||
301 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { | 270 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { |
302 | format!("{:3}, {:3}", x, y) | 271 | format!("{:3}, {:3}", x, y) |
303 | } else { | 272 | } else { |
304 | format!("--, --") | 273 | format!("---, ---") |
305 | }; | 274 | }; |
306 | let status_text = format!( | 275 | let status_text = format!( |
307 | "[BRUSH {}] [SYM {:1}] {}", | 276 | "[BRUSH {}] [SYM {}] {}", |
308 | self.brush_size + 1, | 277 | self.brush_size + 1, |
309 | symmetry_symbol, | 278 | self.symmetry, |
310 | mouse_coords | 279 | mouse_coords |
311 | ); | 280 | ); |
312 | draw_text( | 281 | draw_text( |
@@ -318,32 +287,67 @@ impl<'ctx> AppState<'ctx> { | |||
318 | ); | 287 | ); |
319 | } | 288 | } |
320 | 289 | ||
290 | fn draw_mouse(&mut self) { | ||
291 | let brush_size = self.brush_size; | ||
292 | let cs = self.zoom as u32; | ||
293 | let pt = self.idx_at_coord(self.mouse); | ||
294 | if let Some(center) = pt { | ||
295 | let circle = self.pixmap.get_circle(center, brush_size as u32, false); | ||
296 | for MapPoint { x, y } in circle.into_iter() { | ||
297 | self.canvas.set_draw_color(GREY); | ||
298 | self.canvas | ||
299 | .fill_rect(Rect::new( | ||
300 | x as i32 * cs as i32 + self.start.x(), | ||
301 | y as i32 * cs as i32 + self.start.y(), | ||
302 | cs, | ||
303 | cs, | ||
304 | )) | ||
305 | .unwrap(); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
321 | fn draw(&mut self) { | 310 | fn draw(&mut self) { |
322 | let cs = self.zoom as u32; | 311 | let cs = self.zoom as u32; |
323 | let (width, height) = (self.width(), self.height()); | 312 | let (width, height) = (self.width(), self.height()); |
324 | let start = self.start; | 313 | let start = self.start; |
325 | let canvas = &mut self.canvas; | 314 | let grid_enabled = self.grid.enabled; |
315 | let (winsize_x, winsize_y) = self.canvas.window().size(); | ||
316 | let Symmetry { x: sym_x, y: sym_y } = self.symmetry; | ||
326 | for (idx, val) in self.pixmap.data.iter().enumerate() { | 317 | for (idx, val) in self.pixmap.data.iter().enumerate() { |
327 | if *val { | 318 | if *val { |
328 | let idx = idx as i32; | 319 | let idx = idx as i32; |
329 | let (x, y) = (idx % width as i32, idx / height as i32); | 320 | let (x, y) = (idx % width as i32, idx / height as i32); |
330 | canvas.set_draw_color(WHITE); | 321 | self.canvas.set_draw_color(WHITE); |
331 | canvas | 322 | self.canvas |
332 | .fill_rect(Rect::new( | 323 | .fill_rect(Rect::new( |
333 | // start drawing 1 pixel after the grid line | ||
334 | x * cs as i32 + start.x(), | 324 | x * cs as i32 + start.x(), |
335 | y * cs as i32 + start.y(), | 325 | y * cs as i32 + start.y(), |
336 | // stop drawing 1 pixel before the grid line | ||
337 | cs, | 326 | cs, |
338 | cs, | 327 | cs, |
339 | )) | 328 | )) |
340 | .unwrap(); | 329 | .unwrap(); |
341 | } | 330 | } |
342 | } | 331 | } |
343 | if self.grid.enabled { | 332 | if grid_enabled { |
344 | self.draw_grid(); | 333 | self.draw_grid(); |
345 | } | 334 | } |
335 | if let Some(line) = sym_x { | ||
336 | self.canvas.set_draw_color(CYAN); | ||
337 | let line_coord = (line * cs) as i32 + self.start.y() + (cs / 2) as i32; | ||
338 | self.canvas | ||
339 | .draw_line((0, line_coord), (winsize_x as i32, line_coord)) | ||
340 | .unwrap(); | ||
341 | } | ||
342 | if let Some(line) = sym_y { | ||
343 | self.canvas.set_draw_color(CYAN); | ||
344 | let line_coord = (line * cs) as i32 + self.start.x() + (cs / 2) as i32; | ||
345 | self.canvas | ||
346 | .draw_line((line_coord, 0), (line_coord, winsize_y as i32)) | ||
347 | .unwrap(); | ||
348 | } | ||
346 | self.draw_statusline(); | 349 | self.draw_statusline(); |
350 | self.draw_mouse(); | ||
347 | } | 351 | } |
348 | 352 | ||
349 | fn redraw(&mut self) { | 353 | fn redraw(&mut self) { |
@@ -439,7 +443,7 @@ impl<'ctx> AppState<'ctx> { | |||
439 | Keycode::F => { | 443 | Keycode::F => { |
440 | let end = (mouse.x(), mouse.y()).into(); | 444 | let end = (mouse.x(), mouse.y()).into(); |
441 | if let Some(start) = self.last_point { | 445 | if let Some(start) = self.last_point { |
442 | if let Ok(o) = self.paint_line(start, end) { | 446 | if let Ok(o) = self.paint_line(start, end, self.active_color) { |
443 | self.commit_operation(); | 447 | self.commit_operation(); |
444 | self.current_operation = | 448 | self.current_operation = |
445 | o.into_iter().filter(|v| !v.old_val == v.val).collect(); | 449 | o.into_iter().filter(|v| !v.old_val == v.val).collect(); |
@@ -535,5 +539,5 @@ fn draw_text<S: AsRef<str>>( | |||
535 | .unwrap(); | 539 | .unwrap(); |
536 | let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); | 540 | let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); |
537 | let area = quick_rect!(x, y, width, height); | 541 | let area = quick_rect!(x, y, width, height); |
538 | canvas.copy(&texture, None, area); | 542 | canvas.copy(&texture, None, area).unwrap(); |
539 | } | 543 | } |
diff --git a/src/consts.rs b/src/consts.rs index 8c90693..2f7576e 100644 --- a/src/consts.rs +++ b/src/consts.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use sdl2::pixels::Color; | 1 | pub mod colors { |
2 | 2 | use sdl2::pixels::Color; | |
3 | pub const GRID_COLOR: Color = Color::RGB(64, 64, 64); | 3 | pub const GRID_COLOR: Color = Color::RGB(64, 64, 64); |
4 | pub const WHITE: Color = Color::RGB(255, 255, 255); | 4 | pub const WHITE: Color = Color::RGB(255, 255, 255); |
5 | pub const BLACK: Color = Color::RGB(0, 0, 0); | 5 | pub const BLACK: Color = Color::RGB(0, 0, 0); |
6 | pub const GREY: Color = Color::RGB(127, 127, 127); | ||
7 | pub const CYAN: Color = Color::RGB(121, 255, 225); | ||
8 | pub const PINK: Color = Color::RGB(255, 50, 153); | ||
9 | } | ||
6 | 10 | ||
7 | pub const FONT_PATH: &'static str = "./assets/NerdInput-Regular.ttf"; | 11 | pub const FONT_PATH: &'static str = "./assets/NerdInput-Regular.ttf"; |
diff --git a/src/main.rs b/src/main.rs index bd0590e..1aaa954 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | mod app; | 1 | mod app; |
2 | mod bitmap; | 2 | mod bitmap; |
3 | mod consts; | 3 | mod consts; |
4 | mod symmetry; | ||
4 | mod undo; | 5 | mod undo; |
5 | 6 | ||
6 | use app::AppState; | 7 | use app::AppState; |