split hwphysics into modules
authoralfadur
Fri, 09 Nov 2018 01:05:34 +0300
changeset 14199 a4c17cfaa4c9
parent 14198 a4dd3d307115
child 14200 abbb74b9cb62
split hwphysics into modules
rust/fpnum/src/lib.rs
rust/hwphysics/src/collision.rs
rust/hwphysics/src/common.rs
rust/hwphysics/src/grid.rs
rust/hwphysics/src/lib.rs
rust/hwphysics/src/physics.rs
--- a/rust/fpnum/src/lib.rs	Thu Nov 08 22:00:43 2018 +0100
+++ b/rust/fpnum/src/lib.rs	Fri Nov 09 01:05:34 2018 +0300
@@ -364,6 +364,11 @@
     }
 
     #[inline]
+    pub fn is_zero(&self) -> bool {
+        self.x().is_zero() && self.y().is_zero()
+    }
+
+    #[inline]
     pub fn max_norm(&self) -> FPNum {
         std::cmp::max(self.x().abs(), self.y().abs())
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hwphysics/src/collision.rs	Fri Nov 09 01:05:34 2018 +0300
@@ -0,0 +1,111 @@
+use std::{
+    ops::RangeInclusive
+};
+
+use crate::{
+    common::GearId,
+    physics::PhysicsData,
+    grid::Grid
+};
+
+use fpnum::*;
+use integral_geometry::{
+    Point, Size, GridIndex
+};
+use land2d::Land2D;
+
+pub fn fppoint_round(point: &FPPoint) -> Point {
+    Point::new(point.x().round() as i32, point.y().round() as i32)
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct CircleBounds {
+    pub center: FPPoint,
+    pub radius: FPNum
+}
+
+impl CircleBounds {
+    pub fn intersects(&self, other: &CircleBounds) -> bool {
+        (other.center - self.center).is_in_range(self.radius + other.radius)
+    }
+
+    pub fn rows(&self) -> impl Iterator<Item = (usize, RangeInclusive<usize>)> {
+        let radius = self.radius.abs_round() as usize;
+        let center = Point::from_fppoint(&self.center);
+        (center.y as usize - radius..=center.y as usize + radius)
+            .map(move |row| (row, center.x as usize - radius..=center.x as usize + radius))
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct CollisionData {
+    pub bounds: CircleBounds
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ContactData {
+    pub elasticity: FPNum,
+    pub friction: FPNum
+}
+
+struct EnabledCollisionsCollection {
+    gear_ids: Vec<GearId>,
+    collisions: Vec<CollisionData>
+}
+
+impl EnabledCollisionsCollection {
+    fn push(&mut self, gear_id: GearId, collision: CollisionData) {
+        self.gear_ids.push(gear_id);
+        self.collisions.push(collision);
+    }
+
+    fn iter(&self) -> impl Iterator<Item = (GearId, &CollisionData)> {
+        self.gear_ids.iter().cloned().zip(self.collisions.iter())
+    }
+}
+
+pub struct CollisionProcessor {
+    grid: Grid,
+    enabled_collisions: EnabledCollisionsCollection,
+
+    detected_collisions: DetectedCollisions,
+}
+
+pub struct DetectedCollisions {
+    pub pairs: Vec<(GearId, GearId)>,
+    pub positions: Vec<Point>
+}
+
+impl DetectedCollisions {
+    pub fn new(capacity: usize) -> Self {
+        Self {
+            pairs: Vec::with_capacity(capacity),
+            positions: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn push(&mut self, contact_gear_id1: GearId, contact_gear_id2: GearId, position: &FPPoint) {
+        self.pairs.push((contact_gear_id1, contact_gear_id2));
+        self.positions.push(fppoint_round(position));
+    }
+}
+
+impl CollisionProcessor {
+    pub fn process(&mut self, land: &Land2D<u32>, updates: &crate::physics::PositionUpdate) {
+        self.grid.check_collisions(&mut self.detected_collisions);
+
+        for (gear_id, collision) in self.enabled_collisions.iter() {
+            if collision.bounds.rows().any(|(y, r)| (&land[y][r]).iter().any(|v| *v != 0)) {
+                self.detected_collisions.push(0, 0, &collision.bounds.center)
+            }
+        }
+    }
+
+    pub fn push(&mut self, gear_id: GearId, physics_data: PhysicsData, collision_data: CollisionData) {
+        if physics_data.velocity.is_zero() {
+            self.grid.insert_static(0, &physics_data.position, &collision_data.bounds);
+        } else {
+            self.grid.insert_dynamic(0, &physics_data.position, &collision_data.bounds);
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hwphysics/src/common.rs	Fri Nov 09 01:05:34 2018 +0300
@@ -0,0 +1,1 @@
+pub type GearId = u16;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hwphysics/src/grid.rs	Fri Nov 09 01:05:34 2018 +0300
@@ -0,0 +1,91 @@
+use crate::{
+    common::GearId,
+    collision::{
+        fppoint_round,
+        CircleBounds,
+        DetectedCollisions
+    }
+};
+
+use integral_geometry::{
+    Point,
+    Size,
+    GridIndex
+};
+use fpnum::FPPoint;
+
+struct GridBin {
+    refs: Vec<GearId>,
+    static_entries: Vec<CircleBounds>,
+    dynamic_entries: Vec<CircleBounds>
+}
+
+impl GridBin {
+    fn new() -> Self {
+        Self {
+            refs: vec![],
+            static_entries: vec![],
+            dynamic_entries: vec![]
+        }
+    }
+}
+
+const GRID_BIN_SIZE: usize = 256;
+
+pub struct Grid {
+    bins: Vec<GridBin>,
+    space_size: Size,
+    bins_count: Size,
+    index: GridIndex
+}
+
+impl Grid {
+    pub fn new(size: Size) -> Self {
+        assert!(size.is_power_of_two());
+        let bins_count =
+            Size::new(size.width / GRID_BIN_SIZE,
+                      size.height / GRID_BIN_SIZE);
+
+        Self {
+            bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
+            space_size: size,
+            bins_count,
+            index: Size::square(GRID_BIN_SIZE).to_grid_index()
+        }
+    }
+
+    fn bin_index(&self, position: &FPPoint) -> Point {
+        self.index.map(fppoint_round(position))
+    }
+
+    fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
+        let index = self.bin_index(position);
+        &mut self.bins[index.x as usize * self.bins_count.width + index.y as usize]
+    }
+
+    pub fn insert_static(&mut self, gear_id: GearId, position: &FPPoint, bounds: &CircleBounds) {
+        self.lookup_bin(position).static_entries.push(*bounds)
+    }
+
+    pub fn insert_dynamic(&mut self, gear_id: GearId, position: &FPPoint, bounds: &CircleBounds) {
+        self.lookup_bin(position).dynamic_entries.push(*bounds)
+    }
+
+    pub fn check_collisions(&self, collisions: &mut DetectedCollisions) {
+        for bin in &self.bins {
+            for bounds in &bin.dynamic_entries {
+                for other in &bin.dynamic_entries {
+                    if bounds.intersects(other) && bounds != other {
+                        collisions.push(0, 0, &bounds.center)
+                    }
+                }
+
+                for other in &bin.static_entries {
+                    if bounds.intersects(other) {
+                        collisions.push(0, 0, &bounds.center)
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/rust/hwphysics/src/lib.rs	Thu Nov 08 22:00:43 2018 +0100
+++ b/rust/hwphysics/src/lib.rs	Fri Nov 09 01:05:34 2018 +0300
@@ -1,191 +1,45 @@
-use std::{
-    ops::RangeInclusive
-};
+mod common;
+mod physics;
+mod grid;
+mod collision;
 
-use fpnum::*;
-use integral_geometry::{
-    Point, Size, GridIndex
-};
+use fpnum::FPNum;
 use land2d::Land2D;
 
-type Index = u16;
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-struct PhysicsData {
-    position: FPPoint,
-    velocity: FPPoint,
-}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-struct  CollisionData {
-    bounds: CircleBounds
-}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-struct ContactData {
-    elasticity: FPNum,
-    friction: FPNum
-}
-
-pub struct PhysicsCollection {
-    positions: Vec<FPPoint>,
-    velocities: Vec<FPPoint>
-}
-
-impl PhysicsCollection {
-    fn push(&mut self, data: PhysicsData) {
-        self.positions.push(data.position);
-        self.velocities.push(data.velocity);
+use crate::{
+    common::GearId,
+    physics::{
+        PhysicsProcessor,
+        PhysicsData
+    },
+    collision::{
+        CollisionProcessor,
+        CollisionData,
+        ContactData
     }
-
-    fn iter_mut_pos(&mut self) -> impl Iterator<Item = (&mut FPPoint, &FPPoint)> {
-        self.positions.iter_mut().zip(self.velocities.iter())
-    }
-}
+};
 
 pub struct JoinedData {
+    gear_id: GearId,
     physics: PhysicsData,
     collision: CollisionData,
     contact: ContactData
 }
 
 pub struct World {
-    enabled_physics: PhysicsCollection,
-    disabled_physics: Vec<PhysicsData>,
-
-    enabled_collision: Vec<CollisionData>,
-    disabled_collision: Vec<CollisionData>,
-    grid: Grid,
-
-    physics_cleanup: Vec<PhysicsData>,
-    collision_output: Vec<(Index, Index)>,
-    land_collision_output: Vec<Index>
-}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-struct CircleBounds {
-    center: FPPoint,
-    radius: FPNum
-}
-
-impl CircleBounds {
-    pub fn intersects(&self, other: &CircleBounds) -> bool {
-        (other.center - self.center).is_in_range(self.radius + other.radius)
-    }
-
-    pub fn rows(&self) -> impl Iterator<Item = (usize, RangeInclusive<usize>)> {
-        let radius = self.radius.abs_round() as usize;
-        let center = Point::from_fppoint(&self.center);
-        (center.y as usize - radius..=center.y as usize + radius)
-            .map(move |row| (row, center.x as usize - radius..=center.x as usize + radius))
-    }
-}
-
-fn fppoint_round(point: &FPPoint) -> Point {
-    Point::new(point.x().round() as i32, point.y().round() as i32)
-}
-
-struct GridBin {
-    refs: Vec<Index>,
-    static_entries: Vec<CircleBounds>,
-    dynamic_entries: Vec<CircleBounds>
-}
-
-impl GridBin {
-    fn new() -> Self {
-        Self {
-            refs: vec![],
-            static_entries: vec![],
-            dynamic_entries: vec![]
-        }
-    }
-}
-
-const GRID_BIN_SIZE: usize = 256;
-
-struct Grid {
-    bins: Vec<GridBin>,
-    space_size: Size,
-    bins_count: Size,
-    index: GridIndex
-}
-
-impl Grid {
-    fn new(size: Size) -> Self {
-        assert!(size.is_power_of_two());
-        let bins_count =
-            Size::new(size.width / GRID_BIN_SIZE,
-                      size.height / GRID_BIN_SIZE);
-
-        Self {
-            bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
-            space_size: size,
-            bins_count,
-            index: Size::square(GRID_BIN_SIZE).to_grid_index()
-        }
-    }
-
-    fn bin_index(&self, position: &FPPoint) -> Point {
-        self.index.map(fppoint_round(position))
-    }
-
-    fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
-        let index = self.bin_index(position);
-        &mut self.bins[index.x as usize * self.bins_count.width + index.y as usize]
-    }
-
-    fn insert_static(&mut self, index: Index, position: &FPPoint, bounds: &CircleBounds) {
-        self.lookup_bin(position).static_entries.push(*bounds)
-    }
-
-    fn insert_dynamic(&mut self, index: Index, position: &FPPoint, bounds: &CircleBounds) {
-        self.lookup_bin(position).dynamic_entries.push(*bounds)
-    }
-
-    fn check_collisions(&self, collisions: &mut Vec<(Index, Index)>) {
-        for bin in &self.bins {
-            for bounds in &bin.dynamic_entries {
-                for other in &bin.dynamic_entries {
-                    if bounds.intersects(other) && bounds != other {
-                        collisions.push((0, 0))
-                    }
-                }
-
-                for other in &bin.static_entries {
-                    if bounds.intersects(other) {
-                        collisions.push((0, 0))
-                    }
-                }
-            }
-        }
-    }
+    physics: PhysicsProcessor,
+    collision: CollisionProcessor,
 }
 
 impl World {
     pub fn step(&mut self, time_step: FPNum, land: &Land2D<u32>) {
-        for (pos, vel) in self.enabled_physics.iter_mut_pos() {
-            *pos += *vel
-        }
-
-        self.grid.check_collisions(&mut self.collision_output);
-    }
-
-    fn check_land_collisions(&mut self, land: &Land2D<u32>) {
-        for collision in &self.enabled_collision {
-            if collision.bounds.rows().any(|(y, r)| (&land[y][r]).iter().any(|v| *v != 0)) {
-                self.land_collision_output.push(0)
-            }
-        }
+        let updates = self.physics.process(time_step);
+        self.collision.process(land, &updates);
     }
 
     pub fn add_gear(&mut self, data: JoinedData) {
-        if data.physics.velocity == FPPoint::zero() {
-            self.disabled_physics.push(data.physics);
-            self.grid.insert_static(0, &data.physics.position, &data.collision.bounds);
-        } else {
-            self.enabled_physics.push(data.physics);
-            self.grid.insert_dynamic(0, &data.physics.position, &data.collision.bounds);
-        }
+        self.physics.push(data.gear_id, data.physics);
+        self.collision.push(data.gear_id, data.physics, data.collision);
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hwphysics/src/physics.rs	Fri Nov 09 01:05:34 2018 +0300
@@ -0,0 +1,99 @@
+use crate::{
+    common::GearId
+};
+use fpnum::*;
+use integral_geometry::{
+    Point, Size, GridIndex
+};
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct PhysicsData {
+    pub position: FPPoint,
+    pub velocity: FPPoint,
+}
+
+
+pub struct DynamicPhysicsCollection {
+    gear_ids: Vec<GearId>,
+    positions: Vec<FPPoint>,
+    velocities: Vec<FPPoint>,
+}
+
+impl DynamicPhysicsCollection {
+    fn len(&self) -> usize {
+        self.gear_ids.len()
+    }
+
+    fn push(&mut self, id: GearId, physics: PhysicsData) {
+        self.gear_ids.push(id);
+        self.positions.push(physics.position);
+        self.velocities.push(physics.velocity);
+    }
+
+    fn iter_pos_update(&mut self) -> impl Iterator<Item = (GearId, (&mut FPPoint, &FPPoint))> {
+        self.gear_ids.iter().cloned()
+            .zip(self.positions.iter_mut()
+                .zip(self.velocities.iter()))
+    }
+}
+
+pub struct StaticPhysicsCollection {
+    gear_ids: Vec<GearId>,
+    positions: Vec<FPPoint>
+}
+
+impl StaticPhysicsCollection {
+    fn push(&mut self, gear_id: GearId, physics: PhysicsData) {
+        self.gear_ids.push(gear_id);
+        self.positions.push(physics.position);
+    }
+}
+
+pub struct PhysicsProcessor {
+    dynamic_physics: DynamicPhysicsCollection,
+    static_physics: StaticPhysicsCollection,
+
+    physics_cleanup: Vec<GearId>,
+    position_updates: PositionUpdate
+}
+
+pub struct PositionUpdate {
+    pub gear_ids: Vec<GearId>,
+    pub positions: Vec<FPPoint>
+}
+
+impl PositionUpdate {
+    pub fn new(capacity: usize) -> Self {
+        Self {
+            gear_ids: Vec::with_capacity(capacity),
+            positions: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn push(&mut self, gear_id: GearId, position: &FPPoint) {
+        self.gear_ids.push(gear_id);
+        self.positions.push(*position);
+    }
+}
+
+impl PhysicsProcessor {
+    pub fn process(&mut self, time_step: FPNum) -> &PositionUpdate {
+        for (gear_id, (pos, vel)) in self.dynamic_physics.iter_pos_update() {
+            *pos += *vel * time_step;
+            if !vel.is_zero() {
+                self.position_updates.push(gear_id, pos)
+            } else {
+                self.physics_cleanup.push(gear_id)
+            }
+        }
+        &self.position_updates
+    }
+
+    pub fn push(&mut self, gear_id: GearId, physics_data: PhysicsData) {
+        if physics_data.velocity.is_zero() {
+            self.static_physics.push(gear_id, physics_data);
+        } else {
+            self.dynamic_physics.push(gear_id, physics_data);
+        }
+    }
+}
\ No newline at end of file