зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1479432 - Update Cargo lockfiles and re-vendor rust dependencies. r=jrmuizel
MozReview-Commit-ID: BhLXnUXBZpq --HG-- extra : rebase_source : dc44ec6d19d96071567937a8d5258e4b380a5136
This commit is contained in:
Родитель
83d156a753
Коммит
157d196498
|
@ -37,6 +37,15 @@ dependencies = [
|
|||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "app_units"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.6"
|
||||
|
@ -673,6 +682,14 @@ version = "0.18.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1564,13 +1581,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.10.0"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2354,7 +2371,7 @@ dependencies = [
|
|||
name = "webrender"
|
||||
version = "0.57.2"
|
||||
dependencies = [
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2363,14 +2380,14 @@ dependencies = [
|
|||
"core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2384,14 +2401,14 @@ dependencies = [
|
|||
name = "webrender_api"
|
||||
version = "0.57.2"
|
||||
dependencies = [
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
|
||||
|
@ -2402,12 +2419,12 @@ dependencies = [
|
|||
name = "webrender_bindings"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2543,6 +2560,7 @@ dependencies = [
|
|||
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"
|
||||
"checksum app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dadc668390b373e73e4abbfc1f07238b09a25858f2f39c06cebc6d8e141d774"
|
||||
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
|
||||
"checksum ascii-canvas 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b385d69402821a1c254533a011a312531cbcc0e3e24f19bbb4747a5a2daf37e2"
|
||||
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
|
||||
|
@ -2606,6 +2624,7 @@ dependencies = [
|
|||
"checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47d5eb6310c8dd3e79f973952ddcb180bf6a98c01d341add49126a094b5598cc"
|
||||
"checksum euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70a2ebdf55fb9d6329046e026329a55ef8fbaae5ea833f56e170beb3125a8a5f"
|
||||
"checksum fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478"
|
||||
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
|
@ -2685,7 +2704,7 @@ dependencies = [
|
|||
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
||||
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
|
||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||
"checksum plane-split 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e14382aabad89085fbf714f75d527492bb672725facb9b2ced2fada47cf418c"
|
||||
"checksum plane-split 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f0a0766225cf828672e97948dfa035bb2eae75110757359ae12fbb46509c8b66"
|
||||
"checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
|
||||
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
"checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{".travis.yml":"6b96b2c6bfd7e1acef4b825a2813fc4277859eb9400a16800db8835c25e4087d","Cargo.toml":"e57a15878e84c4628d1ed9cb2db5d6d255eb49f26216dbc46f8912bbdfcfd4b2","README.md":"9f048d969f9f8333cdcdb892744cd0816e4f2922c8817fa5e9e07f9472fe1050","src/app_unit.rs":"bc6bc0f5063bf27c27a84a931b51ee1e4930550af84a4351c1eed81f88f13f00","src/lib.rs":"ed615683418d93046fedb019baf87513c8c490203099144c298bb48e845137b2"},"package":"29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"}
|
|
@ -0,0 +1,8 @@
|
|||
language: rust
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
|
@ -0,0 +1,25 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "app_units"
|
||||
version = "0.6.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Servo app units type (Au)"
|
||||
documentation = "http://doc.servo.org/app_units/"
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/app_units"
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
|
@ -0,0 +1,3 @@
|
|||
# app-units
|
||||
|
||||
[Documentation](http://doc.servo.org/app_units/index.html)
|
|
@ -0,0 +1,369 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use num_traits::Zero;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use std::default::Default;
|
||||
use std::fmt;
|
||||
use std::i32;
|
||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
|
||||
|
||||
/// The number of app units in a pixel.
|
||||
pub const AU_PER_PX: i32 = 60;
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
|
||||
/// An App Unit, the fundamental unit of length in Servo. Usually
|
||||
/// 1/60th of a pixel (see AU_PER_PX)
|
||||
///
|
||||
/// Please ensure that the values are between MIN_AU and MAX_AU.
|
||||
/// It is safe to construct invalid Au values, but it may lead to
|
||||
/// panics and overflows.
|
||||
pub struct Au(pub i32);
|
||||
|
||||
impl<'de> Deserialize<'de> for Au {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Au, D::Error> {
|
||||
Ok(Au(try!(i32::deserialize(deserializer))).clamp())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Au {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Au {
|
||||
#[inline]
|
||||
fn default() -> Au {
|
||||
Au(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for Au {
|
||||
#[inline]
|
||||
fn zero() -> Au {
|
||||
Au(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
// (1 << 30) - 1 lets us add/subtract two Au and check for overflow
|
||||
// after the operation. Gecko uses the same min/max values
|
||||
pub const MAX_AU: Au = Au((1 << 30) - 1);
|
||||
pub const MIN_AU: Au = Au(- ((1 << 30) - 1));
|
||||
|
||||
impl fmt::Debug for Au {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}px", self.to_f64_px())
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Au) -> Au {
|
||||
Au(self.0 + other.0).clamp()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Au) -> Au {
|
||||
Au(self.0 - other.0).clamp()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Mul<i32> for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: i32) -> Au {
|
||||
if let Some(new) = self.0.checked_mul(other) {
|
||||
Au(new).clamp()
|
||||
} else if (self.0 > 0) ^ (other > 0) {
|
||||
MIN_AU
|
||||
} else {
|
||||
MAX_AU
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<i32> for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: i32) -> Au {
|
||||
Au(self.0 / other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<i32> for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: i32) -> Au {
|
||||
Au(self.0 % other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Au {
|
||||
Au(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Au {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Au) {
|
||||
*self = *self + other;
|
||||
self.clamp_self();
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Au {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Au) {
|
||||
*self = *self - other;
|
||||
self.clamp_self();
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<i32> for Au {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: i32) {
|
||||
*self = *self * other;
|
||||
self.clamp_self();
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<i32> for Au {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: i32) {
|
||||
*self = *self / other;
|
||||
self.clamp_self();
|
||||
}
|
||||
}
|
||||
|
||||
impl Au {
|
||||
/// FIXME(pcwalton): Workaround for lack of cross crate inlining of newtype structs!
|
||||
#[inline]
|
||||
pub fn new(value: i32) -> Au {
|
||||
Au(value).clamp()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clamp(self) -> Self {
|
||||
if self.0 > MAX_AU.0 {
|
||||
MAX_AU
|
||||
} else if self.0 < MIN_AU.0 {
|
||||
MIN_AU
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clamp_self(&mut self) {
|
||||
*self = Au::clamp(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale_by(self, factor: f32) -> Au {
|
||||
let new_float = ((self.0 as f64) * factor as f64).round();
|
||||
Au::from_f64_au(new_float)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Scale, but truncate (useful for viewport-relative units)
|
||||
pub fn scale_by_trunc(self, factor: f32) -> Au {
|
||||
let new_float = ((self.0 as f64) * factor as f64).trunc();
|
||||
Au::from_f64_au(new_float)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_f64_au(float: f64) -> Self {
|
||||
// We *must* operate in f64. f32 isn't precise enough
|
||||
// to handle MAX_AU
|
||||
Au(float.min(MAX_AU.0 as f64)
|
||||
.max(MIN_AU.0 as f64)
|
||||
as i32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_px(px: i32) -> Au {
|
||||
Au(px) * AU_PER_PX
|
||||
}
|
||||
|
||||
/// Rounds this app unit down to the pixel towards zero and returns it.
|
||||
#[inline]
|
||||
pub fn to_px(self) -> i32 {
|
||||
self.0 / AU_PER_PX
|
||||
}
|
||||
|
||||
/// Ceil this app unit to the appropriate pixel boundary and return it.
|
||||
#[inline]
|
||||
pub fn ceil_to_px(self) -> i32 {
|
||||
((self.0 as f64) / (AU_PER_PX as f64)).ceil() as i32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_nearest_px(self) -> i32 {
|
||||
((self.0 as f64) / (AU_PER_PX as f64)).round() as i32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_nearest_pixel(self, pixels_per_px: f32) -> f32 {
|
||||
((self.0 as f32) / (AU_PER_PX as f32) * pixels_per_px).round() / pixels_per_px
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_f32_px(self) -> f32 {
|
||||
(self.0 as f32) / (AU_PER_PX as f32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_f64_px(self) -> f64 {
|
||||
(self.0 as f64) / (AU_PER_PX as f64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_f32_px(px: f32) -> Au {
|
||||
let float = (px * AU_PER_PX as f32).round();
|
||||
Au::from_f64_au(float as f64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_f64_px(px: f64) -> Au {
|
||||
let float = (px * AU_PER_PX as f64).round();
|
||||
Au::from_f64_au(float)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn abs(self) -> Self {
|
||||
Au(self.0.abs())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
assert_eq!(Au::zero(), Au(0));
|
||||
assert_eq!(Au::default(), Au(0));
|
||||
assert_eq!(Au::new(7), Au(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations() {
|
||||
assert_eq!(Au(7) + Au(5), Au(12));
|
||||
assert_eq!(MAX_AU + Au(1), MAX_AU);
|
||||
|
||||
assert_eq!(Au(7) - Au(5), Au(2));
|
||||
assert_eq!(MIN_AU - Au(1), MIN_AU);
|
||||
|
||||
assert_eq!(Au(7) * 5, Au(35));
|
||||
assert_eq!(MAX_AU * -1, MIN_AU);
|
||||
assert_eq!(MIN_AU * -1, MAX_AU);
|
||||
|
||||
assert_eq!(Au(35) / 5, Au(7));
|
||||
assert_eq!(Au(35) % 6, Au(5));
|
||||
|
||||
assert_eq!(-Au(7), Au(-7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturate() {
|
||||
let half = MAX_AU / 2;
|
||||
assert_eq!(half + half + half + half + half, MAX_AU);
|
||||
assert_eq!(-half - half - half - half - half, MIN_AU);
|
||||
assert_eq!(half * -10, MIN_AU);
|
||||
assert_eq!(-half * 10, MIN_AU);
|
||||
assert_eq!(half * 10, MAX_AU);
|
||||
assert_eq!(-half * -10, MAX_AU);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scale() {
|
||||
assert_eq!(Au(12).scale_by(1.5), Au(18));
|
||||
assert_eq!(Au(12).scale_by(1.7), Au(20));
|
||||
assert_eq!(Au(12).scale_by(1.8), Au(22));
|
||||
assert_eq!(Au(12).scale_by_trunc(1.8), Au(21));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abs() {
|
||||
assert_eq!(Au(-10).abs(), Au(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert() {
|
||||
assert_eq!(Au::from_px(5), Au(300));
|
||||
|
||||
assert_eq!(Au(300).to_px(), 5);
|
||||
assert_eq!(Au(330).to_px(), 5);
|
||||
assert_eq!(Au(350).to_px(), 5);
|
||||
assert_eq!(Au(360).to_px(), 6);
|
||||
|
||||
assert_eq!(Au(300).ceil_to_px(), 5);
|
||||
assert_eq!(Au(310).ceil_to_px(), 6);
|
||||
assert_eq!(Au(330).ceil_to_px(), 6);
|
||||
assert_eq!(Au(350).ceil_to_px(), 6);
|
||||
assert_eq!(Au(360).ceil_to_px(), 6);
|
||||
|
||||
assert_eq!(Au(300).to_nearest_px(), 5);
|
||||
assert_eq!(Au(310).to_nearest_px(), 5);
|
||||
assert_eq!(Au(330).to_nearest_px(), 6);
|
||||
assert_eq!(Au(350).to_nearest_px(), 6);
|
||||
assert_eq!(Au(360).to_nearest_px(), 6);
|
||||
|
||||
assert_eq!(Au(60).to_nearest_pixel(2.), 1.);
|
||||
assert_eq!(Au(70).to_nearest_pixel(2.), 1.);
|
||||
assert_eq!(Au(80).to_nearest_pixel(2.), 1.5);
|
||||
assert_eq!(Au(90).to_nearest_pixel(2.), 1.5);
|
||||
assert_eq!(Au(100).to_nearest_pixel(2.), 1.5);
|
||||
assert_eq!(Au(110).to_nearest_pixel(2.), 2.);
|
||||
assert_eq!(Au(120).to_nearest_pixel(2.), 2.);
|
||||
|
||||
assert_eq!(Au(300).to_f32_px(), 5.);
|
||||
assert_eq!(Au(312).to_f32_px(), 5.2);
|
||||
assert_eq!(Au(330).to_f32_px(), 5.5);
|
||||
assert_eq!(Au(348).to_f32_px(), 5.8);
|
||||
assert_eq!(Au(360).to_f32_px(), 6.);
|
||||
assert_eq!((Au(367).to_f32_px() * 1000.).round(), 6_117.);
|
||||
assert_eq!((Au(368).to_f32_px() * 1000.).round(), 6_133.);
|
||||
|
||||
assert_eq!(Au(300).to_f64_px(), 5.);
|
||||
assert_eq!(Au(312).to_f64_px(), 5.2);
|
||||
assert_eq!(Au(330).to_f64_px(), 5.5);
|
||||
assert_eq!(Au(348).to_f64_px(), 5.8);
|
||||
assert_eq!(Au(360).to_f64_px(), 6.);
|
||||
assert_eq!((Au(367).to_f64_px() * 1000.).round(), 6_117.);
|
||||
assert_eq!((Au(368).to_f64_px() * 1000.).round(), 6_133.);
|
||||
|
||||
assert_eq!(Au::from_f32_px(5.), Au(300));
|
||||
assert_eq!(Au::from_f32_px(5.2), Au(312));
|
||||
assert_eq!(Au::from_f32_px(5.5), Au(330));
|
||||
assert_eq!(Au::from_f32_px(5.8), Au(348));
|
||||
assert_eq!(Au::from_f32_px(6.), Au(360));
|
||||
assert_eq!(Au::from_f32_px(6.12), Au(367));
|
||||
assert_eq!(Au::from_f32_px(6.13), Au(368));
|
||||
|
||||
assert_eq!(Au::from_f64_px(5.), Au(300));
|
||||
assert_eq!(Au::from_f64_px(5.2), Au(312));
|
||||
assert_eq!(Au::from_f64_px(5.5), Au(330));
|
||||
assert_eq!(Au::from_f64_px(5.8), Au(348));
|
||||
assert_eq!(Au::from_f64_px(6.), Au(360));
|
||||
assert_eq!(Au::from_f64_px(6.12), Au(367));
|
||||
assert_eq!(Au::from_f64_px(6.13), Au(368));
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was
|
||||
//! originally proposed in 2002 as a standard unit of measure in Gecko.
|
||||
//! See <https://bugzilla.mozilla.org/show_bug.cgi?id=177805> for more info.
|
||||
|
||||
extern crate num_traits;
|
||||
extern crate serde;
|
||||
|
||||
mod app_unit;
|
||||
|
||||
pub use app_unit::{Au, MIN_AU, MAX_AU, AU_PER_PX};
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"6b96b2c6bfd7e1acef4b825a2813fc4277859eb9400a16800db8835c25e4087d","Cargo.toml":"e57a15878e84c4628d1ed9cb2db5d6d255eb49f26216dbc46f8912bbdfcfd4b2","README.md":"9f048d969f9f8333cdcdb892744cd0816e4f2922c8817fa5e9e07f9472fe1050","src/app_unit.rs":"bc6bc0f5063bf27c27a84a931b51ee1e4930550af84a4351c1eed81f88f13f00","src/lib.rs":"ed615683418d93046fedb019baf87513c8c490203099144c298bb48e845137b2"},"package":"29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"}
|
||||
{"files":{".travis.yml":"6b96b2c6bfd7e1acef4b825a2813fc4277859eb9400a16800db8835c25e4087d","Cargo.toml":"2b57a8f3f88b35e6ea03b563b2127fe01b90c5d2102910dfb662584e9d72ad26","README.md":"2f4c2188cdcfaa201ecd7c6b1bdb5247a54b534fc50a4e26b339a245cee973a9","src/app_unit.rs":"ab0df325046c8d012202a6a4ec54bbca961aed80529c5cbab4c4c837046a7bf8","src/lib.rs":"594920680604545eed486e6891b9b7d52d3baee3b2d774687fc0ac244331edf5"},"package":"9dadc668390b373e73e4abbfc1f07238b09a25858f2f39c06cebc6d8e141d774"}
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
[package]
|
||||
name = "app_units"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Servo app units type (Au)"
|
||||
documentation = "http://doc.servo.org/app_units/"
|
||||
documentation = "https://docs.rs/app_units/"
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/app_units"
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# app-units
|
||||
|
||||
[Documentation](http://doc.servo.org/app_units/index.html)
|
||||
[Documentation](https://docs.rs/app_units/)
|
||||
|
|
|
@ -15,10 +15,10 @@ pub const AU_PER_PX: i32 = 60;
|
|||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
|
||||
/// An App Unit, the fundamental unit of length in Servo. Usually
|
||||
/// 1/60th of a pixel (see AU_PER_PX)
|
||||
/// 1/60th of a pixel (see `AU_PER_PX`)
|
||||
///
|
||||
/// Please ensure that the values are between MIN_AU and MAX_AU.
|
||||
/// It is safe to construct invalid Au values, but it may lead to
|
||||
/// Please ensure that the values are between `MIN_AU` and `MAX_AU`.
|
||||
/// It is safe to construct invalid `Au` values, but it may lead to
|
||||
/// panics and overflows.
|
||||
pub struct Au(pub i32);
|
||||
|
||||
|
@ -83,6 +83,21 @@ impl Sub for Au {
|
|||
|
||||
}
|
||||
|
||||
impl Mul<Au> for i32 {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: Au) -> Au {
|
||||
if let Some(new) = other.0.checked_mul(self) {
|
||||
Au(new).clamp()
|
||||
} else if (self > 0) ^ (other.0 > 0) {
|
||||
MIN_AU
|
||||
} else {
|
||||
MAX_AU
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<i32> for Au {
|
||||
type Output = Au;
|
||||
|
||||
|
@ -98,6 +113,15 @@ impl Mul<i32> for Au {
|
|||
}
|
||||
}
|
||||
|
||||
impl Div for Au {
|
||||
type Output = i32;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Au) -> i32 {
|
||||
self.0 / other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<i32> for Au {
|
||||
type Output = Au;
|
||||
|
||||
|
@ -107,6 +131,15 @@ impl Div<i32> for Au {
|
|||
}
|
||||
}
|
||||
|
||||
impl Rem for Au {
|
||||
type Output = Au;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: Au) -> Au {
|
||||
Au(self.0 % other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<i32> for Au {
|
||||
type Output = Au;
|
||||
|
||||
|
@ -273,12 +306,21 @@ fn operations() {
|
|||
assert_eq!(MIN_AU - Au(1), MIN_AU);
|
||||
|
||||
assert_eq!(Au(7) * 5, Au(35));
|
||||
assert_eq!(5 * Au(7), Au(35));
|
||||
assert_eq!(MAX_AU * -1, MIN_AU);
|
||||
assert_eq!(MIN_AU * -1, MAX_AU);
|
||||
assert_eq!(-1 * MAX_AU, MIN_AU);
|
||||
assert_eq!(-1 * MIN_AU, MAX_AU);
|
||||
|
||||
assert_eq!((Au(14) / 5) * 5 + Au(14) % 5, Au(14));
|
||||
assert_eq!((Au(14) / Au(5)) * Au(5) + Au(14) % Au(5), Au(14));
|
||||
|
||||
assert_eq!(Au(35) / 5, Au(7));
|
||||
assert_eq!(Au(35) % 6, Au(5));
|
||||
|
||||
assert_eq!(Au(35) / Au(5), 7);
|
||||
assert_eq!(Au(35) / Au(5), 7);
|
||||
|
||||
assert_eq!(-Au(7), Au(-7));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was
|
||||
//! An `Au` is an "App Unit" and represents 1/60th of a CSS pixel. It was
|
||||
//! originally proposed in 2002 as a standard unit of measure in Gecko.
|
||||
//! See <https://bugzilla.mozilla.org/show_bug.cgi?id=177805> for more info.
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{".travis.yml":"301590735ff27f124c03cef8598aa5397c88c59aba3d058edf0bde532965c346","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"d67b137d287c8debd8811e4deb3c2973eb9ee1ea11b31fcdd8217591482021f6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6594377e8f6c20f88f628520d8de9b9a59c5892a0ee9a6ccd13c8400c1499911","src/homogen.rs":"cb26346ad1ea2797bdc1cac7f532872becabf28a1f9c60792f86ad4a655582f9","src/length.rs":"3171315822707728b1bfbdd04a4190ffb7206b4bfc59e9dd072bb2caa05ff292","src/lib.rs":"b3c2303ab06ce972784c2ec4faa09ecdaa8e0706316f427c8a2009445a52f6e9","src/macros.rs":"877b4bd62b63ea120e568803281e7169f33fb811fe1c1515e56bfc44a74c34a2","src/num.rs":"4439479fad5729073e0bfe0b96b547672a237430d48e564519759b9550baa033","src/point.rs":"d18046853e19012e649a01991d45fdb1ba8f51eb55e52273e68f567cd7df932a","src/rect.rs":"1a4fbcf482e447218894c6a31753cb9b5a6c3e5377447ba7b5bceae7941a0772","src/rotation.rs":"982aaca640215bacc5d2dc60a8949bb2510d5b6d492975b8b6946a7c8f69b496","src/scale.rs":"fc07bcf47f3a1215023c830059f0d270e570cbd37fe8c367ef4a47b191f4ae3e","src/side_offsets.rs":"f114cb881256bbeff2ee2aa305d363e2dea65aa8535140f104f6fa9364bd02f5","src/size.rs":"f6a4f12fc50cc54220af089339cb7fde37f22c6dfcc4c2c676d24caab07b1790","src/transform2d.rs":"137344a16162f5cd1dc4a2ae87b8ea3fdde7597874835582378945f55e45513e","src/transform3d.rs":"efd971ba35e8a9ab59b0c4062b2625532147af0e57bf96b8cd09117524cf23ed","src/trig.rs":"97a263c4f178b0332501659ca8143f9f637a0755aca189dd31ac551bcd4cb73c","src/vector.rs":"d84103384907174d2b2206acd60d6b3261edb3ac971ec5e121ae22ce6bcca5d9"},"package":"47d5eb6310c8dd3e79f973952ddcb180bf6a98c01d341add49126a094b5598cc"}
|
|
@ -0,0 +1,25 @@
|
|||
language: rust
|
||||
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
||||
rust:
|
||||
- 1.23.0
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
env:
|
||||
- FEATURES=""
|
||||
- FEATURES="--features serde"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: nightly
|
||||
env: FEATURES="--features unstable"
|
||||
- rust: nightly
|
||||
env: FEATURES="--features unstable,serde"
|
||||
|
||||
script:
|
||||
- cargo build $FEATURES
|
||||
- cargo test --verbose $FEATURES
|
|
@ -0,0 +1,5 @@
|
|||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
option. All files in the project carrying such notice may not be
|
||||
copied, modified, or distributed except according to those terms.
|
|
@ -0,0 +1,38 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "euclid"
|
||||
version = "0.18.1"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Geometry primitives"
|
||||
documentation = "https://docs.rs/euclid/"
|
||||
keywords = ["matrix", "vector", "linear-algebra", "geometry"]
|
||||
categories = ["science"]
|
||||
license = "MIT / Apache-2.0"
|
||||
repository = "https://github.com/servo/euclid"
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["serde_derive"]
|
||||
optional = true
|
||||
[dev-dependencies.rand]
|
||||
version = "0.4"
|
||||
|
||||
[dev-dependencies.serde_test]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
unstable = []
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2012-2013 Mozilla Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,8 @@
|
|||
# euclid
|
||||
|
||||
This is a small library for geometric types with a focus on 2d graphics and
|
||||
layout.
|
||||
|
||||
* [Documentation](https://docs.rs/euclid/)
|
||||
* [Release notes](https://github.com/servo/euclid/releases)
|
||||
* [crates.io](https://crates.io/crates/euclid)
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Trait for testing approximate equality
|
||||
pub trait ApproxEq<Eps> {
|
||||
fn approx_epsilon() -> Eps;
|
||||
fn approx_eq(&self, other: &Self) -> bool;
|
||||
fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! approx_eq {
|
||||
($ty:ty, $eps:expr) => (
|
||||
impl ApproxEq<$ty> for $ty {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> $ty { $eps }
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &$ty) -> bool {
|
||||
self.approx_eq_eps(other, &$eps)
|
||||
}
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &$ty, approx_epsilon: &$ty) -> bool {
|
||||
(*self - *other).abs() < *approx_epsilon
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
approx_eq!(f32, 1.0e-6);
|
||||
approx_eq!(f64, 1.0e-6);
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2018 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use point::{TypedPoint2D, TypedPoint3D};
|
||||
use vector::{TypedVector2D, TypedVector3D};
|
||||
|
||||
use num::{One, Zero};
|
||||
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Div;
|
||||
|
||||
|
||||
define_matrix! {
|
||||
/// Homogeneous vector in 3D space.
|
||||
pub struct HomogeneousVector<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
pub w: T,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, U> HomogeneousVector<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T, w: T) -> Self {
|
||||
HomogeneousVector { x, y, z, w, _unit: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: Copy + Div<T, Output=T> + Zero + PartialOrd, U> HomogeneousVector<T, U> {
|
||||
/// Convert into Cartesian 2D point.
|
||||
///
|
||||
/// Returns None if the point is on or behind the W=0 hemisphere.
|
||||
#[inline]
|
||||
pub fn to_point2d(&self) -> Option<TypedPoint2D<T, U>> {
|
||||
if self.w > T::zero() {
|
||||
Some(TypedPoint2D::new(self.x / self.w, self.y / self.w))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert into Cartesian 3D point.
|
||||
///
|
||||
/// Returns None if the point is on or behind the W=0 hemisphere.
|
||||
#[inline]
|
||||
pub fn to_point3d(&self) -> Option<TypedPoint3D<T, U>> {
|
||||
if self.w > T::zero() {
|
||||
Some(TypedPoint3D::new(self.x / self.w, self.y / self.w, self.z / self.w))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> From<TypedVector2D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(v: TypedVector2D<T, U>) -> Self {
|
||||
HomogeneousVector::new(v.x, v.y, T::zero(), T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> From<TypedVector3D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(v: TypedVector3D<T, U>) -> Self {
|
||||
HomogeneousVector::new(v.x, v.y, v.z, T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + One, U> From<TypedPoint2D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(p: TypedPoint2D<T, U>) -> Self {
|
||||
HomogeneousVector::new(p.x, p.y, T::zero(), T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: One, U> From<TypedPoint3D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(p: TypedPoint3D<T, U>) -> Self {
|
||||
HomogeneousVector::new(p.x, p.y, p.z, T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?},{:?})", self.x, self.y, self.z, self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{},{},{})", self.x, self.y, self.z, self.w)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod homogeneous {
|
||||
use super::HomogeneousVector;
|
||||
use point::{Point2D, Point3D};
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
assert_eq!(Some(Point2D::new(1.0, 2.0)), HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d());
|
||||
assert_eq!(Some(Point3D::new(1.0, -2.0, 0.1)), HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative() {
|
||||
assert_eq!(None, HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d());
|
||||
assert_eq!(None, HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,520 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A one-dimensional length, tagged with its units.
|
||||
|
||||
use scale::TypedScale;
|
||||
use num::Zero;
|
||||
|
||||
use num_traits::{NumCast, Saturating};
|
||||
use num::One;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use core::cmp::Ordering;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::ops::{AddAssign, DivAssign, MulAssign, SubAssign};
|
||||
use core::marker::PhantomData;
|
||||
use core::fmt;
|
||||
|
||||
/// A one-dimensional distance, with value represented by `T` and unit of measurement `Unit`.
|
||||
///
|
||||
/// `T` can be any numeric type, for example a primitive type like `u64` or `f32`.
|
||||
///
|
||||
/// `Unit` is not used in the representation of a `Length` value. It is used only at compile time
|
||||
/// to ensure that a `Length` stored with one unit is converted explicitly before being used in an
|
||||
/// expression that requires a different unit. It may be a type without values, such as an empty
|
||||
/// enum.
|
||||
///
|
||||
/// You can multiply a `Length` by a `scale::TypedScale` to convert it from one unit to
|
||||
/// another. See the [`TypedScale`] docs for an example.
|
||||
///
|
||||
/// [`TypedScale`]: struct.TypedScale.html
|
||||
#[repr(C)]
|
||||
pub struct Length<T, Unit>(pub T, #[doc(hidden)] pub PhantomData<Unit>);
|
||||
|
||||
impl<T: Clone, Unit> Clone for Length<T, Unit> {
|
||||
fn clone(&self) -> Self {
|
||||
Length(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Unit> Copy for Length<T, Unit> {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, Unit, T> Deserialize<'de> for Length<T, Unit>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Length(
|
||||
try!(Deserialize::deserialize(deserializer)),
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Unit> Serialize for Length<T, Unit>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Unit> Length<T, Unit> {
|
||||
pub fn new(x: T) -> Self {
|
||||
Length(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone> Length<T, Unit> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Clone, U> fmt::Debug for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + Clone, U> fmt::Display for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// length + length
|
||||
impl<U, T: Clone + Add<T, Output = T>> Add for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
fn add(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length += length
|
||||
impl<U, T: Clone + AddAssign<T>> AddAssign for Length<T, U> {
|
||||
fn add_assign(&mut self, other: Length<T, U>) {
|
||||
self.0 += other.get();
|
||||
}
|
||||
}
|
||||
|
||||
// length - length
|
||||
impl<U, T: Clone + Sub<T, Output = T>> Sub<Length<T, U>> for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
fn sub(self, other: Length<T, U>) -> <Self as Sub>::Output {
|
||||
Length::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length -= length
|
||||
impl<U, T: Clone + SubAssign<T>> SubAssign for Length<T, U> {
|
||||
fn sub_assign(&mut self, other: Length<T, U>) {
|
||||
self.0 -= other.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Saturating length + length and length - length.
|
||||
impl<U, T: Clone + Saturating> Saturating for Length<T, U> {
|
||||
fn saturating_add(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get().saturating_add(other.get()))
|
||||
}
|
||||
|
||||
fn saturating_sub(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get().saturating_sub(other.get()))
|
||||
}
|
||||
}
|
||||
|
||||
// length / length
|
||||
impl<Src, Dst, T: Clone + Div<T, Output = T>> Div<Length<T, Src>> for Length<T, Dst> {
|
||||
type Output = TypedScale<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn div(self, other: Length<T, Src>) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get() / other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length * scalar
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for Length<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
Length::new(self.get() * scale)
|
||||
}
|
||||
}
|
||||
|
||||
// length *= scalar
|
||||
impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
// length / scalar
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for Length<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
Length::new(self.get() / scale)
|
||||
}
|
||||
}
|
||||
|
||||
// length /= scalar
|
||||
impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
// length * scaleFactor
|
||||
impl<Src, Dst, T: Clone + Mul<T, Output = T>> Mul<TypedScale<T, Src, Dst>> for Length<T, Src> {
|
||||
type Output = Length<T, Dst>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, Src, Dst>) -> Length<T, Dst> {
|
||||
Length::new(self.get() * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length / scaleFactor
|
||||
impl<Src, Dst, T: Clone + Div<T, Output = T>> Div<TypedScale<T, Src, Dst>> for Length<T, Dst> {
|
||||
type Output = Length<T, Src>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, Src, Dst>) -> Length<T, Src> {
|
||||
Length::new(self.get() / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// -length
|
||||
impl<U, T: Clone + Neg<Output = T>> Neg for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
#[inline]
|
||||
fn neg(self) -> Length<T, U> {
|
||||
Length::new(-self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T0: NumCast + Clone> Length<T0, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> Length<T1, Unit> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
pub fn try_cast<T1: NumCast + Clone>(&self) -> Option<Length<T1, Unit>> {
|
||||
NumCast::from(self.get()).map(Length::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialEq> PartialEq for Length<T, Unit> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.get().eq(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialOrd> PartialOrd for Length<T, Unit> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.get().partial_cmp(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + Eq> Eq for Length<T, Unit> {}
|
||||
|
||||
impl<Unit, T: Clone + Ord> Ord for Length<T, Unit> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.get().cmp(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Zero> Zero for Length<T, Unit> {
|
||||
fn zero() -> Self {
|
||||
Length::new(Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Length<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this length and another length.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
Length::new(one_t * self.get() + t * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Length;
|
||||
use num::Zero;
|
||||
|
||||
use num_traits::Saturating;
|
||||
use scale::TypedScale;
|
||||
use core::f32::INFINITY;
|
||||
|
||||
enum Inch {}
|
||||
enum Mm {}
|
||||
enum Cm {}
|
||||
enum Second {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde {
|
||||
use super::*;
|
||||
|
||||
extern crate serde_test;
|
||||
use self::serde_test::Token;
|
||||
use self::serde_test::assert_tokens;
|
||||
|
||||
#[test]
|
||||
fn test_length_serde() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
assert_tokens(&one_cm, &[Token::F32(10.0)]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
// A cloned Length is a separate length with the state matching the
|
||||
// original Length at the point it was cloned.
|
||||
let mut variable_length: Length<f32, Inch> = Length::new(12.0);
|
||||
|
||||
let one_foot = variable_length.clone();
|
||||
variable_length.0 = 24.0;
|
||||
|
||||
assert_eq!(one_foot.get(), 12.0);
|
||||
assert_eq!(variable_length.get(), 24.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_clones_length_value() {
|
||||
// Calling get returns a clone of the Length's value.
|
||||
// To test this, we need something clone-able - hence a vector.
|
||||
let mut length: Length<Vec<i32>, Inch> = Length::new(vec![1, 2, 3]);
|
||||
|
||||
let value = length.get();
|
||||
length.0.push(4);
|
||||
|
||||
assert_eq!(value, vec![1, 2, 3]);
|
||||
assert_eq!(length.get(), vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 + length2;
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement += one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), 15.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 - length2;
|
||||
|
||||
assert_eq!(result.get(), 245);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement -= one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), -5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(6);
|
||||
|
||||
let result = length1.saturating_add(length2);
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(5);
|
||||
let length2: Length<u8, Mm> = Length::new(10);
|
||||
|
||||
let result = length1.saturating_sub(length2);
|
||||
|
||||
assert_eq!(result.get(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_length() {
|
||||
// Division results in a TypedScale from denominator units
|
||||
// to numerator units.
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let duration: Length<f32, Second> = Length::new(10.0);
|
||||
|
||||
let result = length / duration;
|
||||
|
||||
let expected: TypedScale<f32, Second, Cm> = TypedScale::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let length_mm: Length<f32, Mm> = Length::new(10.0);
|
||||
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
|
||||
|
||||
let result = length_mm * cm_per_mm;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(1.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication_with_scalar() {
|
||||
let length_mm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = length_mm * 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(20.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication_assignment() {
|
||||
let mut length: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
length *= 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(20.0);
|
||||
assert_eq!(length, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalefactor() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let cm_per_second: TypedScale<f32, Second, Cm> = TypedScale::new(10.0);
|
||||
|
||||
let result = length / cm_per_second;
|
||||
|
||||
let expected: Length<f32, Second> = Length::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalar() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = length / 2.0;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(2.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_assignment() {
|
||||
let mut length: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
length /= 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(5.0);
|
||||
assert_eq!(length, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = -length;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(-5.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
let length_as_i32: Length<i32, Cm> = Length::new(5);
|
||||
|
||||
let result: Length<f32, Cm> = length_as_i32.cast();
|
||||
|
||||
let length_as_f32: Length<f32, Cm> = Length::new(5.0);
|
||||
assert_eq!(result, length_as_f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 == length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_0 != length_5_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 < length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_1 > length_5_point_0);
|
||||
assert!(length_5_point_1 >= length_5_point_0);
|
||||
assert!(length_5_point_0 >= length_5_point_1 - length_0_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_add() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
|
||||
let result = length - LengthCm::zero();
|
||||
|
||||
assert_eq!(result, length);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_division() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
let length_zero: LengthCm = Length::zero();
|
||||
|
||||
let result = length / length_zero;
|
||||
|
||||
let expected: TypedScale<f32, Cm, Cm> = TypedScale::new(INFINITY);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "unstable", feature(fn_must_use))]
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
//! A collection of strongly typed math tools for computer graphics with an inclination
|
||||
//! towards 2d graphics and layout.
|
||||
//!
|
||||
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
|
||||
//! and tagged with a generic Unit parameter which is useful to prevent mixing
|
||||
//! values from different spaces. For example it should not be legal to translate
|
||||
//! a screen-space position by a world-space vector and this can be expressed using
|
||||
//! the generic Unit parameter.
|
||||
//!
|
||||
//! This unit system is not mandatory and all Typed* structures have an alias
|
||||
//! with the default unit: `UnknownUnit`.
|
||||
//! for example ```Point2D<T>``` is equivalent to ```TypedPoint2D<T, UnknownUnit>```.
|
||||
//! Client code typically creates a set of aliases for each type and doesn't need
|
||||
//! to deal with the specifics of typed units further. For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use euclid::*;
|
||||
//! pub struct ScreenSpace;
|
||||
//! pub type ScreenPoint = TypedPoint2D<f32, ScreenSpace>;
|
||||
//! pub type ScreenSize = TypedSize2D<f32, ScreenSpace>;
|
||||
//! pub struct WorldSpace;
|
||||
//! pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
|
||||
//! pub type ProjectionMatrix = TypedTransform3D<f32, WorldSpace, ScreenSpace>;
|
||||
//! // etc...
|
||||
//! ```
|
||||
//!
|
||||
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
|
||||
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
|
||||
//!
|
||||
//! Components are accessed in their scalar form by default for convenience, and most
|
||||
//! types additionally implement strongly typed accessors which return typed ```Length``` wrappers.
|
||||
//! For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use euclid::*;
|
||||
//! # pub struct WorldSpace;
|
||||
//! # pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
|
||||
//! let p = WorldPoint::new(0.0, 1.0, 1.0);
|
||||
//! // p.x is an f32.
|
||||
//! println!("p.x = {:?} ", p.x);
|
||||
//! // p.x is a Length<f32, WorldSpace>.
|
||||
//! println!("p.x_typed() = {:?} ", p.x_typed());
|
||||
//! // Length::get returns the scalar value (f32).
|
||||
//! assert_eq!(p.x, p.x_typed().get());
|
||||
//! ```
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
extern crate num_traits;
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
|
||||
#[cfg(test)]
|
||||
use std as core;
|
||||
|
||||
pub use length::Length;
|
||||
pub use scale::TypedScale;
|
||||
pub use transform2d::{Transform2D, TypedTransform2D};
|
||||
pub use transform3d::{Transform3D, TypedTransform3D};
|
||||
pub use point::{Point2D, Point3D, TypedPoint2D, TypedPoint3D, point2, point3};
|
||||
pub use vector::{TypedVector2D, TypedVector3D, Vector2D, Vector3D, vec2, vec3};
|
||||
pub use vector::{BoolVector2D, BoolVector3D, bvec2, bvec3};
|
||||
pub use homogen::HomogeneousVector;
|
||||
|
||||
pub use rect::{rect, Rect, TypedRect};
|
||||
pub use rotation::{Angle, Rotation2D, Rotation3D, TypedRotation2D, TypedRotation3D};
|
||||
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
|
||||
pub use size::{Size2D, TypedSize2D, size2};
|
||||
pub use trig::Trig;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod approxeq;
|
||||
mod homogen;
|
||||
pub mod num;
|
||||
mod length;
|
||||
mod point;
|
||||
mod rect;
|
||||
mod rotation;
|
||||
mod scale;
|
||||
mod side_offsets;
|
||||
mod size;
|
||||
mod transform2d;
|
||||
mod transform3d;
|
||||
mod trig;
|
||||
mod vector;
|
||||
|
||||
/// The default unit.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UnknownUnit;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type Matrix2D<T> = Transform2D<T>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type TypedMatrix2D<T, Src, Dst> = TypedTransform2D<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type Matrix4D<T> = Transform3D<T>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type TypedMatrix4D<T, Src, Dst> = TypedTransform3D<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type ScaleFactor<T, Src, Dst> = TypedScale<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub use Angle as Radians;
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
macro_rules! define_matrix {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $name:ident<T, $($phantom:ident),+> {
|
||||
$(pub $field:ident: T,)+
|
||||
}
|
||||
) => (
|
||||
#[repr(C)]
|
||||
$(#[$attr])*
|
||||
pub struct $name<T, $($phantom),+> {
|
||||
$(pub $field: T,)+
|
||||
|
||||
// Keep this (secretly) public for the few cases where we would like to
|
||||
// create static constants which currently can't be initialized with a
|
||||
// function.
|
||||
#[doc(hidden)]
|
||||
pub _unit: PhantomData<($($phantom),+)>
|
||||
}
|
||||
|
||||
impl<T: Clone, $($phantom),+> Clone for $name<T, $($phantom),+> {
|
||||
fn clone(&self) -> Self {
|
||||
$name {
|
||||
$($field: self.$field.clone(),)+
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, $($phantom),+> Copy for $name<T, $($phantom),+> {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, $($phantom),+> ::serde::Deserialize<'de> for $name<T, $($phantom),+>
|
||||
where T: ::serde::Deserialize<'de>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: ::serde::Deserializer<'de>
|
||||
{
|
||||
let ($($field,)+) =
|
||||
try!(::serde::Deserialize::deserialize(deserializer));
|
||||
Ok($name {
|
||||
$($field: $field,)+
|
||||
_unit: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, $($phantom),+> ::serde::Serialize for $name<T, $($phantom),+>
|
||||
where T: ::serde::Serialize
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ::serde::Serializer
|
||||
{
|
||||
($(&self.$field,)+).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::core::cmp::Eq for $name<T, $($phantom),+>
|
||||
where T: ::core::cmp::Eq {}
|
||||
|
||||
impl<T, $($phantom),+> ::core::cmp::PartialEq for $name<T, $($phantom),+>
|
||||
where T: ::core::cmp::PartialEq
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
true $(&& self.$field == other.$field)+
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::core::hash::Hash for $name<T, $($phantom),+>
|
||||
where T: ::core::hash::Hash
|
||||
{
|
||||
fn hash<H: ::core::hash::Hasher>(&self, h: &mut H) {
|
||||
$(self.$field.hash(h);)+
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A one-dimensional length, tagged with its units.
|
||||
|
||||
use num_traits;
|
||||
|
||||
pub trait Zero {
|
||||
fn zero() -> Self;
|
||||
}
|
||||
|
||||
impl<T: num_traits::Zero> Zero for T {
|
||||
fn zero() -> T {
|
||||
num_traits::Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait One {
|
||||
fn one() -> Self;
|
||||
}
|
||||
|
||||
impl<T: num_traits::One> One for T {
|
||||
fn one() -> T {
|
||||
num_traits::One::one()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Round: Copy {
|
||||
fn round(self) -> Self;
|
||||
}
|
||||
pub trait Floor: Copy {
|
||||
fn floor(self) -> Self;
|
||||
}
|
||||
pub trait Ceil: Copy {
|
||||
fn ceil(self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! num_int {
|
||||
($ty:ty) => (
|
||||
impl Round for $ty {
|
||||
#[inline]
|
||||
fn round(self) -> $ty { self }
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty { self }
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty { self }
|
||||
}
|
||||
)
|
||||
}
|
||||
macro_rules! num_float {
|
||||
($ty:ty) => (
|
||||
impl Round for $ty {
|
||||
#[inline]
|
||||
fn round(self) -> $ty { self.round() }
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty { self.floor() }
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty { self.ceil() }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
num_int!(i16);
|
||||
num_int!(u16);
|
||||
num_int!(i32);
|
||||
num_int!(u32);
|
||||
num_int!(i64);
|
||||
num_int!(u64);
|
||||
num_int!(isize);
|
||||
num_int!(usize);
|
||||
num_float!(f32);
|
||||
num_float!(f64);
|
|
@ -0,0 +1,918 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use approxeq::ApproxEq;
|
||||
use length::Length;
|
||||
use scale::TypedScale;
|
||||
use size::TypedSize2D;
|
||||
use num::*;
|
||||
use num_traits::{Float, NumCast};
|
||||
use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d Point tagged with a unit.
|
||||
pub struct TypedPoint2D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d point type with no unit.
|
||||
///
|
||||
/// `Point2D` provides the same methods as `TypedPoint2D`.
|
||||
pub type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint2D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn origin() -> Self {
|
||||
point2(Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Self::origin()
|
||||
}
|
||||
|
||||
/// Convert into a 3d point.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> TypedPoint3D<T, U> {
|
||||
point3(self.x, self.y, Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint2D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
TypedPoint2D {
|
||||
x,
|
||||
y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint2D<T, U> {
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>) -> Self {
|
||||
point2(x.0, y.0)
|
||||
}
|
||||
|
||||
/// Create a 3d point from this one, using the specified z value.
|
||||
#[inline]
|
||||
pub fn extend(&self, z: T) -> TypedPoint3D<T, U> {
|
||||
point3(self.x, self.y, z)
|
||||
}
|
||||
|
||||
/// Cast this point into a vector.
|
||||
///
|
||||
/// Equivalent to subtracting the origin from this point.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Swap x and y.
|
||||
#[inline]
|
||||
pub fn yx(&self) -> Self {
|
||||
point2(self.y, self.x)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.x)
|
||||
}
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.y)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point2D<T> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point2D<T>) -> Self {
|
||||
point2(p.x, p.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
pub fn add_size(&self, other: &TypedSize2D<T, U>) -> Self {
|
||||
point2(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add<TypedSize2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: TypedSize2D<T, U>) -> Self {
|
||||
point2(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> AddAssign<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: TypedVector2D<T, U>) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> SubAssign<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: TypedVector2D<T, U>) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: TypedVector2D<T, U>) -> Self {
|
||||
point2(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedPoint2D<T, U> {
|
||||
type Output = TypedVector2D<T, U>;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: TypedVector2D<T, U>) -> Self {
|
||||
point2(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
point2(self.x.min(other.x), self.y.min(other.y))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
point2(self.x.max(other.x), self.y.max(other.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
point2(self.x * scale, self.y * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
point2(self.x / scale, self.y / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedPoint2D<T, U1> {
|
||||
type Output = TypedPoint2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedPoint2D<T, U2> {
|
||||
point2(self.x * scale.get(), self.y * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedPoint2D<T, U2> {
|
||||
type Output = TypedPoint2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, U1, U2>) -> TypedPoint2D<T, U1> {
|
||||
point2(self.x / scale.get(), self.y / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.round() == { 0.0, -1.0 }`.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round(&self) -> Self {
|
||||
point2(self.x.round(), self.y.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.ceil() == { 0.0, 0.0 }`.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn ceil(&self) -> Self {
|
||||
point2(self.x.ceil(), self.y.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.floor() == { -1.0, -1.0 }`.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn floor(&self) -> Self {
|
||||
point2(self.x.floor(), self.y.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint2D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> TypedPoint2D<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn try_cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint2D<NewT, U>> {
|
||||
match (NumCast::from(self.x), NumCast::from(self.y)) {
|
||||
(Some(x), Some(y)) => Some(point2(x, y)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedPoint2D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` point.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> TypedPoint2D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> TypedPoint2D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> TypedPoint2D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an i32 point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> TypedPoint2D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an i64 point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> TypedPoint2D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint2D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this point and another point.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
point2(one_t * self.x + t * other.x, one_t * self.y + t * other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + ApproxEq<T>, U> ApproxEq<TypedPoint2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
point2(T::approx_epsilon(), T::approx_epsilon())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Into<[T; 2]> for TypedPoint2D<T, U> {
|
||||
fn into(self) -> [T; 2] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 2]> for TypedPoint2D<T, U> {
|
||||
fn from(array: [T; 2]) -> Self {
|
||||
point2(array[0], array[1])
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d Point tagged with a unit.
|
||||
pub struct TypedPoint3D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 3d point type with no unit.
|
||||
///
|
||||
/// `Point3D` provides the same methods as `TypedPoint3D`.
|
||||
pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint3D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn origin() -> Self {
|
||||
point3(Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + One, U> TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
pub fn to_array_4d(&self) -> [T; 4] {
|
||||
[self.x, self.y, self.z, One::one()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint3D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this point and another point.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
point3(
|
||||
one_t * self.x + t * other.x,
|
||||
one_t * self.y + t * other.y,
|
||||
one_t * self.z + t * other.z,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint3D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T) -> Self {
|
||||
TypedPoint3D {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>, z: Length<T, U>) -> Self {
|
||||
point3(x.0, y.0, z.0)
|
||||
}
|
||||
|
||||
/// Cast this point into a vector.
|
||||
///
|
||||
/// Equivalent to subtracting the origin to this point.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector3D<T, U> {
|
||||
vec3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and y coordinates
|
||||
#[inline]
|
||||
pub fn xy(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and z coordinates
|
||||
#[inline]
|
||||
pub fn xz(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.x, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and z coordinates
|
||||
#[inline]
|
||||
pub fn yz(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.y, self.z)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.x)
|
||||
}
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.y)
|
||||
}
|
||||
|
||||
/// Returns self.z as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn z_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] {
|
||||
[self.x, self.y, self.z]
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point3D<T> {
|
||||
point3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point3D<T>) -> Self {
|
||||
point3(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
/// Convert into a 2d point.
|
||||
#[inline]
|
||||
pub fn to_2d(&self) -> TypedPoint2D<T, U> {
|
||||
self.xy()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> AddAssign<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: TypedVector3D<T, U>) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> SubAssign<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: TypedVector3D<T, U>) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: TypedVector3D<T, U>) -> Self {
|
||||
point3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedPoint3D<T, U> {
|
||||
type Output = TypedVector3D<T, U>;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> TypedVector3D<T, U> {
|
||||
vec3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: TypedVector3D<T, U>) -> Self {
|
||||
point3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
point3(self.x * scale, self.y * scale, self.z * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
point3(self.x / scale, self.y / scale, self.z / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
point3(
|
||||
self.x.min(other.x),
|
||||
self.y.min(other.y),
|
||||
self.z.min(other.z),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
point3(
|
||||
self.x.max(other.x),
|
||||
self.y.max(other.y),
|
||||
self.z.max(other.z),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round(&self) -> Self {
|
||||
point3(self.x.round(), self.y.round(), self.z.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn ceil(&self) -> Self {
|
||||
point3(self.x.ceil(), self.y.ceil(), self.z.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn floor(&self) -> Self {
|
||||
point3(self.x.floor(), self.y.floor(), self.z.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint3D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> TypedPoint3D<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
#[inline]
|
||||
pub fn try_cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint3D<NewT, U>> {
|
||||
match (
|
||||
NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
NumCast::from(self.z),
|
||||
) {
|
||||
(Some(x), Some(y), Some(z)) => Some(point3(x, y, z)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedPoint3D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` point.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> TypedPoint3D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> TypedPoint3D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> TypedPoint3D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> TypedPoint3D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> TypedPoint3D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + ApproxEq<T>, U> ApproxEq<TypedPoint3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
point3(
|
||||
T::approx_epsilon(),
|
||||
T::approx_epsilon(),
|
||||
T::approx_epsilon(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y) && self.z.approx_eq(&other.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
&& self.z.approx_eq_eps(&other.z, &eps.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Into<[T; 3]> for TypedPoint3D<T, U> {
|
||||
fn into(self) -> [T; 3] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 3]> for TypedPoint3D<T, U> {
|
||||
fn from(array: [T; 3]) -> Self {
|
||||
point3(array[0], array[1], array[2])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point2<T: Copy, U>(x: T, y: T) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(x, y)
|
||||
}
|
||||
|
||||
pub fn point3<T: Copy, U>(x: T, y: T, z: T) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(x, y, z)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point2d {
|
||||
use super::Point2D;
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1: Point2D<f32> = Point2D::new(3.0, 5.0);
|
||||
|
||||
let result = p1 * 5.0;
|
||||
|
||||
assert_eq!(result, Point2D::new(15.0, 25.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point2D::new(1.0, 3.0);
|
||||
let p2 = Point2D::new(2.0, 2.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point2D::new(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point2D::new(1.0, 3.0);
|
||||
let p2 = Point2D::new(2.0, 2.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point2D::new(2.0, 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod typedpoint2d {
|
||||
use super::{Point2D, TypedPoint2D, point2};
|
||||
use scale::TypedScale;
|
||||
use vector::vec2;
|
||||
|
||||
pub enum Mm {}
|
||||
pub enum Cm {}
|
||||
|
||||
pub type Point2DMm<T> = TypedPoint2D<T, Mm>;
|
||||
pub type Point2DCm<T> = TypedPoint2D<T, Cm>;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Point2DMm::new(1.0, 2.0);
|
||||
let p2 = vec2(3.0, 4.0);
|
||||
|
||||
let result = p1 + p2;
|
||||
|
||||
assert_eq!(result, Point2DMm::new(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_assign() {
|
||||
let mut p1 = Point2DMm::new(1.0, 2.0);
|
||||
p1 += vec2(3.0, 4.0);
|
||||
|
||||
assert_eq!(p1, Point2DMm::new(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1 = Point2DMm::new(1.0, 2.0);
|
||||
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
|
||||
|
||||
let result = p1 * cm_per_mm;
|
||||
|
||||
assert_eq!(result, Point2DCm::new(0.1, 0.2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_conv_vector() {
|
||||
use {Point2D, point2};
|
||||
|
||||
for i in 0..100 {
|
||||
// We don't care about these values as long as they are not the same.
|
||||
let x = i as f32 * 0.012345;
|
||||
let y = i as f32 * 0.987654;
|
||||
let p: Point2D<f32> = point2(x, y);
|
||||
assert_eq!(p.to_vector().to_point(), p);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_swizzling() {
|
||||
let p: Point2D<i32> = point2(1, 2);
|
||||
assert_eq!(p.yx(), point2(2, 1));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point3d {
|
||||
use super::{Point3D, point2, point3};
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p2 = Point3D::new(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point3D::new(1.0, 2.0, -1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p2 = Point3D::new(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point3D::new(2.0, 3.0, 5.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_conv_vector() {
|
||||
use point3;
|
||||
for i in 0..100 {
|
||||
// We don't care about these values as long as they are not the same.
|
||||
let x = i as f32 * 0.012345;
|
||||
let y = i as f32 * 0.987654;
|
||||
let z = x * y;
|
||||
let p: Point3D<f32> = point3(x, y, z);
|
||||
assert_eq!(p.to_vector().to_point(), p);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_swizzling() {
|
||||
let p: Point3D<i32> = point3(1, 2, 3);
|
||||
assert_eq!(p.xy(), point2(1, 2));
|
||||
assert_eq!(p.xz(), point2(1, 3));
|
||||
assert_eq!(p.yz(), point2(2, 3));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,838 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale::TypedScale;
|
||||
use num::*;
|
||||
use point::TypedPoint2D;
|
||||
use vector::TypedVector2D;
|
||||
use side_offsets::TypedSideOffsets2D;
|
||||
use size::TypedSize2D;
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::PartialOrd;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Add, Div, Mul, Sub};
|
||||
|
||||
|
||||
/// A 2d Rectangle optionally tagged with a unit.
|
||||
#[repr(C)]
|
||||
pub struct TypedRect<T, U = UnknownUnit> {
|
||||
pub origin: TypedPoint2D<T, U>,
|
||||
pub size: TypedSize2D<T, U>,
|
||||
}
|
||||
|
||||
/// The default rectangle type with no unit.
|
||||
pub type Rect<T> = TypedRect<T, UnknownUnit>;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T: Copy + Deserialize<'de>, U> Deserialize<'de> for TypedRect<T, U> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let (origin, size) = try!(Deserialize::deserialize(deserializer));
|
||||
Ok(TypedRect::new(origin, size))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: Serialize, U> Serialize for TypedRect<T, U> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
(&self.origin, &self.size).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for TypedRect<T, U> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.origin.hash(h);
|
||||
self.size.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for TypedRect<T, U> {}
|
||||
|
||||
impl<T: Copy, U> Clone for TypedRect<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq<TypedRect<T, U>> for TypedRect<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.origin.eq(&other.origin) && self.size.eq(&other.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for TypedRect<T, U> {}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedRect<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TypedRect({:?} at {:?})", self.size, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedRect<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "Rect({} at {})", self.size, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
/// Constructor.
|
||||
pub fn new(origin: TypedPoint2D<T, U>, size: TypedSize2D<T, U>) -> Self {
|
||||
TypedRect {
|
||||
origin,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + Zero
|
||||
{
|
||||
/// Creates a rect of the given size, at offset zero.
|
||||
pub fn from_size(size: TypedSize2D<T, U>) -> Self {
|
||||
TypedRect {
|
||||
origin: TypedPoint2D::zero(),
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
self.origin.x < other.origin.x + other.size.width
|
||||
&& other.origin.x < self.origin.x + self.size.width
|
||||
&& self.origin.y < other.origin.y + other.size.height
|
||||
&& other.origin.y < self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_x(&self) -> T {
|
||||
self.origin.x + self.size.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_x(&self) -> T {
|
||||
self.origin.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_y(&self) -> T {
|
||||
self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_y(&self) -> T {
|
||||
self.origin.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.max_x())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.min_x())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.min_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
if !self.intersects(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let upper_left = TypedPoint2D::new(
|
||||
max(self.min_x(), other.min_x()),
|
||||
max(self.min_y(), other.min_y()),
|
||||
);
|
||||
let lower_right_x = min(self.max_x(), other.max_x());
|
||||
let lower_right_y = min(self.max_y(), other.max_y());
|
||||
|
||||
Some(TypedRect::new(
|
||||
upper_left,
|
||||
TypedSize2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y),
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the same rectangle, translated by a vector.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn translate(&self, by: &TypedVector2D<T, U>) -> Self {
|
||||
Self::new(self.origin + *by, self.size)
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the point. Points are considered
|
||||
/// in the rectangle if they are on the left or top edge, but outside if they
|
||||
/// are on the right or bottom edge.
|
||||
#[inline]
|
||||
pub fn contains(&self, other: &TypedPoint2D<T, U>) -> bool {
|
||||
self.origin.x <= other.x && other.x < self.origin.x + self.size.width
|
||||
&& self.origin.y <= other.y && other.y < self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the interior of rect. Always
|
||||
/// returns true if rect is empty, and always returns false if rect is
|
||||
/// nonempty but this rectangle is empty.
|
||||
#[inline]
|
||||
pub fn contains_rect(&self, rect: &Self) -> bool {
|
||||
rect.is_empty()
|
||||
|| (self.min_x() <= rect.min_x() && rect.max_x() <= self.max_x()
|
||||
&& self.min_y() <= rect.min_y() && rect.max_y() <= self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn inflate(&self, width: T, height: T) -> Self {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x - width, self.origin.y - height),
|
||||
TypedSize2D::new(
|
||||
self.size.width + width + width,
|
||||
self.size.height + height + height,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn inflate_typed(&self, width: Length<T, U>, height: Length<T, U>) -> Self {
|
||||
self.inflate(width.get(), height.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn top_right(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.max_x(), self.origin.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bottom_left(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.origin.x, self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bottom_right(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.max_x(), self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn translate_by_size(&self, size: &TypedSize2D<T, U>) -> Self {
|
||||
self.translate(&size.to_vector())
|
||||
}
|
||||
|
||||
/// Calculate the size and position of an inner rectangle.
|
||||
///
|
||||
/// Subtracts the side offsets from all sides. The horizontal and vertical
|
||||
/// offsets must not be larger than the original side length.
|
||||
pub fn inner_rect(&self, offsets: TypedSideOffsets2D<T, U>) -> Self {
|
||||
let rect = TypedRect::new(
|
||||
TypedPoint2D::new(
|
||||
self.origin.x + offsets.left,
|
||||
self.origin.y + offsets.top
|
||||
),
|
||||
TypedSize2D::new(
|
||||
self.size.width - offsets.horizontal(),
|
||||
self.size.height - offsets.vertical()
|
||||
)
|
||||
);
|
||||
debug_assert!(rect.size.width >= Zero::zero());
|
||||
debug_assert!(rect.size.height >= Zero::zero());
|
||||
rect
|
||||
}
|
||||
|
||||
/// Calculate the size and position of an outer rectangle.
|
||||
///
|
||||
/// Add the offsets to all sides. The expanded rectangle is returned.
|
||||
pub fn outer_rect(&self, offsets: TypedSideOffsets2D<T, U>) -> Self {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(
|
||||
self.origin.x - offsets.left,
|
||||
self.origin.y - offsets.top
|
||||
),
|
||||
TypedSize2D::new(
|
||||
self.size.width + offsets.horizontal(),
|
||||
self.size.height + offsets.vertical()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the smallest rectangle defined by the top/bottom/left/right-most
|
||||
/// points provided as parameter.
|
||||
///
|
||||
/// Note: This function has a behavior that can be surprising because
|
||||
/// the right-most and bottom-most points are exactly on the edge
|
||||
/// of the rectangle while the `contains` function is has exclusive
|
||||
/// semantic on these edges. This means that the right-most and bottom-most
|
||||
/// points provided to `from_points` will count as not contained by the rect.
|
||||
/// This behavior may change in the future.
|
||||
pub fn from_points<I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<TypedPoint2D<T, U>>,
|
||||
{
|
||||
let mut points = points.into_iter();
|
||||
|
||||
let (mut min_x, mut min_y) = match points.next() {
|
||||
Some(first) => (first.borrow().x, first.borrow().y),
|
||||
None => return TypedRect::zero(),
|
||||
};
|
||||
|
||||
let (mut max_x, mut max_y) = (min_x, min_y);
|
||||
for point in points {
|
||||
let p = point.borrow();
|
||||
if p.x < min_x {
|
||||
min_x = p.x
|
||||
}
|
||||
if p.x > max_x {
|
||||
max_x = p.x
|
||||
}
|
||||
if p.y < min_y {
|
||||
min_y = p.y
|
||||
}
|
||||
if p.y > max_y {
|
||||
max_y = p.y
|
||||
}
|
||||
}
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(min_x, min_y),
|
||||
TypedSize2D::new(max_x - min_x, max_y - min_y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this rectangle and another rectangle.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
Self::new(
|
||||
self.origin.lerp(other.origin, t),
|
||||
self.size.lerp(other.size, t),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + Clone + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
|
||||
{
|
||||
#[inline]
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
if self.size == Zero::zero() {
|
||||
return *other;
|
||||
}
|
||||
if other.size == Zero::zero() {
|
||||
return *self;
|
||||
}
|
||||
|
||||
let upper_left = TypedPoint2D::new(
|
||||
min(self.min_x(), other.min_x()),
|
||||
min(self.min_y(), other.min_y()),
|
||||
);
|
||||
|
||||
let lower_right_x = max(self.max_x(), other.max_x());
|
||||
let lower_right_y = max(self.max_y(), other.max_y());
|
||||
|
||||
TypedRect::new(
|
||||
upper_left,
|
||||
TypedSize2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
#[inline]
|
||||
pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
|
||||
where
|
||||
T: Copy + Clone + Mul<S, Output = T>,
|
||||
{
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x * x, self.origin.y * y),
|
||||
TypedSize2D::new(self.size.width * x, self.size.height * y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq + Zero, U> TypedRect<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
TypedRect::new(TypedPoint2D::origin(), TypedSize2D::zero())
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero, regardless of the origin's value.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.size.width == Zero::zero() || self.size.height == Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
||||
if x <= y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
||||
if x >= y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedRect<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedRect<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedRect<T, U1> {
|
||||
type Output = TypedRect<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedRect<T, U2> {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedRect<T, U2> {
|
||||
type Output = TypedRect<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, U1, U2>) -> TypedRect<T, U1> {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Unit> TypedRect<T, Unit> {
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Rect<T> {
|
||||
TypedRect::new(self.origin.to_untyped(), self.size.to_untyped())
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(r: &Rect<T>) -> TypedRect<T, Unit> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::from_untyped(&r.origin),
|
||||
TypedSize2D::from_untyped(&r.size),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Unit> TypedRect<T0, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> TypedRect<T1, Unit> {
|
||||
TypedRect::new(
|
||||
self.origin.cast(),
|
||||
self.size.cast(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<TypedRect<T1, Unit>> {
|
||||
match (self.origin.try_cast(), self.size.try_cast()) {
|
||||
(Some(origin), Some(size)) => Some(TypedRect::new(origin, size)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor + Ceil + Round + Add<T, Output = T> + Sub<T, Output = T>, U> TypedRect<T, U> {
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the returned rectangle has the same set of pixel centers as the original
|
||||
/// one.
|
||||
/// Edges at offset 0.5 round up.
|
||||
/// Suitable for most places where integral device coordinates
|
||||
/// are needed, but note that any translation should be applied first to
|
||||
/// avoid pixel rounding errors.
|
||||
/// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
/// They are always rounding as floor(n + 0.5).
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round(&self) -> Self {
|
||||
let origin = self.origin.round();
|
||||
let size = self.origin.add_size(&self.size).round() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle contains the resulting rectangle.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round_in(&self) -> Self {
|
||||
let origin = self.origin.ceil();
|
||||
let size = self.origin.add_size(&self.size).floor() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle is contained in the resulting rectangle.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round_out(&self) -> Self {
|
||||
let origin = self.origin.floor();
|
||||
let size = self.origin.add_size(&self.size).ceil() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
impl<T: NumCast + Copy, Unit> TypedRect<T, Unit> {
|
||||
/// Cast into an `f32` rectangle.
|
||||
pub fn to_f32(&self) -> TypedRect<f32, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` rectangle.
|
||||
pub fn to_f64(&self) -> TypedRect<f64, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedRect<usize, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_u32(&self) -> TypedRect<u32, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedRect<i32, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedRect<i64, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> From<TypedSize2D<T, U>> for TypedRect<T, U>
|
||||
where T: Copy + Zero
|
||||
{
|
||||
fn from(size: TypedSize2D<T, U>) -> Self {
|
||||
Self::from_size(size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))`.
|
||||
pub fn rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use point::Point2D;
|
||||
use vector::vec2;
|
||||
use side_offsets::SideOffsets2D;
|
||||
use size::Size2D;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_min_max() {
|
||||
assert!(min(0u32, 1u32) == 0u32);
|
||||
assert!(min(-1.0f32, 0.0f32) == -1.0f32);
|
||||
|
||||
assert!(max(0u32, 1u32) == 1u32);
|
||||
assert!(max(-1.0f32, 0.0f32) == 0.0f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate(&vec2(10, 15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
assert!(pp.origin.x == 10);
|
||||
assert!(pp.origin.y == 15);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate(&vec2(0, -10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate_by_size() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate_by_size(&Size2D::new(10, 15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
assert!(pp.origin.x == 10);
|
||||
assert!(pp.origin.y == 15);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate_by_size(&Size2D::new(0, -10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40));
|
||||
let q = Rect::new(Point2D::new(20, 20), Size2D::new(5, 5));
|
||||
let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15));
|
||||
let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200));
|
||||
|
||||
let pq = p.union(&q);
|
||||
assert!(pq.origin == Point2D::new(0, 0));
|
||||
assert!(pq.size == Size2D::new(50, 40));
|
||||
|
||||
let pr = p.union(&r);
|
||||
assert!(pr.origin == Point2D::new(-15, -30));
|
||||
assert!(pr.size == Size2D::new(200, 70));
|
||||
|
||||
let ps = p.union(&s);
|
||||
assert!(ps.origin == Point2D::new(0, -15));
|
||||
assert!(ps.size == Size2D::new(270, 200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10));
|
||||
let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8));
|
||||
|
||||
let pq = p.intersection(&q);
|
||||
assert!(pq.is_some());
|
||||
let pq = pq.unwrap();
|
||||
assert!(pq.origin == Point2D::new(5, 15));
|
||||
assert!(pq.size == Size2D::new(5, 5));
|
||||
|
||||
let pr = p.intersection(&r);
|
||||
assert!(pr.is_some());
|
||||
let pr = pr.unwrap();
|
||||
assert!(pr.origin == Point2D::new(0, 0));
|
||||
assert!(pr.size == Size2D::new(3, 3));
|
||||
|
||||
let qr = q.intersection(&r);
|
||||
assert!(qr.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
|
||||
|
||||
assert!(r.contains(&Point2D::new(0, 50)));
|
||||
assert!(r.contains(&Point2D::new(-10, 200)));
|
||||
|
||||
// The `contains` method is inclusive of the top/left edges, but not the
|
||||
// bottom/right edges.
|
||||
assert!(r.contains(&Point2D::new(-20, 15)));
|
||||
assert!(!r.contains(&Point2D::new(80, 15)));
|
||||
assert!(!r.contains(&Point2D::new(80, 215)));
|
||||
assert!(!r.contains(&Point2D::new(-20, 215)));
|
||||
|
||||
// Points beyond the top-left corner.
|
||||
assert!(!r.contains(&Point2D::new(-25, 15)));
|
||||
assert!(!r.contains(&Point2D::new(-15, 10)));
|
||||
|
||||
// Points beyond the top-right corner.
|
||||
assert!(!r.contains(&Point2D::new(85, 20)));
|
||||
assert!(!r.contains(&Point2D::new(75, 10)));
|
||||
|
||||
// Points beyond the bottom-right corner.
|
||||
assert!(!r.contains(&Point2D::new(85, 210)));
|
||||
assert!(!r.contains(&Point2D::new(75, 220)));
|
||||
|
||||
// Points beyond the bottom-left corner.
|
||||
assert!(!r.contains(&Point2D::new(-25, 210)));
|
||||
assert!(!r.contains(&Point2D::new(-15, 220)));
|
||||
|
||||
let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
|
||||
assert!(r.contains_rect(&r));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(-0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(0.0, 0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(0.0, -0.1))));
|
||||
// Empty rectangles are always considered as contained in other rectangles,
|
||||
// even if their origin is not.
|
||||
let p = Point2D::new(1.0, 1.0);
|
||||
assert!(!r.contains(&p));
|
||||
assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.scale(10, 15);
|
||||
|
||||
assert!(pp.size.width == 500);
|
||||
assert!(pp.size.height == 600);
|
||||
assert!(pp.origin.x == 0);
|
||||
assert!(pp.origin.y == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.scale(1, 20);
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 800);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inflate() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
|
||||
let pp = p.inflate(10, 20);
|
||||
|
||||
assert!(pp.size.width == 30);
|
||||
assert!(pp.size.height == 50);
|
||||
assert!(pp.origin.x == -10);
|
||||
assert!(pp.origin.y == -20);
|
||||
|
||||
let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let rr = r.inflate(-2, -5);
|
||||
|
||||
assert!(rr.size.width == 6);
|
||||
assert!(rr.size.height == 10);
|
||||
assert!(rr.origin.x == 2);
|
||||
assert!(rr.origin.y == 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inner_outer_rect() {
|
||||
let inner_rect: Rect<i32> = Rect::new(Point2D::new(20, 40), Size2D::new(80, 100));
|
||||
let offsets = SideOffsets2D::new(20, 10, 10, 10);
|
||||
let outer_rect = inner_rect.outer_rect(offsets);
|
||||
assert_eq!(outer_rect.origin.x, 10);
|
||||
assert_eq!(outer_rect.origin.y, 20);
|
||||
assert_eq!(outer_rect.size.width, 100);
|
||||
assert_eq!(outer_rect.size.height, 130);
|
||||
assert_eq!(outer_rect.inner_rect(offsets), inner_rect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max_x_y() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
assert!(p.max_y() == 40);
|
||||
assert!(p.min_y() == 0);
|
||||
assert!(p.max_x() == 50);
|
||||
assert!(p.min_x() == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
assert!(r.max_y() == 35);
|
||||
assert!(r.min_y() == -5);
|
||||
assert!(r.max_x() == 40);
|
||||
assert!(r.min_x() == -10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty());
|
||||
assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty());
|
||||
assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round() {
|
||||
let mut x = -2.0;
|
||||
let mut y = -2.0;
|
||||
let mut w = -2.0;
|
||||
let mut h = -2.0;
|
||||
while x < 2.0 {
|
||||
while y < 2.0 {
|
||||
while w < 2.0 {
|
||||
while h < 2.0 {
|
||||
let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h));
|
||||
|
||||
assert!(rect.contains_rect(&rect.round_in()));
|
||||
assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect));
|
||||
|
||||
assert!(rect.round_out().contains_rect(&rect));
|
||||
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out()));
|
||||
|
||||
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round()));
|
||||
assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect));
|
||||
|
||||
h += 0.1;
|
||||
}
|
||||
w += 0.1;
|
||||
}
|
||||
y += 0.1;
|
||||
}
|
||||
x += 0.1
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,235 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A type-checked scaling factor between units.
|
||||
|
||||
use num::One;
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use core::fmt;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::marker::PhantomData;
|
||||
use {TypedPoint2D, TypedRect, TypedSize2D, TypedVector2D};
|
||||
|
||||
/// A scaling factor between two different units of measurement.
|
||||
///
|
||||
/// This is effectively a type-safe float, intended to be used in combination with other types like
|
||||
/// `length::Length` to enforce conversion between systems of measurement at compile time.
|
||||
///
|
||||
/// `Src` and `Dst` represent the units before and after multiplying a value by a `TypedScale`. They
|
||||
/// may be types without values, such as empty enums. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::TypedScale;
|
||||
/// use euclid::Length;
|
||||
/// enum Mm {};
|
||||
/// enum Inch {};
|
||||
///
|
||||
/// let mm_per_inch: TypedScale<f32, Inch, Mm> = TypedScale::new(25.4);
|
||||
///
|
||||
/// let one_foot: Length<f32, Inch> = Length::new(12.0);
|
||||
/// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch;
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
pub struct TypedScale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, Src, Dst> Deserialize<'de> for TypedScale<T, Src, Dst>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<TypedScale<T, Src, Dst>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(TypedScale(
|
||||
try!(Deserialize::deserialize(deserializer)),
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Src, Dst> Serialize for TypedScale<T, Src, Dst>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedScale<T, Src, Dst> {
|
||||
pub fn new(x: T) -> Self {
|
||||
TypedScale(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> TypedScale<T, Src, Dst> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Src, Dst> TypedScale<f32, Src, Dst> {
|
||||
/// Identity scaling, could be used to safely transit from one space to another.
|
||||
pub const ONE: Self = TypedScale(1.0, PhantomData);
|
||||
}
|
||||
|
||||
impl<T: Clone + One + Div<T, Output = T>, Src, Dst> TypedScale<T, Src, Dst> {
|
||||
/// The inverse TypedScale (1.0 / self).
|
||||
pub fn inv(&self) -> TypedScale<T, Dst, Src> {
|
||||
let one: T = One::one();
|
||||
TypedScale::new(one / self.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 * scale1
|
||||
impl<T: Clone + Mul<T, Output = T>, A, B, C> Mul<TypedScale<T, B, C>> for TypedScale<T, A, B> {
|
||||
type Output = TypedScale<T, A, C>;
|
||||
#[inline]
|
||||
fn mul(self, other: TypedScale<T, B, C>) -> TypedScale<T, A, C> {
|
||||
TypedScale::new(self.get() * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 + scale1
|
||||
impl<T: Clone + Add<T, Output = T>, Src, Dst> Add for TypedScale<T, Src, Dst> {
|
||||
type Output = TypedScale<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn add(self, other: TypedScale<T, Src, Dst>) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 - scale1
|
||||
impl<T: Clone + Sub<T, Output = T>, Src, Dst> Sub for TypedScale<T, Src, Dst> {
|
||||
type Output = TypedScale<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn sub(self, other: TypedScale<T, Src, Dst>) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, Src, Dst0> TypedScale<T, Src, Dst0> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> TypedScale<T1, Src, Dst0> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
pub fn try_cast<T1: NumCast + Clone>(&self) -> Option<TypedScale<T1, Src, Dst0>> {
|
||||
NumCast::from(self.get()).map(TypedScale::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedScale<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Clone + Mul<T, Output = T> + Neg<Output = T> + PartialEq + One,
|
||||
{
|
||||
/// Returns the given point transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
TypedPoint2D::new(point.x * self.get(), point.y * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
TypedVector2D::new(vec.x * self.get(), vec.y * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_size(&self, size: &TypedSize2D<T, Src>) -> TypedSize2D<T, Dst> {
|
||||
TypedSize2D::new(size.width * self.get(), size.height * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given rect transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::new(
|
||||
self.transform_point(&rect.origin),
|
||||
self.transform_size(&rect.size),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the inverse of this scale.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> TypedScale<T, Dst, Src> {
|
||||
TypedScale::new(-self.get())
|
||||
}
|
||||
|
||||
/// Returns true if this scale has no effect.
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> bool {
|
||||
self.get() == T::one()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
|
||||
// https://github.com/mozilla/rust/issues/7671
|
||||
|
||||
impl<T: PartialEq, Src, Dst> PartialEq for TypedScale<T, Src, Dst> {
|
||||
fn eq(&self, other: &TypedScale<T, Src, Dst>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for TypedScale<T, Src, Dst> {
|
||||
fn clone(&self) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for TypedScale<T, Src, Dst> {}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for TypedScale<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for TypedScale<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::TypedScale;
|
||||
|
||||
enum Inch {}
|
||||
enum Cm {}
|
||||
enum Mm {}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let mm_per_inch: TypedScale<f32, Inch, Mm> = TypedScale::new(25.4);
|
||||
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
|
||||
|
||||
let mm_per_cm: TypedScale<f32, Cm, Mm> = cm_per_mm.inv();
|
||||
assert_eq!(mm_per_cm.get(), 10.0);
|
||||
|
||||
let cm_per_inch: TypedScale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
|
||||
assert_eq!(cm_per_inch, TypedScale::new(2.54));
|
||||
|
||||
let a: TypedScale<isize, Inch, Inch> = TypedScale::new(2);
|
||||
let b: TypedScale<isize, Inch, Inch> = TypedScale::new(3);
|
||||
assert!(a != b);
|
||||
assert_eq!(a, a.clone());
|
||||
assert_eq!(a.clone() + b.clone(), TypedScale::new(5));
|
||||
assert_eq!(a - b, TypedScale::new(-1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
|
||||
//! and margins in CSS.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use num::Zero;
|
||||
use core::fmt;
|
||||
use core::ops::Add;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
|
||||
/// and margins in CSS, optionally tagged with a unit.
|
||||
define_matrix! {
|
||||
pub struct TypedSideOffsets2D<T, U> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedSideOffsets2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"({:?},{:?},{:?},{:?})",
|
||||
self.top, self.right, self.bottom, self.left
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The default side offset type with no unit.
|
||||
pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor taking a scalar for each side.
|
||||
pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
|
||||
TypedSideOffsets2D {
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor taking a typed Length for each side.
|
||||
pub fn from_lengths(
|
||||
top: Length<T, U>,
|
||||
right: Length<T, U>,
|
||||
bottom: Length<T, U>,
|
||||
left: Length<T, U>,
|
||||
) -> Self {
|
||||
TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0)
|
||||
}
|
||||
|
||||
/// Access self.top as a typed Length instead of a scalar value.
|
||||
pub fn top_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.top)
|
||||
}
|
||||
|
||||
/// Access self.right as a typed Length instead of a scalar value.
|
||||
pub fn right_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.right)
|
||||
}
|
||||
|
||||
/// Access self.bottom as a typed Length instead of a scalar value.
|
||||
pub fn bottom_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.bottom)
|
||||
}
|
||||
|
||||
/// Access self.left as a typed Length instead of a scalar value.
|
||||
pub fn left_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.left)
|
||||
}
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a scalar value directly.
|
||||
pub fn new_all_same(all: T) -> Self {
|
||||
TypedSideOffsets2D::new(all, all, all, all)
|
||||
}
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a typed Length.
|
||||
pub fn from_length_all_same(all: Length<T, U>) -> Self {
|
||||
TypedSideOffsets2D::new_all_same(all.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSideOffsets2D<T, U>
|
||||
where
|
||||
T: Add<T, Output = T> + Copy,
|
||||
{
|
||||
pub fn horizontal(&self) -> T {
|
||||
self.left + self.right
|
||||
}
|
||||
|
||||
pub fn vertical(&self) -> T {
|
||||
self.top + self.bottom
|
||||
}
|
||||
|
||||
pub fn horizontal_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.horizontal())
|
||||
}
|
||||
|
||||
pub fn vertical_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.vertical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Add for TypedSideOffsets2D<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T>,
|
||||
{
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
TypedSideOffsets2D::new(
|
||||
self.top + other.top,
|
||||
self.right + other.right,
|
||||
self.bottom + other.bottom,
|
||||
self.left + other.left,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Zero, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
TypedSideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale::TypedScale;
|
||||
use vector::{TypedVector2D, vec2, BoolVector2D};
|
||||
use num::*;
|
||||
|
||||
use num_traits::{NumCast, Signed};
|
||||
use core::fmt;
|
||||
use core::ops::{Add, Div, Mul, Sub};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// A 2d size tagged with a unit.
|
||||
define_matrix! {
|
||||
pub struct TypedSize2D<T, U> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d size type with no unit.
|
||||
///
|
||||
/// `Size2D` provides the same methods as `TypedSize2D`.
|
||||
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}×{:?}", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({}x{})", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar values.
|
||||
pub fn new(width: T, height: T) -> Self {
|
||||
TypedSize2D {
|
||||
width,
|
||||
height,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar strongly typed lengths.
|
||||
pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> Self {
|
||||
TypedSize2D::new(width.get(), height.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn round(&self) -> Self {
|
||||
TypedSize2D::new(self.width.round(), self.height.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn ceil(&self) -> Self {
|
||||
TypedSize2D::new(self.width.ceil(), self.height.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn floor(&self) -> Self {
|
||||
TypedSize2D::new(self.width.floor(), self.height.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
TypedSize2D::new(self.width + other.width, self.height + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
TypedSize2D::new(self.width - other.width, self.height - other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Clone + Mul<T>, U> TypedSize2D<T, U> {
|
||||
pub fn area(&self) -> T::Output {
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this size and another size.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
size2(
|
||||
one_t * self.width + t * other.width,
|
||||
one_t * self.height + t * other.height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + PartialOrd, U> TypedSize2D<T, U> {
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
let zero = T::zero();
|
||||
self.width <= zero || self.height <= zero
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> TypedSize2D<T, U> {
|
||||
pub fn zero() -> Self {
|
||||
TypedSize2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> Zero for TypedSize2D<T, U> {
|
||||
fn zero() -> Self {
|
||||
TypedSize2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
TypedSize2D::new(self.width * scale, self.height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
TypedSize2D::new(self.width / scale, self.height / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedSize2D<T, U1> {
|
||||
type Output = TypedSize2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedSize2D<T, U2> {
|
||||
TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedSize2D<T, U2> {
|
||||
type Output = TypedSize2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, U1, U2>) -> TypedSize2D<T, U1> {
|
||||
TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedSize2D<T, U> {
|
||||
/// Returns self.width as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn width_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.width)
|
||||
}
|
||||
|
||||
/// Returns self.height as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn height_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.height)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.width, self.height]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Size2D<T> {
|
||||
TypedSize2D::new(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Size2D<T>) -> Self {
|
||||
TypedSize2D::new(p.width, p.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> TypedSize2D<NewT, Unit> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn try_cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
|
||||
match (NumCast::from(self.width), NumCast::from(self.height)) {
|
||||
(Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` size.
|
||||
pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` size.
|
||||
pub fn to_f64(&self) -> TypedSize2D<f64, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `uint` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedSize2D<usize, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_u32(&self) -> TypedSize2D<u32, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U>
|
||||
where
|
||||
T: Signed,
|
||||
{
|
||||
pub fn abs(&self) -> Self {
|
||||
size2(self.width.abs(), self.height.abs())
|
||||
}
|
||||
|
||||
pub fn is_positive(&self) -> bool {
|
||||
self.width.is_positive() && self.height.is_positive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, U> TypedSize2D<T, U> {
|
||||
pub fn greater_than(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width > other.width,
|
||||
y: self.height > other.height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_than(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width < other.width,
|
||||
y: self.height < other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: PartialEq, U> TypedSize2D<T, U> {
|
||||
pub fn equal(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width == other.width,
|
||||
y: self.height == other.height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_equal(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width != other.width,
|
||||
y: self.height != other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `TypedSize2D::new(w, h)`.
|
||||
pub fn size2<T, U>(w: T, h: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(w, h)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod size2d {
|
||||
use super::Size2D;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(3.0, 4.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(4.0, 6.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(1.0, 2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(-3.0, -4.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(-2.0, -2.0));
|
||||
|
||||
let p1 = Size2D::new(0.0, 0.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(3.0, 4.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(-2.0, -2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(1.0, 2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(-3.0, -4.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(4.0, 6.0));
|
||||
|
||||
let p1 = Size2D::new(0.0, 0.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_area() {
|
||||
let p = Size2D::new(1.5, 2.0);
|
||||
assert_eq!(p.area(), 3.0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,542 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(just_underscores_and_digits))]
|
||||
|
||||
use super::{UnknownUnit, Angle};
|
||||
use num::{One, Zero};
|
||||
use point::TypedPoint2D;
|
||||
use vector::{TypedVector2D, vec2};
|
||||
use rect::TypedRect;
|
||||
use transform3d::TypedTransform3D;
|
||||
use core::ops::{Add, Mul, Div, Sub, Neg};
|
||||
use core::marker::PhantomData;
|
||||
use approxeq::ApproxEq;
|
||||
use trig::Trig;
|
||||
use core::fmt;
|
||||
use num_traits::NumCast;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d transform stored as a 3 by 2 matrix in row-major order in memory.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
/// For example, `TypedTransform2D<f32, WorldSpace, ScreenSpace>::transform_point4d`
|
||||
/// takes a `TypedPoint2D<f32, WorldSpace>` and returns a `TypedPoint2D<f32, ScreenSpace>`.
|
||||
///
|
||||
/// Transforms expose a set of convenience methods for pre- and post-transformations.
|
||||
/// A pre-transformation corresponds to adding an operation that is applied before
|
||||
/// the rest of the transformation, while a post-transformation adds an operation
|
||||
/// that is applied after.
|
||||
pub struct TypedTransform2D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T,
|
||||
pub m21: T, pub m22: T,
|
||||
pub m31: T, pub m32: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 2d transform type with no units.
|
||||
pub type Transform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T: Copy, Src, Dst> TypedTransform2D<T, Src, Dst> {
|
||||
/// Create a transform specifying its matrix elements in row-major order.
|
||||
pub fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
|
||||
TypedTransform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its matrix elements in column-major order.
|
||||
pub fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> Self {
|
||||
TypedTransform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in row-major order (the order
|
||||
/// in which the transform is actually laid out in memory).
|
||||
pub fn to_row_major_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
pub fn to_column_major_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m21, self.m31,
|
||||
self.m12, self.m22, self.m32
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 3 rows in (in row-major order)
|
||||
/// as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_row_arrays(&self) -> [[T; 2]; 3] {
|
||||
[
|
||||
[self.m11, self.m12],
|
||||
[self.m21, self.m22],
|
||||
[self.m31, self.m32],
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 6 elements in row-major order.
|
||||
pub fn from_row_major_array(array: [T; 6]) -> Self {
|
||||
Self::row_major(
|
||||
array[0], array[1],
|
||||
array[2], array[3],
|
||||
array[4], array[5],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 3 rows of 2 elements (row-major order).
|
||||
pub fn from_row_arrays(array: [[T; 2]; 3]) -> Self {
|
||||
Self::row_major(
|
||||
array[0][0], array[0][1],
|
||||
array[1][0], array[1][1],
|
||||
array[2][0], array[2][1],
|
||||
)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Transform2D<T> {
|
||||
Transform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Transform2D<T>) -> Self {
|
||||
TypedTransform2D::row_major(
|
||||
p.m11, p.m12,
|
||||
p.m21, p.m22,
|
||||
p.m31, p.m32
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Src, Dst> TypedTransform2D<T0, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> TypedTransform2D<T1, Src, Dst> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<TypedTransform2D<T1, Src, Dst>> {
|
||||
match (NumCast::from(self.m11), NumCast::from(self.m12),
|
||||
NumCast::from(self.m21), NumCast::from(self.m22),
|
||||
NumCast::from(self.m31), NumCast::from(self.m32)) {
|
||||
(Some(m11), Some(m12),
|
||||
Some(m21), Some(m22),
|
||||
Some(m31), Some(m32)) => {
|
||||
Some(TypedTransform2D::row_major(
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32
|
||||
))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
pub fn identity() -> Self {
|
||||
let (_0, _1) = (Zero::zero(), One::one());
|
||||
TypedTransform2D::row_major(
|
||||
_1, _0,
|
||||
_0, _1,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
// Intentional not public, because it checks for exact equivalence
|
||||
// while most consumers will probably want some sort of approximate
|
||||
// equivalence to deal with floating-point errors.
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform2D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Trig +
|
||||
PartialOrd +
|
||||
One + Zero {
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_mul<NewDst>(&self, mat: &TypedTransform2D<T, Dst, NewDst>) -> TypedTransform2D<T, Src, NewDst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22,
|
||||
self.m21 * mat.m11 + self.m22 * mat.m21,
|
||||
self.m21 * mat.m12 + self.m22 * mat.m22,
|
||||
self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
|
||||
self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform2D<T, NewSrc, Src>) -> TypedTransform2D<T, NewSrc, Dst> {
|
||||
mat.post_mul(self)
|
||||
}
|
||||
|
||||
/// Returns a translation transform.
|
||||
pub fn create_translation(x: T, y: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform2D::row_major(
|
||||
_1, _0,
|
||||
_0, _1,
|
||||
x, y
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a translation after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_translate(&self, v: TypedVector2D<T, Dst>) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Applies a translation before self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_translate(&self, v: TypedVector2D<T, Src>) -> Self {
|
||||
self.pre_mul(&TypedTransform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Returns a scale transform.
|
||||
pub fn create_scale(x: T, y: T) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
TypedTransform2D::row_major(
|
||||
x, _0,
|
||||
_0, y,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a scale after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_scale(&self, x: T, y: T) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_scale(x, y))
|
||||
}
|
||||
|
||||
/// Applies a scale before self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_scale(&self, x: T, y: T) -> Self {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11 * x, self.m12,
|
||||
self.m21, self.m22 * y,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rotation transform.
|
||||
pub fn create_rotation(theta: Angle<T>) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
let cos = theta.get().cos();
|
||||
let sin = theta.get().sin();
|
||||
TypedTransform2D::row_major(
|
||||
cos, _0 - sin,
|
||||
sin, cos,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.pre_mul(&TypedTransform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Returns the given point transformed by this transform.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
TypedPoint2D::new(point.x * self.m11 + point.y * self.m21 + self.m31,
|
||||
point.x * self.m12 + point.y * self.m22 + self.m32)
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this matrix.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
vec2(vec.x * self.m11 + vec.y * self.m21,
|
||||
vec.x * self.m12 + vec.y * self.m22)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
|
||||
/// transform.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::from_points(&[
|
||||
self.transform_point(&rect.origin),
|
||||
self.transform_point(&rect.top_right()),
|
||||
self.transform_point(&rect.bottom_left()),
|
||||
self.transform_point(&rect.bottom_right()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Computes and returns the determinant of this transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m11 * self.m22 - self.m12 * self.m21
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn inverse(&self) -> Option<TypedTransform2D<T, Dst, Src>> {
|
||||
let det = self.determinant();
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
|
||||
if det == _0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inv_det = _1 / det;
|
||||
Some(TypedTransform2D::row_major(
|
||||
inv_det * self.m22,
|
||||
inv_det * (_0 - self.m12),
|
||||
inv_det * (_0 - self.m21),
|
||||
inv_det * self.m11,
|
||||
inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
|
||||
inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> TypedTransform2D<T, Src, NewDst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> TypedTransform2D<T, NewSrc, Dst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Neg<Output=T> +
|
||||
PartialOrd +
|
||||
Trig +
|
||||
One + Zero {
|
||||
/// Create a 3D transform from the current transform
|
||||
pub fn to_3d(&self) -> TypedTransform3D<T, Src, Dst> {
|
||||
TypedTransform3D::row_major_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> Default for TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + PartialEq + One + Zero
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApproxEq<T>, Src, Dst> TypedTransform2D<T, Src, Dst> {
|
||||
pub fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
|
||||
self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
|
||||
self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + fmt::Debug, Src, Dst> fmt::Debug for TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + fmt::Debug +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_identity() {
|
||||
write!(f, "[I]")
|
||||
} else {
|
||||
self.to_row_major_array().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use approxeq::ApproxEq;
|
||||
use point::Point2D;
|
||||
use Angle;
|
||||
|
||||
use core::f32::consts::FRAC_PI_2;
|
||||
|
||||
type Mat = Transform2D<f32>;
|
||||
|
||||
fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mat::create_translation(1.0, 2.0);
|
||||
let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
|
||||
let t3 = Mat::identity().post_translate(vec2(1.0, 2.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_mul(&t1), Mat::create_translation(2.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
|
||||
let r3 = Mat::identity().post_rotate(rad(FRAC_PI_2));
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r1, r3);
|
||||
|
||||
assert!(r1.transform_point(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
|
||||
|
||||
assert!(r1.post_mul(&r1).approx_eq(&Mat::create_rotation(rad(FRAC_PI_2*2.0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mat::create_scale(2.0, 3.0);
|
||||
let s2 = Mat::identity().pre_scale(2.0, 3.0);
|
||||
let s3 = Mat::identity().post_scale(2.0, 3.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
assert!(s1.transform_point(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_major() {
|
||||
assert_eq!(
|
||||
Mat::row_major(
|
||||
1.0, 2.0,
|
||||
3.0, 4.0,
|
||||
5.0, 6.0
|
||||
),
|
||||
Mat::column_major(
|
||||
1.0, 3.0, 5.0,
|
||||
2.0, 4.0, 6.0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_simple() {
|
||||
let m1 = Mat::identity();
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.approx_eq(&m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mat::create_scale(1.5, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mat::create_translation(-132.0, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inverse_none() {
|
||||
assert!(Mat::create_scale(2.0, 0.0).inverse().is_none());
|
||||
assert!(Mat::create_scale(2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = Transform2D::identity().post_scale(1.0, 2.0).post_translate(vec2(1.0, 2.0));
|
||||
let m2 = Transform2D::identity().pre_translate(vec2(1.0, 2.0)).pre_scale(1.0, 2.0);
|
||||
assert!(m1.approx_eq(&m2));
|
||||
|
||||
let r = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let t = Mat::create_translation(2.0, 3.0);
|
||||
|
||||
let a = Point2D::new(1.0, 1.0);
|
||||
|
||||
assert!(r.post_mul(&t).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.post_mul(&r).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.post_mul(&r).transform_point(&a).approx_eq(&r.transform_point(&t.transform_point(&a))));
|
||||
|
||||
assert!(r.pre_mul(&t).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&t.transform_point(&r.transform_point(&a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use core::mem::size_of;
|
||||
assert_eq!(size_of::<Transform2D<f32>>(), 6*size_of::<f32>());
|
||||
assert_eq!(size_of::<Transform2D<f64>>(), 6*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = Transform2D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec2(0.1, 0.0));
|
||||
assert!(!m2.is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_vector() {
|
||||
// Translation does not apply to vectors.
|
||||
let m1 = Mat::create_translation(1.0, 1.0);
|
||||
let v1 = vec2(10.0, -10.0);
|
||||
assert_eq!(v1, m1.transform_vector(&v1));
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Trait for basic trigonometry functions, so they can be used on generic numeric types
|
||||
pub trait Trig {
|
||||
fn sin(self) -> Self;
|
||||
fn cos(self) -> Self;
|
||||
fn tan(self) -> Self;
|
||||
fn fast_atan2(y: Self, x: Self) -> Self;
|
||||
fn degrees_to_radians(deg: Self) -> Self;
|
||||
fn radians_to_degrees(rad: Self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! trig {
|
||||
($ty:ident) => (
|
||||
impl Trig for $ty {
|
||||
#[inline]
|
||||
fn sin(self) -> $ty { self.sin() }
|
||||
#[inline]
|
||||
fn cos(self) -> $ty { self.cos() }
|
||||
#[inline]
|
||||
fn tan(self) -> $ty { self.tan() }
|
||||
|
||||
/// A slightly faster approximation of `atan2`.
|
||||
///
|
||||
/// Note that it does not deal with the case where both x and y are 0.
|
||||
#[inline]
|
||||
fn fast_atan2(y: $ty, x: $ty) -> $ty {
|
||||
// This macro is used with f32 and f64 and clippy warns about the extra
|
||||
// precision with f32.
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(excessive_precision))]
|
||||
|
||||
// See https://math.stackexchange.com/questions/1098487/atan2-faster-approximation#1105038
|
||||
use core::$ty::consts;
|
||||
let x_abs = x.abs();
|
||||
let y_abs = y.abs();
|
||||
let a = x_abs.min(y_abs) / x_abs.max(y_abs);
|
||||
let s = a * a;
|
||||
let mut result = ((-0.046_496_474_9 * s + 0.159_314_22) * s - 0.327_622_764) * s * a + a;
|
||||
if y_abs > x_abs {
|
||||
result = consts::FRAC_PI_2 - result;
|
||||
}
|
||||
if x < 0.0 {
|
||||
result = consts::PI - result
|
||||
}
|
||||
if y < 0.0 {
|
||||
result = -result
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn degrees_to_radians(deg: Self) -> Self {
|
||||
deg.to_radians()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn radians_to_degrees(rad: Self) -> Self {
|
||||
rad.to_degrees()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
trig!(f32);
|
||||
trig!(f64);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"301590735ff27f124c03cef8598aa5397c88c59aba3d058edf0bde532965c346","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"d67b137d287c8debd8811e4deb3c2973eb9ee1ea11b31fcdd8217591482021f6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6594377e8f6c20f88f628520d8de9b9a59c5892a0ee9a6ccd13c8400c1499911","src/homogen.rs":"cb26346ad1ea2797bdc1cac7f532872becabf28a1f9c60792f86ad4a655582f9","src/length.rs":"3171315822707728b1bfbdd04a4190ffb7206b4bfc59e9dd072bb2caa05ff292","src/lib.rs":"b3c2303ab06ce972784c2ec4faa09ecdaa8e0706316f427c8a2009445a52f6e9","src/macros.rs":"877b4bd62b63ea120e568803281e7169f33fb811fe1c1515e56bfc44a74c34a2","src/num.rs":"4439479fad5729073e0bfe0b96b547672a237430d48e564519759b9550baa033","src/point.rs":"d18046853e19012e649a01991d45fdb1ba8f51eb55e52273e68f567cd7df932a","src/rect.rs":"1a4fbcf482e447218894c6a31753cb9b5a6c3e5377447ba7b5bceae7941a0772","src/rotation.rs":"982aaca640215bacc5d2dc60a8949bb2510d5b6d492975b8b6946a7c8f69b496","src/scale.rs":"fc07bcf47f3a1215023c830059f0d270e570cbd37fe8c367ef4a47b191f4ae3e","src/side_offsets.rs":"f114cb881256bbeff2ee2aa305d363e2dea65aa8535140f104f6fa9364bd02f5","src/size.rs":"f6a4f12fc50cc54220af089339cb7fde37f22c6dfcc4c2c676d24caab07b1790","src/transform2d.rs":"137344a16162f5cd1dc4a2ae87b8ea3fdde7597874835582378945f55e45513e","src/transform3d.rs":"efd971ba35e8a9ab59b0c4062b2625532147af0e57bf96b8cd09117524cf23ed","src/trig.rs":"97a263c4f178b0332501659ca8143f9f637a0755aca189dd31ac551bcd4cb73c","src/vector.rs":"d84103384907174d2b2206acd60d6b3261edb3ac971ec5e121ae22ce6bcca5d9"},"package":"47d5eb6310c8dd3e79f973952ddcb180bf6a98c01d341add49126a094b5598cc"}
|
||||
{"files":{".travis.yml":"301590735ff27f124c03cef8598aa5397c88c59aba3d058edf0bde532965c346","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"99a87c4c1a5ca910ddfd7d0d5a4bcd78aceb9d7f3604952bb5310fde801b1a72","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6594377e8f6c20f88f628520d8de9b9a59c5892a0ee9a6ccd13c8400c1499911","src/homogen.rs":"cb26346ad1ea2797bdc1cac7f532872becabf28a1f9c60792f86ad4a655582f9","src/length.rs":"3171315822707728b1bfbdd04a4190ffb7206b4bfc59e9dd072bb2caa05ff292","src/lib.rs":"b3c2303ab06ce972784c2ec4faa09ecdaa8e0706316f427c8a2009445a52f6e9","src/macros.rs":"877b4bd62b63ea120e568803281e7169f33fb811fe1c1515e56bfc44a74c34a2","src/num.rs":"4439479fad5729073e0bfe0b96b547672a237430d48e564519759b9550baa033","src/point.rs":"d18046853e19012e649a01991d45fdb1ba8f51eb55e52273e68f567cd7df932a","src/rect.rs":"dc7131fe3884803d549fb66dec589ad671790451dbb3770d360509fbfabcf6ec","src/rotation.rs":"982aaca640215bacc5d2dc60a8949bb2510d5b6d492975b8b6946a7c8f69b496","src/scale.rs":"fc07bcf47f3a1215023c830059f0d270e570cbd37fe8c367ef4a47b191f4ae3e","src/side_offsets.rs":"f114cb881256bbeff2ee2aa305d363e2dea65aa8535140f104f6fa9364bd02f5","src/size.rs":"f6a4f12fc50cc54220af089339cb7fde37f22c6dfcc4c2c676d24caab07b1790","src/transform2d.rs":"137344a16162f5cd1dc4a2ae87b8ea3fdde7597874835582378945f55e45513e","src/transform3d.rs":"efd971ba35e8a9ab59b0c4062b2625532147af0e57bf96b8cd09117524cf23ed","src/trig.rs":"97a263c4f178b0332501659ca8143f9f637a0755aca189dd31ac551bcd4cb73c","src/vector.rs":"d84103384907174d2b2206acd60d6b3261edb3ac971ec5e121ae22ce6bcca5d9"},"package":"70a2ebdf55fb9d6329046e026329a55ef8fbaae5ea833f56e170beb3125a8a5f"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "euclid"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Geometry primitives"
|
||||
documentation = "https://docs.rs/euclid/"
|
||||
|
@ -21,8 +21,7 @@ categories = ["science"]
|
|||
license = "MIT / Apache-2.0"
|
||||
repository = "https://github.com/servo/euclid"
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
default-features = false
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
|
|
|
@ -348,6 +348,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Div<Output = T>,
|
||||
{
|
||||
pub fn center(&self) -> TypedPoint2D<T, U> {
|
||||
let two = T::one() + T::one();
|
||||
self.origin + self.size.to_vector() / two
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + Clone + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
|
||||
|
@ -590,7 +600,7 @@ pub fn rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> TypedRect<T, U> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use point::Point2D;
|
||||
use point::{Point2D, point2};
|
||||
use vector::vec2;
|
||||
use side_offsets::SideOffsets2D;
|
||||
use size::Size2D;
|
||||
|
@ -835,4 +845,13 @@ mod tests {
|
|||
x += 0.1
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_center() {
|
||||
let r: Rect<i32> = rect(-2, 5, 4, 10);
|
||||
assert_eq!(r.center(), point2(0, 10));
|
||||
|
||||
let r: Rect<f32> = rect(1.0, 2.0, 3.0, 4.0);
|
||||
assert_eq!(r.center(), point2(2.5, 4.0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"ee68f6bd7972f6918091b4557302bbb655327d822413ba09102c0e63b49502fb","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"a65ed5c817c867fe23bc2029f34baea4a645a07dd5d101a0027e796d2923be58","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"98cd4982b1641146ac0a2bd4878a634768b94cbe2db7719bad30501e4ae8e82e","src/lib.rs":"ac7a25ea2f1543dc2b45decd3ec27e0c27dcc19c78bee8a3e0bbb3324e158d53","src/polygon.rs":"ee10d7ddc8369b964d548e53ad9cfa09d84a65b22e02ec2866175dc44469bb6e","tests/clip.rs":"606aff2a26a2054ed0dbd5377a356791440b5f88e6bf0c8447702cc5851fac4c","tests/main.rs":"c64d2d597a1f3c702921639a2af49c4a61ea6952b983a79749458c18c64eb69f","tests/split.rs":"0eb1afb1f26cdecd5fffbf32d57e889f8f69254c0a57eecb8ccbbdf38efcdf27"},"package":"6e14382aabad89085fbf714f75d527492bb672725facb9b2ced2fada47cf418c"}
|
||||
{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"19f5083a16929492a2e2123262e9dec955d67cc0ae49365857937846e2047bb6","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"a65ed5c817c867fe23bc2029f34baea4a645a07dd5d101a0027e796d2923be58","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"1dedfc48b79d767e28126cefd24af8ecf7eadd937bb5c7dc6b64e30b9c7d48c7","src/clip.rs":"aa4643264580fa55bc6a5cc02a8f9c0354a590e98545a132daf380603e4f0897","src/lib.rs":"3e9fb055066623b08be1abc06d0fd30f838d840ad13a6acab76d117f0c8bf5ff","src/polygon.rs":"3a37af114c44f531ad1565937ee473d4b21d286bbec11cfb351d8fcee6c5f4ce","tests/clip.rs":"0745faa4cb679c5eef4103a26a8714d6feb37ffd00b6c3dafad5937ec5392f10","tests/main.rs":"f782509823dfdba84de83dd36ba5ad5a468d8657950e74db697ee2e8886158a5","tests/split.rs":"0eb1afb1f26cdecd5fffbf32d57e889f8f69254c0a57eecb8ccbbdf38efcdf27"},"package":"f0a0766225cf828672e97948dfa035bb2eae75110757359ae12fbb46509c8b66"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "plane-split"
|
||||
version = "0.10.0"
|
||||
version = "0.12.0"
|
||||
authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
|
||||
description = "Plane splitting"
|
||||
documentation = "https://docs.rs/plane-split"
|
||||
|
@ -23,11 +23,11 @@ repository = "https://github.com/servo/plane-split"
|
|||
version = "0.1.2"
|
||||
|
||||
[dependencies.euclid]
|
||||
version = "0.18"
|
||||
version = "0.19"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.37"
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use {Intersection, Plane, Polygon, Splitter};
|
||||
|
||||
use binary_space_partition::{BspNode, Plane as BspPlane, PlaneCut};
|
||||
use euclid::{TypedPoint3D, TypedVector3D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use num_traits::{Float, One, Zero};
|
||||
|
||||
use std::{fmt, ops};
|
||||
use {Intersection, Plane, Polygon, Splitter};
|
||||
|
||||
|
||||
impl<T, U> BspPlane for Polygon<T, U> where
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
use {Intersection, Plane, Polygon};
|
||||
|
||||
use euclid::{Trig, TypedRect, TypedScale, TypedTransform3D, TypedVector3D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use num_traits::{Float, One, Zero};
|
||||
|
||||
use std::{fmt, mem, ops};
|
||||
|
||||
|
||||
/// A helper object to clip polygons by a number of planes.
|
||||
#[derive(Debug)]
|
||||
pub struct Clipper<T, U> {
|
||||
clips: Vec<Plane<T, U>>,
|
||||
results: Vec<Polygon<T, U>>,
|
||||
temp: Vec<Polygon<T, U>>,
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Copy + fmt::Debug + ApproxEq<T> +
|
||||
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
|
||||
ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
|
||||
Zero + One + Float,
|
||||
U: fmt::Debug,
|
||||
> Clipper<T, U> {
|
||||
/// Create a new clipper object.
|
||||
pub fn new() -> Self {
|
||||
Clipper {
|
||||
clips: Vec::new(),
|
||||
results: Vec::new(),
|
||||
temp: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the clipper internals, but preserve the allocation.
|
||||
pub fn reset(&mut self) {
|
||||
self.clips.clear();
|
||||
}
|
||||
|
||||
/// Add a set of planes that define the frustum for a given transformation.
|
||||
pub fn add_frustum<V>(
|
||||
&mut self,
|
||||
t: &TypedTransform3D<T, U, V>,
|
||||
bounds: Option<TypedRect<T, V>>,
|
||||
) {
|
||||
//Note: this is not the near plane, but the positive hemisphere
|
||||
// in homogeneous space.
|
||||
let mw = TypedVector3D::new(t.m14, t.m24, t.m34);
|
||||
self.clips.extend(Plane::from_unnormalized(mw, t.m44));
|
||||
|
||||
if let Some(bounds) = bounds {
|
||||
let mx = TypedVector3D::new(t.m11, t.m21, t.m31);
|
||||
let left = bounds.origin.x;
|
||||
self.clips.extend(Plane::from_unnormalized(
|
||||
mx - mw * TypedScale::new(left),
|
||||
t.m41 - t.m44 * left,
|
||||
));
|
||||
let right = bounds.origin.x + bounds.size.width;
|
||||
self.clips.extend(Plane::from_unnormalized(
|
||||
mw * TypedScale::new(right) - mx,
|
||||
t.m44 * right - t.m41,
|
||||
));
|
||||
|
||||
let my = TypedVector3D::new(t.m12, t.m22, t.m32);
|
||||
let top = bounds.origin.y;
|
||||
self.clips.extend(Plane::from_unnormalized(
|
||||
my - mw * TypedScale::new(top),
|
||||
t.m42 - t.m44 * top,
|
||||
));
|
||||
let bottom = bounds.origin.y + bounds.size.height;
|
||||
self.clips.extend(Plane::from_unnormalized(
|
||||
mw * TypedScale::new(bottom) - my,
|
||||
t.m44 * bottom - t.m42,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a clipping plane to the list. The plane will clip everything behind it,
|
||||
/// where the direction is set by the plane normal.
|
||||
pub fn add(&mut self, plane: Plane<T, U>) {
|
||||
self.clips.push(plane);
|
||||
}
|
||||
|
||||
/// Clip specified polygon by the contained planes, return the fragmented polygons.
|
||||
pub fn clip(&mut self, polygon: Polygon<T, U>) -> &[Polygon<T, U>] {
|
||||
self.results.clear();
|
||||
self.results.push(polygon);
|
||||
|
||||
for clip in &self.clips {
|
||||
self.temp.clear();
|
||||
mem::swap(&mut self.results, &mut self.temp);
|
||||
|
||||
for mut poly in self.temp.drain(..) {
|
||||
if let Intersection::Inside(line) = poly.intersect_plane(clip) {
|
||||
let (res1, res2) = poly.split(&line);
|
||||
self.results.extend(
|
||||
res1
|
||||
.into_iter()
|
||||
.chain(res2)
|
||||
.filter(|p| clip.signed_distance_sum_to(p) > T::zero())
|
||||
);
|
||||
}
|
||||
// Note: if the intersection has happened, the `poly` will now
|
||||
// contain the remainder of the original polygon.
|
||||
if clip.signed_distance_sum_to(&poly) > T::zero() {
|
||||
self.results.push(poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&self.results
|
||||
}
|
||||
|
||||
/// Clip the primitive with the frustum of the specified transformation,
|
||||
/// returning a sequence of polygons in the transformed space.
|
||||
pub fn clip_transformed<'a, V>(
|
||||
&'a mut self,
|
||||
polygon: Polygon<T, U>,
|
||||
transform: &'a TypedTransform3D<T, U, V>,
|
||||
bounds: Option<TypedRect<T, V>>,
|
||||
) -> impl 'a + Iterator<Item = Polygon<T, V>>
|
||||
where
|
||||
T: Trig,
|
||||
V: 'a + fmt::Debug,
|
||||
{
|
||||
let num_planes = if bounds.is_some() {5} else {1};
|
||||
self.add_frustum(transform, bounds);
|
||||
self.clip(polygon);
|
||||
// remove the frustum planes
|
||||
for _ in 0 .. num_planes {
|
||||
self.clips.pop();
|
||||
}
|
||||
self.results
|
||||
.drain(..)
|
||||
.map(move |poly| poly.transform(transform).unwrap())
|
||||
}
|
||||
}
|
|
@ -17,14 +17,17 @@ extern crate log;
|
|||
extern crate num_traits;
|
||||
|
||||
mod bsp;
|
||||
mod clip;
|
||||
mod polygon;
|
||||
|
||||
use std::{fmt, mem, ops};
|
||||
use euclid::{TypedPoint3D, TypedVector3D};
|
||||
use euclid::{TypedPoint3D, TypedScale, TypedVector3D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use num_traits::{Float, One, Zero};
|
||||
|
||||
use std::ops;
|
||||
|
||||
pub use self::bsp::BspSplitter;
|
||||
pub use self::clip::Clipper;
|
||||
pub use self::polygon::{Intersection, LineProjection, Polygon};
|
||||
|
||||
|
||||
|
@ -95,6 +98,20 @@ impl<
|
|||
ops::Mul<T, Output=T> + ops::Div<T, Output=T>,
|
||||
U,
|
||||
> Plane<T, U> {
|
||||
/// Construct a new plane from unnormalized equation.
|
||||
pub fn from_unnormalized(normal: TypedVector3D<T, U>, offset: T) -> Option<Self> {
|
||||
let square_len = normal.square_length();
|
||||
if square_len < T::approx_epsilon() {
|
||||
None
|
||||
} else {
|
||||
let kf = T::one() / square_len.sqrt();
|
||||
Some(Plane {
|
||||
normal: normal * TypedScale::new(kf),
|
||||
offset: offset * kf,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this plane contains another one.
|
||||
pub fn contains(&self, other: &Self) -> bool {
|
||||
//TODO: actually check for inside/outside
|
||||
|
@ -147,7 +164,11 @@ impl<
|
|||
// v = a*n1/w + b*n2/w; w = (n1, n2)
|
||||
// v = (d2*w - d1) / (1 - w*w) * n1 - (d2 - d1*w) / (1 - w*w) * n2
|
||||
let w = self.normal.dot(other.normal);
|
||||
let factor = T::one() / (T::one() - w * w);
|
||||
let divisor = T::one() - w * w;
|
||||
if divisor < T::approx_epsilon() {
|
||||
return None
|
||||
}
|
||||
let factor = T::one() / divisor;
|
||||
let origin = TypedPoint3D::origin() +
|
||||
self.normal * ((other.offset * w - self.offset) * factor) -
|
||||
other.normal* ((other.offset - self.offset * w) * factor);
|
||||
|
@ -193,67 +214,6 @@ pub trait Splitter<T, U> {
|
|||
}
|
||||
|
||||
|
||||
/// A helper object to clip polygons by a number of planes.
|
||||
#[derive(Debug)]
|
||||
pub struct Clipper<T, U> {
|
||||
clips: Vec<Plane<T, U>>,
|
||||
results: Vec<Polygon<T, U>>,
|
||||
temp: Vec<Polygon<T, U>>,
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Copy + fmt::Debug + ApproxEq<T> +
|
||||
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
|
||||
ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
|
||||
Zero + One + Float,
|
||||
U: fmt::Debug,
|
||||
> Clipper<T, U> {
|
||||
/// Create a new clipper object.
|
||||
pub fn new() -> Self {
|
||||
Clipper {
|
||||
clips: Vec::new(),
|
||||
results: Vec::new(),
|
||||
temp: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a clipping plane to the list. The plane will clip everything behind it,
|
||||
/// where the direction is set by the plane normal.
|
||||
pub fn add(&mut self, plane: Plane<T, U>) {
|
||||
self.clips.push(plane);
|
||||
}
|
||||
|
||||
/// Clip specified polygon by the contained planes, return the fragmented polygons.
|
||||
pub fn clip(&mut self, polygon: Polygon<T, U>) -> &[Polygon<T, U>] {
|
||||
self.results.clear();
|
||||
self.results.push(polygon);
|
||||
for clip in &self.clips {
|
||||
self.temp.clear();
|
||||
mem::swap(&mut self.results, &mut self.temp);
|
||||
|
||||
for mut poly in self.temp.drain(..) {
|
||||
if let Intersection::Inside(line) = poly.intersect_plane(clip) {
|
||||
let (res1, res2) = poly.split(&line);
|
||||
self.results.extend(
|
||||
res1
|
||||
.into_iter()
|
||||
.chain(res2.into_iter())
|
||||
.filter(|p| clip.signed_distance_sum_to(p) > T::zero())
|
||||
);
|
||||
}
|
||||
// Note: if the intersection has happened, the `poly` will now
|
||||
// contain the remainder of the original polygon.
|
||||
if clip.signed_distance_sum_to(&poly) > T::zero() {
|
||||
self.results.push(poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&self.results
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Helper method used for benchmarks and tests.
|
||||
/// Constructs a 3D grid of polygons.
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use {Line, Plane, is_zero};
|
||||
|
||||
use std::{fmt, mem, ops};
|
||||
use euclid::{Point2D, TypedTransform3D, TypedPoint3D, TypedVector3D, TypedRect};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use euclid::Trig;
|
||||
use num_traits::{Float, One, Zero};
|
||||
|
||||
use std::{fmt, mem, ops};
|
||||
|
||||
|
||||
/// The projection of a `Polygon` on a line.
|
||||
pub struct LineProjection<T> {
|
||||
|
@ -122,9 +123,24 @@ impl<T, U> Polygon<T, U> where
|
|||
points: [TypedPoint3D<T, U>; 4],
|
||||
anchor: usize,
|
||||
) -> Self {
|
||||
let normal = (points[1] - points[0])
|
||||
.cross(points[2] - points[0])
|
||||
.normalize();
|
||||
let edge1 = points[1] - points[0];
|
||||
let edge2 = points[2] - points[0];
|
||||
let edge3 = points[3] - points[0];
|
||||
|
||||
// one of them can be zero for redundant polygons produced by plane splitting
|
||||
//Note: this would be nicer if we used triangles instead of quads in the first place...
|
||||
// see https://github.com/servo/plane-split/issues/17
|
||||
debug_assert!(edge2.square_length() > T::approx_epsilon());
|
||||
let normal_rough1 = edge1.cross(edge2);
|
||||
let normal_rough2 = edge2.cross(edge3);
|
||||
let square_length1 = normal_rough1.square_length();
|
||||
let square_length2 = normal_rough2.square_length();
|
||||
let normal = if square_length1 > square_length2 {
|
||||
normal_rough1 / square_length1.sqrt()
|
||||
} else {
|
||||
normal_rough2 / square_length2.sqrt()
|
||||
};
|
||||
|
||||
let offset = -points[0].to_vector()
|
||||
.dot(normal);
|
||||
|
||||
|
@ -138,6 +154,19 @@ impl<T, U> Polygon<T, U> where
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct a polygon from a non-transformed rectangle.
|
||||
pub fn from_rect(rect: TypedRect<T, U>, anchor: usize) -> Self {
|
||||
Self::from_points(
|
||||
[
|
||||
rect.origin.to_3d(),
|
||||
rect.top_right().to_3d(),
|
||||
rect.bottom_right().to_3d(),
|
||||
rect.bottom_left().to_3d(),
|
||||
],
|
||||
anchor,
|
||||
)
|
||||
}
|
||||
|
||||
/// Construct a polygon from a rectangle with 3D transform.
|
||||
pub fn from_transformed_rect<V>(
|
||||
rect: TypedRect<T, V>,
|
||||
|
@ -181,6 +210,27 @@ impl<T, U> Polygon<T, U> where
|
|||
Point2D::new(x, y) / denom
|
||||
}
|
||||
|
||||
/// Transform a polygon by an affine transform (preserving straight lines).
|
||||
pub fn transform<V>(
|
||||
&self, transform: &TypedTransform3D<T, U, V>
|
||||
) -> Option<Polygon<T, V>>
|
||||
where
|
||||
T: Trig,
|
||||
V: fmt::Debug,
|
||||
{
|
||||
let mut points = [TypedPoint3D::origin(); 4];
|
||||
for (out, point) in points.iter_mut().zip(self.points.iter()) {
|
||||
let mut homo = transform.transform_point3d_homogeneous(point);
|
||||
homo.w = homo.w.max(T::approx_epsilon());
|
||||
*out = homo.to_point3d()?;
|
||||
}
|
||||
|
||||
//Note: this code path could be more efficient if we had inverse-transpose
|
||||
//let n4 = transform.transform_point4d(&TypedPoint4D::new(T::zero(), T::zero(), T::one(), T::zero()));
|
||||
//let normal = TypedPoint3D::new(n4.x, n4.y, n4.z);
|
||||
Some(Polygon::from_points(points, self.anchor))
|
||||
}
|
||||
|
||||
/// Check if all the points are indeed placed on the plane defined by
|
||||
/// the normal and offset, and the winding order is consistent.
|
||||
pub fn is_valid(&self) -> bool {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
extern crate euclid;
|
||||
extern crate plane_split;
|
||||
|
||||
use euclid::{point3, vec3};
|
||||
use euclid::{point3, rect, vec3};
|
||||
use euclid::{Angle, TypedRect, TypedTransform3D};
|
||||
use plane_split::{Clipper, Plane, Polygon};
|
||||
|
||||
use std::f32::consts::FRAC_PI_4;
|
||||
|
||||
|
||||
#[test]
|
||||
fn clip_in() {
|
||||
let plane: Plane<f32, ()> = Plane {
|
||||
normal: vec3(1.0, 0.0, 1.0).normalize(),
|
||||
offset: 20.0,
|
||||
};
|
||||
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), 20.0).unwrap();
|
||||
let mut clipper = Clipper::new();
|
||||
clipper.add(plane);
|
||||
|
||||
|
@ -28,10 +28,7 @@ fn clip_in() {
|
|||
|
||||
#[test]
|
||||
fn clip_out() {
|
||||
let plane: Plane<f32, ()> = Plane {
|
||||
normal: vec3(1.0, 0.0, 1.0).normalize(),
|
||||
offset: -20.0,
|
||||
};
|
||||
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), -20.0).unwrap();
|
||||
let mut clipper = Clipper::new();
|
||||
clipper.add(plane);
|
||||
|
||||
|
@ -68,10 +65,7 @@ fn clip_parallel() {
|
|||
|
||||
#[test]
|
||||
fn clip_repeat() {
|
||||
let plane: Plane<f32, ()> = Plane {
|
||||
normal: vec3(1.0, 0.0, 1.0).normalize(),
|
||||
offset: 0.0,
|
||||
};
|
||||
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), 0.0).unwrap();
|
||||
let mut clipper = Clipper::new();
|
||||
clipper.add(plane.clone());
|
||||
clipper.add(plane.clone());
|
||||
|
@ -87,3 +81,21 @@ fn clip_repeat() {
|
|||
assert_eq!(results.len(), 1);
|
||||
assert!(plane.signed_distance_sum_to(&results[0]) > 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clip_transformed() {
|
||||
let t_rot: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
|
||||
let t_div: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_perspective(5.0);
|
||||
let transform = t_rot.post_mul(&t_div);
|
||||
|
||||
let poly_rect: TypedRect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let polygon = Polygon::from_rect(poly_rect, 0);
|
||||
let bounds: TypedRect<f32, ()> = rect(-1.0, -1.0, 2.0, 2.0);
|
||||
|
||||
let mut clipper = Clipper::new();
|
||||
let results = clipper.clip_transformed(polygon, &transform, Some(bounds));
|
||||
// iterating enforces the transformation checks/unwraps
|
||||
assert_ne!(0, results.count());
|
||||
}
|
||||
|
|
|
@ -245,3 +245,14 @@ fn split() {
|
|||
dir: vec3(-0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plane_unnormalized() {
|
||||
let mut plane: Option<Plane<f32, ()>> = Plane::from_unnormalized(vec3(0.0, 0.0, 0.0), 1.0);
|
||||
assert_eq!(plane, None);
|
||||
plane = Plane::from_unnormalized(vec3(-3.0, 4.0, 0.0), 2.0);
|
||||
assert_eq!(plane, Some(Plane {
|
||||
normal: vec3(-3.0/5.0, 4.0/5.0, 0.0),
|
||||
offset: 2.0/5.0,
|
||||
}));
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче