From 60058b918569190f437fe996dfc79daf5a431b91 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tomas=20Wenstr=C3=B6m?= Date: Mon, 1 Feb 2021 19:34:35 +0100 Subject: [PATCH] WIP: Wall intersection --- src/common/geometry.rs | 57 +++++++++++++++++++++++++++++++++++++++++--------- src/common/mod.rs | 22 +++++++++++++++---- src/core/game.rs | 24 +++++++++++++++++---- src/core/level/mod.rs | 44 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 127 insertions(+), 20 deletions(-) diff --git a/src/common/geometry.rs b/src/common/geometry.rs index 5a03ffb..455e600 100644 --- a/src/common/geometry.rs +++ b/src/common/geometry.rs @@ -1,5 +1,7 @@ use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg}; +////////// POINT /////////////////////////////////////////////////////////////// + #[macro_export] macro_rules! point { ( $x:expr, $y:expr ) => { @@ -181,6 +183,36 @@ impl Radians { } } +////////// INTERSECTION //////////////////////////////////////////////////////// + +#[derive(Debug)] +pub enum Intersection { + Point(Point), + //Line(Point, Point), // TODO: overlapping collinear + None, +} + +impl Intersection { + pub fn lines(p1: Point, p2: Point, p3: Point, p4: Point) -> 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 ) => { @@ -210,16 +242,7 @@ impl From<(T, T)> for Dimension { } } -#[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 { @@ -296,4 +319,18 @@ 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!(); + } + } } diff --git a/src/common/mod.rs b/src/common/mod.rs index 9c39453..505860d 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,8 +1,22 @@ 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 + } + } +} diff --git a/src/core/game.rs b/src/core/game.rs index 554bde7..386057b 100644 --- a/src/core/game.rs +++ b/src/core/game.rs @@ -4,7 +4,7 @@ use common::{Point, Radians}; 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; @@ -271,12 +271,11 @@ impl Object for Boll { 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; @@ -288,6 +287,23 @@ impl Object for Boll { ..*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 } diff --git a/src/core/level/mod.rs b/src/core/level/mod.rs index 3945776..2a90739 100644 --- a/src/core/level/mod.rs +++ b/src/core/level/mod.rs @@ -1,4 +1,4 @@ -use common::{Point, Dimension}; +use common::{Point, Dimension, Intersection}; use core::render::Renderer; use sprites::SpriteManager; use std::rc::Rc; @@ -96,6 +96,26 @@ impl Level { } } } + + pub fn intersect_walls(&self, p1: Point, p2: Point) -> 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), + None } ////////// GRID //////////////////////////////////////////////////////////////// @@ -107,6 +127,19 @@ pub struct Grid { pub cells: Vec>, } +impl Grid { + pub fn at(&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)] @@ -116,7 +149,7 @@ pub struct WallRegion { impl WallRegion { pub fn new(points: Vec>) -> Rc { - let mut edges = vec!(); + let mut edges = Vec::with_capacity(points.len()); for i in 0..points.len() { let edge = Rc::new(WallEdge { @@ -151,3 +184,10 @@ struct WallEdge { pub p1: Point, pub p2: Point, } + +////////// WALL //////////////////////////////////////////////////////////////// + +pub struct Wall { +// region: Rc, + edge: Rc, +} -- 2.11.0