extern crate nalgebra;
extern crate ncollide2d;
extern crate quicksilver;
use nalgebra::{zero, Isometry2};
use ncollide2d::query::{Ray, RayCast};
use quicksilver::{
Result,
geom::{Rectangle, Vector},
graphics::{Color, GpuTriangle, Mesh, Vertex},
lifecycle::{Event, Settings, State, Window, run},
};
use std::cmp::Ordering;
struct Raycast {
regions: Vec<Rectangle>,
targets: Vec<Vector>,
mesh: Mesh
}
impl State for Raycast {
fn new() -> Result<Raycast> {
let regions = vec![
Rectangle::new_sized((800, 600)),
Rectangle::new((200, 200), (100, 100)),
Rectangle::new((400, 200), (100, 100)),
Rectangle::new((400, 400), (100, 100)),
Rectangle::new((200, 400), (100, 100)),
Rectangle::new((50, 50), (50, 50)),
Rectangle::new((550, 300), (64, 64))
];
let targets = regions
.iter()
.flat_map(|region| {
vec![
region.top_left(),
region.top_left() + region.size().x_comp(),
region.top_left() + region.size().y_comp(),
region.top_left() + region.size(),
].into_iter()
})
.collect();
Ok(Raycast {
regions,
targets,
mesh: Mesh::new(),
})
}
fn event(&mut self, event: &Event, window: &mut Window) -> Result<()> {
if let &Event::MouseMoved(_) = event {
let mouse = window.mouse().pos();
self.mesh.clear();
let distance_to = |point: &Vector| (*point - mouse).len();
let angle_to = |point: &Vertex| (point.pos - mouse).angle();
for i in 0..self.targets.len() {
let angle = (self.targets[i] - mouse).angle();
let mut cast_ray = |direction: f32| {
let start = mouse.into_point();
let direction = Vector::from_angle(direction).into_vector();
let ray = Ray::new(start, direction);
let identity = Isometry2::new(zero(), zero());
let cast = self.regions
.iter()
.filter_map(|region| {
region.into_aabb().toi_with_ray(&identity, &ray, false)
})
.map(|toi: f32| (ray.origin + toi * ray.dir).into())
.min_by(|a: &Vector, b: &Vector| {
distance_to(a)
.partial_cmp(&distance_to(b))
.unwrap_or(Ordering::Equal)
});
if let Some(pos) = cast {
self.mesh.vertices.push(Vertex {
pos,
tex_pos: None,
col: Color::WHITE,
});
}
};
cast_ray(angle - 0.001);
cast_ray(angle);
cast_ray(angle + 0.001);
}
self.mesh.vertices.sort_by(|a, b| {
angle_to(a)
.partial_cmp(&angle_to(b))
.unwrap_or(Ordering::Equal)
});
self.mesh.vertices.insert(
0,
Vertex {
pos: mouse,
tex_pos: None,
col: Color::WHITE,
},
);
let triangle_count = self.mesh.vertices.len() as u32 - 1;
for index in 0..triangle_count {
self.mesh.triangles.push(GpuTriangle {
z: 0.0,
indices: [
0,
index as u32 + 1,
(index as u32 + 1) % triangle_count + 1
],
image: None
});
}
}
Ok(())
}
fn draw(&mut self, window: &mut Window) -> Result<()> {
window.clear(Color::BLACK)?;
window.mesh().extend(&self.mesh);
Ok(())
}
}
fn main() {
run::<Raycast>("Raycast", Vector::new(800, 600), Settings::default());
}