1use std::ops::{Index, IndexMut};
19use std::cmp;
20use crate::universe::Region;
21use crate::rle::Pattern;
22
23
24#[derive(Eq, PartialEq, Debug, Clone, Copy)]
25pub enum BitOperation {
26 Clear,
27 Set,
28 Toggle
29}
30
31
32#[derive(Debug, PartialEq, Clone)]
33pub struct BitGrid(pub Vec<Vec<u64>>);
34
35impl BitGrid {
36 pub fn new(width_in_words: usize, height: usize) -> Self {
42 assert!(width_in_words != 0);
43 assert!(height != 0);
44
45 let mut rows: Vec<Vec<u64>> = Vec::with_capacity(height);
46 for _ in 0 .. height {
47 let row: Vec<u64> = vec![0; width_in_words];
48 rows.push(row);
49 }
50 BitGrid(rows)
51 }
52
53 pub fn width_in_words(&self) -> usize {
54 if self.height() > 0 {
55 self.0[0].len()
56 } else {
57 0
58 }
59 }
60
61 #[inline]
62 pub fn modify_bits_in_word(&mut self, row: usize, word_col: usize, mask: u64, op: BitOperation) {
63 match op {
64 BitOperation::Set => self[row][word_col] |= mask,
65 BitOperation::Clear => self[row][word_col] &= !mask,
66 BitOperation::Toggle => self[row][word_col] ^= mask,
67 }
68 }
69
70 pub fn modify_region(&mut self, region: Region, op: BitOperation) {
76 for y in region.top() .. region.bottom() + 1 {
77 assert!(y >= 0);
78 for word_col in 0 .. self[y as usize].len() {
79 let x_left = word_col * 64;
80 let x_right = x_left + 63;
81
82 if region.right() >= x_left as isize && region.left() <= x_right as isize {
83 let mut mask = u64::max_value();
84
85 for shift in (0..64).rev() {
86 let x = x_right - shift;
87 if (x as isize) < region.left() || (x as isize) > region.right() {
88 mask &= !(1 << shift);
89 }
90 }
91
92 self.modify_bits_in_word(y as usize, word_col, mask, op);
94 }
95 }
96 }
97 }
98
99 pub fn bounding_box(&self) -> Option<Region> {
101 let (width, height) = (self.width(), self.height());
102 let mut first_row = None;
103 let mut last_row = None;
104 let mut first_col = None;
105 let mut last_col = None;
106 for row in 0..height {
107 let mut col = 0;
108 while col < width {
109 let (rle_len, ch) = self.get_run(col, row, None);
110 if ch == 'o' {
111 if let Some(_first_col) = first_col {
112 if _first_col > col {
113 first_col = Some(col);
114 }
115 } else {
116 first_col = Some(col);
117 }
118 if first_row.is_none() {
119 first_row = Some(row);
120 }
121 let run_last_col = col + rle_len - 1;
122 if let Some(_last_col) = last_col {
123 if _last_col < run_last_col {
124 last_col = Some(run_last_col);
125 }
126 } else {
127 last_col = Some(run_last_col);
128 }
129 last_row = Some(row);
130 }
131 col += rle_len;
132 }
133 }
134 if first_row.is_some() {
135 let width = last_col.unwrap() - first_col.unwrap() + 1;
136 let height = last_row.unwrap() - first_row.unwrap() + 1;
137 Some(Region::new(first_col.unwrap() as isize, first_row.unwrap() as isize, width, height))
138 } else {
139 None
140 }
141 }
142
143 pub fn copy(src: &BitGrid, dst: &mut BitGrid, dst_region: Region) {
152 let dst_left = cmp::max(0, dst_region.left()) as usize;
153 let dst_right = cmp::min(dst.width() as isize - 1, dst_region.right()) as usize;
154 let dst_top = cmp::max(0, dst_region.top()) as usize;
155 let dst_bottom = cmp::min(dst.height() as isize - 1, dst_region.bottom()) as usize;
156 if dst_left > dst_right || dst_top > dst_bottom {
157 return;
159 }
160
161 for src_row in 0..src.height() {
162 let dst_row = src_row + dst_top;
163 if dst_row > dst_bottom {
164 break;
165 }
166 let mut src_col = 0; while src_col < src.width() {
168 let dst_col = src_col + dst_left; if dst_col > dst_right {
170 break;
171 }
172 let dst_word_idx = dst_col / 64;
173 let shift = dst_col - dst_word_idx*64; let mut word = src[src_row][src_col/64];
175 if dst_right - dst_col + 1 < 64 {
177 let mask = !((1u64 << (64 - (dst_right - dst_col + 1))) - 1);
178 word &= mask;
179 }
180 dst[dst_row][dst_word_idx] |= word >> shift;
181 if shift > 0 && dst_word_idx+1 < dst.width_in_words() {
182 dst[dst_row][dst_word_idx+1] |= word << (64 - shift);
183 }
184 src_col += 64;
185 }
186 }
187 }
188
189 pub fn region(&self) -> Region {
191 Region::new(0, 0, self.width(), self.height())
192 }
193
194 pub fn clear(&mut self) {
196 for row in &mut self.0 {
197 for col_idx in 0..row.len() {
198 row[col_idx] = 0;
199 }
200 }
201 }
202}
203
204
205impl Index<usize> for BitGrid {
206 type Output = Vec<u64>;
207
208 fn index(&self, i: usize) -> &Vec<u64> {
209 &self.0[i]
210 }
211}
212
213impl IndexMut<usize> for BitGrid {
214 fn index_mut(&mut self, i: usize) -> &mut Vec<u64> {
215 &mut self.0[i]
216 }
217}
218
219
220pub trait CharGrid {
221 fn write_at_position(&mut self, col: usize, row: usize, ch: char, visibility: Option<usize>);
230
231 fn is_valid(ch: char) -> bool;
233
234 fn width(&self) -> usize;
236
237 fn height(&self) -> usize;
239
240 fn to_pattern(&self, visibility: Option<usize>) -> Pattern {
243
244 fn push(result: &mut String, output_col: &mut usize, rle_len: usize, ch: char) {
245 let what_to_add = if rle_len == 1 {
246 let mut s = String::with_capacity(1);
247 s.push(ch);
248 s
249 } else { format!("{}{}", rle_len, ch) };
250 if *output_col + what_to_add.len() > 70 {
251 result.push_str("\r\n");
252 *output_col = 0;
253 }
254 result.push_str(what_to_add.as_str());
255 *output_col += what_to_add.len();
256 }
257
258 let mut result = "".to_owned();
259 let (mut col, mut row) = (0, 0);
260 let mut line_ends_buffered = 0;
261 let mut output_col = 0;
262 while row < self.height() {
263 while col < self.width() {
264 let (rle_len, ch) = self.get_run(col, row, visibility);
265
266 match ch {
267 'b' => {
268 if col + rle_len < self.width() {
272 if line_ends_buffered > 0 {
273 push(&mut result, &mut output_col, line_ends_buffered, '$');
274 line_ends_buffered = 0;
275 }
276 push(&mut result, &mut output_col, rle_len, ch);
277 }
278 }
279 _ => {
280 if line_ends_buffered > 0 {
282 push(&mut result, &mut output_col, line_ends_buffered, '$');
283 line_ends_buffered = 0;
284 }
285 push(&mut result, &mut output_col, rle_len, ch);
286 }
287 }
288
289 col += rle_len;
290 }
291
292 row += 1;
293 col = 0;
294 line_ends_buffered += 1;
295 }
296 push(&mut result, &mut output_col, 1, '!');
297 Pattern(result)
298 }
299
300 fn get_run(&self, col: usize, row: usize, visibility: Option<usize>) -> (usize, char);
315}
316
317
318const VALID_BIT_GRID_CHARS: [char; 2] = ['b', 'o'];
319
320impl CharGrid for BitGrid {
321 fn width(&self) -> usize {
323 self.width_in_words() * 64
324 }
325
326 fn height(&self) -> usize {
328 self.0.len()
329 }
330
331 fn write_at_position(&mut self, col: usize, row: usize, ch: char, _visibility: Option<usize>) {
333 let word_col = col/64;
334 let shift = 63 - (col & (64 - 1));
335 match ch {
336 'b' => {
337 self.modify_bits_in_word(row, word_col, 1 << shift, BitOperation::Clear)
338 }
339 'o' => {
340 self.modify_bits_in_word(row, word_col, 1 << shift, BitOperation::Set)
341 }
342 _ => panic!("invalid character: {:?}", ch)
343 }
344 }
345
346 fn is_valid(ch: char) -> bool {
347 VALID_BIT_GRID_CHARS.contains(&ch)
348 }
349
350 fn get_run(&self, col: usize, row: usize, _visibility: Option<usize>) -> (usize, char) {
364 let word_col = col/64;
365 let shift = 63 - (col & (64 - 1));
366 let mut word = self.0[row][word_col];
367 let mut mask = 1 << shift;
368 let is_set = (word & (1 << shift)) > 0;
369 let mut end_col = col + 1;
370 let ch = if is_set { 'o' } else { 'b' };
371
372 mask >>= 1;
374 while mask > 0 {
375 if (word & mask > 0) != is_set {
376 return (end_col - col, ch);
377 }
378 end_col += 1;
379 mask >>= 1;
380 }
381 assert_eq!(end_col % 64, 0);
382
383 let mut end_word_col = word_col + 1;
385 while end_word_col < self.0[row].len() {
386 word = self.0[row][end_word_col];
387 if is_set && word < u64::max_value() {
388 break;
389 }
390 if !is_set && word > 0 {
391 break;
392 }
393 end_col += 64;
394 end_word_col += 1;
395 }
396 if end_word_col == self.0[row].len() {
398 return (end_col - col, ch);
399 }
400 mask = 1 << 63;
401 while mask > 0 {
402 if (word & mask > 0) != is_set {
403 break;
404 }
405 end_col += 1;
406 mask >>= 1;
407 }
408 return (end_col - col, ch);
409 }
410}