195 lines
5.2 KiB
Rust
195 lines
5.2 KiB
Rust
use gc_arena::Collect;
|
|
use swf::Matrix;
|
|
use swf::Twips;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Collect)]
|
|
#[collect(require_static)]
|
|
pub struct BoundingBox {
|
|
pub x_min: Twips,
|
|
pub y_min: Twips,
|
|
pub x_max: Twips,
|
|
pub y_max: Twips,
|
|
pub valid: bool,
|
|
}
|
|
|
|
impl BoundingBox {
|
|
/// Clamps the given point inside this bounding box.
|
|
pub fn clamp(&self, point: (Twips, Twips)) -> (Twips, Twips) {
|
|
if self.valid {
|
|
use std::cmp::{max, min};
|
|
(
|
|
min(max(point.0, self.x_min), self.x_max),
|
|
min(max(point.1, self.y_min), self.y_max),
|
|
)
|
|
} else {
|
|
point
|
|
}
|
|
}
|
|
|
|
pub fn transform(&self, matrix: &Matrix) -> Self {
|
|
if !self.valid {
|
|
return Self::default();
|
|
}
|
|
|
|
use std::cmp::{max, min};
|
|
let pt0 = *matrix * (self.x_min, self.y_min);
|
|
let pt1 = *matrix * (self.x_min, self.y_max);
|
|
let pt2 = *matrix * (self.x_max, self.y_min);
|
|
let pt3 = *matrix * (self.x_max, self.y_max);
|
|
BoundingBox {
|
|
x_min: min(pt0.0, min(pt1.0, min(pt2.0, pt3.0))),
|
|
y_min: min(pt0.1, min(pt1.1, min(pt2.1, pt3.1))),
|
|
x_max: max(pt0.0, max(pt1.0, max(pt2.0, pt3.0))),
|
|
y_max: max(pt0.1, max(pt1.1, max(pt2.1, pt3.1))),
|
|
valid: true,
|
|
}
|
|
}
|
|
|
|
pub fn encompass(&mut self, x: Twips, y: Twips) {
|
|
if self.valid {
|
|
if x < self.x_min {
|
|
self.x_min = x;
|
|
}
|
|
if x > self.x_max {
|
|
self.x_max = x;
|
|
}
|
|
if y < self.y_min {
|
|
self.y_min = y;
|
|
}
|
|
if y > self.y_max {
|
|
self.y_max = y;
|
|
}
|
|
} else {
|
|
self.x_min = x;
|
|
self.x_max = x;
|
|
self.y_min = y;
|
|
self.y_max = y;
|
|
self.valid = true;
|
|
}
|
|
}
|
|
|
|
pub fn union(&mut self, other: &BoundingBox) {
|
|
use std::cmp::{max, min};
|
|
if other.valid {
|
|
if self.valid {
|
|
self.x_min = min(self.x_min, other.x_min);
|
|
self.x_max = max(self.x_max, other.x_max);
|
|
self.y_min = min(self.y_min, other.y_min);
|
|
self.y_max = max(self.y_max, other.y_max);
|
|
} else {
|
|
*self = other.clone();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn intersects(&self, other: &BoundingBox) -> bool {
|
|
if !self.valid || !other.valid {
|
|
return false;
|
|
}
|
|
|
|
use std::cmp::{max, min};
|
|
let x_min = max(self.x_min, other.x_min);
|
|
let y_min = max(self.y_min, other.y_min);
|
|
let x_max = min(self.x_max, other.x_max);
|
|
let y_max = min(self.y_max, other.y_max);
|
|
|
|
x_min <= x_max && y_min <= y_max
|
|
}
|
|
|
|
pub fn contains(&self, (x, y): (Twips, Twips)) -> bool {
|
|
self.valid && x >= self.x_min && x <= self.x_max && y >= self.y_min && y <= self.y_max
|
|
}
|
|
|
|
/// Set the X coordinate to a particular value, maintaining the width of
|
|
/// this box (if possible).
|
|
pub fn set_x(&mut self, x: Twips) {
|
|
let width = self.width();
|
|
self.x_min = x;
|
|
self.x_max = x + width;
|
|
|
|
if self.y_max >= self.y_min {
|
|
self.valid = true;
|
|
}
|
|
}
|
|
|
|
/// Set the Y coordinate to a particular value, maintaining the width of
|
|
/// this box (if possible).
|
|
pub fn set_y(&mut self, y: Twips) {
|
|
let height = self.height();
|
|
self.y_min = y;
|
|
self.y_max = y + height;
|
|
|
|
if self.x_max >= self.x_min {
|
|
self.valid = true;
|
|
}
|
|
}
|
|
|
|
/// Determine the width of the bounding box.
|
|
pub fn width(&self) -> Twips {
|
|
if self.valid {
|
|
self.x_max - self.x_min
|
|
} else {
|
|
Default::default()
|
|
}
|
|
}
|
|
|
|
/// Adjust the width of the bounding box.
|
|
pub fn set_width(&mut self, width: Twips) {
|
|
self.x_max = self.x_min + width;
|
|
|
|
self.valid = self.x_max >= self.x_min && self.y_max >= self.y_min;
|
|
}
|
|
|
|
/// Determine the height of the bounding box.
|
|
pub fn height(&self) -> Twips {
|
|
if self.valid {
|
|
self.y_max - self.y_min
|
|
} else {
|
|
Default::default()
|
|
}
|
|
}
|
|
|
|
/// Adjust the height of the bounding box.
|
|
pub fn set_height(&mut self, height: Twips) {
|
|
self.y_max = self.y_min + height;
|
|
|
|
self.valid = self.x_max >= self.x_min && self.y_max >= self.y_min;
|
|
}
|
|
}
|
|
|
|
impl Default for BoundingBox {
|
|
fn default() -> Self {
|
|
Self {
|
|
x_min: Default::default(),
|
|
y_min: Default::default(),
|
|
x_max: Default::default(),
|
|
y_max: Default::default(),
|
|
valid: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<swf::Rectangle> for BoundingBox {
|
|
fn from(rect: swf::Rectangle) -> Self {
|
|
Self {
|
|
x_min: rect.x_min,
|
|
y_min: rect.y_min,
|
|
x_max: rect.x_max,
|
|
y_max: rect.y_max,
|
|
valid: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&swf::Rectangle> for BoundingBox {
|
|
fn from(rect: &swf::Rectangle) -> Self {
|
|
Self {
|
|
x_min: rect.x_min,
|
|
y_min: rect.y_min,
|
|
x_max: rect.x_max,
|
|
y_max: rect.y_max,
|
|
valid: true,
|
|
}
|
|
}
|
|
}
|