# HG changeset patch # User unc0rr # Date 1559680482 -7200 # Node ID febccab419b15e4edf1fc998e2d387f9a655ab72 # Parent 901751d3cd808b35de54febef79d643af5b3d4ec Apply dos2unix to rust sources diff -r 901751d3cd80 -r febccab419b1 rust/hedgewars-server/src/core.rs --- a/rust/hedgewars-server/src/core.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hedgewars-server/src/core.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,5 +1,5 @@ -pub mod client; -pub mod indexslab; -pub mod room; -pub mod server; -pub mod types; +pub mod client; +pub mod indexslab; +pub mod room; +pub mod server; +pub mod types; diff -r 901751d3cd80 -r febccab419b1 rust/hedgewars-server/src/core/indexslab.rs --- a/rust/hedgewars-server/src/core/indexslab.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hedgewars-server/src/core/indexslab.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,71 +1,71 @@ -use std::{ - iter, - mem::replace, - ops::{Index, IndexMut}, -}; - -pub struct IndexSlab { - data: Vec>, -} - -impl IndexSlab { - pub fn new() -> Self { - Self { data: Vec::new() } - } - - pub fn with_capacity(capacity: usize) -> Self { - Self { - data: Vec::with_capacity(capacity), - } - } - - pub fn insert(&mut self, index: usize, value: T) { - if index >= self.data.len() { - self.data.reserve(index - self.data.len() + 1); - self.data.extend((self.data.len()..index).map(|_| None)); - self.data.push(Some(value)) - } else { - self.data[index] = Some(value); - } - } - - pub fn contains(&self, index: usize) -> bool { - self.data.get(index).and_then(|x| x.as_ref()).is_some() - } - - pub fn remove(&mut self, index: usize) -> Option { - if let Some(x) = self.data.get_mut(index) { - replace(x, None) - } else { - None - } - } - - pub fn iter(&self) -> impl Iterator { - self.data - .iter() - .enumerate() - .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x)))) - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.data - .iter_mut() - .enumerate() - .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x)))) - } -} - -impl Index for IndexSlab { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - self.data[index].as_ref().unwrap() - } -} - -impl IndexMut for IndexSlab { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.data[index].as_mut().unwrap() - } -} +use std::{ + iter, + mem::replace, + ops::{Index, IndexMut}, +}; + +pub struct IndexSlab { + data: Vec>, +} + +impl IndexSlab { + pub fn new() -> Self { + Self { data: Vec::new() } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + data: Vec::with_capacity(capacity), + } + } + + pub fn insert(&mut self, index: usize, value: T) { + if index >= self.data.len() { + self.data.reserve(index - self.data.len() + 1); + self.data.extend((self.data.len()..index).map(|_| None)); + self.data.push(Some(value)) + } else { + self.data[index] = Some(value); + } + } + + pub fn contains(&self, index: usize) -> bool { + self.data.get(index).and_then(|x| x.as_ref()).is_some() + } + + pub fn remove(&mut self, index: usize) -> Option { + if let Some(x) = self.data.get_mut(index) { + replace(x, None) + } else { + None + } + } + + pub fn iter(&self) -> impl Iterator { + self.data + .iter() + .enumerate() + .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x)))) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.data + .iter_mut() + .enumerate() + .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x)))) + } +} + +impl Index for IndexSlab { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + self.data[index].as_ref().unwrap() + } +} + +impl IndexMut for IndexSlab { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.data[index].as_mut().unwrap() + } +} diff -r 901751d3cd80 -r febccab419b1 rust/hedgewars-server/src/handlers/checker.rs --- a/rust/hedgewars-server/src/handlers/checker.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hedgewars-server/src/handlers/checker.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,13 +1,13 @@ -use log::*; -use mio; - -use crate::{ - core::{server::HwServer, types::ClientId}, - protocol::messages::HwProtocolMessage, -}; - -pub fn handle(_server: &mut HwServer, _client_id: ClientId, message: HwProtocolMessage) { - match message { - _ => warn!("Unknown command"), - } -} +use log::*; +use mio; + +use crate::{ + core::{server::HwServer, types::ClientId}, + protocol::messages::HwProtocolMessage, +}; + +pub fn handle(_server: &mut HwServer, _client_id: ClientId, message: HwProtocolMessage) { + match message { + _ => warn!("Unknown command"), + } +} diff -r 901751d3cd80 -r febccab419b1 rust/hedgewars-server/src/server/io.rs --- a/rust/hedgewars-server/src/server/io.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hedgewars-server/src/server/io.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,162 +1,162 @@ -use std::{ - fs::{File, OpenOptions}, - io::{Error, ErrorKind, Read, Result, Write}, - sync::mpsc, - thread, -}; - -use crate::{ - handlers::{IoResult, IoTask}, - server::database::Database, -}; -use log::*; -use mio::{Evented, Poll, PollOpt}; -use mio_extras::channel; - -pub type RequestId = u32; - -pub struct IoThread { - core_tx: mpsc::Sender<(RequestId, IoTask)>, - core_rx: channel::Receiver<(RequestId, IoResult)>, -} - -impl IoThread { - pub fn new() -> Self { - let (core_tx, io_rx) = mpsc::channel(); - let (io_tx, core_rx) = channel::channel(); - - let mut db = Database::new(); - db.connect("localhost"); - - thread::spawn(move || { - while let Ok((request_id, task)) = io_rx.recv() { - let response = match task { - IoTask::CheckRegistered { nick } => match db.is_registered(&nick) { - Ok(is_registered) => IoResult::AccountRegistered(is_registered), - Err(e) => { - warn!("Unable to check account's existence: {}", e); - IoResult::AccountRegistered(false) - } - }, - - IoTask::GetAccount { - nick, - protocol, - password_hash, - client_salt, - server_salt, - } => { - match db.get_account( - &nick, - protocol, - &password_hash, - &client_salt, - &server_salt, - ) { - Ok(account) => IoResult::Account(account), - Err(e) => { - warn!("Unable to get account data: {}", e); - IoResult::Account(None) - } - } - } - - IoTask::GetReplay { id } => { - let result = match db.get_replay_name(id) { - Ok(Some(filename)) => { - let filename = format!( - "checked/{}", - if filename.starts_with("replays/") { - &filename[8..] - } else { - &filename - } - ); - match load_file(&filename) { - Ok(contents) => Some(unimplemented!()), - Err(e) => { - warn!( - "Error while writing the room config file \"{}\": {}", - filename, e - ); - None - } - } - } - Ok(None) => None, - Err(e) => { - warn!("Unable to get replay name: {}", e); - None - } - }; - IoResult::Replay(result) - } - - IoTask::SaveRoom { - room_id, - filename, - contents, - } => { - let result = match save_file(&filename, &contents) { - Ok(()) => true, - Err(e) => { - warn!( - "Error while writing the room config file \"{}\": {}", - filename, e - ); - false - } - }; - IoResult::SaveRoom(room_id, result) - } - - IoTask::LoadRoom { room_id, filename } => { - let result = match load_file(&filename) { - Ok(contents) => Some(contents), - Err(e) => { - warn!( - "Error while writing the room config file \"{}\": {}", - filename, e - ); - None - } - }; - IoResult::LoadRoom(room_id, result) - } - }; - io_tx.send((request_id, response)); - } - }); - - Self { core_rx, core_tx } - } - - pub fn send(&self, request_id: RequestId, task: IoTask) { - self.core_tx.send((request_id, task)).unwrap(); - } - - pub fn try_recv(&self) -> Option<(RequestId, IoResult)> { - match self.core_rx.try_recv() { - Ok(result) => Some(result), - Err(mpsc::TryRecvError::Empty) => None, - Err(mpsc::TryRecvError::Disconnected) => unreachable!(), - } - } - - pub fn register_rx(&self, poll: &mio::Poll, token: mio::Token) -> Result<()> { - self.core_rx - .register(poll, token, mio::Ready::readable(), PollOpt::edge()) - } -} - -fn save_file(filename: &str, contents: &str) -> Result<()> { - let mut writer = OpenOptions::new().create(true).write(true).open(filename)?; - writer.write_all(contents.as_bytes()) -} - -fn load_file(filename: &str) -> Result { - let mut reader = File::open(filename)?; - let mut result = String::new(); - reader.read_to_string(&mut result)?; - Ok(result) -} +use std::{ + fs::{File, OpenOptions}, + io::{Error, ErrorKind, Read, Result, Write}, + sync::mpsc, + thread, +}; + +use crate::{ + handlers::{IoResult, IoTask}, + server::database::Database, +}; +use log::*; +use mio::{Evented, Poll, PollOpt}; +use mio_extras::channel; + +pub type RequestId = u32; + +pub struct IoThread { + core_tx: mpsc::Sender<(RequestId, IoTask)>, + core_rx: channel::Receiver<(RequestId, IoResult)>, +} + +impl IoThread { + pub fn new() -> Self { + let (core_tx, io_rx) = mpsc::channel(); + let (io_tx, core_rx) = channel::channel(); + + let mut db = Database::new(); + db.connect("localhost"); + + thread::spawn(move || { + while let Ok((request_id, task)) = io_rx.recv() { + let response = match task { + IoTask::CheckRegistered { nick } => match db.is_registered(&nick) { + Ok(is_registered) => IoResult::AccountRegistered(is_registered), + Err(e) => { + warn!("Unable to check account's existence: {}", e); + IoResult::AccountRegistered(false) + } + }, + + IoTask::GetAccount { + nick, + protocol, + password_hash, + client_salt, + server_salt, + } => { + match db.get_account( + &nick, + protocol, + &password_hash, + &client_salt, + &server_salt, + ) { + Ok(account) => IoResult::Account(account), + Err(e) => { + warn!("Unable to get account data: {}", e); + IoResult::Account(None) + } + } + } + + IoTask::GetReplay { id } => { + let result = match db.get_replay_name(id) { + Ok(Some(filename)) => { + let filename = format!( + "checked/{}", + if filename.starts_with("replays/") { + &filename[8..] + } else { + &filename + } + ); + match load_file(&filename) { + Ok(contents) => Some(unimplemented!()), + Err(e) => { + warn!( + "Error while writing the room config file \"{}\": {}", + filename, e + ); + None + } + } + } + Ok(None) => None, + Err(e) => { + warn!("Unable to get replay name: {}", e); + None + } + }; + IoResult::Replay(result) + } + + IoTask::SaveRoom { + room_id, + filename, + contents, + } => { + let result = match save_file(&filename, &contents) { + Ok(()) => true, + Err(e) => { + warn!( + "Error while writing the room config file \"{}\": {}", + filename, e + ); + false + } + }; + IoResult::SaveRoom(room_id, result) + } + + IoTask::LoadRoom { room_id, filename } => { + let result = match load_file(&filename) { + Ok(contents) => Some(contents), + Err(e) => { + warn!( + "Error while writing the room config file \"{}\": {}", + filename, e + ); + None + } + }; + IoResult::LoadRoom(room_id, result) + } + }; + io_tx.send((request_id, response)); + } + }); + + Self { core_rx, core_tx } + } + + pub fn send(&self, request_id: RequestId, task: IoTask) { + self.core_tx.send((request_id, task)).unwrap(); + } + + pub fn try_recv(&self) -> Option<(RequestId, IoResult)> { + match self.core_rx.try_recv() { + Ok(result) => Some(result), + Err(mpsc::TryRecvError::Empty) => None, + Err(mpsc::TryRecvError::Disconnected) => unreachable!(), + } + } + + pub fn register_rx(&self, poll: &mio::Poll, token: mio::Token) -> Result<()> { + self.core_rx + .register(poll, token, mio::Ready::readable(), PollOpt::edge()) + } +} + +fn save_file(filename: &str, contents: &str) -> Result<()> { + let mut writer = OpenOptions::new().create(true).write(true).open(filename)?; + writer.write_all(contents.as_bytes()) +} + +fn load_file(filename: &str) -> Result { + let mut reader = File::open(filename)?; + let mut result = String::new(); + reader.read_to_string(&mut result)?; + Ok(result) +} diff -r 901751d3cd80 -r febccab419b1 rust/hwphysics/src/collision.rs --- a/rust/hwphysics/src/collision.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hwphysics/src/collision.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,129 +1,129 @@ -use std::ops::RangeInclusive; - -use crate::{ - common::{GearData, GearDataProcessor, GearId}, - grid::Grid, - physics::PhysicsData, -}; - -use fpnum::*; -use integral_geometry::{GridIndex, Point, Size}; -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)> { - 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, -} - -impl GearData for CollisionData {} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ContactData { - pub elasticity: FPNum, - pub friction: FPNum, -} - -impl GearData for ContactData {} - -struct EnabledCollisionsCollection { - gear_ids: Vec, - collisions: Vec, -} - -impl EnabledCollisionsCollection { - fn new() -> Self { - Self { - gear_ids: Vec::new(), - collisions: Vec::new(), - } - } - - fn push(&mut self, gear_id: GearId, collision: CollisionData) { - self.gear_ids.push(gear_id); - self.collisions.push(collision); - } - - fn iter(&self) -> impl Iterator { - 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, -} - -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 new(size: Size) -> Self { - Self { - grid: Grid::new(size), - enabled_collisions: EnabledCollisionsCollection::new(), - detected_collisions: DetectedCollisions::new(0), - } - } - - pub fn process(&mut self, land: &Land2D, updates: &crate::physics::PositionUpdates) { - 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(gear_id, 0, &collision.bounds.center) - } - } - } -} - -impl GearDataProcessor for CollisionProcessor { - fn add(&mut self, gear_id: GearId, gear_data: CollisionData) { - self.grid.insert_static(gear_id, &gear_data.bounds); - } -} +use std::ops::RangeInclusive; + +use crate::{ + common::{GearData, GearDataProcessor, GearId}, + grid::Grid, + physics::PhysicsData, +}; + +use fpnum::*; +use integral_geometry::{GridIndex, Point, Size}; +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)> { + 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, +} + +impl GearData for CollisionData {} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ContactData { + pub elasticity: FPNum, + pub friction: FPNum, +} + +impl GearData for ContactData {} + +struct EnabledCollisionsCollection { + gear_ids: Vec, + collisions: Vec, +} + +impl EnabledCollisionsCollection { + fn new() -> Self { + Self { + gear_ids: Vec::new(), + collisions: Vec::new(), + } + } + + fn push(&mut self, gear_id: GearId, collision: CollisionData) { + self.gear_ids.push(gear_id); + self.collisions.push(collision); + } + + fn iter(&self) -> impl Iterator { + 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, +} + +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 new(size: Size) -> Self { + Self { + grid: Grid::new(size), + enabled_collisions: EnabledCollisionsCollection::new(), + detected_collisions: DetectedCollisions::new(0), + } + } + + pub fn process(&mut self, land: &Land2D, updates: &crate::physics::PositionUpdates) { + 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(gear_id, 0, &collision.bounds.center) + } + } + } +} + +impl GearDataProcessor for CollisionProcessor { + fn add(&mut self, gear_id: GearId, gear_data: CollisionData) { + self.grid.insert_static(gear_id, &gear_data.bounds); + } +} diff -r 901751d3cd80 -r febccab419b1 rust/hwphysics/src/common.rs --- a/rust/hwphysics/src/common.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hwphysics/src/common.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,10 +1,10 @@ -pub type GearId = u16; -pub trait GearData {} - -pub trait GearDataProcessor { - fn add(&mut self, gear_id: GearId, gear_data: T); -} - -pub trait GearDataAggregator { - fn find_processor(&mut self) -> &mut GearDataProcessor; -} +pub type GearId = u16; +pub trait GearData {} + +pub trait GearDataProcessor { + fn add(&mut self, gear_id: GearId, gear_data: T); +} + +pub trait GearDataAggregator { + fn find_processor(&mut self) -> &mut GearDataProcessor; +} diff -r 901751d3cd80 -r febccab419b1 rust/hwphysics/src/grid.rs --- a/rust/hwphysics/src/grid.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hwphysics/src/grid.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,83 +1,83 @@ -use crate::{ - collision::{fppoint_round, CircleBounds, DetectedCollisions}, - common::GearId, -}; - -use fpnum::FPPoint; -use integral_geometry::{GridIndex, Point, Size}; - -struct GridBin { - refs: Vec, - static_entries: Vec, - dynamic_entries: Vec, -} - -impl GridBin { - fn new() -> Self { - Self { - refs: vec![], - static_entries: vec![], - dynamic_entries: vec![], - } - } -} - -const GRID_BIN_SIZE: usize = 128; - -pub struct Grid { - bins: Vec, - 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, bounds: &CircleBounds) { - self.lookup_bin(&bounds.center).static_entries.push(*bounds) - } - - pub fn insert_dynamic(&mut self, gear_id: GearId, bounds: &CircleBounds) { - self.lookup_bin(&bounds.center) - .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) - } - } - } - } - } -} +use crate::{ + collision::{fppoint_round, CircleBounds, DetectedCollisions}, + common::GearId, +}; + +use fpnum::FPPoint; +use integral_geometry::{GridIndex, Point, Size}; + +struct GridBin { + refs: Vec, + static_entries: Vec, + dynamic_entries: Vec, +} + +impl GridBin { + fn new() -> Self { + Self { + refs: vec![], + static_entries: vec![], + dynamic_entries: vec![], + } + } +} + +const GRID_BIN_SIZE: usize = 128; + +pub struct Grid { + bins: Vec, + 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, bounds: &CircleBounds) { + self.lookup_bin(&bounds.center).static_entries.push(*bounds) + } + + pub fn insert_dynamic(&mut self, gear_id: GearId, bounds: &CircleBounds) { + self.lookup_bin(&bounds.center) + .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) + } + } + } + } + } +} diff -r 901751d3cd80 -r febccab419b1 rust/hwphysics/src/physics.rs --- a/rust/hwphysics/src/physics.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/hwphysics/src/physics.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,137 +1,137 @@ -use crate::common::{GearData, GearDataProcessor, GearId}; -use fpnum::*; -use integral_geometry::{GridIndex, Point, Size}; - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct PhysicsData { - pub position: FPPoint, - pub velocity: FPPoint, -} - -impl GearData for PhysicsData {} - -impl PhysicsData { - pub fn new(position: FPPoint, velocity: FPPoint) -> Self { - Self { position, velocity } - } -} - -pub struct DynamicPhysicsCollection { - gear_ids: Vec, - positions: Vec, - velocities: Vec, -} - -impl DynamicPhysicsCollection { - fn new() -> Self { - Self { - gear_ids: Vec::new(), - positions: Vec::new(), - velocities: Vec::new(), - } - } - - 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 { - self.gear_ids - .iter() - .cloned() - .zip(self.positions.iter_mut().zip(self.velocities.iter())) - } -} - -pub struct StaticPhysicsCollection { - gear_ids: Vec, - positions: Vec, -} - -impl StaticPhysicsCollection { - fn new() -> Self { - Self { - gear_ids: Vec::new(), - positions: Vec::new(), - } - } - - 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, - position_updates: PositionUpdates, -} - -pub struct PositionUpdates { - pub gear_ids: Vec, - pub positions: Vec, -} - -impl PositionUpdates { - 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 new() -> Self { - PhysicsProcessor { - dynamic_physics: DynamicPhysicsCollection::new(), - static_physics: StaticPhysicsCollection::new(), - physics_cleanup: Vec::new(), - position_updates: PositionUpdates::new(0), - } - } - - pub fn process(&mut self, time_step: FPNum) -> &PositionUpdates { - 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); - } - } -} - -impl GearDataProcessor for PhysicsProcessor { - fn add(&mut self, gear_id: GearId, gear_data: PhysicsData) { - if gear_data.velocity.is_zero() { - self.static_physics.push(gear_id, gear_data); - } else { - self.dynamic_physics.push(gear_id, gear_data); - } - } -} +use crate::common::{GearData, GearDataProcessor, GearId}; +use fpnum::*; +use integral_geometry::{GridIndex, Point, Size}; + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct PhysicsData { + pub position: FPPoint, + pub velocity: FPPoint, +} + +impl GearData for PhysicsData {} + +impl PhysicsData { + pub fn new(position: FPPoint, velocity: FPPoint) -> Self { + Self { position, velocity } + } +} + +pub struct DynamicPhysicsCollection { + gear_ids: Vec, + positions: Vec, + velocities: Vec, +} + +impl DynamicPhysicsCollection { + fn new() -> Self { + Self { + gear_ids: Vec::new(), + positions: Vec::new(), + velocities: Vec::new(), + } + } + + 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 { + self.gear_ids + .iter() + .cloned() + .zip(self.positions.iter_mut().zip(self.velocities.iter())) + } +} + +pub struct StaticPhysicsCollection { + gear_ids: Vec, + positions: Vec, +} + +impl StaticPhysicsCollection { + fn new() -> Self { + Self { + gear_ids: Vec::new(), + positions: Vec::new(), + } + } + + 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, + position_updates: PositionUpdates, +} + +pub struct PositionUpdates { + pub gear_ids: Vec, + pub positions: Vec, +} + +impl PositionUpdates { + 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 new() -> Self { + PhysicsProcessor { + dynamic_physics: DynamicPhysicsCollection::new(), + static_physics: StaticPhysicsCollection::new(), + physics_cleanup: Vec::new(), + position_updates: PositionUpdates::new(0), + } + } + + pub fn process(&mut self, time_step: FPNum) -> &PositionUpdates { + 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); + } + } +} + +impl GearDataProcessor for PhysicsProcessor { + fn add(&mut self, gear_id: GearId, gear_data: PhysicsData) { + if gear_data.velocity.is_zero() { + self.static_physics.push(gear_id, gear_data); + } else { + self.dynamic_physics.push(gear_id, gear_data); + } + } +} diff -r 901751d3cd80 -r febccab419b1 rust/lib-hedgewars-engine/src/render.rs --- a/rust/lib-hedgewars-engine/src/render.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/lib-hedgewars-engine/src/render.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,9 +1,9 @@ -pub mod atlas; -pub mod camera; -mod gear; -mod gl; -mod map; - -use self::gl::*; -pub use self::map::*; -pub use gear::*; +pub mod atlas; +pub mod camera; +mod gear; +mod gl; +mod map; + +use self::gl::*; +pub use self::map::*; +pub use gear::*; diff -r 901751d3cd80 -r febccab419b1 rust/lib-hedgewars-engine/src/render/atlas.rs --- a/rust/lib-hedgewars-engine/src/render/atlas.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/lib-hedgewars-engine/src/render/atlas.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,398 +1,398 @@ -use integral_geometry::{Rect, Size}; -use std::cmp::{max, min, Ordering}; - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] -struct Fit { - short_side: u32, - long_side: u32, -} - -impl Fit { - fn new() -> Self { - Self { - short_side: u32::max_value(), - long_side: u32::max_value(), - } - } - - fn measure(container: Size, size: Size) -> Option { - if container.contains(size) { - let x_leftover = container.width - size.width; - let y_leftover = container.height - size.height; - Some(Self { - short_side: min(x_leftover, y_leftover) as u32, - long_side: max(x_leftover, y_leftover) as u32, - }) - } else { - None - } - } -} - -#[derive(PartialEq, Eq)] -pub struct UsedSpace { - used_area: usize, - total_area: usize, -} - -impl UsedSpace { - const fn new(used_area: usize, total_area: usize) -> Self { - Self { - used_area, - total_area, - } - } - - const fn used(&self) -> usize { - self.used_area - } - - const fn total(&self) -> usize { - self.total_area - } -} - -impl std::fmt::Debug for UsedSpace { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!( - f, - "{:.2}%", - self.used() as f32 / self.total() as f32 / 100.0 - )?; - Ok(()) - } -} - -pub struct Atlas { - size: Size, - free_rects: Vec, - used_rects: Vec, - splits: Vec, -} - -impl Atlas { - pub fn new(size: Size) -> Self { - Self { - size, - free_rects: vec![Rect::at_origin(size)], - used_rects: vec![], - splits: vec![], - } - } - - pub fn size(&self) -> Size { - self.size - } - - pub fn used_space(&self) -> UsedSpace { - let used = self.used_rects.iter().map(|r| r.size().area()).sum(); - UsedSpace::new(used, self.size.area()) - } - - fn find_position(&self, size: Size) -> Option<(Rect, Fit)> { - let mut best_rect = Rect::EMPTY; - let mut best_fit = Fit::new(); - - for rect in &self.free_rects { - if let Some(fit) = Fit::measure(rect.size(), size) { - if fit < best_fit { - best_fit = fit; - best_rect = Rect::from_size(rect.top_left(), size); - } - } - - if let Some(fit) = Fit::measure(rect.size(), size.transpose()) { - if fit < best_fit { - best_fit = fit; - best_rect = Rect::from_size(rect.top_left(), size.transpose()); - } - } - } - - if best_rect == Rect::EMPTY { - None - } else { - Some((best_rect, best_fit)) - } - } - - fn split_insert(&mut self, rect: Rect) { - let mut splits = std::mem::replace(&mut self.splits, vec![]); - let mut buffer = [Rect::EMPTY; 4]; - - for i in (0..self.free_rects.len()).rev() { - if let Some(count) = split_rect(self.free_rects[i], rect, &mut splits, &mut buffer) { - self.free_rects.swap_remove(i as usize); - splits.extend_from_slice(&buffer[0..count]); - } - } - - filter_swap_remove(&mut splits, |s| { - self.free_rects.iter().any(|r| r.contains_rect(s)) - }); - self.free_rects.extend(splits.drain(..)); - std::mem::replace(&mut self.splits, splits); - - self.used_rects.push(rect); - } - - pub fn insert(&mut self, size: Size) -> Option { - let (rect, _) = self.find_position(size)?; - self.split_insert(rect); - Some(rect) - } - - pub fn insert_set(&mut self, sizes: Iter) -> Vec - where - Iter: Iterator, - { - let mut sizes: Vec<_> = sizes.collect(); - let mut result = vec![]; - - while let Some((index, (rect, _))) = sizes - .iter() - .enumerate() - .filter_map(|(i, s)| self.find_position(*s).map(|res| (i, res))) - .min_by_key(|(_, (_, fit))| fit.clone()) - { - self.split_insert(rect); - - result.push(rect); - sizes.swap_remove(index); - } - result - } - - pub fn reset(&mut self) { - self.free_rects.clear(); - self.used_rects.clear(); - self.free_rects.push(Rect::at_origin(self.size)); - } -} - -pub struct AtlasCollection { - texture_size: Size, - atlases: Vec, -} - -impl AtlasCollection { - pub fn new(texture_size: Size) -> Self { - Self { - texture_size, - atlases: vec![], - } - } - - fn repack(&mut self, size: Size) -> bool { - for atlas in &mut self.atlases { - let mut temp_atlas = Atlas::new(atlas.size()); - let sizes = atlas - .used_rects - .iter() - .map(|r| r.size()) - .chain(std::iter::once(size)); - if !temp_atlas.insert_set(sizes).is_empty() { - std::mem::swap(atlas, &mut temp_atlas); - return true; - } - } - false - } - - pub fn insert_sprite(&mut self, size: Size) -> bool { - if !self.texture_size.contains(size) { - false - } else { - if let Some(rect) = self.atlases.iter_mut().find_map(|a| a.insert(size)) { - - } else if !self.repack(size) { - let mut atlas = Atlas::new(self.texture_size); - atlas.insert(size); - self.atlases.push(atlas); - } - true - } - } -} - -#[inline] -fn filter_swap_remove(vec: &mut Vec, predicate: F) -where - F: Fn(&T) -> bool, -{ - let mut i = 0; - while i < vec.len() { - if predicate(&vec[i]) { - vec.swap_remove(i); - } else { - i += 1; - } - } -} - -#[inline] -fn prune_push( - previous_splits: &mut Vec, - buffer: &mut [Rect; 4], - buffer_size: &mut usize, - rect: Rect, -) { - if !previous_splits.iter().any(|r| r.contains_rect(&rect)) { - filter_swap_remove(previous_splits, |s| rect.contains_rect(s)); - buffer[*buffer_size] = rect; - *buffer_size += 1; - } -} - -fn split_rect( - free_rect: Rect, - rect: Rect, - previous_splits: &mut Vec, - buffer: &mut [Rect; 4], -) -> Option { - let mut buffer_size = 0usize; - let split = free_rect.intersects(&rect); - if split { - if rect.left() > free_rect.left() { - let trim = free_rect.right() - rect.left() + 1; - prune_push( - previous_splits, - buffer, - &mut buffer_size, - free_rect.with_margins(0, -trim, 0, 0), - ); - } - if rect.right() < free_rect.right() { - let trim = rect.right() - free_rect.left() + 1; - prune_push( - previous_splits, - buffer, - &mut buffer_size, - free_rect.with_margins(-trim, 0, 0, 0), - ); - } - if rect.top() > free_rect.top() { - let trim = free_rect.bottom() - rect.top() + 1; - prune_push( - previous_splits, - buffer, - &mut buffer_size, - free_rect.with_margins(0, 0, 0, -trim), - );; - } - if rect.bottom() < free_rect.bottom() { - let trim = rect.bottom() - free_rect.top() + 1; - prune_push( - previous_splits, - buffer, - &mut buffer_size, - free_rect.with_margins(0, 0, -trim, 0), - );; - } - } - if split { - Some(buffer_size) - } else { - None - } -} - -#[cfg(test)] -mod tests { - use super::Atlas; - use integral_geometry::{Rect, Size}; - use itertools::Itertools as _; - use proptest::prelude::*; - - #[test] - fn insert() { - let atlas_size = Size::square(16); - let mut atlas = Atlas::new(atlas_size); - - assert_eq!(None, atlas.insert(Size::square(20))); - - let rect_size = Size::new(11, 3); - let rect = atlas.insert(rect_size).unwrap(); - - assert_eq!(rect, Rect::at_origin(rect_size)); - assert_eq!(2, atlas.free_rects.len()); - } - - #[derive(Debug, Clone)] - struct TestRect(Size); - struct TestRectParameters(Size); - - impl Default for TestRectParameters { - fn default() -> Self { - Self(Size::square(64)) - } - } - - impl Arbitrary for TestRect { - type Parameters = TestRectParameters; - - fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { - (1..=args.0.width, 1..=args.0.height) - .prop_map(|(w, h)| TestRect(Size::new(w, h))) - .boxed() - } - - type Strategy = BoxedStrategy; - } - - trait HasSize { - fn size(&self) -> Size; - } - - impl HasSize for TestRect { - fn size(&self) -> Size { - self.0 - } - } - - impl HasSize for Rect { - fn size(&self) -> Size { - self.size() - } - } - - fn sum_area(items: &[S]) -> usize { - items.iter().map(|s| s.size().area()).sum() - } - - proptest! { - #[test] - fn prop_insert(rects in Vec::::arbitrary()) { - let container = Rect::at_origin(Size::square(2048)); - let mut atlas = Atlas::new(container.size()); - let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect(); - - let mut inserted_pairs = inserted.iter().cartesian_product(inserted.iter()); - - assert!(inserted.iter().all(|r| container.contains_rect(r))); - assert!(inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2))); - - assert_eq!(inserted.len(), rects.len()); - assert_eq!(sum_area(&inserted), sum_area(&rects)); - } - } - - proptest! { - #[test] - fn prop_insert_set(rects in Vec::::arbitrary()) { - let container = Rect::at_origin(Size::square(2048)); - let mut atlas = Atlas::new(container.size()); - let mut set_atlas = Atlas::new(container.size()); - - let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect(); - let set_inserted: Vec<_> = set_atlas.insert_set(rects.iter().map(|TestRect(size)| *size)); - - let mut set_inserted_pairs = set_inserted.iter().cartesian_product(set_inserted.iter()); - - assert!(set_inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2))); - assert!(set_atlas.used_space().used() <= atlas.used_space().used()); - - assert_eq!(sum_area(&set_inserted), sum_area(&inserted)); - } - } -} +use integral_geometry::{Rect, Size}; +use std::cmp::{max, min, Ordering}; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +struct Fit { + short_side: u32, + long_side: u32, +} + +impl Fit { + fn new() -> Self { + Self { + short_side: u32::max_value(), + long_side: u32::max_value(), + } + } + + fn measure(container: Size, size: Size) -> Option { + if container.contains(size) { + let x_leftover = container.width - size.width; + let y_leftover = container.height - size.height; + Some(Self { + short_side: min(x_leftover, y_leftover) as u32, + long_side: max(x_leftover, y_leftover) as u32, + }) + } else { + None + } + } +} + +#[derive(PartialEq, Eq)] +pub struct UsedSpace { + used_area: usize, + total_area: usize, +} + +impl UsedSpace { + const fn new(used_area: usize, total_area: usize) -> Self { + Self { + used_area, + total_area, + } + } + + const fn used(&self) -> usize { + self.used_area + } + + const fn total(&self) -> usize { + self.total_area + } +} + +impl std::fmt::Debug for UsedSpace { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!( + f, + "{:.2}%", + self.used() as f32 / self.total() as f32 / 100.0 + )?; + Ok(()) + } +} + +pub struct Atlas { + size: Size, + free_rects: Vec, + used_rects: Vec, + splits: Vec, +} + +impl Atlas { + pub fn new(size: Size) -> Self { + Self { + size, + free_rects: vec![Rect::at_origin(size)], + used_rects: vec![], + splits: vec![], + } + } + + pub fn size(&self) -> Size { + self.size + } + + pub fn used_space(&self) -> UsedSpace { + let used = self.used_rects.iter().map(|r| r.size().area()).sum(); + UsedSpace::new(used, self.size.area()) + } + + fn find_position(&self, size: Size) -> Option<(Rect, Fit)> { + let mut best_rect = Rect::EMPTY; + let mut best_fit = Fit::new(); + + for rect in &self.free_rects { + if let Some(fit) = Fit::measure(rect.size(), size) { + if fit < best_fit { + best_fit = fit; + best_rect = Rect::from_size(rect.top_left(), size); + } + } + + if let Some(fit) = Fit::measure(rect.size(), size.transpose()) { + if fit < best_fit { + best_fit = fit; + best_rect = Rect::from_size(rect.top_left(), size.transpose()); + } + } + } + + if best_rect == Rect::EMPTY { + None + } else { + Some((best_rect, best_fit)) + } + } + + fn split_insert(&mut self, rect: Rect) { + let mut splits = std::mem::replace(&mut self.splits, vec![]); + let mut buffer = [Rect::EMPTY; 4]; + + for i in (0..self.free_rects.len()).rev() { + if let Some(count) = split_rect(self.free_rects[i], rect, &mut splits, &mut buffer) { + self.free_rects.swap_remove(i as usize); + splits.extend_from_slice(&buffer[0..count]); + } + } + + filter_swap_remove(&mut splits, |s| { + self.free_rects.iter().any(|r| r.contains_rect(s)) + }); + self.free_rects.extend(splits.drain(..)); + std::mem::replace(&mut self.splits, splits); + + self.used_rects.push(rect); + } + + pub fn insert(&mut self, size: Size) -> Option { + let (rect, _) = self.find_position(size)?; + self.split_insert(rect); + Some(rect) + } + + pub fn insert_set(&mut self, sizes: Iter) -> Vec + where + Iter: Iterator, + { + let mut sizes: Vec<_> = sizes.collect(); + let mut result = vec![]; + + while let Some((index, (rect, _))) = sizes + .iter() + .enumerate() + .filter_map(|(i, s)| self.find_position(*s).map(|res| (i, res))) + .min_by_key(|(_, (_, fit))| fit.clone()) + { + self.split_insert(rect); + + result.push(rect); + sizes.swap_remove(index); + } + result + } + + pub fn reset(&mut self) { + self.free_rects.clear(); + self.used_rects.clear(); + self.free_rects.push(Rect::at_origin(self.size)); + } +} + +pub struct AtlasCollection { + texture_size: Size, + atlases: Vec, +} + +impl AtlasCollection { + pub fn new(texture_size: Size) -> Self { + Self { + texture_size, + atlases: vec![], + } + } + + fn repack(&mut self, size: Size) -> bool { + for atlas in &mut self.atlases { + let mut temp_atlas = Atlas::new(atlas.size()); + let sizes = atlas + .used_rects + .iter() + .map(|r| r.size()) + .chain(std::iter::once(size)); + if !temp_atlas.insert_set(sizes).is_empty() { + std::mem::swap(atlas, &mut temp_atlas); + return true; + } + } + false + } + + pub fn insert_sprite(&mut self, size: Size) -> bool { + if !self.texture_size.contains(size) { + false + } else { + if let Some(rect) = self.atlases.iter_mut().find_map(|a| a.insert(size)) { + + } else if !self.repack(size) { + let mut atlas = Atlas::new(self.texture_size); + atlas.insert(size); + self.atlases.push(atlas); + } + true + } + } +} + +#[inline] +fn filter_swap_remove(vec: &mut Vec, predicate: F) +where + F: Fn(&T) -> bool, +{ + let mut i = 0; + while i < vec.len() { + if predicate(&vec[i]) { + vec.swap_remove(i); + } else { + i += 1; + } + } +} + +#[inline] +fn prune_push( + previous_splits: &mut Vec, + buffer: &mut [Rect; 4], + buffer_size: &mut usize, + rect: Rect, +) { + if !previous_splits.iter().any(|r| r.contains_rect(&rect)) { + filter_swap_remove(previous_splits, |s| rect.contains_rect(s)); + buffer[*buffer_size] = rect; + *buffer_size += 1; + } +} + +fn split_rect( + free_rect: Rect, + rect: Rect, + previous_splits: &mut Vec, + buffer: &mut [Rect; 4], +) -> Option { + let mut buffer_size = 0usize; + let split = free_rect.intersects(&rect); + if split { + if rect.left() > free_rect.left() { + let trim = free_rect.right() - rect.left() + 1; + prune_push( + previous_splits, + buffer, + &mut buffer_size, + free_rect.with_margins(0, -trim, 0, 0), + ); + } + if rect.right() < free_rect.right() { + let trim = rect.right() - free_rect.left() + 1; + prune_push( + previous_splits, + buffer, + &mut buffer_size, + free_rect.with_margins(-trim, 0, 0, 0), + ); + } + if rect.top() > free_rect.top() { + let trim = free_rect.bottom() - rect.top() + 1; + prune_push( + previous_splits, + buffer, + &mut buffer_size, + free_rect.with_margins(0, 0, 0, -trim), + );; + } + if rect.bottom() < free_rect.bottom() { + let trim = rect.bottom() - free_rect.top() + 1; + prune_push( + previous_splits, + buffer, + &mut buffer_size, + free_rect.with_margins(0, 0, -trim, 0), + );; + } + } + if split { + Some(buffer_size) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::Atlas; + use integral_geometry::{Rect, Size}; + use itertools::Itertools as _; + use proptest::prelude::*; + + #[test] + fn insert() { + let atlas_size = Size::square(16); + let mut atlas = Atlas::new(atlas_size); + + assert_eq!(None, atlas.insert(Size::square(20))); + + let rect_size = Size::new(11, 3); + let rect = atlas.insert(rect_size).unwrap(); + + assert_eq!(rect, Rect::at_origin(rect_size)); + assert_eq!(2, atlas.free_rects.len()); + } + + #[derive(Debug, Clone)] + struct TestRect(Size); + struct TestRectParameters(Size); + + impl Default for TestRectParameters { + fn default() -> Self { + Self(Size::square(64)) + } + } + + impl Arbitrary for TestRect { + type Parameters = TestRectParameters; + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + (1..=args.0.width, 1..=args.0.height) + .prop_map(|(w, h)| TestRect(Size::new(w, h))) + .boxed() + } + + type Strategy = BoxedStrategy; + } + + trait HasSize { + fn size(&self) -> Size; + } + + impl HasSize for TestRect { + fn size(&self) -> Size { + self.0 + } + } + + impl HasSize for Rect { + fn size(&self) -> Size { + self.size() + } + } + + fn sum_area(items: &[S]) -> usize { + items.iter().map(|s| s.size().area()).sum() + } + + proptest! { + #[test] + fn prop_insert(rects in Vec::::arbitrary()) { + let container = Rect::at_origin(Size::square(2048)); + let mut atlas = Atlas::new(container.size()); + let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect(); + + let mut inserted_pairs = inserted.iter().cartesian_product(inserted.iter()); + + assert!(inserted.iter().all(|r| container.contains_rect(r))); + assert!(inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2))); + + assert_eq!(inserted.len(), rects.len()); + assert_eq!(sum_area(&inserted), sum_area(&rects)); + } + } + + proptest! { + #[test] + fn prop_insert_set(rects in Vec::::arbitrary()) { + let container = Rect::at_origin(Size::square(2048)); + let mut atlas = Atlas::new(container.size()); + let mut set_atlas = Atlas::new(container.size()); + + let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect(); + let set_inserted: Vec<_> = set_atlas.insert_set(rects.iter().map(|TestRect(size)| *size)); + + let mut set_inserted_pairs = set_inserted.iter().cartesian_product(set_inserted.iter()); + + assert!(set_inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2))); + assert!(set_atlas.used_space().used() <= atlas.used_space().used()); + + assert_eq!(sum_area(&set_inserted), sum_area(&inserted)); + } + } +} diff -r 901751d3cd80 -r febccab419b1 rust/lib-hedgewars-engine/src/render/camera.rs --- a/rust/lib-hedgewars-engine/src/render/camera.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/lib-hedgewars-engine/src/render/camera.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,64 +1,64 @@ -use integral_geometry::{Point, Rect, Size}; - -#[derive(Debug)] -pub struct Camera { - pub position: Point, - pub zoom: f32, - size: Size, -} - -impl Camera { - pub fn new() -> Self { - Self::with_size(Size::new(1024, 768)) - } - - pub fn with_size(size: Size) -> Self { - Self { - position: Point::ZERO, - zoom: 1.0, - size, - } - } - - pub fn viewport(&self) -> Rect { - #[inline] - fn scale(value: usize, zoom: f32) -> i32 { - (value as f32 / zoom / 2.0) as i32 - } - let half_width = scale(self.size.width, self.zoom); - let half_height = scale(self.size.height, self.zoom); - Rect::from_box( - self.position.x - half_width, - self.position.x + half_width, - self.position.y - half_height, - self.position.y + half_height, - ) - } - - pub fn projection(&self) -> [f32; 16] { - let viewport = self.viewport(); - let left = viewport.left() as f32; - let width = viewport.width() as f32; - let height = viewport.height() as f32; - let top = viewport.top() as f32; - - [ - 2f32 / width, - 0f32, - 0f32, - 0f32, - 0f32, - 2f32 / -height, - 0f32, - 0f32, - 0f32, - 0f32, - 0.5f32, - 0f32, - -(2.0 * left + width) / width, - (2.0 * top + height) / height, - 0.5f32, - 1f32, - ] - } -} +use integral_geometry::{Point, Rect, Size}; + +#[derive(Debug)] +pub struct Camera { + pub position: Point, + pub zoom: f32, + size: Size, +} + +impl Camera { + pub fn new() -> Self { + Self::with_size(Size::new(1024, 768)) + } + + pub fn with_size(size: Size) -> Self { + Self { + position: Point::ZERO, + zoom: 1.0, + size, + } + } + + pub fn viewport(&self) -> Rect { + #[inline] + fn scale(value: usize, zoom: f32) -> i32 { + (value as f32 / zoom / 2.0) as i32 + } + let half_width = scale(self.size.width, self.zoom); + let half_height = scale(self.size.height, self.zoom); + Rect::from_box( + self.position.x - half_width, + self.position.x + half_width, + self.position.y - half_height, + self.position.y + half_height, + ) + } + + pub fn projection(&self) -> [f32; 16] { + let viewport = self.viewport(); + let left = viewport.left() as f32; + let width = viewport.width() as f32; + let height = viewport.height() as f32; + let top = viewport.top() as f32; + + [ + 2f32 / width, + 0f32, + 0f32, + 0f32, + 0f32, + 2f32 / -height, + 0f32, + 0f32, + 0f32, + 0f32, + 0.5f32, + 0f32, + -(2.0 * left + width) / width, + (2.0 * top + height) / height, + 0.5f32, + 1f32, + ] + } +} diff -r 901751d3cd80 -r febccab419b1 rust/lib-hedgewars-engine/src/render/gear.rs --- a/rust/lib-hedgewars-engine/src/render/gear.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/lib-hedgewars-engine/src/render/gear.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,15 +1,15 @@ -use super::atlas::AtlasCollection; -use integral_geometry::Size; - -struct GearRenderer { - atlas: AtlasCollection, -} - -const ATLAS_SIZE: Size = Size::square(2048); - -impl GearRenderer { - pub fn new() -> Self { - let atlas = AtlasCollection::new(ATLAS_SIZE); - Self { atlas } - } -} +use super::atlas::AtlasCollection; +use integral_geometry::Size; + +struct GearRenderer { + atlas: AtlasCollection, +} + +const ATLAS_SIZE: Size = Size::square(2048); + +impl GearRenderer { + pub fn new() -> Self { + let atlas = AtlasCollection::new(ATLAS_SIZE); + Self { atlas } + } +} diff -r 901751d3cd80 -r febccab419b1 rust/mapgen/src/theme.rs --- a/rust/mapgen/src/theme.rs Tue Jun 04 23:19:18 2019 +0300 +++ b/rust/mapgen/src/theme.rs Tue Jun 04 22:34:42 2019 +0200 @@ -1,194 +1,194 @@ -use png::{ColorType, Decoder, DecodingError}; -use std::{ - fs::{read_dir, File}, - io, - io::BufReader, - path::Path, - slice::{from_raw_parts, from_raw_parts_mut}, -}; - -use integral_geometry::Size; -use vec2d::Vec2D; - -pub struct ThemeSprite { - pixels: Vec2D, -} - -impl ThemeSprite { - #[inline] - pub fn size(&self) -> Size { - self.pixels.size() - } - - #[inline] - pub fn width(&self) -> usize { - self.size().width - } - - #[inline] - pub fn height(&self) -> usize { - self.size().height - } - - #[inline] - pub fn rows(&self) -> impl DoubleEndedIterator { - self.pixels.rows() - } - - #[inline] - pub fn get_row(&self, index: usize) -> &[u32] { - &self.pixels[index] - } - - #[inline] - pub fn get_pixel(&self, x: usize, y: usize) -> u32 { - self.pixels[y][x] - } - - pub fn to_transposed(&self) -> ThemeSprite { - let size = self.size().transpose(); - let mut pixels = Vec2D::new(size, 0u32); - for (y, row) in self.pixels.rows().enumerate() { - for (x, v) in row.iter().enumerate() { - pixels[x][y] = *v; - } - } - ThemeSprite { pixels } - } - - pub fn to_tiled(&self) -> TiledSprite { - let size = self.size(); - assert!(size.is_power_of_two()); - let tile_width_shift = size.width.trailing_zeros() as usize + 2; - let mut pixels = vec![0u32; size.area()]; - - for (y, row) in self.pixels.rows().enumerate() { - for (x, v) in row.iter().enumerate() { - pixels[get_tiled_index(x, y, tile_width_shift)] = *v; - } - } - - TiledSprite { - tile_width_shift, - size, - pixels, - } - } -} - -#[inline] -fn get_tiled_index(x: usize, y: usize, tile_width_shift: usize) -> usize { - (((y >> 2) << tile_width_shift) + ((x >> 2) << 4)) + ((y & 0b11) << 2) + (x & 0b11) -} - -pub struct TiledSprite { - tile_width_shift: usize, - size: Size, - pixels: Vec, -} - -impl TiledSprite { - #[inline] - pub fn size(&self) -> Size { - self.size - } - - #[inline] - pub fn width(&self) -> usize { - self.size().width - } - - #[inline] - pub fn height(&self) -> usize { - self.size().height - } - - #[inline] - pub fn get_pixel(&self, x: usize, y: usize) -> u32 { - self.pixels[get_tiled_index(x, y, self.tile_width_shift)] - } -} - -pub struct Theme { - land_texture: Option, - border_texture: Option, -} - -impl Theme { - pub fn land_texture(&self) -> Option<&ThemeSprite> { - self.land_texture.as_ref() - } - - pub fn border_texture(&self) -> Option<&ThemeSprite> { - self.border_texture.as_ref() - } -} - -#[derive(Debug)] -pub enum ThemeLoadError { - File(io::Error), - Decoding(DecodingError), - Format(String), -} - -impl From for ThemeLoadError { - fn from(e: io::Error) -> Self { - ThemeLoadError::File(e) - } -} - -impl From for ThemeLoadError { - fn from(e: DecodingError) -> Self { - ThemeLoadError::Decoding(e) - } -} - -impl Theme { - pub fn new() -> Self { - Theme { - land_texture: None, - border_texture: None, - } - } - - pub fn load(path: &Path) -> Result { - let mut theme = Self::new(); - - for entry in read_dir(path)? { - let file = entry?; - if file.file_name() == "LandTex.png" { - theme.land_texture = Some(load_sprite(&file.path())?) - } else if file.file_name() == "Border.png" { - theme.border_texture = Some(load_sprite(&file.path())?) - } - } - - Ok(theme) - } -} - -fn load_sprite(path: &Path) -> Result { - let decoder = Decoder::new(BufReader::new(File::open(path)?)); - let (info, mut reader) = decoder.read_info()?; - - if info.color_type != ColorType::RGBA { - return Err(ThemeLoadError::Format(format!( - "Unexpected format: {:?}", - info.color_type - ))); - } - let size = Size::new(info.width as usize, info.height as usize); - - let mut pixels: Vec2D = Vec2D::new(size, 0); - reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?; - - Ok(ThemeSprite { pixels }) -} - -pub fn slice_u32_to_u8(slice_u32: &[u32]) -> &[u8] { - unsafe { from_raw_parts::(slice_u32.as_ptr() as *const u8, slice_u32.len() * 4) } -} - -pub fn slice_u32_to_u8_mut(slice_u32: &mut [u32]) -> &mut [u8] { - unsafe { from_raw_parts_mut::(slice_u32.as_mut_ptr() as *mut u8, slice_u32.len() * 4) } -} +use png::{ColorType, Decoder, DecodingError}; +use std::{ + fs::{read_dir, File}, + io, + io::BufReader, + path::Path, + slice::{from_raw_parts, from_raw_parts_mut}, +}; + +use integral_geometry::Size; +use vec2d::Vec2D; + +pub struct ThemeSprite { + pixels: Vec2D, +} + +impl ThemeSprite { + #[inline] + pub fn size(&self) -> Size { + self.pixels.size() + } + + #[inline] + pub fn width(&self) -> usize { + self.size().width + } + + #[inline] + pub fn height(&self) -> usize { + self.size().height + } + + #[inline] + pub fn rows(&self) -> impl DoubleEndedIterator { + self.pixels.rows() + } + + #[inline] + pub fn get_row(&self, index: usize) -> &[u32] { + &self.pixels[index] + } + + #[inline] + pub fn get_pixel(&self, x: usize, y: usize) -> u32 { + self.pixels[y][x] + } + + pub fn to_transposed(&self) -> ThemeSprite { + let size = self.size().transpose(); + let mut pixels = Vec2D::new(size, 0u32); + for (y, row) in self.pixels.rows().enumerate() { + for (x, v) in row.iter().enumerate() { + pixels[x][y] = *v; + } + } + ThemeSprite { pixels } + } + + pub fn to_tiled(&self) -> TiledSprite { + let size = self.size(); + assert!(size.is_power_of_two()); + let tile_width_shift = size.width.trailing_zeros() as usize + 2; + let mut pixels = vec![0u32; size.area()]; + + for (y, row) in self.pixels.rows().enumerate() { + for (x, v) in row.iter().enumerate() { + pixels[get_tiled_index(x, y, tile_width_shift)] = *v; + } + } + + TiledSprite { + tile_width_shift, + size, + pixels, + } + } +} + +#[inline] +fn get_tiled_index(x: usize, y: usize, tile_width_shift: usize) -> usize { + (((y >> 2) << tile_width_shift) + ((x >> 2) << 4)) + ((y & 0b11) << 2) + (x & 0b11) +} + +pub struct TiledSprite { + tile_width_shift: usize, + size: Size, + pixels: Vec, +} + +impl TiledSprite { + #[inline] + pub fn size(&self) -> Size { + self.size + } + + #[inline] + pub fn width(&self) -> usize { + self.size().width + } + + #[inline] + pub fn height(&self) -> usize { + self.size().height + } + + #[inline] + pub fn get_pixel(&self, x: usize, y: usize) -> u32 { + self.pixels[get_tiled_index(x, y, self.tile_width_shift)] + } +} + +pub struct Theme { + land_texture: Option, + border_texture: Option, +} + +impl Theme { + pub fn land_texture(&self) -> Option<&ThemeSprite> { + self.land_texture.as_ref() + } + + pub fn border_texture(&self) -> Option<&ThemeSprite> { + self.border_texture.as_ref() + } +} + +#[derive(Debug)] +pub enum ThemeLoadError { + File(io::Error), + Decoding(DecodingError), + Format(String), +} + +impl From for ThemeLoadError { + fn from(e: io::Error) -> Self { + ThemeLoadError::File(e) + } +} + +impl From for ThemeLoadError { + fn from(e: DecodingError) -> Self { + ThemeLoadError::Decoding(e) + } +} + +impl Theme { + pub fn new() -> Self { + Theme { + land_texture: None, + border_texture: None, + } + } + + pub fn load(path: &Path) -> Result { + let mut theme = Self::new(); + + for entry in read_dir(path)? { + let file = entry?; + if file.file_name() == "LandTex.png" { + theme.land_texture = Some(load_sprite(&file.path())?) + } else if file.file_name() == "Border.png" { + theme.border_texture = Some(load_sprite(&file.path())?) + } + } + + Ok(theme) + } +} + +fn load_sprite(path: &Path) -> Result { + let decoder = Decoder::new(BufReader::new(File::open(path)?)); + let (info, mut reader) = decoder.read_info()?; + + if info.color_type != ColorType::RGBA { + return Err(ThemeLoadError::Format(format!( + "Unexpected format: {:?}", + info.color_type + ))); + } + let size = Size::new(info.width as usize, info.height as usize); + + let mut pixels: Vec2D = Vec2D::new(size, 0); + reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?; + + Ok(ThemeSprite { pixels }) +} + +pub fn slice_u32_to_u8(slice_u32: &[u32]) -> &[u8] { + unsafe { from_raw_parts::(slice_u32.as_ptr() as *const u8, slice_u32.len() * 4) } +} + +pub fn slice_u32_to_u8_mut(slice_u32: &mut [u32]) -> &mut [u8] { + unsafe { from_raw_parts_mut::(slice_u32.as_mut_ptr() as *mut u8, slice_u32.len() * 4) } +}