use std::cell::RefCell;
use std::path;
use crate::animation::{Animation, AnimationStatus, MoveAnimation, RotateAnimation};
use crate::query::{DrawingCommand, Query, Request, Response, StateUpdate};
use crate::radians::Radians;
use crate::renderer_process::RendererProcess;
use crate::state::{DrawingState, Path, TurtleState};
use crate::timer::Timer;
use crate::{start, Distance, Event, Point};
use DrawingCommand::*;
pub struct TurtleWindow {
renderer: RefCell<RendererProcess>,
}
impl TurtleWindow {
pub fn new() -> TurtleWindow {
start();
Self {
renderer: RefCell::new(RendererProcess::new()),
}
}
pub fn fetch_turtle(&self) -> TurtleState {
match self.renderer.borrow_mut().send_query(Query::Request(Request::TurtleState)) {
Some(Response::TurtleState(state)) => state,
_ => panic!("bug: the renderer process did not sent back TurtleState"),
}
}
pub fn with_turtle_mut<F, T>(&mut self, update: F) -> T
where
F: FnOnce(&mut TurtleState) -> T,
{
let mut turtle = self.fetch_turtle();
let result = update(&mut turtle);
self.renderer
.borrow_mut()
.send_query(Query::Update(StateUpdate::TurtleState(turtle)));
result
}
pub fn fetch_drawing(&self) -> DrawingState {
match self.renderer.borrow_mut().send_query(Query::Request(Request::DrawingState)) {
Some(Response::DrawingState(state)) => state,
_ => panic!("bug: the renderer process did not sent back DrawingState"),
}
}
pub fn with_drawing_mut<F, T>(&mut self, update: F) -> T
where
F: FnOnce(&mut DrawingState) -> T,
{
let mut drawing = self.fetch_drawing();
let result = update(&mut drawing);
self.renderer
.borrow_mut()
.send_query(Query::Update(StateUpdate::DrawingState(drawing)));
result
}
fn set_temporary_path(&mut self, path: Option<Path>) {
self.renderer
.borrow_mut()
.send_query(Query::Update(StateUpdate::TemporaryPath(path)));
}
pub fn poll_event(&mut self) -> Option<Event> {
match self.renderer.borrow_mut().send_query(Query::Request(Request::Event)) {
Some(Response::Event(event)) => event,
_ => panic!("bug: the renderer process did not sent back an Event"),
}
}
pub fn begin_fill(&mut self) {
let fill_color = self.fetch_turtle().fill_color;
self.send_drawing_command(BeginFill(fill_color));
}
pub fn end_fill(&mut self) {
self.send_drawing_command(EndFill);
}
pub fn clear(&mut self) {
self.send_drawing_command(Clear);
}
pub fn save_svg<P: AsRef<path::Path>>(&self, path: P) {
self.send_drawing_command(SaveSVG((*path.as_ref()).to_path_buf()));
}
pub fn go_to(&mut self, end: Point) {
let TurtleState {
position: start,
speed,
pen,
..
} = self.fetch_turtle();
let distance = (end - start).len();
if !distance.is_normal() {
return;
}
let speed = speed.to_movement(); let total_millis = (distance / speed * 1000.).abs();
let animation = MoveAnimation {
path: Path { start, end, pen },
timer: Timer::start(),
total_millis,
};
self.play_animation(animation);
}
pub fn forward(&mut self, distance: Distance) {
if !distance.is_normal() {
return;
}
let TurtleState {
position: start,
speed,
heading,
pen,
..
} = self.fetch_turtle();
let movement = Point {
x: distance * heading.cos(),
y: distance * heading.sin(),
};
let end = start + movement;
let speed = speed.to_movement(); let total_millis = (distance / speed * 1000.).abs();
let animation = MoveAnimation {
path: Path { start, end, pen },
timer: Timer::start(),
total_millis,
};
self.play_animation(animation);
}
pub fn rotate(&mut self, angle: Radians, clockwise: bool) {
if !angle.is_normal() {
return;
}
let TurtleState { heading, speed, .. } = self.fetch_turtle();
let speed = speed.to_rotation(); let total_millis = angle / speed * 1000.;
let total_millis = total_millis.to_radians().abs();
let animation = RotateAnimation {
start: heading,
delta_angle: angle,
clockwise,
timer: Timer::start(),
total_millis,
};
self.play_animation(animation);
}
fn play_animation<A: Animation>(&mut self, animation: A) {
loop {
let status = self.with_turtle_mut(|turtle| animation.advance(turtle));
match status {
AnimationStatus::Running(path) => self.set_temporary_path(path),
AnimationStatus::Complete(path) => {
if let Some(path) = path {
self.set_temporary_path(None);
self.send_drawing_command(StorePath(path));
}
break;
}
}
}
}
fn send_drawing_command(&self, command: DrawingCommand) {
self.renderer.borrow_mut().send_query(Query::Drawing(command));
}
}