Some basic collision detection.
This commit is contained in:
153
src/lib.rs
153
src/lib.rs
@@ -88,7 +88,7 @@ struct Ship {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ship {
|
impl Ship {
|
||||||
fn update(&mut self, dt_s: f64, keys: &HashSet<String>) {
|
fn update(&mut self, dt_s: f64, keys: &HashSet<String>, boundary: &CGA) {
|
||||||
let vel = self.vel.clone(); //Velocity at beginning of frame
|
let vel = self.vel.clone(); //Velocity at beginning of frame
|
||||||
let mut vel_2 = self.vel.clone();
|
let mut vel_2 = self.vel.clone();
|
||||||
|
|
||||||
@@ -107,29 +107,33 @@ impl Ship {
|
|||||||
self.orientation = bivector_exponential(&(-dt_s * CGA::e12())) * &self.orientation;
|
self.orientation = bivector_exponential(&(-dt_s * CGA::e12())) * &self.orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vel_2 = vel_2 + a_vec*dt_s; //Velocity at end of frame
|
vel_2 = vel_2 + a_vec*dt_s; //Velocity at end of frame
|
||||||
let vel_3 = 0.5*(vel+&vel_2);
|
let vel_3 = 0.5*(vel+&vel_2);
|
||||||
let delta = vel_3 * dt_s;
|
let delta = vel_3 * dt_s;
|
||||||
let pos = bivector_exponential(&delta)*&self.com_rotor; //Update position.
|
let pos = bivector_exponential(&delta)*&self.com_rotor; //Update position.
|
||||||
self.vel = vel_2;
|
let old = self.com_rotor.clone();
|
||||||
self.com_rotor = pos;
|
self.com_rotor = pos;
|
||||||
|
self.vel = vel_2;
|
||||||
|
if self.intersects_circle(boundary) {
|
||||||
|
self.com_rotor = 0.99*old;
|
||||||
|
//self.vel = CGA::zero();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, context: &CanvasRenderingContext2d) {
|
fn draw(&self, context: &CanvasRenderingContext2d) {
|
||||||
let origin = gen_hyperbolic_point(&CGA::zero()); //Origin in the body frame.
|
let origin = gen_hyperbolic_point(&CGA::zero()); //Origin in the body frame.
|
||||||
let com = self.fixed_to_world(&origin);
|
let com = self.fixed_to_world(&origin);
|
||||||
let p0 = self.fixed_to_world(&self.verts[0]);
|
let p1 = self.fixed_to_world(&self.verts[0]);
|
||||||
let p1 = self.fixed_to_world(&self.verts[1]);
|
let p2 = self.fixed_to_world(&self.verts[1]);
|
||||||
let p2 = self.fixed_to_world(&self.verts[2]);
|
let p3 = self.fixed_to_world(&self.verts[2]);
|
||||||
|
|
||||||
draw_point(context, &com);
|
draw_point(context, &com);
|
||||||
draw_point(context, &p0);
|
|
||||||
draw_point(context, &p1);
|
draw_point(context, &p1);
|
||||||
draw_point(context, &p2);
|
draw_point(context, &p2);
|
||||||
draw_line_between(context, &p0, &p1);
|
draw_point(context, &p3);
|
||||||
draw_line_between(context, &p1, &p2);
|
draw_line_between(context, &p1, &p2);
|
||||||
draw_line_between(context, &p2, &p0);
|
draw_line_between(context, &p2, &p3);
|
||||||
|
draw_line_between(context, &p3, &p1);
|
||||||
|
|
||||||
//Construct and draw line pointing straight ahead, to help orient the player.
|
//Construct and draw line pointing straight ahead, to help orient the player.
|
||||||
let delta_gen = self.fixed_to_world(&CGA::e14());
|
let delta_gen = self.fixed_to_world(&CGA::e14());
|
||||||
@@ -141,6 +145,83 @@ impl Ship {
|
|||||||
fn fixed_to_world(&self, vec: &CGA) -> CGA {
|
fn fixed_to_world(&self, vec: &CGA) -> CGA {
|
||||||
&self.com_rotor * &self.orientation * vec * &self.orientation.Reverse() * &self.com_rotor.Reverse()
|
&self.com_rotor * &self.orientation * vec * &self.orientation.Reverse() * &self.com_rotor.Reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn intersects_circle(&self, circle: &CGA) -> bool {
|
||||||
|
//Explicitly select only tri-vector part.
|
||||||
|
let circle = grade_selection(circle, 3);
|
||||||
|
//Find vertices in world space.
|
||||||
|
let p1 = self.fixed_to_world(&self.verts[0]);
|
||||||
|
let p2 = self.fixed_to_world(&self.verts[1]);
|
||||||
|
let p3 = self.fixed_to_world(&self.verts[2]);
|
||||||
|
//Construct sides of ship in world space.
|
||||||
|
let e4 = CGA::e4();
|
||||||
|
let side_1 = &e4 ^ &p1 ^ &p2;
|
||||||
|
let side_2 = &e4 ^ &p1 ^ &p3;
|
||||||
|
let side_3 = &e4 ^ &p2 ^ &p3;
|
||||||
|
//Find intersections between any side and the given circle.
|
||||||
|
let int_1 = circle_intersection(&side_1, &circle);
|
||||||
|
let int_2 = circle_intersection(&side_2, &circle);
|
||||||
|
let int_3 = circle_intersection(&side_3, &circle);
|
||||||
|
//For each side, test if the intersection points are between the two vertices defining the
|
||||||
|
//side.
|
||||||
|
match int_1 {
|
||||||
|
CircleIntersection::TwoPoints(a, b) => {
|
||||||
|
if between_points(&a, &p1, &p2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if between_points(&b, &p1, &p2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CircleIntersection::None => {},
|
||||||
|
}
|
||||||
|
match int_2 {
|
||||||
|
CircleIntersection::TwoPoints(a, b) => {
|
||||||
|
if between_points(&a, &p1, &p3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if between_points(&b, &p1, &p3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CircleIntersection::None => {},
|
||||||
|
}
|
||||||
|
match int_3 {
|
||||||
|
CircleIntersection::TwoPoints(a, b) => {
|
||||||
|
if between_points(&a, &p2, &p3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if between_points(&b, &p2, &p3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CircleIntersection::None => {},
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hyperbolic_distance(point_1: &CGA, point_2: &CGA) -> f64 {
|
||||||
|
let a = grade_selection(point_1, 1);
|
||||||
|
let b = grade_selection(point_2, 1);
|
||||||
|
let a = hyperbolic_normalize(&a);
|
||||||
|
let b = hyperbolic_normalize(&b);
|
||||||
|
let dot = (&a | &b)[SCALAR];
|
||||||
|
let rad = -0.5*dot;
|
||||||
|
let root = rad.sqrt();
|
||||||
|
let dist = root.asinh();
|
||||||
|
dist
|
||||||
|
}
|
||||||
|
|
||||||
|
fn between_points(a: &CGA, p1: &CGA, p2: &CGA) -> bool {
|
||||||
|
let total = hyperbolic_distance(p1, p2);
|
||||||
|
let p1_a = hyperbolic_distance(p1, a);
|
||||||
|
let a_p2 = hyperbolic_distance(a, p2);
|
||||||
|
if p1_a + a_p2 - total < ERROR {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Geometry {
|
enum Geometry {
|
||||||
@@ -190,7 +271,6 @@ fn draw_point(context: &CanvasRenderingContext2d, point: &CGA) {
|
|||||||
let (x,y) = point_to_screen_space(x,y);
|
let (x,y) = point_to_screen_space(x,y);
|
||||||
|
|
||||||
context.begin_path();
|
context.begin_path();
|
||||||
context.set_fill_style_str(BLUE);
|
|
||||||
context.arc(x, y, 2., 0., TWO_PI).unwrap();
|
context.arc(x, y, 2., 0., TWO_PI).unwrap();
|
||||||
context.fill();
|
context.fill();
|
||||||
|
|
||||||
@@ -225,6 +305,11 @@ fn get_hyperbolic_point(point: &CGA) -> (f64, f64) {
|
|||||||
(r*x,r*y)
|
(r*x,r*y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hyperbolic_normalize(point: &CGA) -> CGA {
|
||||||
|
let norm = -(point | CGA::e4())[SCALAR];
|
||||||
|
point * (1. / norm)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_point(point: &CGA) -> (f64, f64) {
|
fn get_point(point: &CGA) -> (f64, f64) {
|
||||||
let x = point[E1];
|
let x = point[E1];
|
||||||
let y = point[E2];
|
let y = point[E2];
|
||||||
@@ -263,8 +348,6 @@ fn get_geometry(trivector: &CGA) -> Geometry {
|
|||||||
|
|
||||||
fn draw_line(context: &CanvasRenderingContext2d, line: &CGA) {
|
fn draw_line(context: &CanvasRenderingContext2d, line: &CGA) {
|
||||||
context.begin_path();
|
context.begin_path();
|
||||||
context.set_stroke_style_str(GREEN);
|
|
||||||
|
|
||||||
let geometry = get_geometry(line);
|
let geometry = get_geometry(line);
|
||||||
match geometry {
|
match geometry {
|
||||||
Geometry::Circle(x,y,r) => {
|
Geometry::Circle(x,y,r) => {
|
||||||
@@ -289,7 +372,6 @@ fn draw_line_between(context: &CanvasRenderingContext2d, a: &CGA, b: &CGA) {
|
|||||||
let line = CGA::e4() ^ a ^ b;
|
let line = CGA::e4() ^ a ^ b;
|
||||||
let geometry = get_geometry(&line);
|
let geometry = get_geometry(&line);
|
||||||
context.begin_path();
|
context.begin_path();
|
||||||
context.set_stroke_style_str(GREEN);
|
|
||||||
match geometry {
|
match geometry {
|
||||||
Geometry::Circle(x,y,r) => {
|
Geometry::Circle(x,y,r) => {
|
||||||
let (a_x, a_y) = get_point(a);
|
let (a_x, a_y) = get_point(a);
|
||||||
@@ -483,7 +565,7 @@ impl Asteroid {
|
|||||||
let v = 0.;
|
let v = 0.;
|
||||||
let (v_x, v_y) = (v * 1.0_f64.cos(), v*1.0_f64.sin());
|
let (v_x, v_y) = (v * 1.0_f64.cos(), v*1.0_f64.sin());
|
||||||
let vel = v_x*CGA::e15() + v_y*CGA::e25();
|
let vel = v_x*CGA::e15() + v_y*CGA::e25();
|
||||||
let circle = circle_from_coords(0.9, 0., 0.1);
|
let circle = circle_from_coords(0.5, 0., 0.4);
|
||||||
Asteroid { circle, vel }
|
Asteroid { circle, vel }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,6 +594,7 @@ pub struct Game {
|
|||||||
asteroids: Vec::<Asteroid>,
|
asteroids: Vec::<Asteroid>,
|
||||||
boundary: CGA,
|
boundary: CGA,
|
||||||
keys: HashSet<String>,
|
keys: HashSet<String>,
|
||||||
|
dt: f64
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@@ -560,6 +643,7 @@ impl Game {
|
|||||||
boundary,
|
boundary,
|
||||||
asteroids: vec![Asteroid::new()],
|
asteroids: vec![Asteroid::new()],
|
||||||
keys: HashSet::new(),
|
keys: HashSet::new(),
|
||||||
|
dt: 100.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,29 +656,30 @@ impl Game {
|
|||||||
self.context.arc(CENTER_X, CENTER_Y, RADIUS, 0., TWO_PI).unwrap();
|
self.context.arc(CENTER_X, CENTER_Y, RADIUS, 0., TWO_PI).unwrap();
|
||||||
self.context.stroke();
|
self.context.stroke();
|
||||||
|
|
||||||
|
let fps = 1000. / self.dt;
|
||||||
|
self.context.set_fill_style_str(BLACK);
|
||||||
|
self.context.set_font("20px Arial");
|
||||||
|
let message = format!("FPS: {}", fps);
|
||||||
|
self.context.fill_text(&message, 10., 30.).unwrap();
|
||||||
|
|
||||||
draw_line(&self.context, &self.boundary);
|
draw_line(&self.context, &self.boundary);
|
||||||
self.ship.draw(&self.context);
|
|
||||||
let line = 100.*self.ship.fixed_to_world(&self.ship.verts[0]) ^ self.ship.fixed_to_world(&self.ship.verts[1]) ^ CGA::e4(); //Scaled to keep above floating point error
|
let mut intersects = false;
|
||||||
|
|
||||||
|
self.context.set_stroke_style_str(RED);
|
||||||
for asteroid in &self.asteroids {
|
for asteroid in &self.asteroids {
|
||||||
let pair = circle_intersection(&asteroid.circle, &self.boundary);
|
if self.ship.intersects_circle(&asteroid.circle) {
|
||||||
match pair {
|
intersects = true;
|
||||||
CircleIntersection::TwoPoints(a,b) => {
|
|
||||||
draw_point(&self.context, &a);
|
|
||||||
draw_point(&self.context, &b);
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
asteroid.draw(&self.context);
|
asteroid.draw(&self.context);
|
||||||
let pair = circle_intersection(&asteroid.circle, &line);
|
|
||||||
match pair {
|
|
||||||
CircleIntersection::TwoPoints(a,b) => {
|
|
||||||
draw_point(&self.context, &a);
|
|
||||||
draw_point(&self.context, &b);
|
|
||||||
draw_line(&self.context, &line);
|
|
||||||
}
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if intersects {
|
||||||
|
self.context.set_stroke_style_str(RED);
|
||||||
|
} else {
|
||||||
|
self.context.set_stroke_style_str(GREEN);
|
||||||
|
}
|
||||||
|
self.ship.draw(&self.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, dt_m: f64) {
|
pub fn update(&mut self, dt_m: f64) {
|
||||||
@@ -604,11 +689,13 @@ impl Game {
|
|||||||
dt_m / 1000.
|
dt_m / 1000.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.dt = dt_m;
|
||||||
|
|
||||||
for asteroid in &mut self.asteroids {
|
for asteroid in &mut self.asteroids {
|
||||||
asteroid.update(dt_s);
|
asteroid.update(dt_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ship.update(dt_s, &self.keys);
|
self.ship.update(dt_s, &self.keys, &self.boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_down(&mut self, key: String) {
|
pub fn key_down(&mut self, key: String) {
|
||||||
|
|||||||
Reference in New Issue
Block a user