1use std::cmp::Ordering;
16
17use geom::{Vec2, DirVec2, v2, Card, CardMask};
18use core::{Hitbox, HbVel};
19use float::n64;
20
21mod normals;
22#[cfg(test)] mod tests;
23
24#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
26pub enum ShapeKind {
27 Circle,
29 Rect
31}
32
33#[derive(PartialEq, Copy, Clone, Debug)]
37pub struct Shape {
38 kind: ShapeKind,
39 dims: Vec2
40}
41
42impl Shape {
43 pub fn new(kind: ShapeKind, dims: Vec2) -> Shape {
48 assert!(dims.x >= 0.0 && dims.y >= 0.0, "dims must be non-negative");
49 Shape::with_any_dims(kind, dims)
50 }
51
52 fn with_any_dims(kind: ShapeKind, dims: Vec2) -> Shape {
54 if kind == ShapeKind::Circle {
55 assert_eq!(dims.x, dims.y, "circle width must equal height");
56 }
57 Shape { kind: kind, dims: dims }
58 }
59
60 #[inline]
62 pub fn circle(diam: f64) -> Shape {
63 Shape::new(ShapeKind::Circle, v2(diam, diam))
64 }
65
66 #[inline]
68 pub fn rect(dims: Vec2) -> Shape {
69 Shape::new(ShapeKind::Rect, dims)
70 }
71
72 #[inline]
74 pub fn square(width: f64) -> Shape {
75 Shape::new(ShapeKind::Rect, v2(width, width))
76 }
77
78 #[inline]
80 pub fn kind(&self) -> ShapeKind {
81 self.kind
82 }
83
84 #[inline]
86 pub fn dims(&self) -> Vec2 {
87 self.dims
88 }
89
90 #[inline]
92 pub fn place(self, pos: Vec2) -> PlacedShape {
93 PlacedShape::new(pos, self)
94 }
95
96 pub(crate) fn advance(&self, resize_vel: Vec2, elapsed: f64) -> Shape {
97 Shape::with_any_dims(self.kind, self.dims + resize_vel * elapsed)
98 }
99}
100
101#[derive(PartialEq, Copy, Clone, Debug)]
103pub struct PlacedShape {
104 pub pos: Vec2,
106 pub shape: Shape
108}
109
110impl PlacedShape {
111 #[inline]
113 pub fn new(pos: Vec2, shape: Shape) -> PlacedShape {
114 PlacedShape { pos: pos, shape: shape }
115 }
116
117 #[inline]
119 pub fn kind(&self) -> ShapeKind {
120 self.shape.kind()
121 }
122
123 #[inline]
125 pub fn dims(&self) -> Vec2 {
126 self.shape.dims()
127 }
128
129 pub fn min_x(&self) -> f64 { self.bounds_left() }
131
132 pub fn min_y(&self) -> f64 { self.bounds_bottom() }
134
135 pub fn max_x(&self) -> f64 { self.bounds_right() }
137
138 pub fn max_y(&self) -> f64 { self.bounds_top() }
140
141 pub fn overlaps(&self, other: &PlacedShape) -> bool {
143 self.normal_from(other).len() >= 0.0
144 }
145
146 pub fn normal_from(&self, other: &PlacedShape) -> DirVec2 {
158 match (self.kind(), other.kind()) {
159 (ShapeKind::Rect, ShapeKind::Rect) => normals::rect_rect_normal(self, other),
160 (ShapeKind::Rect, ShapeKind::Circle) => normals::rect_circle_normal(self, other),
161 (ShapeKind::Circle, ShapeKind::Rect) => normals::rect_circle_normal(other, self).flip(),
162 (ShapeKind::Circle, ShapeKind::Circle) => normals::circle_circle_normal(self, other),
163 }
164 }
165
166 pub fn masked_normal_from(&self, other: &PlacedShape, mask: CardMask) -> DirVec2 {
173 match (self.kind(), other.kind()) {
174 (ShapeKind::Rect, ShapeKind::Rect) => normals::masked_rect_rect_normal(self, other, mask),
175 (ShapeKind::Rect, ShapeKind::Circle) => normals::masked_rect_circle_normal(self, other, mask),
176 (ShapeKind::Circle, ShapeKind::Rect) => normals::masked_rect_circle_normal(other, self, mask.flip()).flip(),
177 (ShapeKind::Circle, ShapeKind::Circle) => normals::masked_circle_circle_normal(self, other, mask),
178 }
179 }
180
181 pub fn contact_point(&self, other: &PlacedShape) -> Vec2 {
185 match (self.kind(), other.kind()) {
186 (ShapeKind::Rect, ShapeKind::Rect) => normals::rect_rect_contact(self, other),
187 (ShapeKind::Circle, _) => normals::circle_any_contact(self, other),
188 (ShapeKind::Rect, ShapeKind::Circle) => normals::circle_any_contact(other, self),
189 }
190 }
191
192 #[inline]
194 pub fn moving(self, vel: Vec2) -> Hitbox {
195 Hitbox::new(self, HbVel::moving(vel))
196 }
197
198 #[inline]
200 pub fn moving_until(self, vel: Vec2, end_time: f64) -> Hitbox {
201 Hitbox::new(self, HbVel::moving_until(vel, end_time))
202 }
203
204 #[inline]
206 pub fn still(self) -> Hitbox {
207 Hitbox::new(self, HbVel::still())
208 }
209
210 #[inline]
212 pub fn still_until(self, end_time: f64) -> Hitbox {
213 Hitbox::new(self, HbVel::still_until(end_time))
214 }
215
216 pub(crate) fn sector(&self, point: Vec2) -> Sector {
217 let x = interval_sector(self.min_x(), self.max_x(), point.x);
218 let y = interval_sector(self.min_y(), self.max_y(), point.y);
219 Sector::new(x, y)
220 }
221
222 pub(crate) fn as_rect(&self) -> PlacedShape {
223 PlacedShape::new(self.pos, Shape::rect(self.shape.dims()))
224 }
225
226 pub(crate) fn bounding_box(&self, other: &PlacedShape) -> PlacedShape {
227 let right = self.max_x().max(other.max_x());
228 let top = self.max_y().max(other.max_y());
229 let left = self.min_x().min(other.min_x());
230 let bottom = self.min_y().min(other.min_y());
231
232 let shape = Shape::rect(v2(right - left, top - bottom));
233 let pos = v2(left + shape.dims().x * 0.5, bottom + shape.dims().y * 0.5);
234 PlacedShape::new(pos, shape)
235 }
236
237 pub(crate) fn advance(&self, vel: Vec2, resize_vel: Vec2, elapsed: f64) -> PlacedShape {
238 PlacedShape::new(self.pos + vel * elapsed, self.shape.advance(resize_vel, elapsed))
239 }
240}
241
242pub(crate) trait PlacedBounds {
243 fn bounds_center(&self) -> &Vec2;
244 fn bounds_dims(&self) -> &Vec2;
245
246 fn bounds_bottom(&self) -> f64 { self.bounds_center().y - self.bounds_dims().y * 0.5 }
247 fn bounds_left(&self) -> f64 { self.bounds_center().x - self.bounds_dims().x * 0.5 }
248 fn bounds_top(&self) -> f64 { self.bounds_center().y + self.bounds_dims().y * 0.5 }
249 fn bounds_right(&self) -> f64 { self.bounds_center().x + self.bounds_dims().x * 0.5 }
250
251 fn edge(&self, card: Card) -> f64 {
252 match card {
253 Card::MinusY => -self.bounds_bottom(),
254 Card::MinusX => -self.bounds_left(),
255 Card::PlusY => self.bounds_top(),
256 Card::PlusX => self.bounds_right(),
257 }
258 }
259
260 fn max_edge(&self) -> f64 {
261 Card::values().iter()
262 .map(|&card| self.edge(card).abs())
263 .max_by_key(|&edge| n64(edge))
264 .unwrap()
265 }
266
267 fn card_overlap(&self, src: &Self, card: Card) -> f64 {
268 src.edge(card) + self.edge(card.flip())
269 }
270
271 fn corner(&self, sector: Sector) -> Vec2 {
272 let x = match sector.x {
273 Ordering::Less => self.bounds_left(),
274 Ordering::Greater => self.bounds_right(),
275 Ordering::Equal => panic!("expected corner sector")
276 };
277 let y = match sector.y {
278 Ordering::Less => self.bounds_bottom(),
279 Ordering::Greater => self.bounds_top(),
280 Ordering::Equal => panic!("expected corner sector")
281 };
282 v2(x, y)
283 }
284}
285
286impl PlacedBounds for PlacedShape {
287 fn bounds_center(&self) -> &Vec2 { &self.pos }
288 fn bounds_dims(&self) -> &Vec2 { &self.shape.dims }
289}
290
291fn interval_sector(left: f64, right: f64, val: f64) -> Ordering {
292 if val < left {
293 Ordering::Less
294 } else if val > right {
295 Ordering::Greater
296 } else {
297 Ordering::Equal
298 }
299}
300
301#[derive(PartialEq, Eq, Copy, Clone)]
302pub(crate) struct Sector {
303 x: Ordering,
304 y: Ordering
305}
306
307impl Sector {
308 pub fn new(x: Ordering, y: Ordering) -> Sector {
309 Sector { x: x, y: y }
310 }
311
312 pub fn is_corner(&self) -> bool {
313 self.x != Ordering::Equal && self.y != Ordering::Equal
314 }
315
316 pub fn corner_cards(&self) -> Option<(Card, Card)> {
317 if self.is_corner() {
318 Some((
319 if self.x == Ordering::Greater { Card::PlusX } else { Card::MinusX },
320 if self.y == Ordering::Greater { Card::PlusY } else { Card::MinusY },
321 ))
322 } else {
323 None
324 }
325 }
326}