#[macro_use]
extern crate log;
use std::{
collections::VecDeque,
thread,
time::{Duration, Instant},
};
use crow::{
glutin::{
dpi::LogicalSize,
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
},
target::{Offset, Scaled},
Context, DrawConfig, DrawTarget, Texture,
};
use rand::Rng;
const SCALE: u32 = 2;
const WINDOW_SIZE: (u32, u32) = (360, 240);
pub struct Rectangle {
position: (i32, i32),
size: (u32, u32),
color: (f32, f32, f32),
}
fn main() -> Result<(), crow::Error> {
pretty_env_logger::formatted_timed_builder()
.filter_level(log::LevelFilter::max())
.init();
let event_loop = EventLoop::new();
let mut ctx = Context::new(
WindowBuilder::new()
.with_inner_size(LogicalSize::new(
WINDOW_SIZE.0 * SCALE,
WINDOW_SIZE.1 * SCALE,
))
.with_resizable(false),
&event_loop,
)?;
let rectangle_vertical = Texture::load(&mut ctx, "textures/rectangle_vertical.png")?;
let rectangle_horizontal = Texture::load(&mut ctx, "textures/rectangle_horizontal.png")?;
let mut rng = rand::thread_rng();
let mut rectangles = VecDeque::new();
rectangles.push_back(Rectangle {
position: (-10, -10),
size: (WINDOW_SIZE.0 * 3 / 5 + 10, WINDOW_SIZE.1 / 3 + 10),
color: rng.gen(),
});
rectangles.push_back(Rectangle {
position: (WINDOW_SIZE.0 as i32 * 4 / 5, WINDOW_SIZE.1 as i32 / 2),
size: (150, 100),
color: rng.gen(),
});
let mut position = 0;
let mut frames_to_next = 0;
let mut fps = FrameRateLimiter::new(60);
event_loop.run(
move |event: Event<()>, _window_target: _, control_flow: &mut ControlFlow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => ctx.window().request_redraw(),
Event::RedrawRequested(_) => {
let mut surface = Scaled::new(ctx.surface(), (SCALE, SCALE));
ctx.clear_color(&mut surface, (0.3, 0.3, 0.8, 1.0));
if frames_to_next == 0 {
frames_to_next = rng.gen_range(50, 170);
rectangles.push_back(Rectangle {
position: (
position + WINDOW_SIZE.0 as i32,
rng.gen_range(-40, WINDOW_SIZE.1 as i32),
),
size: (rng.gen_range(40, 200), rng.gen_range(40, 200)),
color: rng.gen(),
})
} else {
frames_to_next -= 1;
}
position += 1;
if let Some(first) = rectangles.front() {
if (first.position.0 + first.size.0 as i32) < position as i32 {
rectangles.pop_front();
}
}
draw_rectangles(
&rectangles,
&rectangle_vertical,
&rectangle_horizontal,
&mut Offset::new(&mut surface, (position, 0)),
&mut ctx,
);
ctx.present(surface.into_inner()).unwrap();
}
Event::RedrawEventsCleared => fps.frame(),
_ => (),
},
)
}
fn mat((r, g, b): (f32, f32, f32)) -> [[f32; 4]; 4] {
[
[r, 0.0, 0.0, 0.0],
[0.0, g, 0.0, 0.0],
[0.0, 0.0, b, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
}
pub fn draw_rectangles(
rectangles: &VecDeque<Rectangle>,
vertical: &Texture,
horizontal: &Texture,
surface: &mut impl DrawTarget,
ctx: &mut Context,
) {
for rectangle in rectangles.iter() {
let right_pos = rectangle.position.0 + rectangle.size.0 as i32 - vertical.width() as i32;
let mut height = rectangle.size.1;
while let Some(h) = height.checked_sub(vertical.height()) {
height = h;
ctx.draw(
surface,
vertical,
(rectangle.position.0, height as i32 + rectangle.position.1),
&DrawConfig {
color_modulation: mat(rectangle.color),
..Default::default()
},
);
ctx.draw(
surface,
vertical,
(right_pos, height as i32 + rectangle.position.1),
&DrawConfig {
color_modulation: mat(rectangle.color),
flip_horizontally: true,
..Default::default()
},
);
}
let vertical_section =
vertical.get_section((0, vertical.height() - height), (vertical.width(), height));
ctx.draw(
surface,
&vertical_section,
(rectangle.position.0, rectangle.position.1),
&DrawConfig {
color_modulation: mat(rectangle.color),
..Default::default()
},
);
ctx.draw(
surface,
&vertical_section,
(right_pos, rectangle.position.1),
&DrawConfig {
color_modulation: mat(rectangle.color),
flip_horizontally: true,
..Default::default()
},
);
let horizontal_height =
rectangle.position.1 + rectangle.size.1 as i32 - horizontal.height() as i32;
let mut horizontal_pos = rectangle.size.0;
while let Some(p) = horizontal_pos.checked_sub(horizontal.width()) {
horizontal_pos = p;
ctx.draw(
surface,
horizontal,
(
rectangle.position.0 + horizontal_pos as i32,
horizontal_height,
),
&DrawConfig {
color_modulation: mat(rectangle.color),
..Default::default()
},
);
ctx.draw(
surface,
horizontal,
(
rectangle.position.0 + horizontal_pos as i32,
rectangle.position.1,
),
&DrawConfig {
color_modulation: mat(rectangle.color),
flip_vertically: true,
..Default::default()
},
);
}
let horizontal_section = horizontal.get_section(
(horizontal.width() - horizontal_pos, 0),
(horizontal_pos, horizontal.height()),
);
ctx.draw(
surface,
&horizontal_section,
(rectangle.position.0, horizontal_height),
&DrawConfig {
color_modulation: mat(rectangle.color),
..Default::default()
},
);
ctx.draw(
surface,
&horizontal_section,
(rectangle.position.0, rectangle.position.1),
&DrawConfig {
color_modulation: mat(rectangle.color),
flip_vertically: true,
..Default::default()
},
);
}
}
pub struct FrameRateLimiter {
start: Instant,
frame_count: u32,
fps: u32,
}
impl FrameRateLimiter {
pub fn new(fps: u32) -> Self {
Self {
start: Instant::now(),
frame_count: 0,
fps,
}
}
pub fn frame(&mut self) {
self.frame_count += 1;
let finish = Duration::from_micros(1_000_000 / u64::from(self.fps)) * self.frame_count;
if self.start.elapsed() < finish {
while self.start.elapsed() < finish {
thread::yield_now();
}
} else {
warn!("Lag at frame {}", self.frame_count)
}
}
}