use crate::util::getter::Getter;
use crate::util::vector::Vector;
use crate::visual::Paint;
use crate::visual::view::View;
use super::strategy::PixelStrategy;
use super::util::AngleIterator;
use super::{Image, ImageMut, Painter, Scan};
type ImageMapper<'a, T, R> = dyn FnMut((i32, i32), T, (i32, i32), R) -> T + 'a;
fn scanline_segment_i32(segment: (Vector<i32>, Vector<i32>), scanline: i32) -> Scan<i32> {
let (from, to) = if segment.0.y() < segment.1.y() {
(segment.0, segment.1)
} else {
(segment.1, segment.0)
};
let (delta_x, delta_y) = (to - from).split();
if scanline < from.y() || scanline > to.y() {
return Scan::None;
}
if delta_y == 0 {
return Scan::Inclusive(from.x(), to.x()).sorted();
}
let steep = delta_x.abs() < delta_y;
if steep {
let horizontal_segments = delta_x + delta_x.signum();
let vertical_segments = delta_y + 1;
let x = from.x() + (horizontal_segments * (scanline - from.y())) / vertical_segments;
Scan::Single(x)
} else {
let horizontal_segments = delta_x + delta_x.signum();
let vertical_segments = delta_y + 1;
let left_x = from.x() + (scanline - from.y()) * horizontal_segments / vertical_segments;
let right_x = from.x()
+ (scanline - from.y() + 1) * horizontal_segments / vertical_segments
- delta_x.signum();
Scan::Inclusive(left_x, right_x).sorted()
}
}
impl<T> Painter<'_, T>
where
T: Clone,
{
fn paint_line(&mut self, from: Vector<i32>, to: Vector<i32>, strategy: &mut PixelStrategy<T>) {
if from.x() == to.x() {
self.vertical_line(from.x(), from.y()..=to.y(), strategy);
return;
}
if from.y() == to.y() {
self.horizontal_line(from.x()..=to.x(), from.y(), strategy);
return;
}
let mut iter = from.y()..=to.y();
let mut iter_rev = (to.y()..=from.y()).rev();
let iter_ref: &mut dyn Iterator<Item = i32> = if from.y() < to.y() {
&mut iter
} else {
&mut iter_rev
};
let rev = from.x() > to.x();
for y in iter_ref {
let scan = scanline_segment_i32((from, to), y);
let mut scan_rev = scan.rev().into_iter();
let mut scan = scan.into_iter();
let scan: &mut dyn Iterator<Item = i32> = if rev { &mut scan_rev } else { &mut scan };
for x in scan {
let pose = (x, y).into();
self.apply_strategy(pose, strategy);
}
}
}
fn filled_triangle(&mut self, vertices: [Vector<i32>; 3], strategy: &mut PixelStrategy<T>) {
let mut vertices = vertices;
vertices.sort_by(|a, b| a.y_ref().cmp(b.y_ref()));
let [a, b, c] = vertices;
if a.y() == c.y() {
vertices.sort_by(|a, b| a.x().cmp(b.x_ref()));
self.horizontal_line(vertices[0].x()..=vertices[2].x(), vertices[0].y(), strategy);
return;
}
let middle = if b.y() == c.y() { b.y() } else { b.y() - 1 };
for y in a.y()..=middle {
let left_range = scanline_segment_i32((a, b), y);
let right_range = scanline_segment_i32((a, c), y);
let left = left_range
.start_unchecked()
.min(right_range.start_unchecked());
let right = left_range.end_unchecked().max(right_range.end_unchecked());
self.horizontal_line(left..=right, y, strategy);
}
let middle = middle + 1;
for y in middle..=c.y() {
let left_range = scanline_segment_i32((a, c), y);
let right_range = scanline_segment_i32((b, c), y);
let left = left_range
.start_unchecked()
.min(right_range.start_unchecked());
let right = left_range.end_unchecked().max(right_range.end_unchecked());
self.horizontal_line(left..=right, y, strategy);
}
}
fn filled_polygon_raw(&mut self, vertices: &[Vector<i32>], strategy: &mut PixelStrategy<T>) {
enum FlipType {
Opening,
Closing,
Singular,
}
struct Flip {
edge_type: FlipType,
position: i32,
smooth: Option<bool>,
}
let ((left, top), (_right, bottom)) = vertices[..].iter().fold(
(vertices[0].split(), vertices[0].split()),
|((left, top), (right, bottom)), value| {
let left = left.min(value.x());
let right = right.max(value.x());
let top = top.min(value.y());
let bottom = bottom.max(value.y());
((left, top), (right, bottom))
},
);
for y in top..=bottom {
let segments = AngleIterator::new(vertices);
let intersections = segments
.map(|(a, b, c)| {
(scanline_segment_i32((*a, *b), y), {
if b.y() == y {
Some((b.y() > a.y()) == (b.y() < c.y()))
} else {
None
}
})
})
.filter(|(scan, _)| !matches!(*scan, Scan::None));
let mut flips = Vec::new();
for (intersection, smooth) in intersections {
match intersection {
Scan::Single(a) => flips.push(Flip {
edge_type: FlipType::Singular,
position: a,
smooth,
}),
Scan::Inclusive(a, b) => {
flips.push(Flip {
edge_type: FlipType::Opening,
position: a,
smooth,
});
flips.push(Flip {
edge_type: FlipType::Closing,
position: b,
smooth,
});
}
Scan::None => {}
}
}
flips.sort_by(|a, b| a.position.cmp(&b.position));
let mut counter = 0;
let mut current_left = left;
for flip in flips {
if counter % 2 == 1 || counter / 2 % 2 == 1 {
self.horizontal_line(
current_left + self.offset.x()..=flip.position + self.offset.x(),
y + self.offset.y(),
strategy,
);
}
current_left = flip.position;
counter += match (flip.edge_type, flip.smooth) {
(FlipType::Singular, Some(false)) => 2,
(FlipType::Opening, None) => 1,
(FlipType::Closing, None) => 1,
(FlipType::Singular, None) => 2,
(FlipType::Opening, Some(false)) => 1,
(FlipType::Closing, Some(false)) => 1,
_ => 0,
};
}
}
}
fn filled_circle(&mut self, center: Vector<i32>, radius: i32, function: &mut PixelStrategy<T>) {
self.horizontal_line(
center.x() - radius..=center.x() + radius,
center.y(),
function,
);
let mut x = 0;
let mut y = radius;
let mut decision = 1 - radius;
let mut checker_x = 1;
let mut checker_y = -2 * radius;
while x < y {
if decision > 0 {
self.horizontal_line(center.x() - x..=center.x() + x, center.y() + y, function);
self.horizontal_line(center.x() - x..=center.x() + x, center.y() - y, function);
y -= 1;
checker_y += 2;
decision += checker_y;
} else {
x += 1;
checker_x += 2;
decision += checker_x;
self.horizontal_line(center.x() - y..=center.x() + y, center.y() + x, function);
self.horizontal_line(center.x() - y..=center.x() + y, center.y() - x, function);
}
}
}
fn circle(&mut self, center: Vector<i32>, radius: i32, strategy: &mut PixelStrategy<T>) {
self.apply_strategy(center + (radius, 0), strategy);
self.apply_strategy(center - (radius, 0), strategy);
self.apply_strategy(center + (0, radius), strategy);
self.apply_strategy(center - (0, radius), strategy);
let mut x = 0;
let mut y = radius;
let mut decision = 1 - radius;
let mut checker_x = 1;
let mut checker_y = -2 * radius;
let mut mapper = |x, y| {
self.apply_strategy(center + (x, y), strategy);
self.apply_strategy(center + (x, -y), strategy);
self.apply_strategy(center + (-x, y), strategy);
self.apply_strategy(center + (-x, -y), strategy);
self.apply_strategy(center + (y, x), strategy);
self.apply_strategy(center + (y, -x), strategy);
self.apply_strategy(center + (-y, x), strategy);
self.apply_strategy(center + (-y, -x), strategy);
};
while x < y - 2 {
if decision > 0 {
y -= 1;
checker_y += 2;
decision += checker_y;
}
x += 1;
checker_x += 2;
decision += checker_x;
mapper(x, y);
}
if x == y - 2 {
let x = x + 1;
self.apply_strategy(center + (x, x), strategy);
self.apply_strategy(center + (x, -x), strategy);
self.apply_strategy(center + (-x, x), strategy);
self.apply_strategy(center + (-x, -x), strategy);
}
}
fn draw_view<O: Clone>(
&mut self,
at: Vector<i32>,
view: &View<O>,
function: &mut ImageMapper<T, O>,
) {
let (this_start_x, this_start_y) = self.target.get_start().split();
let image_start_x = if at.x() < this_start_x {
-at.x() + this_start_x
} else {
this_start_x
};
let image_start_y = if at.y() < this_start_y {
-at.y() + this_start_y
} else {
this_start_y
};
let image_start_x = image_start_x.max(view.get_start().x());
let image_start_y = image_start_y.max(view.get_start().y());
let image_end_x = if at.x() + view.width() >= self.target.width() {
self.target.width() - at.x()
} else {
view.width()
};
let image_end_y = if at.y() + view.height() >= self.target.height() {
self.target.height() - at.y()
} else {
view.height()
};
for x in image_start_x..image_end_x {
for y in image_start_y..image_end_y {
let step = (x, y).into();
let pose = at + step;
unsafe {
let color = Image::pixel_unchecked(view, step);
let pixel = function(
pose.split(),
self.target.pixel_unchecked(pose).clone(),
(x, y),
color.clone(),
);
self.target.set_pixel(pose, &pixel);
}
}
}
}
}
impl<T> Paint<T, i32> for Painter<'_, T>
where
T: Clone,
{
fn pixel(&self, position: Vector<i32>) -> Option<T> {
let position = self.position_i32(position);
Image::pixel(&self.target, position)
}
fn mod_pixel<'a, S>(&mut self, position: Vector<i32>, strategy: S)
where
T: 'a,
S: Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let position = self.position_i32(position);
self.apply_strategy(position, &mut strategy);
}
fn line<'a, S>(&mut self, from: Vector<i32>, to: Vector<i32>, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let from = self.position_i32(from);
let to = self.position_i32(to);
self.paint_line(from, to, &mut strategy);
}
fn rect_f<'a, S>(&mut self, from: Vector<i32>, dimensions: Vector<i32>, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let from = self.position_i32(from);
self.filled_rect(from, from + dimensions - (1, 1), &mut strategy);
}
fn rect_b<'a, S>(&mut self, from: Vector<i32>, dimensions: Vector<i32>, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let from = self.position_i32(from);
let (from, to) = (from, from + dimensions - (1, 1));
self.horizontal_line(from.x()..=to.x() - 1, from.y(), &mut strategy);
self.horizontal_line(from.x() + 1..=to.x(), to.y(), &mut strategy);
self.vertical_line(from.x(), from.y() + 1..=to.y(), &mut strategy);
self.vertical_line(to.x(), from.y()..=to.y() - 1, &mut strategy);
}
fn triangle_f<'a, S>(&mut self, vertices: [Vector<i32>; 3], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let vertices = vertices.map(|v| self.position_i32(v));
self.filled_triangle(vertices, &mut strategy);
}
fn triangle_b<'a, S>(&mut self, vertices: [Vector<i32>; 3], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let [a, b, c] = vertices.map(|v| self.position_i32(v));
let mut strategy = strategy.into();
self.paint_line(a, b, &mut strategy);
self.paint_line(b, c, &mut strategy);
self.paint_line(c, a, &mut strategy);
}
fn polygon_f<'a, S>(&mut self, vertices: &[Vector<i32>], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
self.filled_polygon_raw(vertices, &mut strategy);
}
fn polygon_b<'a, S>(&mut self, vertices: &[Vector<i32>], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
if vertices.len() > 2 {
self.paint_line(
self.position_i32(*vertices.last().unwrap()),
self.position_i32(vertices[0]),
&mut strategy,
);
}
for window in vertices.windows(2) {
self.paint_line(
self.position_i32(window[0]),
self.position_i32(window[1]),
&mut strategy,
);
}
}
fn circle_f<'a, S>(&mut self, center: Vector<i32>, radius: i32, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let center = self.position_i32(center);
self.filled_circle(center, radius, &mut strategy);
}
fn circle_b<'a, S>(&mut self, center: Vector<i32>, radius: i32, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>,
{
let mut strategy = strategy.into();
let center = self.position_i32(center);
self.circle(center, radius, &mut strategy);
}
}
impl<T> Painter<'_, T>
where
T: Clone,
{
pub unsafe fn pixel_unchecked(&self, position: Vector<i32>) -> T {
let position = self.position_i32(position);
unsafe { Image::pixel_unchecked(&self.target, position) }
}
pub unsafe fn set_pixel_unchecked(&mut self, position: Vector<i32>, value: T) {
let position = self.position_i32(position);
unsafe { ImageMut::set_pixel_unchecked(&mut self.target, position, &value) }
}
pub fn image<I, O>(&mut self, at: Vector<i32>, image: &I, function: &mut ImageMapper<T, O>)
where
O: Clone,
for<'a> &'a I: Into<View<'a, O>>,
{
let at = self.position_i32(at);
let view = image.into();
self.draw_view(at, &view, function)
}
pub fn text<M, O>(
&mut self,
at: Vector<i32>,
mapper: M,
font: &dyn Getter<Index = char, Item = impl Image<O>>,
text: &str,
function: &mut ImageMapper<T, O>,
) where
M: FnMut(char, &dyn Image<O>) -> Vector<i32>,
O: Clone,
{
let at = self.position_i32(at);
let mut mapper = mapper;
for code_point in text.chars() {
if let Some(symbol) = font.get(&code_point) {
let local = at + mapper(code_point, symbol);
let view = View::from(symbol);
self.draw_view(local, &view, function);
}
}
}
}