use super::view::{utils, ColChar, Point, Vec2D, ViewElement};
pub struct Line {
pub pos0: Vec2D,
pub pos1: Vec2D,
pub fill_char: ColChar,
}
impl Line {
pub fn new(pos0: Vec2D, pos1: Vec2D, fill_char: ColChar) -> Self {
Line {
pos0,
pos1,
fill_char,
}
}
pub fn draw(pos0: Vec2D, pos1: Vec2D) -> Vec<Vec2D> {
let mut points = Vec::new();
let (mut x, mut y) = pos0.as_tuple();
let (x1, y1) = pos1.as_tuple();
let dx = (x1 - x).abs();
let sx = if x < x1 { 1 } else { -1 };
let dy = -(y1 - y).abs();
let sy = if y < y1 { 1 } else { -1 };
let mut error = dx + dy;
loop {
let pixel = Vec2D::new(x, y);
points.push(pixel);
let e2 = error * 2;
if e2 >= dy {
if x == x1 {
break;
};
error += dy;
x += sx;
};
if e2 <= dx {
if y == y1 {
break;
};
error += dx;
y += sy;
};
}
points
}
}
impl ViewElement for Line {
fn active_pixels(&self) -> Vec<Point> {
utils::points_to_pixels(Self::draw(self.pos0, self.pos1), self.fill_char)
}
}
pub struct Triangle {
pub pos0: Vec2D,
pub pos1: Vec2D,
pub pos2: Vec2D,
pub fill_char: ColChar,
}
impl Triangle {
pub const fn new(pos0: Vec2D, pos1: Vec2D, pos2: Vec2D, fill_char: ColChar) -> Self {
Triangle {
pos0,
pos1,
pos2,
fill_char,
}
}
pub const fn with_array(points: &[Vec2D], fill_char: ColChar) -> Self {
if points.len() != 3 {
panic!(
"points parameter should have exactly 3 items, one for each point of the triangle"
)
}
Self::new(points[0], points[1], points[2], fill_char)
}
pub fn corners(&self) -> [Vec2D; 3] {
[self.pos0, self.pos1, self.pos2]
}
pub fn draw(corners: [Vec2D; 3]) -> Vec<Vec2D> {
let mut points = vec![];
let mut corners = corners;
corners.sort_unstable_by_key(|k| k.y);
let (x0, y0) = corners[0].as_tuple();
let (x1, y1) = corners[1].as_tuple();
let (x2, y2) = corners[2].as_tuple();
let mut x01 = utils::interpolate(y0, x0 as f64, y1, x1 as f64);
let x12 = utils::interpolate(y1, x1 as f64, y2, x2 as f64);
let x02 = utils::interpolate(y0, x0 as f64, y2, x2 as f64);
x01.pop();
let mut x012 = x01;
x012.extend(x12);
let m = (x012.len() as f64 / 2.0).floor() as usize;
let (x_left, x_right) = match x02[m] < x012[m] {
true => (x02, x012),
false => (x012, x02),
};
for (i, y) in (y0..y2).enumerate() {
for x in x_left[i]..x_right[i] {
points.push(Vec2D::new(x, y));
}
}
points
}
}
impl ViewElement for Triangle {
fn active_pixels(&self) -> Vec<Point> {
utils::points_to_pixels(Self::draw(self.corners()), self.fill_char)
}
}
pub struct Polygon {
pub points: Vec<Vec2D>,
pub fill_char: ColChar,
}
impl Polygon {
pub fn new(points: Vec<Vec2D>, fill_char: ColChar) -> Self {
Self { points, fill_char }
}
pub fn triangulate(vertices: &[Vec2D]) -> Vec<[Vec2D; 3]> {
let mut points = vec![];
for fi in 1..(vertices.len() - 1) {
points.push([vertices[0], vertices[fi], vertices[fi + 1]])
}
points
}
pub fn draw(vertices: &[Vec2D]) -> Vec<Vec2D> {
Self::triangulate(vertices)
.iter()
.flat_map(|corners| Triangle::draw(*corners))
.collect()
}
}
impl ViewElement for Polygon {
fn active_pixels(&self) -> Vec<Point> {
utils::points_to_pixels(Self::draw(&self.points), self.fill_char)
}
}
pub struct Rect {
pub pos: Vec2D,
pub size: Vec2D,
pub fill_char: ColChar,
}
impl Rect {
pub fn new(pos: Vec2D, size: Vec2D, fill_char: ColChar) -> Self {
Self {
pos,
size,
fill_char,
}
}
}
impl ViewElement for Rect {
fn active_pixels(&self) -> Vec<Point> {
let mut points = vec![];
for x in 0..self.size.x {
for y in 0..self.size.y {
points.push(self.pos + Vec2D { x, y })
}
}
utils::points_to_pixels(points, self.fill_char)
}
}