use crate::kit::shape2d::{Fill, Rotation, Shape, Stroke};
use crate::kit::{Origin, ZDepth};
use crate::view::{ViewCoords, ViewExtent};
use rgx::core::{Rect, Rgba8};
use rgx::math::{Point2, Vector2};
use std::collections::BTreeSet;
use std::fmt;
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum BrushState {
NotDrawing,
DrawStarted(ViewExtent),
Drawing(ViewExtent),
DrawEnded(ViewExtent),
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
pub enum BrushMode {
Erase,
Multi,
Perfect,
XSym,
YSym,
XRay,
}
impl fmt::Display for BrushMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Erase => "erase".fmt(f),
Self::Multi => "multi".fmt(f),
Self::Perfect => "perfect".fmt(f),
Self::XSym => "xsym".fmt(f),
Self::YSym => "ysym".fmt(f),
Self::XRay => "xray".fmt(f),
}
}
}
#[derive(Debug, Clone)]
pub struct Brush {
pub size: usize,
pub state: BrushState,
pub stroke: Vec<Point2<i32>>,
pub color: Rgba8,
modes: BTreeSet<BrushMode>,
curr: Point2<i32>,
prev: Point2<i32>,
}
impl Default for Brush {
fn default() -> Self {
Self {
size: 1,
state: BrushState::NotDrawing,
stroke: Vec::with_capacity(32),
color: Rgba8::TRANSPARENT,
modes: BTreeSet::new(),
curr: Point2::new(0, 0),
prev: Point2::new(0, 0),
}
}
}
impl Brush {
pub fn is_set(&self, m: BrushMode) -> bool {
self.modes.contains(&m)
}
pub fn set(&mut self, m: BrushMode) -> bool {
self.modes.insert(m)
}
pub fn unset(&mut self, m: BrushMode) -> bool {
self.modes.remove(&m)
}
pub fn toggle(&mut self, m: BrushMode) {
if self.is_set(m) {
self.unset(m);
} else {
self.set(m);
}
}
pub fn is_drawing(&self) -> bool {
match self.state {
BrushState::NotDrawing => false,
_ => true,
}
}
#[allow(dead_code)]
pub fn reset(&mut self) {
self.modes.clear();
}
pub fn update(&mut self) {
if let BrushState::DrawEnded(_) = self.state {
self.state = BrushState::NotDrawing;
self.stroke.clear();
}
}
pub fn start_drawing(&mut self, p: ViewCoords<i32>, color: Rgba8, extent: ViewExtent) {
self.state = BrushState::DrawStarted(extent);
self.color = color;
self.stroke = Vec::with_capacity(32);
self.draw(p);
}
pub fn draw(&mut self, p: ViewCoords<i32>) {
self.prev = if let BrushState::DrawStarted(_) = self.state {
*p
} else {
self.curr
};
self.curr = *p;
Brush::line(self.prev, self.curr, &mut self.stroke);
self.stroke.dedup();
if self.is_set(BrushMode::Perfect) {
self.stroke = Brush::filter(&self.stroke);
}
match self.state {
BrushState::Drawing(_) => {}
BrushState::DrawStarted(extent) => {
self.state = BrushState::Drawing(extent);
}
_ => unreachable!(),
}
}
pub fn stop_drawing(&mut self) {
match self.state {
BrushState::DrawStarted(ex) | BrushState::Drawing(ex) => {
self.state = BrushState::DrawEnded(ex);
}
_ => unreachable!(),
}
}
pub fn expand(&self, p: ViewCoords<i32>, extent: ViewExtent) -> Vec<ViewCoords<i32>> {
let mut pixels = vec![*p];
let ViewExtent { fw, fh, nframes } = extent;
if self.is_set(BrushMode::XSym) {
for p in pixels.clone() {
let frame_index = p.x / fw as i32;
pixels.push(Point2::new(
(frame_index + 1) * fw as i32 - (p.x - frame_index * fw as i32) - 1,
p.y,
));
}
}
if self.is_set(BrushMode::YSym) {
for p in pixels.clone() {
pixels.push(Point2::new(p.x, fh as i32 - p.y - 1));
}
}
if self.is_set(BrushMode::Multi) {
for p in pixels.clone() {
let frame_index = p.x / fw as i32;
for i in 0..nframes as i32 - frame_index {
let offset = Vector2::new((i as u32 * fw) as i32, 0);
pixels.push(p + offset);
}
}
}
pixels.iter().map(|p| ViewCoords::new(p.x, p.y)).collect()
}
pub fn output(&self, stroke: Stroke, fill: Fill, scale: f32, origin: Origin) -> Vec<Shape> {
match self.state {
BrushState::DrawStarted(extent)
| BrushState::Drawing(extent)
| BrushState::DrawEnded(extent) => {
let mut pixels = Vec::new();
for p in &self.stroke {
pixels.extend_from_slice(
self.expand(ViewCoords::new(p.x, p.y), extent).as_slice(),
);
}
pixels
.iter()
.map(|p| {
self.shape(
Point2::new(p.x as f32, p.y as f32),
ZDepth::ZERO,
stroke,
fill,
scale,
origin,
)
})
.collect()
}
_ => Vec::new(),
}
}
pub fn shape(
&self,
p: Point2<f32>,
z: ZDepth,
stroke: Stroke,
fill: Fill,
scale: f32,
origin: Origin,
) -> Shape {
let x = p.x;
let y = p.y;
let size = self.size as f32;
let offset = match origin {
Origin::Center => size * scale / 2.,
Origin::BottomLeft => (self.size / 2) as f32 * scale,
Origin::TopLeft => unreachable!(),
};
Shape::Rectangle(
Rect::new(x, y, x + size * scale, y + size * scale) - Vector2::new(offset, offset),
z,
Rotation::ZERO,
stroke,
fill,
)
}
fn line(mut p0: Point2<i32>, p1: Point2<i32>, canvas: &mut Vec<Point2<i32>>) {
let dx = i32::abs(p1.x - p0.x);
let dy = i32::abs(p1.y - p0.y);
let sx = if p0.x < p1.x { 1 } else { -1 };
let sy = if p0.y < p1.y { 1 } else { -1 };
let mut err1 = (if dx > dy { dx } else { -dy }) / 2;
let mut err2;
loop {
canvas.push(p0);
if p0 == p1 {
break;
}
err2 = err1;
if err2 > -dx {
err1 -= dy;
p0.x += sx;
}
if err2 < dy {
err1 += dx;
p0.y += sy;
}
}
}
fn filter(stroke: &[Point2<i32>]) -> Vec<Point2<i32>> {
let mut filtered = Vec::with_capacity(stroke.len());
if stroke.len() <= 2 {
return stroke.to_owned();
}
let mut iter = 0..stroke.len();
if let Some(i) = iter.next() {
filtered.push(stroke[i]);
}
while let Some(i) = iter.next() {
let p = stroke[i];
if let Some(prev) = stroke.get(i - 1) {
if let Some(next) = stroke.get(i + 1) {
if (prev.y == p.y && next.y != p.y && next.x == p.x)
|| (prev.x == p.x && next.x != p.x && next.y == p.y)
{
if let Some(i) = iter.next() {
filtered.push(stroke[i]);
}
continue;
}
}
}
filtered.push(p);
}
filtered
}
}