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}