bidivec/algorithms/editing.rs
1//! A module containing functions to alter the contents of a [`BidiViewMut`][crate::BidiViewMut],
2//! by copy/cloning rectangles of data from another view, flood-filling, etc.
3//!
4//! The most important entry points are:
5//! - [`copy()`]: A function that copies a rectangle from a [`BidiView`][crate::BidiView]
6//! to a [`BidiViewMut`][crate::BidiViewMut]. Requires the type to be [`Copy`].
7//! - [`clone_over()`]: A function that copies a rectangle from a [`BidiView`][crate::BidiView]
8//! to a [`BidiViewMut`][crate::BidiViewMut] and works if the type is [`Clone`].
9//! - [`blend()`]: A function that operates on a rectangle from a [`BidiView`][crate::BidiView]
10//! and uses a customizable closure to alter a [`BidiViewMut`][crate::BidiViewMut]. Can be used
11//! for copying types that aren't [`Copy`]/[`Clone`], or to blend those types (e.g. alpha-blending),
12//! or for whatever operation the calling code decides.
13//! - [`flood_fill()`]: Performs a flood-fill on the [`BidiViewMut`][crate::BidiViewMut], using a custom
14//! comparison closure and a custom action for painting/filling.
15
16use crate::*;
17use std::{cmp::min, collections::VecDeque};
18
19/// Copies a rectangle from a [`BidiView`][crate::BidiView] to a [`BidiViewMut`][crate::BidiViewMut].
20/// The type is required to be [`Copy`]; if the type is [`Clone`], see
21/// the [`clone_over()`] function. If the type is neither [`Copy`] nor [`Clone`], see the
22/// [`blend()`] function.
23///
24/// # Examples
25///
26/// ```
27/// use bidivec::{bidivec, editing, BidiRect};
28///
29/// let v1 = bidivec!{
30/// [0, 1, 2, 3],
31/// [4, 5, 6, 7],
32/// [8, 9, 10, 11],
33/// [12, 13, 14, 15],
34/// };
35///
36/// let mut v2 = bidivec![-1; 4, 4];
37///
38/// editing::copy(
39/// &v1,
40/// &mut v2,
41/// &BidiRect::new(0, 0, 2, 2),
42/// (0, 0),
43/// )?;
44///
45/// assert_eq!(v2[(0, 0)], 0);
46/// assert_eq!(v2[(1, 0)], 1);
47/// assert_eq!(v2[(0, 1)], 4);
48/// assert_eq!(v2[(1, 1)], 5);
49/// assert_eq!(v2[(2, 0)], -1);
50/// assert_eq!(v2[(0, 2)], -1);
51/// # Ok::<(), bidivec::BidiError>(())
52/// ```
53pub fn copy<S, D>(
54 source: &S,
55 dest: &mut D,
56 from: &BidiRect,
57 to: (usize, usize),
58) -> Result<(), BidiError>
59where
60 S: BidiView,
61 D: BidiViewMut<Output = S::Output>,
62 S::Output: Copy + Sized,
63{
64 if from.x >= source.width()
65 || from.y >= source.height()
66 || to.0 >= dest.width()
67 || to.1 >= dest.height()
68 {
69 return Err(BidiError::OutOfBounds);
70 }
71
72 for dy in to.1..min(to.1 + from.height, to.1 + source.height()) {
73 let sy = dy - to.1 + from.y;
74 for dx in to.0..min(to.0 + from.width, to.0 + source.width()) {
75 let sx = dx - to.0 + from.x;
76 println!("copying {},{} to {},{}", sx, sy, dx, dy);
77 dest[(dx, dy)] = source[(sx, sy)];
78 }
79 }
80
81 Ok(())
82}
83
84/// Clones a rectangle from a [`BidiView`][crate::BidiView] to a [`BidiViewMut`][crate::BidiViewMut].
85/// The type is required to be [`Clone`]; if the type is also [`Copy`], see
86/// the [`copy()`] function which might be slightly faster.
87/// If the type is neither [`Copy`] nor [`Clone`], see the
88/// [`blend()`] function.
89///
90/// # Examples
91///
92/// ```
93/// use bidivec::{bidivec, editing, BidiRect};
94///
95/// #[derive(Clone)]
96/// struct Cloneable(i32);
97///
98/// let v1 = bidivec!{
99/// [Cloneable(0), Cloneable(1), Cloneable(2), Cloneable(3)],
100/// [Cloneable(4), Cloneable(5), Cloneable(6), Cloneable(7)],
101/// [Cloneable(8), Cloneable(9), Cloneable(10), Cloneable(11)],
102/// [Cloneable(12), Cloneable(13), Cloneable(14), Cloneable(15)],
103/// };
104/// let mut v2 = bidivec![Cloneable(-1); 4, 4];
105///
106/// editing::clone_over(
107/// &v1,
108/// &mut v2,
109/// &BidiRect::new(0, 0, 2, 2),
110/// (0, 0),
111/// )?;
112///
113/// assert_eq!(v2[(0, 0)].0, 0);
114/// assert_eq!(v2[(1, 0)].0, 1);
115/// assert_eq!(v2[(0, 1)].0, 4);
116/// assert_eq!(v2[(1, 1)].0, 5);
117/// assert_eq!(v2[(2, 0)].0, -1);
118/// assert_eq!(v2[(0, 2)].0, -1);
119/// # Ok::<(), bidivec::BidiError>(())
120/// ```
121pub fn clone_over<S, D>(
122 source: &S,
123 dest: &mut D,
124 from: &BidiRect,
125 to: (usize, usize),
126) -> Result<(), BidiError>
127where
128 S: BidiView,
129 D: BidiViewMut<Output = S::Output>,
130 S::Output: Clone + Sized,
131{
132 blend(source, dest, from, to, |s, d| *d = s.clone())
133}
134
135/// Blends a rectangle from a [`BidiView`][crate::BidiView] to a [`BidiViewMut`][crate::BidiViewMut]
136/// using a closure to customize the behavior.
137/// This can be used to implement copies for types that aren't [`Copy`]/[`Clone`],
138/// or to support alpha-blending or similar algorithms, being the `blender`
139/// function entirely customizable.
140///
141/// The blender function is a [`FnMut(&S::Output, &mut D::Output)`][FnMut], where the
142/// first argument is the element in the source [`BidiView`][crate::BidiView], and
143/// the second element is a mutable element in the destination [`BidiViewMut`][crate::BidiViewMut].
144///
145/// # Examples
146///
147/// ```
148/// use bidivec::{bidivec, editing, BidiRect};
149///
150/// let v1 = bidivec!{
151/// [0, 1, 2, 3],
152/// [4, 5, 6, 7],
153/// [8, 9, 10, 11],
154/// [12, 13, 14, 15],
155/// };
156/// let mut v2 = bidivec![100; 4, 4];
157///
158/// editing::blend(
159/// &v1,
160/// &mut v2,
161/// &BidiRect::new(0, 0, 2, 2),
162/// (0, 0),
163/// |src, dst| *dst = src + 2 * (*dst),
164/// )?;
165///
166/// assert_eq!(v2[(0, 0)], 200);
167/// assert_eq!(v2[(1, 0)], 201);
168/// assert_eq!(v2[(0, 1)], 204);
169/// assert_eq!(v2[(1, 1)], 205);
170/// assert_eq!(v2[(2, 0)], 100);
171/// assert_eq!(v2[(0, 2)], 100);
172/// # Ok::<(), bidivec::BidiError>(())
173/// ```
174pub fn blend<S, D, F>(
175 source: &S,
176 dest: &mut D,
177 from: &BidiRect,
178 to: (usize, usize),
179 mut blender: F,
180) -> Result<(), BidiError>
181where
182 S: BidiView,
183 D: BidiViewMut,
184 D::Output: Sized,
185 F: FnMut(&S::Output, &mut D::Output),
186{
187 if from.x >= source.width()
188 || from.y >= source.height()
189 || to.0 >= dest.width()
190 || to.1 >= dest.height()
191 {
192 return Err(BidiError::OutOfBounds);
193 }
194
195 for dy in to.1..min(to.1 + from.height, to.1 + source.height()) {
196 let sy = dy - to.1 + from.y;
197 for dx in to.0..min(to.0 + from.width, to.0 + source.width()) {
198 let sx = dx - to.0 + from.x;
199
200 blender(&source[(sx, sy)], &mut dest[(dx, dy)]);
201 }
202 }
203
204 Ok(())
205}
206
207/// Performs a flood-fill like operation, using custom comparisons and
208/// custom painter.
209///
210/// A flood-fill starts from a coordinate (`pos`) and expands in all
211/// the directions (according to the `neighbouring` argument) over all
212/// the values for which the comparison function (`comparer`) returns
213/// true.
214/// All those values are then "painted" using the `painter` function.
215///
216/// The `comparer` function is a [`Fn(&V::Output, &V::Output, &V::Output) -> bool`][Fn]
217/// that takes a pair of elements and returns `true` if the fill should
218/// expand from the second element to the third. The first element is
219/// always the element from which the flood-fill started.
220///
221/// The `painter` function is a [`FnMut(&mut V::Output, (usize, usize))`][FnMut]
222/// that takes the element to be written as the first argument, and its
223/// coordinates as the second argument.
224///
225/// Returns the number of elements that have been passed to the painter
226/// function (that is, the number of elements to which the flood-fill
227/// expanded to, including the starting position).
228///
229/// # Examples
230///
231/// ```
232/// use bidivec::{bidivec, editing, BidiNeighbours};
233///
234/// let mut v = bidivec!{
235/// [0, 0, 1, 1],
236/// [0, 0, 1, 0],
237/// [1, 0, 1, 1],
238/// [1, 0, 0, 1],
239/// };
240///
241/// editing::flood_fill(
242/// &mut v,
243/// (0, 0),
244/// BidiNeighbours::Adjacent,
245/// |_, val1, val2| val1 == val2,
246/// |val, _| { *val = 5; },
247/// )?;
248///
249/// assert_eq!(v, bidivec!{
250/// [5, 5, 1, 1],
251/// [5, 5, 1, 0],
252/// [1, 5, 1, 1],
253/// [1, 5, 5, 1],
254/// });
255/// # Ok::<(), bidivec::BidiError>(())
256/// ```
257pub fn flood_fill<V, FC, FF>(
258 dest: &mut V,
259 pos: (usize, usize),
260 neighbouring: BidiNeighbours,
261 comparer: FC,
262 mut painter: FF,
263) -> Result<usize, BidiError>
264where
265 V: BidiViewMut,
266 V::Output: Sized,
267 FC: Fn(&V::Output, &V::Output, &V::Output) -> bool,
268 FF: FnMut(&mut V::Output, (usize, usize)),
269{
270 #[derive(Copy, Clone, PartialEq)]
271 enum FloodFillState {
272 Unvisited,
273 Border,
274 Paint,
275 }
276
277 if pos.0 >= dest.width() || pos.1 >= dest.height() {
278 return Err(BidiError::OutOfBounds);
279 }
280
281 let (width, height) = (dest.width(), dest.height());
282 let initial_elem = &dest[pos];
283 let mut queue = VecDeque::new();
284 let mut neighbours = neighbouring.prealloc_vec();
285
286 let mut visited = BidiArray::with_elem(FloodFillState::Unvisited, width, height);
287
288 visited[(pos)] = FloodFillState::Paint;
289 queue.push_back(pos);
290
291 while let Some(point) = queue.pop_front() {
292 let cur_val = &dest[point];
293 neighbouring.generate_points_on(&mut neighbours, point, width, height);
294
295 while let Some(neighbour) = neighbours.pop() {
296 if visited[neighbour] != FloodFillState::Unvisited {
297 continue;
298 }
299
300 if comparer(initial_elem, cur_val, &dest[neighbour]) {
301 queue.push_back(neighbour);
302 visited[neighbour] = FloodFillState::Paint;
303 } else {
304 visited[neighbour] = FloodFillState::Border;
305 }
306 }
307 }
308
309 for (x, y, elem) in visited.iter().with_coords() {
310 if *elem == FloodFillState::Paint {
311 painter(&mut dest[(x, y)], (x, y));
312 }
313 }
314
315 Ok(visited.len())
316}