diff options
-rw-r--r-- | src/app.rs | 60 | ||||
-rw-r--r-- | src/bitmap.rs | 68 |
2 files changed, 120 insertions, 8 deletions
@@ -38,6 +38,12 @@ struct Grid { | |||
38 | color: Color, | 38 | color: Color, |
39 | } | 39 | } |
40 | 40 | ||
41 | #[derive(Debug, Default, Copy, Clone)] | ||
42 | struct Symmetry { | ||
43 | x: Option<u32>, | ||
44 | y: Option<u32>, | ||
45 | } | ||
46 | |||
41 | impl Grid { | 47 | impl Grid { |
42 | fn new() -> Self { | 48 | fn new() -> Self { |
43 | Self { | 49 | Self { |
@@ -99,6 +105,44 @@ impl<'ctx> AppState<'ctx> { | |||
99 | self.grid.enabled = !self.grid.enabled; | 105 | self.grid.enabled = !self.grid.enabled; |
100 | } | 106 | } |
101 | 107 | ||
108 | fn cycle_symmetry(&mut self) { | ||
109 | let Symmetry { x, y } = self.symmetry; | ||
110 | self.symmetry = match (x, y) { | ||
111 | (None, None) => Symmetry { | ||
112 | x: Some(self.width() / 2), | ||
113 | y: None, | ||
114 | }, | ||
115 | (_, None) => Symmetry { | ||
116 | x: None, | ||
117 | y: Some(self.height() / 2), | ||
118 | }, | ||
119 | (None, y) => Symmetry { | ||
120 | x: Some(self.width() / 2), | ||
121 | y, | ||
122 | }, | ||
123 | (Some(_), Some(_)) => Symmetry { x: None, y: None }, | ||
124 | } | ||
125 | } | ||
126 | |||
127 | fn apply_symmetry(&self, figure: &[MapPoint]) -> Vec<MapPoint> { | ||
128 | let Symmetry { x, y } = self.symmetry; | ||
129 | match (x, y) { | ||
130 | (None, None) => vec![], | ||
131 | (Some(line), None) => self.pixmap.mirror_figure(figure, line, Axis::X), | ||
132 | (None, Some(line)) => self.pixmap.mirror_figure(figure, line, Axis::Y), | ||
133 | (Some(x), Some(y)) => { | ||
134 | let along_x = self.pixmap.mirror_figure(figure, x, Axis::X); | ||
135 | let along_y = self.pixmap.mirror_figure(figure, y, Axis::Y); | ||
136 | let reflected = self.pixmap.reflect_figure(figure, (x, y).into()); | ||
137 | along_x | ||
138 | .into_iter() | ||
139 | .chain(along_y) | ||
140 | .chain(reflected) | ||
141 | .collect() | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
102 | fn paint_point<P: Into<Point>>( | 146 | fn paint_point<P: Into<Point>>( |
103 | &mut self, | 147 | &mut self, |
104 | center: P, | 148 | center: P, |
@@ -106,21 +150,24 @@ impl<'ctx> AppState<'ctx> { | |||
106 | ) -> Result<Vec<ModifyRecord>, ()> { | 150 | ) -> Result<Vec<ModifyRecord>, ()> { |
107 | let radius = self.brush_size as u32; | 151 | let radius = self.brush_size as u32; |
108 | let center = self.idx_at_coord(center).ok_or(())?; | 152 | let center = self.idx_at_coord(center).ok_or(())?; |
109 | let mut circle_modify_record = vec![]; | 153 | let mut modify_record = vec![]; |
110 | for point in self.pixmap.get_circle(center, radius) { | 154 | let circle = self.pixmap.get_circle(center, radius); |
155 | let sym_circle = self.apply_symmetry(&circle); | ||
156 | for point in circle.into_iter().chain(sym_circle) { | ||
111 | let old_val = self.pixmap.set(point, val); | 157 | let old_val = self.pixmap.set(point, val); |
112 | circle_modify_record.push(ModifyRecord::new(point, old_val, val)); | 158 | modify_record.push(ModifyRecord::new(point, old_val, val)); |
113 | } | 159 | } |
114 | Ok(circle_modify_record) | 160 | Ok(modify_record) |
115 | } | 161 | } |
116 | 162 | ||
117 | fn paint_line<P: Into<Point>>(&mut self, start: P, end: P) -> Result<Vec<ModifyRecord>, ()> { | 163 | fn paint_line<P: Into<Point>>(&mut self, start: P, end: P) -> Result<Vec<ModifyRecord>, ()> { |
118 | let start = self.idx_at_coord(start).ok_or(())?; | 164 | let start = self.idx_at_coord(start).ok_or(())?; |
119 | let end = self.idx_at_coord(end).ok_or(())?; | 165 | let end = self.idx_at_coord(end).ok_or(())?; |
120 | let line_coords = self.pixmap.get_line(start, end); | 166 | let line = self.pixmap.get_line(start, end); |
167 | let sym_line = self.apply_symmetry(&line); | ||
121 | let mut line_modify_record = vec![]; | 168 | let mut line_modify_record = vec![]; |
122 | let val = self.active_color; | 169 | let val = self.active_color; |
123 | for point in line_coords { | 170 | for point in line.into_iter().chain(sym_line) { |
124 | let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32); | 171 | let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32); |
125 | for c in circle_around_point { | 172 | for c in circle_around_point { |
126 | let old_val = self.pixmap.set(c, val); | 173 | let old_val = self.pixmap.set(c, val); |
@@ -292,7 +339,6 @@ impl<'ctx> AppState<'ctx> { | |||
292 | self.canvas.present(); | 339 | self.canvas.present(); |
293 | 340 | ||
294 | let mut event_pump = self.context.event_pump().unwrap(); | 341 | let mut event_pump = self.context.event_pump().unwrap(); |
295 | |||
296 | 'running: loop { | 342 | 'running: loop { |
297 | let mouse = event_pump.mouse_state(); | 343 | let mouse = event_pump.mouse_state(); |
298 | for event in event_pump.poll_iter() { | 344 | for event in event_pump.poll_iter() { |
diff --git a/src/bitmap.rs b/src/bitmap.rs index 8ff311b..726e0a2 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | convert::{From, Into, TryFrom}, | 2 | convert::{From, Into, TryFrom}, |
3 | ops::Sub, | 3 | ops::{Add, Sub}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | #[derive(Debug)] | 6 | #[derive(Debug)] |
@@ -50,6 +50,56 @@ impl TryFrom<(i64, i64)> for MapPoint { | |||
50 | } | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
53 | impl Add for MapPoint { | ||
54 | type Output = Self; | ||
55 | fn add(self, rhs: Self) -> Self::Output { | ||
56 | MapPoint { | ||
57 | x: self.x + rhs.x, | ||
58 | y: self.y + rhs.y, | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | |||
63 | impl Sub for MapPoint { | ||
64 | type Output = Self; | ||
65 | fn sub(self, rhs: Self) -> Self::Output { | ||
66 | MapPoint { | ||
67 | x: self.x - rhs.x, | ||
68 | y: self.y - rhs.y, | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | #[derive(Debug, Copy, Clone)] | ||
74 | pub enum Axis { | ||
75 | X, | ||
76 | Y, | ||
77 | } | ||
78 | |||
79 | impl MapPoint { | ||
80 | #[inline] | ||
81 | pub fn scale(self, c: u32) -> MapPoint { | ||
82 | MapPoint { | ||
83 | x: self.x * c, | ||
84 | y: self.y * c, | ||
85 | } | ||
86 | } | ||
87 | |||
88 | #[inline] | ||
89 | pub fn mirror_about(self, line: u32, axis: Axis) -> MapPoint { | ||
90 | let MapPoint { x, y } = self; | ||
91 | match axis { | ||
92 | Axis::X => MapPoint { x, y: 2 * line - y }, | ||
93 | Axis::Y => MapPoint { x: 2 * line - x, y }, | ||
94 | } | ||
95 | } | ||
96 | |||
97 | #[inline] | ||
98 | pub fn reflect(self, around: MapPoint) -> MapPoint { | ||
99 | around.scale(2) - self | ||
100 | } | ||
101 | } | ||
102 | |||
53 | impl<T> Pixmap<T> | 103 | impl<T> Pixmap<T> |
54 | where | 104 | where |
55 | T: Copy + Clone + Default, | 105 | T: Copy + Clone + Default, |
@@ -179,6 +229,22 @@ where | |||
179 | .filter(|&pt| self.is_inside(pt)) | 229 | .filter(|&pt| self.is_inside(pt)) |
180 | .collect() | 230 | .collect() |
181 | } | 231 | } |
232 | |||
233 | pub fn mirror_figure(&self, figure: &[MapPoint], line: u32, axis: Axis) -> Vec<MapPoint> { | ||
234 | figure | ||
235 | .iter() | ||
236 | .map(|pt| pt.mirror_about(line, axis)) | ||
237 | .filter(|&pt| self.is_inside(pt)) | ||
238 | .collect() | ||
239 | } | ||
240 | |||
241 | pub fn reflect_figure(&self, figure: &[MapPoint], around: MapPoint) -> Vec<MapPoint> { | ||
242 | figure | ||
243 | .iter() | ||
244 | .map(|pt| pt.reflect(around)) | ||
245 | .filter(|&pt| self.is_inside(pt)) | ||
246 | .collect() | ||
247 | } | ||
182 | } | 248 | } |
183 | 249 | ||
184 | fn abs_difference<T: Sub<Output = T> + Ord>(x: T, y: T) -> T { | 250 | fn abs_difference<T: Sub<Output = T> + Ord>(x: T, y: T) -> T { |