#![warn(missing_docs)]
extern crate sdl2;
#[macro_use]
#[cfg(feature = "toml_cfg")]
extern crate serde_derive;
#[cfg(feature = "toml_cfg")]
extern crate toml;
mod prelude;
pub use prelude::*;
#[derive(Debug)]
enum Drawable {
Line(Point, Point, Color),
Rect(Point, u32, u32, Color),
Quad(Point, Point, Point, Point, Color),
RoundedRect(Point, Point, i16, Color),
Arc(Point, i16, i16, f64, f64, i16, Color),
Ellipse(Point, i16, i16, i16, Color),
Pixel(Point, Color),
}
impl Drawable {
fn draw(&self, canvas: &mut sdl2::render::Canvas<sdl2::video::Window>) {
match *self {
Drawable::Line(point1, point2, c) => {
canvas.set_draw_color(c);
canvas
.draw_line(point1, point2)
.expect("Failed to draw line");
}
Drawable::Rect(top_left, width, height, c) => {
canvas.set_draw_color(c);
canvas
.draw_rect(Rect::new(top_left.x(), top_left.y(), width, height))
.expect("Failed to draw Rect");
}
Drawable::Quad(point1, point2, point3, point4, c) => {
canvas.set_draw_color(c);
canvas
.draw_line(point1, point2)
.expect("Failed to draw Quad");
canvas
.draw_line(point2, point3)
.expect("Failed to draw Quad");
canvas
.draw_line(point3, point4)
.expect("Failed to draw Quad");
canvas
.draw_line(point4, point1)
.expect("Failed to draw Quad");
}
Drawable::RoundedRect(p1, p2, r, c) => unimplemented!(),
Drawable::Ellipse(center, radius_x, radius_y, point_count, c) => {
canvas.set_draw_color(c);
Drawable::draw_arc(
canvas,
center,
radius_x,
radius_y,
0.0,
PI * 2.0,
point_count,
)
}
Drawable::Arc(center, radius_x, radius_y, start, end, point_count, c) => {
canvas.set_draw_color(c);
Drawable::draw_arc(canvas, center, radius_x, radius_y, start, end, point_count)
}
Drawable::Pixel(point, c) => {
canvas.set_draw_color(c);
canvas.draw_point(point).expect("Failed to draw Pixel");
}
};
}
fn draw_arc(
canvas: &mut sdl2::render::Canvas<sdl2::video::Window>,
center: Point,
radius_x: i16,
radius_y: i16,
start: f64,
end: f64,
point_count: i16,
) {
let step = (start - end) / f64::from(point_count);
let (mut prev_x, mut prev_y) = (
-(start.sin() * (radius_x as f64)),
-(start.cos() * (radius_y as f64)),
);
for i in 1..(point_count + 1) {
let (new_x, new_y) = (
-(start + f64::from(i) * step).sin() * f64::from(radius_x),
-(start + f64::from(i) * step).cos() * f64::from(radius_y),
);
canvas
.draw_line(
Point::new(center.x() + prev_x as i32, center.y() + prev_y as i32),
Point::new(center.x() + new_x as i32, center.y() + new_y as i32),
)
.expect("Failed to draw arc");
prev_x = new_x;
prev_y = new_y;
}
}
}
use std::collections::HashMap;
pub type ItemID = u64;
pub struct Axle {
canvas: sdl2::render::Canvas<sdl2::video::Window>,
event_pump: sdl2::EventPump,
items: HashMap<u64, Drawable>,
background: Color,
pen_down: bool,
item_id: ItemID,
}
impl Default for Axle {
fn default() -> Self {
Self::new(&Config::default())
}
}
use std::thread;
use std::time::Duration;
impl Axle {
pub fn new(cfg: &Config) -> Axle {
let ctx = sdl2::init().expect("Error initialising SDL2");
let video_subsystem = ctx.video().expect("Error initialising video subsystem");
let window = video_subsystem
.window(&cfg.title, cfg.width, cfg.height)
.position_centered()
.build()
.expect("Error building window");
let mut canvas = window.into_canvas().build().expect("Error building canvas");
canvas.set_draw_color(Color::RGB(0, 0, 0));
let event_pump = ctx.event_pump().expect("Error getting event pump");
Axle {
canvas,
event_pump,
items: HashMap::new(),
pen_down: true,
background: Color::RGB(0, 0, 0),
item_id: 0,
}
}
pub fn set_draw_color(&mut self, color: Color) {
self.canvas.set_draw_color(color)
}
pub fn background(&mut self, color: Color) {
self.background = color;
}
pub fn line(&mut self, point1: Point, point2: Point) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Line(point1, point2, self.canvas.draw_color()),
);
return Some(self.item_id);
}
None
}
pub fn rect(&mut self, top_left: Point, width: u32, height: u32) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Rect(top_left, width, height, self.canvas.draw_color()),
);
return Some(self.item_id);
}
None
}
pub fn quad(
&mut self,
point1: Point,
point2: Point,
point3: Point,
point4: Point,
) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Quad(point1, point2, point3, point4, self.canvas.draw_color()),
);
return Some(self.item_id);
}
None
}
pub fn ellipse(&mut self, center: Point, radius_x: i16, radius_y: i16) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Ellipse(center, radius_x, radius_y, 36, self.canvas.draw_color()),
);
return Some(self.item_id);
}
None
}
pub fn circle(&mut self, center: Point, radius: i16) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Ellipse(center, radius, radius, 36, self.canvas.draw_color()),
);
return Some(self.item_id);
}
None
}
pub fn regular_ngon(&mut self, center: Point, radius: i16, point_count: i16) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Ellipse(
center,
radius,
radius,
point_count,
self.canvas.draw_color(),
),
);
return Some(self.item_id);
}
None
}
pub fn ellipsoidal_ngon(
&mut self,
center: Point,
radius_x: i16,
radius_y: i16,
point_count: i16,
) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Ellipse(
center,
radius_x,
radius_y,
point_count,
self.canvas.draw_color(),
),
);
return Some(self.item_id);
}
None
}
pub fn circle_arc(
&mut self,
center: Point,
radius: i16,
start: f64,
end: f64,
) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Arc(
center,
radius,
radius,
start,
end,
36,
self.canvas.draw_color(),
),
);
return Some(self.item_id);
}
None
}
pub fn ellipse_arc(
&mut self,
center: Point,
radius_x: i16,
radius_y: i16,
start: f64,
end: f64,
) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Arc(
center,
radius_x,
radius_y,
start,
end,
36,
self.canvas.draw_color(),
),
);
return Some(self.item_id);
}
None
}
pub fn n_point_ellipse_arc(
&mut self,
center: Point,
radius_x: i16,
radius_y: i16,
start: f64,
end: f64,
point_count: i16,
) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Arc(
center,
radius_x,
radius_y,
start,
end,
point_count,
self.canvas.draw_color(),
),
);
return Some(self.item_id);
}
None
}
pub fn set_pixel(&mut self, point: Point) -> Option<ItemID> {
if self.pen_down {
self.item_id += 1;
self.items.insert(
self.item_id,
Drawable::Pixel(point, self.canvas.draw_color()),
);
return Some(self.item_id);
}
None
}
pub fn mouse_pos(&self) -> (i32, i32) {
let mouse_state = sdl2::mouse::MouseState::new(&self.event_pump);
(mouse_state.x(), mouse_state.y())
}
pub fn mouse_pressed(&self, button: MouseButton) -> bool {
let mouse_state = sdl2::mouse::MouseState::new(&self.event_pump);
mouse_state.is_mouse_button_pressed(button)
}
pub fn keyboard_pressed(&self, key: Key) -> bool {
let keyboard_state = sdl2::keyboard::KeyboardState::new(&self.event_pump);
keyboard_state.is_scancode_pressed(key)
}
pub fn draw(&mut self) {
let c = self.canvas.draw_color();
self.canvas.set_draw_color(self.background);
self.canvas.clear();
self.canvas.set_draw_color(c);
for item in self.items.values() {
item.draw(&mut self.canvas)
}
self.canvas.present();
}
pub fn sleep(&self, time: f64) {
thread::sleep(Duration::from_millis((time * 1_000.0) as u64))
}
pub fn sleep_ms(&self, time: u64) {
thread::sleep(Duration::from_millis(time))
}
pub fn fps(&self, fps: f64) -> f64 {
1.0 / fps
}
pub fn events(&mut self) -> Vec<Event> {
self.event_pump.poll_iter().collect()
}
pub fn wait_event(&mut self) -> Event {
self.event_pump.wait_event()
}
pub fn clear(&mut self) {
self.items.clear()
}
pub fn remove(&mut self, item_id: ItemID) {
self.items.remove(&item_id);
}
pub fn pen_down(&mut self) {
self.pen_down = true;
}
pub fn pen_up(&mut self) {
self.pen_down = false;
}
pub fn size(&self) -> (u32, u32) {
self.canvas
.output_size()
.expect("Error getting output size")
}
}
#[cfg_attr(feature = "toml_cfg", derive(Serialize, Deserialize))]
pub struct Config {
pub width: u32,
pub height: u32,
pub title: String,
}
impl Default for Config {
fn default() -> Self {
Self {
width: 640,
height: 480,
title: String::from("Axle"),
}
}
}
#[cfg(feature = "toml_cfg")]
use std::fs::File;
#[cfg(feature = "toml_cfg")]
use std::io::{Read, Write};
impl Config {
#[cfg(feature = "toml_cfg")]
pub fn from_toml(path: &str) -> Config {
let mut file = File::open(path).expect("Error opening file");
let mut buf = String::new();
file.read_to_string(&mut buf)
.expect("Error reading to string");
toml::from_str(&buf).expect("Error deserialising toml")
}
#[cfg(feature = "toml_cfg")]
pub fn export_toml(&self, path: &str) {
let s = toml::to_string(self).expect("Error serialing to toml");
let mut file = File::create(path).expect("Error creating file");
file.write_all(s.as_bytes()).expect("Error writing to file");
}
}