1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
use super::{ColChar, Point, Vec2D};
/// Combine a vector of [`Vec2D`]s and a single `fill_char` into a vector of `(Vec2D, char)` tuples, ready to return for `ViewElement::active_pixels`. Useful if your [`ViewElement`](super::ViewElement) only has one fill character across all of it
pub fn points_to_pixels(points: Vec<Vec2D>, fill_char: ColChar) -> Vec<Point> {
points
.iter()
.map(|e| Point::new(e.clone(), fill_char))
.collect()
}
pub fn interpolate(i0: isize, d0: f64, i1: isize, d1: f64) -> Vec<isize> {
if i0 == i1 {
return vec![d0.round() as isize];
}
let mut values = vec![];
let a = (d1 - d0) / (i1 - i0) as f64;
let mut d = d0;
for _i in i0..(i1 + 1) {
values.push(d.clone().round() as isize);
d += a;
}
values
}
/// Returns true if the points in the vector are arranged in a clockwise orientation
pub fn is_clockwise(points: &Vec<Vec2D>) -> bool {
let mut m = vec![];
for i in 0..points.len() {
let (p1, p2) = (points[i], points[(i + 1) % points.len()]);
m.push((p1.x - p2.x) * (p1.y + p2.y));
}
m.iter().sum::<isize>() <= 0
}
/// Wrapping is used to determine how you want to handle out-of-bounds pixels during plotting pixels to the screen. Here's how each possible value functions:
///
/// [`Wrapping::Wrap`] wraps any out of bounds pixels around to the other side. This is useful if you have an object that travels the entirety of the screen and appears on the other side when it reaches the end.
///
/// [`Wrapping::Ignore`] simply skips all out-of-bounds pixels. This is useful if you might have an object clipping through the edge of the screen.
///
/// [`Wrapping::Panic`] will `panic!` if any pixels are out of bounds. You should use this if you have your own wrapping system implemented
#[derive(Copy)]
pub enum Wrapping {
Wrap,
Ignore,
Panic,
}
impl Clone for Wrapping {
fn clone(&self) -> Self {
match self {
Wrapping::Wrap => Wrapping::Wrap,
Wrapping::Ignore => Wrapping::Ignore,
Wrapping::Panic => Wrapping::Panic,
}
}
}
/// `BlitCache` is used if there is chance that you might have to render the same thing multiple times without moving or changing it.
#[derive(Debug)]
pub struct BlitCache<T> {
independent: Vec<T>,
dependent: Vec<Vec2D>,
}
impl<T> BlitCache<T>
where
T: PartialEq<T>,
{
pub const DEFAULT: BlitCache<T> = BlitCache {
independent: vec![],
dependent: vec![],
};
pub fn new(independent: Vec<T>, dependent: Vec<Vec2D>) -> Self {
Self {
independent,
dependent,
}
}
pub fn is_default(&self) -> bool {
self == &BlitCache::DEFAULT
}
/// Returns the stored dependent value. Returns None if the cache is set to its default
pub fn dependent(&self) -> Option<Vec<Vec2D>> {
match self.is_default() {
false => Some(self.dependent.clone()),
true => None,
}
}
pub fn is_cache_valid(&self, other_independent: &Vec<T>) -> bool {
if self.independent.len() != other_independent.len() {
return false;
}
self.independent
.iter()
.zip(other_independent)
.filter(|&(a, b)| a == b)
.count()
== self.independent.len()
}
}
impl<T> Clone for BlitCache<T>
where
T: PartialEq<T>,
T: Clone,
{
fn clone(&self) -> Self {
Self {
independent: self.independent.clone(),
dependent: self.dependent.clone(),
}
}
}
impl<T> PartialEq for BlitCache<T>
where
T: PartialEq<T>,
{
fn eq(&self, other: &Self) -> bool {
self.independent == other.independent && self.dependent == other.dependent
}
}