aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-03-13 17:24:21 +0000
committerAkshay <[email protected]>2021-03-13 17:24:21 +0000
commitef37a6552a71f86eb1e393b61a3bbb5d81815783 (patch)
treed5f3fb74756508941f2d36960b60d52ecf2d52de
parent4b4ebe84d2cfbb8b0ddf7b678c5fe4cff53e5089 (diff)
factor out line and circle drawing into bitmap
-rw-r--r--src/app.rs115
-rw-r--r--src/bitmap.rs103
-rw-r--r--src/undo.rs8
3 files changed, 160 insertions, 66 deletions
diff --git a/src/app.rs b/src/app.rs
index c0d3018..3c57f27 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -103,61 +103,30 @@ impl<'ctx> AppState<'ctx> {
103 center: P, 103 center: P,
104 val: bool, 104 val: bool,
105 ) -> Result<Vec<ModifyRecord>, ()> { 105 ) -> Result<Vec<ModifyRecord>, ()> {
106 let radius = self.brush_size; 106 let radius = self.brush_size as u32;
107 if radius == 1 { 107 let center = self.idx_at_coord(center).ok_or(())?;
108 let center_point: Point = center.into(); 108 let mut circle_modify_record = vec![];
109 return Ok(if let Some(pt) = self.idx_at_coord(center_point) { 109 for point in self.pixmap.get_circle(center, radius) {
110 let old_val = self.pixmap.set(pt, val); 110 let old_val = self.pixmap.set(point, val);
111 Ok(ModifyRecord::new(pt, old_val, val)) 111 circle_modify_record.push(ModifyRecord::new(point, old_val, val));
112 } else { 112 }
113 Err(()) 113 Ok(circle_modify_record)
114 } 114 }
115 .map(|x| vec![x])?); 115
116 } else { 116 fn paint_line<P: Into<Point>>(&mut self, start: P, end: P) -> Result<Vec<ModifyRecord>, ()> {
117 if let Some(center_on_grid) = self.idx_at_coord(center) { 117 let start = self.idx_at_coord(start).ok_or(())?;
118 // center_on_grid is now a coordinate on the drawing grid 118 let end = self.idx_at_coord(end).ok_or(())?;
119 let (x0, y0) = (center_on_grid.0 as i64, center_on_grid.1 as i64); 119 let line_coords = self.pixmap.get_line(start, end);
120 let (mut dx, mut dy, mut err) = (radius as i64, 0i64, 1 - radius as i64); 120 let mut line_modify_record = vec![];
121 let mut circle = vec![]; 121 let val = self.active_color;
122 let mut old_vals = vec![]; 122 for point in line_coords {
123 while dx >= dy { 123 let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32);
124 circle.push((x0 + dx, y0 + dy)); 124 for c in circle_around_point {
125 circle.push((x0 - dx, y0 + dy)); 125 let old_val = self.pixmap.set(c, val);
126 circle.push((x0 + dx, y0 - dy)); 126 line_modify_record.push(ModifyRecord::new(c, old_val, val));
127 circle.push((x0 - dx, y0 - dy));
128 circle.push((x0 + dy, y0 + dx));
129 circle.push((x0 - dy, y0 + dx));
130 circle.push((x0 + dy, y0 - dx));
131 circle.push((x0 - dy, y0 - dx));
132 dy = dy + 1;
133 if err < 0 {
134 err = err + 2 * dy + 1;
135 } else {
136 dx -= 1;
137 err += 2 * (dy - dx) + 1;
138 }
139 }
140 // circle's insides
141 for x in 0..radius as i64 {
142 for y in 0..radius as i64 {
143 if x.pow(2) + y.pow(2) < (radius as i64).pow(2) {
144 circle.push((x0 + x, y0 + y));
145 circle.push((x0 - x, y0 + y));
146 circle.push((x0 + x, y0 - y));
147 circle.push((x0 - x, y0 - y));
148 }
149 }
150 }
151 for circumference_pt in circle {
152 if let Ok(mp) = MapPoint::try_from(circumference_pt) {
153 let old_val = self.pixmap.set(mp, val);
154 old_vals.push(ModifyRecord::new((mp.x, mp.y), old_val, val));
155 }
156 }
157 return Ok(old_vals);
158 } 127 }
159 } 128 }
160 return Err(()); 129 Ok(line_modify_record)
161 } 130 }
162 131
163 fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { 132 fn apply_operation(&mut self, op: Operation, op_kind: OpKind) {
@@ -176,6 +145,17 @@ impl<'ctx> AppState<'ctx> {
176 } 145 }
177 } 146 }
178 147
148 fn commit_operation(&mut self) {
149 if !self.current_operation.is_empty() {
150 let op = self
151 .current_operation
152 .drain(..)
153 .filter(|v| !v.old_val == v.val)
154 .collect::<Vec<_>>();
155 self.undo_stack.push(op);
156 }
157 }
158
179 fn zoom_in(&mut self, p: (i32, i32)) { 159 fn zoom_in(&mut self, p: (i32, i32)) {
180 // attempt to center around cursor 160 // attempt to center around cursor
181 if let Some(p) = self.idx_at_coord(p) { 161 if let Some(p) = self.idx_at_coord(p) {
@@ -192,8 +172,8 @@ impl<'ctx> AppState<'ctx> {
192 self.brush_size += 1; 172 self.brush_size += 1;
193 } 173 }
194 174
195 fn descrease_brush_size(&mut self) { 175 fn decrease_brush_size(&mut self) {
196 if self.brush_size > 1 { 176 if self.brush_size > 0 {
197 self.brush_size -= 1; 177 self.brush_size -= 1;
198 } 178 }
199 } 179 }
@@ -296,7 +276,7 @@ impl<'ctx> AppState<'ctx> {
296 Self { 276 Self {
297 start: Point::new(60, 60), 277 start: Point::new(60, 60),
298 zoom: 5, 278 zoom: 5,
299 brush_size: 1, 279 brush_size: 0,
300 pixmap, 280 pixmap,
301 grid: Grid::new(), 281 grid: Grid::new(),
302 canvas, 282 canvas,
@@ -339,12 +319,25 @@ impl<'ctx> AppState<'ctx> {
339 self.modify(|e| e.zoom_out(cursor)); 319 self.modify(|e| e.zoom_out(cursor));
340 } 320 }
341 // brush ops 321 // brush ops
342 Keycode::Q => self.modify(|e| e.descrease_brush_size()), 322 Keycode::Q => self.decrease_brush_size(),
343 Keycode::E => self.modify(|e| e.increase_brush_size()), 323 Keycode::E => self.increase_brush_size(),
344 // flip color 324 // flip color
345 Keycode::X => self.modify(|e| e.change_active_color()), 325 Keycode::X => self.modify(|e| e.change_active_color()),
346 // toggle grid 326 // toggle grid
347 Keycode::Tab => self.modify(|e| e.toggle_grid()), 327 Keycode::Tab => self.modify(|e| e.toggle_grid()),
328 // line drawing
329 Keycode::F => self.modify(|e| {
330 let end = (mouse.x(), mouse.y()).into();
331 if let Some(start) = e.last_point {
332 if let Ok(o) = e.paint_line(start, end) {
333 e.commit_operation();
334 e.current_operation =
335 o.into_iter().filter(|v| !v.old_val == v.val).collect();
336 e.commit_operation();
337 e.last_point = Some(end);
338 }
339 }
340 }),
348 // exit 341 // exit
349 Keycode::Escape => break 'running, 342 Keycode::Escape => break 'running,
350 // undo & redo 343 // undo & redo
@@ -381,9 +374,7 @@ impl<'ctx> AppState<'ctx> {
381 Event::MouseMotion { 374 Event::MouseMotion {
382 x, y, mousestate, .. 375 x, y, mousestate, ..
383 } => { 376 } => {
384 let is_left = mousestate.is_mouse_button_pressed(MouseButton::Left); 377 if mousestate.is_mouse_button_pressed(MouseButton::Left) {
385 let is_right = mousestate.is_mouse_button_pressed(MouseButton::Right);
386 if is_left {
387 self.modify(|e| { 378 self.modify(|e| {
388 let pt = (x, y); 379 let pt = (x, y);
389 let val = e.active_color; 380 let val = e.active_color;
@@ -391,7 +382,7 @@ impl<'ctx> AppState<'ctx> {
391 e.current_operation.extend(o); 382 e.current_operation.extend(o);
392 } 383 }
393 }); 384 });
394 } else if is_right { 385 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) {
395 self.modify(|e| { 386 self.modify(|e| {
396 let pt = (x, y); 387 let pt = (x, y);
397 let val = !e.active_color; 388 let val = !e.active_color;
diff --git a/src/bitmap.rs b/src/bitmap.rs
index 01a39d9..8ff311b 100644
--- a/src/bitmap.rs
+++ b/src/bitmap.rs
@@ -1,4 +1,7 @@
1use std::convert::{From, Into, TryFrom}; 1use std::{
2 convert::{From, Into, TryFrom},
3 ops::Sub,
4};
2 5
3#[derive(Debug)] 6#[derive(Debug)]
4pub struct Pixmap<T> { 7pub struct Pixmap<T> {
@@ -59,6 +62,7 @@ where
59 data, 62 data,
60 } 63 }
61 } 64 }
65
62 pub fn new_with(width: u32, height: u32, start: T) -> Self { 66 pub fn new_with(width: u32, height: u32, start: T) -> Self {
63 let data = vec![start; (width * height) as usize]; 67 let data = vec![start; (width * height) as usize];
64 Pixmap { 68 Pixmap {
@@ -67,18 +71,22 @@ where
67 data, 71 data,
68 } 72 }
69 } 73 }
74
70 pub fn is_inside<P: Into<MapPoint>>(&self, pt: P) -> bool { 75 pub fn is_inside<P: Into<MapPoint>>(&self, pt: P) -> bool {
71 let MapPoint { x, y } = pt.into(); 76 let MapPoint { x, y } = pt.into();
72 x < self.width && y < self.height 77 x < self.width && y < self.height
73 } 78 }
79
74 pub fn idx<P: Into<MapPoint>>(&self, pt: P) -> usize { 80 pub fn idx<P: Into<MapPoint>>(&self, pt: P) -> usize {
75 let MapPoint { x, y } = pt.into(); 81 let MapPoint { x, y } = pt.into();
76 (y * self.width + x) as usize 82 (y * self.width + x) as usize
77 } 83 }
84
78 pub fn get<P: Into<MapPoint>>(&self, pt: P) -> T { 85 pub fn get<P: Into<MapPoint>>(&self, pt: P) -> T {
79 let idx = self.idx(pt); 86 let idx = self.idx(pt);
80 self.data[idx] 87 self.data[idx]
81 } 88 }
89
82 pub fn set<P: Into<MapPoint>>(&mut self, pt: P, new_val: T) -> T { 90 pub fn set<P: Into<MapPoint>>(&mut self, pt: P, new_val: T) -> T {
83 let pt = pt.into(); 91 let pt = pt.into();
84 let old_val = self.get(pt); 92 let old_val = self.get(pt);
@@ -86,4 +94,97 @@ where
86 self.data[idx] = new_val; 94 self.data[idx] = new_val;
87 old_val 95 old_val
88 } 96 }
97
98 pub fn get_circle<P: Into<MapPoint>>(&self, center: P, radius: u32) -> Vec<MapPoint> {
99 let mut circle: Vec<(i64, i64)> = vec![];
100 let MapPoint { x, y } = center.into();
101 let x = x as i64;
102 let y = y as i64;
103 let (mut dx, mut dy, mut err) = (radius as i64, 0i64, 1 - radius as i64);
104 while dx >= dy {
105 circle.push((x + dx, y + dy));
106 circle.push((x - dx, y + dy));
107 circle.push((x + dx, y - dy));
108 circle.push((x - dx, y - dy));
109 circle.push((x + dy, y + dx));
110 circle.push((x - dy, y + dx));
111 circle.push((x + dy, y - dx));
112 circle.push((x - dy, y - dx));
113 dy = dy + 1;
114 if err < 0 {
115 err = err + 2 * dy + 1;
116 } else {
117 dx -= 1;
118 err += 2 * (dy - dx) + 1;
119 }
120 }
121 for xi in 0..radius as i64 {
122 for yi in 0..radius as i64 {
123 if xi.pow(2) + yi.pow(2) < (radius as i64).pow(2) {
124 circle.push((x + xi, y + yi));
125 circle.push((x - xi, y + yi));
126 circle.push((x + xi, y - yi));
127 circle.push((x - xi, y - yi));
128 }
129 }
130 }
131 circle
132 .into_iter()
133 .flat_map(|pt| MapPoint::try_from(pt))
134 .filter(|&pt| self.is_inside(pt))
135 .collect()
136 }
137
138 pub fn get_line<P: Into<MapPoint>>(&self, start: P, end: P) -> Vec<MapPoint> {
139 let MapPoint { x: x1, y: y1 } = start.into();
140 let MapPoint { x: x2, y: y2 } = end.into();
141 let mut coordinates = vec![];
142 let dx = abs_difference(x1, x2) as i64;
143 let dy = abs_difference(y1, y2) as i64;
144 let sx = {
145 if x1 < x2 {
146 1
147 } else {
148 -1
149 }
150 };
151 let sy = {
152 if y1 < y2 {
153 1
154 } else {
155 -1
156 }
157 };
158 let mut err: i64 = dx - dy;
159 let mut current_x = x1 as i64;
160 let mut current_y = y1 as i64;
161 loop {
162 coordinates.push((current_x, current_y));
163 if current_x == x2 as i64 && current_y == y2 as i64 {
164 break;
165 }
166 let e2 = 2 * err;
167 if e2 > -dy {
168 err -= dy;
169 current_x += sx;
170 }
171 if e2 < dx {
172 err += dx;
173 current_y += sy;
174 }
175 }
176 coordinates
177 .into_iter()
178 .flat_map(|pt| MapPoint::try_from(pt))
179 .filter(|&pt| self.is_inside(pt))
180 .collect()
181 }
182}
183
184fn abs_difference<T: Sub<Output = T> + Ord>(x: T, y: T) -> T {
185 if x < y {
186 y - x
187 } else {
188 x - y
189 }
89} 190}
diff --git a/src/undo.rs b/src/undo.rs
index b590312..ae96a66 100644
--- a/src/undo.rs
+++ b/src/undo.rs
@@ -1,14 +1,16 @@
1use crate::bitmap::MapPoint;
2
1#[derive(Copy, Clone, Debug)] 3#[derive(Copy, Clone, Debug)]
2pub struct ModifyRecord { 4pub struct ModifyRecord {
3 pub point: (u32, u32), 5 pub point: MapPoint,
4 pub old_val: bool, 6 pub old_val: bool,
5 pub val: bool, 7 pub val: bool,
6} 8}
7 9
8impl ModifyRecord { 10impl ModifyRecord {
9 pub fn new(point: (u32, u32), old_val: bool, val: bool) -> Self { 11 pub fn new<P: Into<MapPoint>>(point: P, old_val: bool, val: bool) -> Self {
10 ModifyRecord { 12 ModifyRecord {
11 point, 13 point: point.into(),
12 old_val, 14 old_val,
13 val, 15 val,
14 } 16 }