use crate::color::*;
use cgmath::Point2;
use mini_gl_fb::glutin::{EventsLoop, GlWindow, GlContext};
use mini_gl_fb::*;
use rusttype::{point, Font, FontCollection, PositionedGlyph, Scale};
use std::collections::HashMap;
use crate::state::*;
pub struct App<'a, D> {
window: Window<'a>,
events_loop: EventsLoop,
pub data: D,
}
pub struct Window<'a> {
width: i32,
height: i32,
fb: Framebuffer,
gl_window: GlWindow,
buffer: Vec<[u8; 4]>,
clear_buffer: bool,
running: bool,
fonts: HashMap<&'static str, Font<'a>>,
}
impl<'a> Window<'a> {
pub fn draw_pixel(&mut self, x: i32, y: i32, color: &Color) {
if x >= self.width() || x < 0 {
return;
}
if y >= self.height() || y < 0 {
return;
}
self.buffer[(y * self.width as i32 + x) as usize] = color.to_arr();
}
pub fn width(&self) -> i32 {
self.width as i32
}
pub fn exit(&mut self) {
self.running = false
}
pub fn height(&self) -> i32 {
self.height as i32
}
pub fn fill(&mut self, color: &Color) {
for y in 0..self.height() {
for x in 0..self.width() {
self.draw_pixel(x, y, color);
}
}
}
#[allow(unused_assignments)]
pub fn draw_line(&mut self, mut p1: Point2<i32>, mut p2: Point2<i32>, color: &Color) {
let mut x = 0;
let mut y = 0;
let mut dx = 0;
let mut dy = 0;
let mut dx1 = 0;
let mut dy1 = 0;
let mut px = 0;
let mut py = 0;
let mut xe = 0;
let mut ye = 0;
dx = p2.x - p1.x;
dy = p2.y - p1.y;
if dx == 0 {
if p2.y < p1.y {
std::mem::swap(&mut p2.y, &mut p1.y);
}
for y in p1.y..=p2.y {
self.draw_pixel(p1.x, y, color);
}
}
if dy == 0 {
if p2.x < p1.x {
std::mem::swap(&mut p2.x, &mut p1.x);
}
for x in p1.x..=p2.x {
self.draw_pixel(x, p1.y, color);
}
}
dx1 = dx.abs();
dy1 = dy.abs();
px = 2 * dy1 - dx1;
py = 2 * dx1 - dy1;
if dy1 <= dx1 {
if dx >= 0 {
x = p1.x;
y = p1.y;
xe = p2.x;
} else {
x = p2.x;
y = p2.y;
xe = p1.x;
}
self.draw_pixel(x, y, color);
for _ in x..xe {
x += 1;
if px < 0 {
px += 2 * dy1;
} else {
if (dx < 0 && dy < 0) || (dx > 0 && dy > 0) {
y += 1;
} else {
y -= 1;
}
px += 2 * (dy1 - dx1);
}
self.draw_pixel(x, y, color);
}
} else {
if dy >= 0 {
x = p1.x;
y = p1.y;
ye = p2.y;
} else {
x = p2.x;
y = p2.y;
ye = p1.y;
}
self.draw_pixel(x, y, color);
for _ in y..ye {
y += 1;
if py <= 0 {
py += 2 * dx1;
} else {
if (dx < 0 && dy < 0) || (dx > 0 && dy > 0) {
x += 1;
} else {
x -= 1;
}
py += 2 * (dx1 - dy1);
}
self.draw_pixel(x, y, color);
}
}
}
pub fn draw_circle(&mut self, point: Point2<i32>, radius: i32, color: &Color) {
let mut x0 = 0;
let mut y0 = radius;
let mut d = 3 - 2 * radius;
if radius == 0 {
return;
}
while y0 >= x0 {
self.draw_pixel(point.x - x0, point.y - y0, color);
self.draw_pixel(point.x - y0, point.y - x0, color);
self.draw_pixel(point.x + y0, point.y - x0, color);
self.draw_pixel(point.x + x0, point.y - y0, color);
self.draw_pixel(point.x - x0, point.y + y0, color);
self.draw_pixel(point.x - y0, point.y + x0, color);
self.draw_pixel(point.x + y0, point.y + x0, color);
self.draw_pixel(point.x + x0, point.y + y0, color);
if d < 0 {
d += 4 * x0 + 6;
x0 += 1;
} else {
d += 4 * (x0 - y0) + 10;
x0 += 1;
y0 -= 1;
}
}
}
pub fn draw_rect(&mut self, point: Point2<i32>, width: i32, height: i32, color: &Color) {
self.draw_line(point, Point2::new(point.x + width, point.y), color);
self.draw_line(
Point2::new(point.x + width, point.y),
Point2::new(point.x + width, point.y + height),
color,
);
self.draw_line(
Point2::new(point.x + width, point.y + height),
Point2::new(point.x, point.y + height),
color,
);
self.draw_line(Point2::new(point.x, point.y + height), point, color);
}
pub fn text<T>(&mut self, text: T, font: &'static str, size: f32, position: Point2<i32>, color: &Color)
where
T: Into<String>,
{
let font = self.fonts.get(font).expect("Can not get font.");
let scale = Scale { x: size, y: size };
let v_metric = font.v_metrics(scale);
let glyphs: Vec<PositionedGlyph<'_>> = font
.layout(&text.into(), scale, point(0.0, v_metric.ascent))
.collect();
let mut buffer: Vec<Point2<i32>> = Vec::new();
for glyph in glyphs {
if let Some(bb) = glyph.pixel_bounding_box() {
glyph.draw(|x, y, v| {
let x = x as i32 + bb.min.x;
let y = y as i32 + bb.min.y;
if v > 0.1 {
buffer.push(Point2::new(x, y * -1));
}
})
}
}
for point in buffer {
self.draw_pixel(point.x + position.x, point.y + position.y, color);
}
}
pub fn set_title<T>(&mut self, title: T)
where
T: Into<String>,
{
self.gl_window.set_title(&title.into());
}
}
impl<'a, D> App<'a, D>
where
D: OnStart + OnUpdate + OnEvent + OnStop,
{
pub fn construct<T>((width, height): (i32, i32), pixel_size: i32, title: T, data: D) -> Self
where
T: Into<String>,
{
let config = Config {
window_size: (f64::from(width), f64::from(height)),
resizable: false,
window_title: title.into(),
buffer_size: ((width / pixel_size) as u32, (height / pixel_size) as u32),
};
let mut window = mini_gl_fb::get_fancy(config);
let buffer = vec![[0u8, 0, 0, 0]; ((width / pixel_size) * (height / pixel_size)) as usize];
window.update_buffer(&buffer);
let breakout = window.glutin_breakout();
App {
window: Window {
width: width / pixel_size,
height: height / pixel_size,
fb: breakout.fb,
gl_window: breakout.gl_window,
buffer,
clear_buffer: true,
running: true,
fonts: HashMap::new(),
},
events_loop: breakout.events_loop,
data,
}
}
pub fn with_font(&mut self, name: &'static str, data: &[u8]) {
let font = FontCollection::from_bytes(data.to_vec())
.unwrap_or_else(|e| {
panic!("error constructing a FontCollection from bytes: {}", e);
})
.into_font()
.unwrap_or_else(|e| {
panic!("error turning FontCollection into a Font: {}", e);
});
self.window.fonts.insert(name, font);
}
pub fn persist_buffer(&mut self) {
self.window.clear_buffer = false;
}
pub fn run(&mut self) {
self.data.on_start();
let start = std::time::Instant::now();
while self.window.running {
let tock = std::time::Instant::now().duration_since(start);
let elapsed = u128::from(tock.as_secs()) * 1_000_000 + u128::from(tock.subsec_micros());
if elapsed % 16666 == 0 {
std::thread::sleep(std::time::Duration::from_micros(2));
if self.window.clear_buffer {
self.window.fill(&BLACK);
}
self.data.on_event(&mut self.window, &mut self.events_loop);
self.data.on_update(elapsed / 1000, &mut self.window);
self.window.fb.update_buffer(&self.window.buffer);
self.window.gl_window.swap_buffers().unwrap()
}
}
}
}
pub trait State {
fn on_start(&mut self);
fn on_event(&mut self, window: &mut Window, events: &mut EventsLoop);
fn on_update(&mut self, elapsed: u128, window: &mut Window);
fn on_stop(&mut self);
}