use crate::transform::Transform;
use crate::path_command::{PathCommand, CubicBezierCurve};
use crate::point::{Vec2D, Unit, WorldUnit, ScreenUnit};
use crate::shape::{ShapeBuilder, ShapeFinished};
use crate::geom::{thirds, bisector};
use crate::shape::stored::path::Path;
use crate::draw_commands::DrawCommand;
use crate::style::Style;
fn add_smooth<T: Unit + 'static>(sec: Option<PathCommand<T>>, last: PathCommand<T>, pos: Vec2D<T>) -> (PathCommand<T>, Option<PathCommand<T>>) {
match last {
PathCommand::MoveTo(p) | PathCommand::LineTo(p) => {
if p == pos {
(last, None)
} else {
let (pt1, pt2) = thirds(p, pos);
(last, Some(PathCommand::CurveTo(CubicBezierCurve {
pt1,
pt2,
to: pos,
})))
}
}
PathCommand::CurveTo(c) => {
if c.to == pos {
(last, None)
} else {
let prev = sec.unwrap().to();
let bis = bisector(prev, c.to, pos);
let fixed_prev_handle = bis * (c.to - prev).magnitude()/-3.0 + c.to;
let new_pt1 = bis * (pos - c.to).magnitude()/3.0 + c.to;
let new_pt2 = (pos - c.to) * 2.0/3.0 + c.to;
if fixed_prev_handle.is_nan() || new_pt1.is_nan() || new_pt2.is_nan() {
(last, None)
} else {
(PathCommand::CurveTo(CubicBezierCurve {
pt2: fixed_prev_handle,
..c
}), Some(PathCommand::CurveTo(CubicBezierCurve {
pt1: new_pt1,
pt2: new_pt2,
to: pos,
})))
}
}
}
}
}
fn add_point<T: Unit + 'static>(commands: &mut Vec<PathCommand<T>>, pos: Vec2D<T>) {
let sub = commands
.len()
.checked_sub(2)
.and_then(|index| commands.get(index))
.copied();
let last = commands.pop().unwrap();
let (last, new) = add_smooth(sub, last, pos);
commands.push(last);
if let Some(new) = new {
commands.push(new);
}
}
#[derive(Debug, PartialEq)]
pub struct FreeHandLine {
commands: Vec<PathCommand<WorldUnit>>,
style: Style<WorldUnit>,
}
impl FreeHandLine {
pub fn start(initial: Vec2D<WorldUnit>, style: Style<WorldUnit>) -> FreeHandLine {
let mut commands = Vec::with_capacity(1000);
commands.push(PathCommand::MoveTo(initial));
FreeHandLine {
commands,
style,
}
}
pub fn with_params(commands: Vec<PathCommand<WorldUnit>>, style: Style<WorldUnit>) -> FreeHandLine {
FreeHandLine {
commands, style,
}
}
}
impl ShapeBuilder for FreeHandLine {
fn handle_mouse_moved(&mut self, pos: Vec2D<ScreenUnit>, t: Transform, _snap: ScreenUnit) {
add_point(&mut self.commands, t.to_world_coordinates(pos));
}
fn handle_button_pressed(&mut self, _pos: Vec2D<ScreenUnit>, _t: Transform, _snap: ScreenUnit) { }
fn handle_button_released(&mut self, _pos: Vec2D<ScreenUnit>, _t: Transform, _snap: ScreenUnit) -> ShapeFinished {
ShapeFinished::Yes(vec![Box::new(Path::from_parts(
self.commands.clone(), self.style,
))])
}
fn draw_commands(&self, _t: Transform, _snap: ScreenUnit) -> Vec<DrawCommand> {
vec![DrawCommand::Path {
commands: self.commands.clone(),
style: self.style,
}]
}
}