use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg};
+////////// POINT ///////////////////////////////////////////////////////////////
+
#[macro_export]
macro_rules! point {
( $x:expr, $y:expr ) => {
}
}
+////////// INTERSECTION ////////////////////////////////////////////////////////
+
+#[derive(Debug)]
+pub enum Intersection {
+ Point(Point<f64>),
+ //Line(Point<f64>, Point<f64>), // TODO: overlapping collinear
+ None,
+}
+
+impl Intersection {
+ pub fn lines(p1: Point<f64>, p2: Point<f64>, p3: Point<f64>, p4: Point<f64>) -> Intersection {
+ let s1 = p2 - p1;
+ let s2 = p4 - p3;
+
+ let denomimator = -s2.x * s1.y + s1.x * s2.y;
+ if denomimator != 0.0 {
+ let s = (-s1.y * (p1.x - p3.x) + s1.x * (p1.y - p3.y)) / denomimator;
+ let t = ( s2.x * (p1.y - p3.y) - s2.y * (p1.x - p3.x)) / denomimator;
+
+ if s >= 0.0 && s <= 1.0 && t >= 0.0 && t <= 1.0 {
+ return Intersection::Point(p1 + (s1 * t))
+ }
+ }
+
+ Intersection::None
+ }
+}
+
+////////// DIMENSION ///////////////////////////////////////////////////////////
+
#[macro_export]
macro_rules! dimen {
( $w:expr, $h:expr ) => {
}
}
-#[macro_export]
-macro_rules! hashmap {
- ($($k:expr => $v:expr),*) => {
- {
- let mut map = std::collections::HashMap::new();
- $(map.insert($k, $v);)*
- map
- }
- }
-}
+////////// TESTS ///////////////////////////////////////////////////////////////
#[cfg(test)]
mod tests {
assert_eq!(r.area(), 30 * 20);
// let a = Dimension::from(("a".to_string(), "b".to_string())).area(); // this doesn't work, because area() is not implemented for String
}
+
+ #[test]
+ fn intersection_of_lines() {
+ let p1 = point!(0.0, 0.0);
+ let p2 = point!(2.0, 2.0);
+ let p3 = point!(0.0, 2.0);
+ let p4 = point!(2.0, 0.0);
+ let r = Intersection::lines(p1, p2, p3, p4);
+ if let Intersection::Point(p) = r {
+ assert_eq!(p, point!(1.0, 1.0));
+ } else {
+ panic!();
+ }
+ }
}
mod geometry;
-pub use common::geometry::Point;
-pub use common::geometry::Dimension;
-pub use common::geometry::Radians;
-pub use common::geometry::Degrees;
+pub use common::geometry::{
+ Point,
+ Dimension,
+ Radians,
+ Degrees,
+ Intersection,
+};
mod time;
pub use common::time::ScopeTimer;
+
+#[macro_export]
+macro_rules! hashmap {
+ ($($k:expr => $v:expr),*) => {
+ {
+ let mut map = std::collections::HashMap::new();
+ $(map.insert($k, $v);)*
+ map
+ }
+ }
+}
use core::app::StateChange;
use core::controller::Controller;
use core::controller::ControllerManager;
-use core::level::{Level, LevelGenerator};
+use core::level::{Level, LevelGenerator, Wall, IntersectResult::Intersection};
use core::render::Renderer;
use point;
use sdl2::event::Event;
self.vel += lvl.gravity;
self.pos += self.vel;
- let x = (self.pos.x / lvl.grid.cell_size.width as f64).min(lvl.grid.size.width as f64 - 1.0).max(0.0) as usize;
- let y = (self.pos.y / lvl.grid.cell_size.height as f64).min(lvl.grid.size.height as f64 - 1.0).max(0.0) as usize;
- if lvl.grid.cells[x][y] {
+ if let Intersection(wall, pos) = lvl.intersect_walls(self.pos - self.vel, self.pos) {
if self.bounces == 0 {
return Dead
}
+ self.pos = pos;
self.vel *= -0.25;
self.pos += self.vel;
self.bounces -= 1;
..*self
}));
}
+ // let x = (self.pos.x / lvl.grid.cell_size.width as f64).min(lvl.grid.size.width as f64 - 1.0).max(0.0) as usize;
+ // let y = (self.pos.y / lvl.grid.cell_size.height as f64).min(lvl.grid.size.height as f64 - 1.0).max(0.0) as usize;
+ // if lvl.grid.cells[x][y] {
+ // if self.bounces == 0 {
+ // return Dead
+ // }
+ // self.vel *= -0.25;
+ // self.pos += self.vel;
+ // self.bounces -= 1;
+ // use rand::distributions::{Distribution, Normal};
+ // let mut rng = rand::thread_rng();
+ // let a = Radians(self.vel.to_radians().0 + Normal::new(0.0, 0.75).sample(&mut rng));
+ // objects.push(Box::new(Boll {
+ // vel: Point::from(a) * Normal::new(1.0, 0.25).sample(&mut rng) * self.vel.length(),
+ // ..*self
+ // }));
+ // }
Alive
}
-use common::{Point, Dimension};
+use common::{Point, Dimension, Intersection};
use core::render::Renderer;
use sprites::SpriteManager;
use std::rc::Rc;
}
}
}
+
+ pub fn intersect_walls(&self, p1: Point<f64>, p2: Point<f64>) -> IntersectResult {
+ let c = point!(p2.x as isize / self.wall_grid.cell_size.width as isize, p2.y as isize / self.wall_grid.cell_size.height as isize);
+ if let Some(walls) = self.wall_grid.at(c) {
+ for w in walls {
+ if let Intersection::Point(p) = Intersection::lines(p1, p2, w.p1, w.p2) {
+ let wall = Wall {
+ edge: Rc::clone(&w),
+ };
+ return IntersectResult::Intersection(wall, p)
+ }
+ }
+ }
+ IntersectResult::None
+ }
+}
+
+pub enum IntersectResult {
+ Intersection(Wall, Point<f64>),
+ None
}
////////// GRID ////////////////////////////////////////////////////////////////
pub cells: Vec<Vec<T>>,
}
+impl<T> Grid<T> {
+ pub fn at<C>(&self, c: C) -> Option<&T>
+ where C: Into<(isize, isize)>
+ {
+ let c = c.into();
+ if c.0 >= 0 && c.0 < self.size.width as isize && c.1 >= 0 && c.1 < self.size.height as isize {
+ Some(&self.cells[c.0 as usize][c.1 as usize])
+ } else {
+ None
+ }
+ }
+}
+
////////// WALL REGION /////////////////////////////////////////////////////////
#[derive(Debug)]
impl WallRegion {
pub fn new(points: Vec<Point<f64>>) -> Rc<Self> {
- let mut edges = vec!();
+ let mut edges = Vec::with_capacity(points.len());
for i in 0..points.len() {
let edge = Rc::new(WallEdge {
pub p1: Point<f64>,
pub p2: Point<f64>,
}
+
+////////// WALL ////////////////////////////////////////////////////////////////
+
+pub struct Wall {
+// region: Rc<WallRegion>,
+ edge: Rc<WallEdge>,
+}