1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use super::Vec2;
use super::bounding::AxisAlignmentBoundingBox;
pub fn min_distance(distance: f32, length: u32) -> f32 {
let length = length as f32;
let half_length = length / 2.0;
if distance > half_length {
if distance <= length { distance - length } else { half_length }
}
else if distance < -half_length {
if distance >= -length { distance + length } else { -half_length }
}
else {
distance
}
}
pub fn min_coordinate(coordinate: f32, length: u32) -> f32 {
if coordinate >= 0.0 {
let uint_coordinate = coordinate as u32;
let mod_coordinate = (uint_coordinate % length) as f32 + coordinate - uint_coordinate as f32;
return if coordinate < 0.0 { mod_coordinate + length as f32} else { mod_coordinate }
}
else {
let coordinate = -coordinate;
let uint_coordinate = coordinate as u32;
let mod_coordinate = (uint_coordinate % length) as f32 + coordinate - uint_coordinate as f32;
let mod_coordinate = length as f32 - mod_coordinate;
return if coordinate < 0.0 { mod_coordinate + length as f32} else { mod_coordinate }
}
}
pub struct Bounds {
pub width: u32,
pub height: u32,
}
impl Bounds {
pub fn new(width: u32, height: u32) -> Bounds {
Bounds { width, height }
}
pub fn get_toroidal_distance(&self, distance: Vec2) -> Vec2 {
Vec2::xy(
min_distance(distance.x, self.width),
min_distance(distance.y, self.height),
)
}
pub fn get_toroidal_position(&self, position: Vec2) -> Vec2 {
Vec2::xy(
min_coordinate(position.x, self.width),
min_coordinate(position.y, self.height),
)
}
pub fn get_toroidal_aabb(&self, aabb: &AxisAlignmentBoundingBox) -> AxisAlignmentBoundingBox {
AxisAlignmentBoundingBox::from_bounds(
min_coordinate(aabb.left(), self.width),
min_coordinate(aabb.right(), self.width),
min_coordinate(aabb.top(), self.height),
min_coordinate(aabb.bottom(), self.height),
)
}
pub fn dimension(&self) -> Vec2 {
Vec2::xy(self.width as f32, self.height as f32)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn positive_coordinate() {
assert_eq!(min_coordinate(12.5, 20), 12.5);
assert_eq!(min_coordinate(32.5, 20), 12.5);
assert_eq!(min_coordinate(52.5, 20), 12.5);
}
#[test]
fn negative_coordinate() {
assert_eq!(min_coordinate(-12.5, 20), 7.5);
assert_eq!(min_coordinate(-32.5, 20), 7.5);
assert_eq!(min_coordinate(-52.5, 20), 7.5);
}
#[test]
fn positive_distance() {
assert_eq!(min_distance(7.5, 20), 7.5);
assert_eq!(min_distance(12.5, 20), -7.5);
assert_eq!(min_distance(10.0, 20), 10.0);
assert_eq!(min_distance(20.0, 20), 0.0);
assert_eq!(min_distance(27.5, 20), 10.0);
}
#[test]
fn negative_distance() {
assert_eq!(min_distance(-7.5, 20), -7.5);
assert_eq!(min_distance(-12.5, 20), 7.5);
assert_eq!(min_distance(-10.0, 20), -10.0);
assert_eq!(min_distance(-20.0, 20), -0.0);
assert_eq!(min_distance(-27.5, 20), -10.0);
}
}