зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1424280 - Update cargo lockfiles and re-vendor rust dependencies. r=jrmuizel
MozReview-Commit-ID: 1G7oWr52koH
This commit is contained in:
Родитель
071a573dc4
Коммит
0fb09505ab
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"6b96b2c6bfd7e1acef4b825a2813fc4277859eb9400a16800db8835c25e4087d","Cargo.toml":"41d47153a6043d3e4599f827888e1ac43c204e52ed5f6998b1e275fcae21a3cc","README.md":"9f048d969f9f8333cdcdb892744cd0816e4f2922c8817fa5e9e07f9472fe1050","src/app_unit.rs":"0f4fde2c0481b6dd021f48c8ef548090e7c577c02c429c41626c2b5e7a006949","src/lib.rs":"2df7d863c47d8b22f9af66caeafa87e6a206ee713a8aeaa55c5a80a42a92513b"},"package":"ed0a4de09a3b8449515e649f3bb84f72ea15fc2d10639beb0776a09b7d308074"}
|
|
@ -1,8 +0,0 @@
|
|||
language: rust
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
|
@ -1,31 +0,0 @@
|
|||
# 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.5.6"
|
||||
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.rustc-serialize]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
|
||||
[dependencies.heapsize]
|
||||
version = ">=0.3, < 0.5"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
|
@ -1,3 +0,0 @@
|
|||
# app-units
|
||||
|
||||
[Documentation](http://doc.servo.org/app_units/index.html)
|
|
@ -1,388 +0,0 @@
|
|||
/* 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 heapsize::HeapSizeOf;
|
||||
use num_traits::Zero;
|
||||
use rustc_serialize::{Encodable, Encoder};
|
||||
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 HeapSizeOf for Au {
|
||||
fn heap_size_of_children(&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
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 Encodable for Au {
|
||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
||||
e.emit_f64(self.to_f64_px())
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heapsize() {
|
||||
use heapsize::HeapSizeOf;
|
||||
fn f<T: HeapSizeOf>(_: T) {}
|
||||
f(Au::new(0));
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/* 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 heapsize;
|
||||
extern crate num_traits;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
|
||||
mod app_unit;
|
||||
|
||||
pub use app_unit::{Au, MIN_AU, MAX_AU, AU_PER_PX};
|
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"13574ca06216b94913348afb2beae9db9929f8964fbc45b3c00344ee281e1f52","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"1951103509b9ee4036df52e5f11c9d1e2ba18c09eab673de25c37ad1f6dabab4","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6cf810ad389c73a27141a7a67454ed12d4b01c3c16605b9a7414b389bc0615dd","src/length.rs":"73b0aed12a0c9acfd77a6b9ac0cd3a7ec522c41ffafad4448753cb6bba47b6a4","src/lib.rs":"43b594eebf1cd2c8fb7a7f4616d872d9e09f5e0c7c0172a16d4e5841ab552328","src/macros.rs":"a3f4deaa4323da6398546720548dda20b0b39427603ccc35ab49d220a83467a8","src/num.rs":"749b201289fc6663199160a2f9204e17925fd3053f8ab7779e7bfb377ad06227","src/point.rs":"859e3da88bf45123b10d90642b807b9ef35751699594d85012cb32b45e9e970b","src/rect.rs":"761b3e1c841c03ec87e99ed9dd9c37d669bec6967c2dac2b6be2f056e9c8b7e0","src/rotation.rs":"0b0a299268a76fbc15c58aec788ad0bfc27f7f68bcfeade3dce71cd2585166f2","src/scale_factor.rs":"b093243256df3f2b8a2e2bf98236e6ec1032c3d358596f384313614dbefaca49","src/side_offsets.rs":"fd95ffc9a74e9e84314875c388e763d0780486eb7f9034423e3a22048361e379","src/size.rs":"5ecb66be6c42f07770662c925017a7ef6e1d0e332b3576e1884c488fbf9d4b59","src/transform2d.rs":"82b5a41881fc4ab947df0b337ad2ac2e1dce7d532df1a225eb5abf2d32776007","src/transform3d.rs":"3b944cae37968b3c4e98a25323ac57ba331d97638b0a747fff28f139fcb6043c","src/trig.rs":"ef290927af252ca90a29ba9f17158b591ed591604e66cb9df045dd47b9cfdca5","src/vector.rs":"7f076e77a9a6fbecf44c1802233e9c2d59015a94cccf94fd9d8832cfb037bdb8"},"package":"f5ed7d77e46f6600f490463ad7b6349c3ebb2d2319af56e679e279e4c66495d9"}
|
|
@ -1,24 +0,0 @@
|
|||
language: rust
|
||||
rust:
|
||||
- 1.17.0
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: stable
|
||||
env: FEATURES=""
|
||||
- rust: beta
|
||||
env: FEATURES=""
|
||||
- rust: nightly
|
||||
env: FEATURES=""
|
||||
- rust: nightly
|
||||
env: FEATURES="unstable"
|
||||
|
||||
script:
|
||||
- cargo build --verbose --features "$FEATURES"
|
||||
- cargo test --verbose --features "$FEATURES"
|
|
@ -1,5 +0,0 @@
|
|||
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.
|
|
@ -1,42 +0,0 @@
|
|||
# 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.15.5"
|
||||
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.heapsize]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
default-features = false
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.3.1"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
[dev-dependencies.serde_test]
|
||||
version = "1.0"
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.3.7"
|
||||
|
||||
[features]
|
||||
unstable = []
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
|
@ -1,25 +0,0 @@
|
|||
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.
|
|
@ -1,8 +0,0 @@
|
|||
# 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)
|
|
@ -1,36 +0,0 @@
|
|||
// 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);
|
|
@ -1,461 +0,0 @@
|
|||
// 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_factor::ScaleFactor;
|
||||
use num::Zero;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::{NumCast, Saturating};
|
||||
use num::One;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Sub, Mul, Div, Neg};
|
||||
use std::ops::{AddAssign, SubAssign};
|
||||
use std::marker::PhantomData;
|
||||
use std::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_factor::ScaleFactor` to convert it from one unit to
|
||||
/// another. See the `ScaleFactor` docs for an example.
|
||||
// Uncomment the derive, and remove the macro call, once heapsize gets
|
||||
// PhantomData<T> support.
|
||||
#[repr(C)]
|
||||
pub struct Length<T, Unit>(pub T, 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> {}
|
||||
|
||||
impl<Unit, T: HeapSizeOf> HeapSizeOf for Length<T, Unit> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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 = ScaleFactor<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn div(self, other: Length<T, Src>) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get() / other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length * scaleFactor
|
||||
impl<Src, Dst, T: Clone + Mul<T, Output=T>> Mul<ScaleFactor<T, Src, Dst>> for Length<T, Src> {
|
||||
type Output = Length<T, Dst>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, Src, Dst>) -> Length<T, Dst> {
|
||||
Length::new(self.get() * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length / scaleFactor
|
||||
impl<Src, Dst, T: Clone + Div<T, Output=T>> Div<ScaleFactor<T, Src, Dst>> for Length<T, Dst> {
|
||||
type Output = Length<T, Src>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<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) -> 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 heapsize::HeapSizeOf;
|
||||
use num_traits::Saturating;
|
||||
use scale_factor::ScaleFactor;
|
||||
use std::f32::INFINITY;
|
||||
|
||||
extern crate serde_test;
|
||||
use self::serde_test::Token;
|
||||
use self::serde_test::assert_tokens;
|
||||
|
||||
enum Inch {}
|
||||
enum Mm {}
|
||||
enum Cm {}
|
||||
enum Second {}
|
||||
|
||||
#[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_heapsizeof_builtins() {
|
||||
// Heap size of built-ins is zero by default.
|
||||
let one_foot: Length<f32, Inch> = Length::new(12.0);
|
||||
|
||||
let heap_size_length_f32 = one_foot.heap_size_of_children();
|
||||
|
||||
assert_eq!(heap_size_length_f32, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_heapsizeof_length_vector() {
|
||||
// Heap size of any Length is just the heap size of the length value.
|
||||
for n in 0..5 {
|
||||
let length: Length<Vec<f32>, Inch> = Length::new(Vec::with_capacity(n));
|
||||
|
||||
assert_eq!(length.heap_size_of_children(), length.0.heap_size_of_children());
|
||||
}
|
||||
}
|
||||
|
||||
#[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_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_fmt_debug() {
|
||||
// Debug and display format the value only.
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = format!("{:?}", one_cm);
|
||||
|
||||
assert_eq!(result, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt_display() {
|
||||
// Debug and display format the value only.
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = format!("{}", one_cm);
|
||||
|
||||
assert_eq!(result, "10");
|
||||
}
|
||||
|
||||
#[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 ScaleFactor 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: ScaleFactor<f32, Second, Cm> = ScaleFactor::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: ScaleFactor<f32, Mm, Cm> = ScaleFactor::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_division_by_scalefactor() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let cm_per_second: ScaleFactor<f32, Second, Cm> = ScaleFactor::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_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().unwrap();
|
||||
|
||||
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: ScaleFactor<f32, Cm, Cm> = ScaleFactor::new(INFINITY);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
// 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(asm, repr_simd, test, fn_must_use))]
|
||||
|
||||
//! 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());
|
||||
//! ```
|
||||
|
||||
extern crate heapsize;
|
||||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate log;
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
#[cfg(feature = "unstable")]
|
||||
extern crate test;
|
||||
extern crate num_traits;
|
||||
|
||||
pub use length::Length;
|
||||
pub use scale_factor::ScaleFactor;
|
||||
pub use transform2d::{Transform2D, TypedTransform2D};
|
||||
pub use transform3d::{Transform3D, TypedTransform3D};
|
||||
pub use point::{
|
||||
Point2D, TypedPoint2D, point2,
|
||||
Point3D, TypedPoint3D, point3,
|
||||
};
|
||||
pub use vector::{
|
||||
Vector2D, TypedVector2D, vec2,
|
||||
Vector3D, TypedVector3D, vec3,
|
||||
};
|
||||
|
||||
pub use rect::{Rect, TypedRect, rect};
|
||||
pub use rotation::{TypedRotation2D, Rotation2D, TypedRotation3D, Rotation3D};
|
||||
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
|
||||
#[cfg(feature = "unstable")] pub use side_offsets::SideOffsets2DSimdI32;
|
||||
pub use size::{Size2D, TypedSize2D, size2};
|
||||
pub use trig::Trig;
|
||||
|
||||
pub mod approxeq;
|
||||
pub mod num;
|
||||
mod length;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod transform2d;
|
||||
mod transform3d;
|
||||
mod point;
|
||||
mod rect;
|
||||
mod rotation;
|
||||
mod scale_factor;
|
||||
mod side_offsets;
|
||||
mod size;
|
||||
mod trig;
|
||||
mod vector;
|
||||
|
||||
/// The default unit.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UnknownUnit;
|
||||
|
||||
/// Unit for angles in radians.
|
||||
pub struct Rad;
|
||||
|
||||
/// Unit for angles in degrees.
|
||||
pub struct Deg;
|
||||
|
||||
/// A value in radians.
|
||||
pub type Radians<T> = Length<T, Rad>;
|
||||
|
||||
/// A value in Degrees.
|
||||
pub type Degrees<T> = Length<T, Deg>;
|
||||
|
||||
/// 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>;
|
|
@ -1,87 +0,0 @@
|
|||
// 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,)+
|
||||
_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),+> {}
|
||||
|
||||
impl<T, $($phantom),+> ::heapsize::HeapSizeOf for $name<T, $($phantom),+>
|
||||
where T: ::heapsize::HeapSizeOf
|
||||
{
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
$(self.$field.heap_size_of_children() +)+ 0
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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),+> ::std::cmp::Eq for $name<T, $($phantom),+>
|
||||
where T: ::std::cmp::Eq {}
|
||||
|
||||
impl<T, $($phantom),+> ::std::cmp::PartialEq for $name<T, $($phantom),+>
|
||||
where T: ::std::cmp::PartialEq
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
true $(&& self.$field == other.$field)+
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::std::hash::Hash for $name<T, $($phantom),+>
|
||||
where T: ::std::hash::Hash
|
||||
{
|
||||
fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) {
|
||||
$(self.$field.hash(h);)+
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// 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);
|
|
@ -1,834 +0,0 @@
|
|||
// 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_factor::ScaleFactor;
|
||||
use size::TypedSize2D;
|
||||
use num::*;
|
||||
use num_traits::{Float, NumCast};
|
||||
use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Mul, Sub, Div, AddAssign, SubAssign, MulAssign, DivAssign};
|
||||
use std::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: Copy, U> TypedPoint2D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
TypedPoint2D { x: x, y: y, _unit: PhantomData }
|
||||
}
|
||||
|
||||
/// 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<ScaleFactor<T, U1, U2>> for TypedPoint2D<T, U1> {
|
||||
type Output = TypedPoint2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<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<ScaleFactor<T, U1, U2>> for TypedPoint2D<T, U2> {
|
||||
type Output = TypedPoint2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<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) -> 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
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 copmonents 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: x, y: y, z: 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 substracting 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) -> 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
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::{TypedPoint2D, Point2D, point2};
|
||||
use scale_factor::ScaleFactor;
|
||||
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: ScaleFactor<f32, Mm, Cm> = ScaleFactor::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));
|
||||
}
|
||||
}
|
|
@ -1,700 +0,0 @@
|
|||
// 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_factor::ScaleFactor;
|
||||
use num::*;
|
||||
use point::TypedPoint2D;
|
||||
use vector::TypedVector2D;
|
||||
use size::TypedSize2D;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::NumCast;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::PartialOrd;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, Sub, Mul, Div};
|
||||
|
||||
/// 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>;
|
||||
|
||||
impl<T: HeapSizeOf, U> HeapSizeOf for TypedRect<T, U> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.origin.heap_size_of_children() + self.size.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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: origin,
|
||||
size: 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())
|
||||
}
|
||||
|
||||
/// Returns the smallest rectangle containing the four points.
|
||||
pub fn from_points(points: &[TypedPoint2D<T, U>]) -> Self {
|
||||
if points.len() == 0 {
|
||||
return TypedRect::zero();
|
||||
}
|
||||
let (mut min_x, mut min_y) = (points[0].x, points[0].y);
|
||||
let (mut max_x, mut max_y) = (min_x, min_y);
|
||||
for point in &points[1..] {
|
||||
if point.x < min_x {
|
||||
min_x = point.x
|
||||
}
|
||||
if point.x > max_x {
|
||||
max_x = point.x
|
||||
}
|
||||
if point.y < min_y {
|
||||
min_y = point.y
|
||||
}
|
||||
if point.y > max_y {
|
||||
max_y = point.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 rectange.
|
||||
///
|
||||
/// `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<Scale: Copy>(&self, x: Scale, y: Scale) -> Self
|
||||
where T: Copy + Clone + Mul<Scale, 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<ScaleFactor<T, U1, U2>> for TypedRect<T, U1> {
|
||||
type Output = TypedRect<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedRect<T, U2> {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedRect<T, U2> {
|
||||
type Output = TypedRect<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<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) -> Option<TypedRect<T1, Unit>> {
|
||||
match (self.origin.cast(), self.size.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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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_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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,696 +0,0 @@
|
|||
// 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 approxeq::ApproxEq;
|
||||
use num_traits::{Float, One, Zero};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Neg, Mul, Sub, Div};
|
||||
use std::marker::PhantomData;
|
||||
use trig::Trig;
|
||||
use {TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D, Vector3D, point2, point3, vec3};
|
||||
use {TypedTransform3D, TypedTransform2D, UnknownUnit, Radians};
|
||||
|
||||
|
||||
define_matrix! {
|
||||
/// A transform that can represent rotations in 2d, represented as an angle in radians.
|
||||
pub struct TypedRotation2D<T, Src, Dst> {
|
||||
pub angle : T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 2d rotation type with no units.
|
||||
pub type Rotation2D<T> = TypedRotation2D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T, Src, Dst> TypedRotation2D<T, Src, Dst> {
|
||||
#[inline]
|
||||
/// Creates a rotation from an angle in radians.
|
||||
pub fn new(angle: Radians<T>) -> Self {
|
||||
TypedRotation2D {
|
||||
angle: angle.0,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn radians(angle: T) -> Self {
|
||||
Self::new(Radians::new(angle))
|
||||
}
|
||||
|
||||
/// Creates the identity rotation.
|
||||
#[inline]
|
||||
pub fn identity() -> Self where T: Zero {
|
||||
Self::radians(T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedRotation2D<T, Src, Dst> where T: Clone
|
||||
{
|
||||
/// Returns self.angle as a strongly typed `Radians<T>`.
|
||||
pub fn get_angle(&self) -> Radians<T> {
|
||||
Radians::new(self.angle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, Src, Dst> TypedRotation2D<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> +
|
||||
ApproxEq<T> +
|
||||
PartialOrd +
|
||||
Float +
|
||||
One + Zero
|
||||
{
|
||||
/// Creates a 3d rotation (around the z axis) from this 2d rotation.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> TypedRotation3D<T, Src, Dst> {
|
||||
TypedRotation3D::around_z(self.get_angle())
|
||||
}
|
||||
|
||||
/// Returns the inverse of this rotation.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> TypedRotation2D<T, Dst, Src> {
|
||||
TypedRotation2D::radians(-self.angle)
|
||||
}
|
||||
|
||||
/// Returns a rotation representing the other rotation followed by this rotation.
|
||||
#[inline]
|
||||
pub fn pre_rotate<NewSrc>(&self, other: &TypedRotation2D<T, NewSrc, Src>) -> TypedRotation2D<T, NewSrc, Dst> {
|
||||
TypedRotation2D::radians(self.angle + other.angle)
|
||||
}
|
||||
|
||||
/// Returns a rotation representing this rotation followed by the other rotation.
|
||||
#[inline]
|
||||
pub fn post_rotate<NewDst>(&self, other: &TypedRotation2D<T, Dst, NewDst>) -> TypedRotation2D<T, Src, NewDst> {
|
||||
other.pre_rotate(self)
|
||||
}
|
||||
|
||||
/// Returns the given 2d point transformed by this rotation.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
let (sin, cos) = Float::sin_cos(self.angle);
|
||||
point2(
|
||||
point.x * cos - point.y * sin,
|
||||
point.y * cos + point.x * sin,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the given 2d vector transformed by this rotation.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector(&self, vector: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
self.transform_point(&vector.to_point()).to_vector()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedRotation2D<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 matrix representation of this rotation.
|
||||
#[inline]
|
||||
pub fn to_transform(&self) -> TypedTransform2D<T, Src, Dst> {
|
||||
TypedTransform2D::create_rotation(self.get_angle())
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A transform that can represent rotations in 3d, represented as a quaternion.
|
||||
///
|
||||
/// Most methods expect the quaternion to be normalized.
|
||||
/// When in doubt, use `unit_quaternion` instead of `quaternion` to create
|
||||
/// a rotation as the former will ensure that its result is normalized.
|
||||
///
|
||||
/// Some people use the `x, y, z, w` (or `w, x, y, z`) notations. The equivalence is
|
||||
/// as follows: `x -> i`, `y -> j`, `z -> k`, `w -> r`.
|
||||
/// The memory layout of this type corresponds to the `x, y, z, w` notation
|
||||
pub struct TypedRotation3D<T, Src, Dst> {
|
||||
// Component multiplied by the imaginary number `i`.
|
||||
pub i: T,
|
||||
// Component multiplied by the imaginary number `j`.
|
||||
pub j: T,
|
||||
// Component multiplied by the imaginary number `k`.
|
||||
pub k: T,
|
||||
// The real part.
|
||||
pub r: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 3d rotation type with no units.
|
||||
pub type Rotation3D<T> = TypedRotation3D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T, Src, Dst> TypedRotation3D<T, Src, Dst> {
|
||||
/// Creates a rotation around from a quaternion representation.
|
||||
///
|
||||
/// The parameters are a, b, c and r compose the quaternion `a*i + b*j + c*k + r`
|
||||
/// where `a`, `b` and `c` describe the vector part and the last parameter `r` is
|
||||
/// the real part.
|
||||
///
|
||||
/// The resulting quaternion is not necessarily normalized. See `unit_quaternion`.
|
||||
#[inline]
|
||||
pub fn quaternion(a: T, b: T, c: T, r: T) -> Self {
|
||||
TypedRotation3D { i: a, j: b, k: c, r, _unit: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, Src, Dst> TypedRotation3D<T, Src, Dst> where T: Copy {
|
||||
/// Returns the vector part (i, j, k) of this quaternion.
|
||||
#[inline]
|
||||
pub fn vector_part(&self) -> Vector3D<T> { vec3(self.i, self.j, self.k) }
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedRotation3D<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> +
|
||||
ApproxEq<T> +
|
||||
PartialOrd +
|
||||
Float +
|
||||
One + Zero
|
||||
{
|
||||
/// Creates the identity rotation.
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
let zero = T::zero();
|
||||
let one = T::one();
|
||||
Self::quaternion(zero, zero, zero, one)
|
||||
}
|
||||
|
||||
/// Creates a rotation around from a quaternion representation and normalizes it.
|
||||
///
|
||||
/// The parameters are a, b, c and r compose the quaternion `a*i + b*j + c*k + r`
|
||||
/// before normalization, where `a`, `b` and `c` describe the vector part and the
|
||||
/// last parameter `r` is the real part.
|
||||
#[inline]
|
||||
pub fn unit_quaternion(i: T, j: T, k: T, r: T) -> Self {
|
||||
Self::quaternion(i, j, k, r).normalize()
|
||||
}
|
||||
|
||||
/// Creates a rotation around a given axis.
|
||||
pub fn around_axis(axis: TypedVector3D<T, Src>, angle: Radians<T>) -> Self {
|
||||
let axis = axis.normalize();
|
||||
let two = T::one() + T::one();
|
||||
let (sin, cos) = Float::sin_cos(angle.get() / two);
|
||||
Self::quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos)
|
||||
}
|
||||
|
||||
/// Creates a rotation around the x axis.
|
||||
pub fn around_x(angle: Radians<T>) -> Self {
|
||||
let zero = Zero::zero();
|
||||
let two = T::one() + T::one();
|
||||
let (sin, cos) = Float::sin_cos(angle.get() / two);
|
||||
Self::quaternion(sin, zero, zero, cos)
|
||||
}
|
||||
|
||||
/// Creates a rotation around the y axis.
|
||||
pub fn around_y(angle: Radians<T>) -> Self {
|
||||
let zero = Zero::zero();
|
||||
let two = T::one() + T::one();
|
||||
let (sin, cos) = Float::sin_cos(angle.get() / two);
|
||||
Self::quaternion(zero, sin, zero, cos)
|
||||
}
|
||||
|
||||
/// Creates a rotation around the z axis.
|
||||
pub fn around_z(angle: Radians<T>) -> Self {
|
||||
let zero = Zero::zero();
|
||||
let two = T::one() + T::one();
|
||||
let (sin, cos) = Float::sin_cos(angle.get() / two);
|
||||
Self::quaternion(zero, zero, sin, cos)
|
||||
}
|
||||
|
||||
/// Creates a rotation from euler angles.
|
||||
///
|
||||
/// The rotations are applied in roll then pitch then yaw order.
|
||||
///
|
||||
/// - Roll (also calld bank) is a rotation around the x axis.
|
||||
/// - Pitch (also calld bearing) is a rotation around the y axis.
|
||||
/// - Yaw (also calld heading) is a rotation around the z axis.
|
||||
pub fn euler(roll: Radians<T>, pitch: Radians<T>, yaw: Radians<T>) -> Self {
|
||||
let half = T::one() / (T::one() + T::one());
|
||||
|
||||
let (sy, cy) = Float::sin_cos(half * yaw.get());
|
||||
let (sp, cp) = Float::sin_cos(half * pitch.get());
|
||||
let (sr, cr) = Float::sin_cos(half * roll.get());
|
||||
|
||||
Self::quaternion(
|
||||
cy * sr * cp - sy * cr * sp,
|
||||
cy * cr * sp + sy * sr * cp,
|
||||
sy * cr * cp - cy * sr * sp,
|
||||
cy * cr * cp + sy * sr * sp,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the inverse of this rotation.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> TypedRotation3D<T, Dst, Src> {
|
||||
TypedRotation3D::quaternion(-self.i, -self.j, -self.k, self.r)
|
||||
}
|
||||
|
||||
/// Computes the norm of this quaternion
|
||||
#[inline]
|
||||
pub fn norm(&self) -> T {
|
||||
self.square_norm().sqrt()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square_norm(&self) -> T {
|
||||
(self.i * self.i + self.j * self.j + self.k * self.k + self.r *self.r)
|
||||
}
|
||||
|
||||
/// Returns a unit quaternion from this one.
|
||||
#[inline]
|
||||
pub fn normalize(&self) -> Self {
|
||||
self.mul(T::one() / self.norm())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_normalized(&self) -> bool {
|
||||
// TODO: we might need to relax the threshold here, because of floating point imprecision.
|
||||
self.square_norm().approx_eq(&T::one())
|
||||
}
|
||||
|
||||
/// Spherical linear interpolation between this rotation and another rotation.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
pub fn slerp(&self, other: &Self, t: T) -> Self {
|
||||
debug_assert!(self.is_normalized());
|
||||
debug_assert!(other.is_normalized());
|
||||
|
||||
let r1 = *self;
|
||||
let mut r2 = *other;
|
||||
|
||||
let mut dot = r1.i * r2.i + r1.j * r2.j + r1.k * r2.k + r1.r * r2.r;
|
||||
|
||||
let one = T::one();
|
||||
|
||||
if dot.approx_eq(&T::one()) {
|
||||
// If the inputs are too close, linearly interpolate to avoid precision issues.
|
||||
return r1.lerp(&r2, t);
|
||||
}
|
||||
|
||||
// If the dot product is negative, the quaternions
|
||||
// have opposite handed-ness and slerp won't take
|
||||
// the shorter path. Fix by reversing one quaternion.
|
||||
if dot < T::zero() {
|
||||
r2 = r2.mul(-T::one());
|
||||
dot = -dot;
|
||||
}
|
||||
|
||||
// For robustness, stay within the domain of acos.
|
||||
dot = Float::min(dot, one);
|
||||
|
||||
// Angle between r1 and the result.
|
||||
let theta = Float::acos(dot) * t;
|
||||
|
||||
// r1 and r3 form an orthonormal basis.
|
||||
let r3 = r2.sub(r1.mul(dot)).normalize();
|
||||
let (sin, cos) = Float::sin_cos(theta);
|
||||
r1.mul(cos).add(r3.mul(sin))
|
||||
}
|
||||
|
||||
/// Basic Linear interpolation between this rotation and another rotation.
|
||||
///
|
||||
/// `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;
|
||||
return self.mul(one_t).add(other.mul(t)).normalize();
|
||||
}
|
||||
|
||||
/// Returns the given 3d point transformed by this rotation.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
pub fn rotate_point3d(&self, point: &TypedPoint3D<T, Src>) -> TypedPoint3D<T, Dst> {
|
||||
debug_assert!(self.is_normalized());
|
||||
|
||||
let two = T::one() + T::one();
|
||||
let cross = self.vector_part().cross(point.to_vector().to_untyped()) * two;
|
||||
|
||||
point3(
|
||||
point.x + self.r * cross.x + self.j * cross.z - self.k * cross.y,
|
||||
point.y + self.r * cross.y + self.k * cross.x - self.i * cross.z,
|
||||
point.z + self.r * cross.z + self.i * cross.y - self.j * cross.x,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the given 2d point transformed by this rotation then projected on the xy plane.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn rotate_point2d(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
self.rotate_point3d(&point.to_3d()).xy()
|
||||
}
|
||||
|
||||
/// Returns the given 3d vector transformed by this rotation then projected on the xy plane.
|
||||
///
|
||||
/// The input vector must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn rotate_vector3d(&self, vector: &TypedVector3D<T, Src>) -> TypedVector3D<T, Dst> {
|
||||
self.rotate_point3d(&vector.to_point()).to_vector()
|
||||
}
|
||||
|
||||
/// Returns the given 2d vector transformed by this rotation then projected on the xy plane.
|
||||
///
|
||||
/// The input vector must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn rotate_vector2d(&self, vector: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
self.rotate_vector3d(&vector.to_3d()).xy()
|
||||
}
|
||||
|
||||
/// Returns the matrix representation of this rotation.
|
||||
#[inline]
|
||||
pub fn to_transform(&self) -> TypedTransform3D<T, Src, Dst> {
|
||||
debug_assert!(self.is_normalized());
|
||||
|
||||
let i2 = self.i + self.i;
|
||||
let j2 = self.j + self.j;
|
||||
let k2 = self.k + self.k;
|
||||
let ii = self.i * i2;
|
||||
let ij = self.i * j2;
|
||||
let ik = self.i * k2;
|
||||
let jj = self.j * j2;
|
||||
let jk = self.j * k2;
|
||||
let kk = self.k * k2;
|
||||
let ri = self.r * i2;
|
||||
let rj = self.r * j2;
|
||||
let rk = self.r * k2;
|
||||
|
||||
let one = T::one();
|
||||
let zero = T::zero();
|
||||
|
||||
let m11 = one - (jj + kk);
|
||||
let m12 = ij + rk;
|
||||
let m13 = ik - rj;
|
||||
|
||||
let m21 = ij - rk;
|
||||
let m22 = one - (ii + kk);
|
||||
let m23 = jk + ri;
|
||||
|
||||
let m31 = ik + rj;
|
||||
let m32 = jk - ri;
|
||||
let m33 = one - (ii + jj);
|
||||
|
||||
TypedTransform3D::row_major(
|
||||
m11, m12, m13, zero,
|
||||
m21, m22, m23, zero,
|
||||
m31, m32, m33, zero,
|
||||
zero, zero, zero, one,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rotation representing the other rotation followed by this rotation.
|
||||
pub fn pre_rotate<NewSrc>(&self, other: &TypedRotation3D<T, NewSrc, Src>) -> TypedRotation3D<T, NewSrc, Dst> {
|
||||
debug_assert!(self.is_normalized());
|
||||
TypedRotation3D::quaternion(
|
||||
self.i * other.r + self.r * other.i + self.j * other.k - self.k * other.j,
|
||||
self.j * other.r + self.r * other.j + self.k * other.i - self.i * other.k,
|
||||
self.k * other.r + self.r * other.k + self.i * other.j - self.j * other.i,
|
||||
self.r * other.r - self.i * other.i - self.j * other.j - self.k * other.k,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rotation representing this rotation followed by the other rotation.
|
||||
#[inline]
|
||||
pub fn post_rotate<NewDst>(&self, other: &TypedRotation3D<T, Dst, NewDst>) -> TypedRotation3D<T, Src, NewDst> {
|
||||
other.pre_rotate(self)
|
||||
}
|
||||
|
||||
// add, sub and mul are used internally for intermediate computation but aren't public
|
||||
// because they don't carry real semantic meanings (I think?).
|
||||
|
||||
#[inline]
|
||||
fn add(&self, other: Self) -> Self {
|
||||
Self::quaternion(
|
||||
self.i + other.i,
|
||||
self.j + other.j,
|
||||
self.k + other.k,
|
||||
self.r + other.r,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub(&self, other: Self) -> Self {
|
||||
Self::quaternion(
|
||||
self.i - other.i,
|
||||
self.j - other.j,
|
||||
self.k - other.k,
|
||||
self.r - other.r,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul(&self, factor: T) -> Self {
|
||||
Self::quaternion(
|
||||
self.i * factor,
|
||||
self.j * factor,
|
||||
self.k * factor,
|
||||
self.r * factor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for TypedRotation3D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Quat({:?}*i + {:?}*j + {:?}*k + {:?})", self.i, self.j, self.k, self.r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for TypedRotation3D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Quat({}*i + {}*j + {}*k + {})", self.i, self.j, self.k, self.r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> ApproxEq<T> for TypedRotation3D<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Neg<Output=T> + ApproxEq<T>
|
||||
{
|
||||
fn approx_epsilon() -> T {
|
||||
T::approx_epsilon()
|
||||
}
|
||||
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.approx_eq_eps(other, &Self::approx_epsilon())
|
||||
}
|
||||
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
|
||||
(
|
||||
self.i.approx_eq_eps(&other.i, eps)
|
||||
&& self.j.approx_eq_eps(&other.j, eps)
|
||||
&& self.k.approx_eq_eps(&other.k, eps)
|
||||
&& self.r.approx_eq_eps(&other.r, eps)
|
||||
) || (
|
||||
self.i.approx_eq_eps(&-other.i, eps)
|
||||
&& self.j.approx_eq_eps(&-other.j, eps)
|
||||
&& self.k.approx_eq_eps(&-other.k, eps)
|
||||
&& self.r.approx_eq_eps(&-other.r, eps)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_rotation_2d() {
|
||||
use std::f32::consts::{PI, FRAC_PI_2};
|
||||
let ri = Rotation2D::identity();
|
||||
let r90 = Rotation2D::radians(FRAC_PI_2);
|
||||
let rm90 = Rotation2D::radians(-FRAC_PI_2);
|
||||
let r180 = Rotation2D::radians(PI);
|
||||
|
||||
assert!(ri.transform_point(&point2(1.0, 2.0)).approx_eq(&point2(1.0, 2.0)));
|
||||
assert!(r90.transform_point(&point2(1.0, 2.0)).approx_eq(&point2(-2.0, 1.0)));
|
||||
assert!(rm90.transform_point(&point2(1.0, 2.0)).approx_eq(&point2(2.0, -1.0)));
|
||||
assert!(r180.transform_point(&point2(1.0, 2.0)).approx_eq(&point2(-1.0, -2.0)));
|
||||
|
||||
assert!(
|
||||
r90.inverse().inverse().transform_point(&point2(1.0, 2.0)).approx_eq(
|
||||
&r90.transform_point(&point2(1.0, 2.0))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_rotation_3d_in_2d() {
|
||||
use std::f32::consts::{PI, FRAC_PI_2};
|
||||
let ri = Rotation3D::identity();
|
||||
let r90 = Rotation3D::around_z(Radians::new(FRAC_PI_2));
|
||||
let rm90 = Rotation3D::around_z(Radians::new(-FRAC_PI_2));
|
||||
let r180 = Rotation3D::around_z(Radians::new(PI));
|
||||
|
||||
assert!(ri.rotate_point2d(&point2(1.0, 2.0)).approx_eq(&point2(1.0, 2.0)));
|
||||
assert!(r90.rotate_point2d(&point2(1.0, 2.0)).approx_eq(&point2(-2.0, 1.0)));
|
||||
assert!(rm90.rotate_point2d(&point2(1.0, 2.0)).approx_eq(&point2(2.0, -1.0)));
|
||||
assert!(r180.rotate_point2d(&point2(1.0, 2.0)).approx_eq(&point2(-1.0, -2.0)));
|
||||
|
||||
assert!(
|
||||
r90.inverse().inverse().rotate_point2d(&point2(1.0, 2.0)).approx_eq(
|
||||
&r90.rotate_point2d(&point2(1.0, 2.0))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_post() {
|
||||
use std::f32::consts::{FRAC_PI_2};
|
||||
let r1 = Rotation3D::around_x(Radians::new(FRAC_PI_2));
|
||||
let r2 = Rotation3D::around_y(Radians::new(FRAC_PI_2));
|
||||
let r3 = Rotation3D::around_z(Radians::new(FRAC_PI_2));
|
||||
|
||||
let t1 = r1.to_transform();
|
||||
let t2 = r2.to_transform();
|
||||
let t3 = r3.to_transform();
|
||||
|
||||
let p = point3(1.0, 2.0, 3.0);
|
||||
|
||||
// Check that the order of transformations is correct (corresponds to what
|
||||
// we do in Transfor3D).
|
||||
let p1 = r1.post_rotate(&r2).post_rotate(&r3).rotate_point3d(&p);
|
||||
let p2 = t1.post_mul(&t2).post_mul(&t3).transform_point3d(&p);
|
||||
|
||||
assert!(p1.approx_eq(&p2));
|
||||
|
||||
// Check that changing the order indeed matters.
|
||||
let p3 = t3.post_mul(&t1).post_mul(&t2).transform_point3d(&p);
|
||||
assert!(!p1.approx_eq(&p3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_transform3d() {
|
||||
use std::f32::consts::{PI, FRAC_PI_2};
|
||||
let rotations = [
|
||||
Rotation3D::identity(),
|
||||
Rotation3D::around_x(Radians::new(FRAC_PI_2)),
|
||||
Rotation3D::around_x(Radians::new(-FRAC_PI_2)),
|
||||
Rotation3D::around_x(Radians::new(PI)),
|
||||
Rotation3D::around_y(Radians::new(FRAC_PI_2)),
|
||||
Rotation3D::around_y(Radians::new(-FRAC_PI_2)),
|
||||
Rotation3D::around_y(Radians::new(PI)),
|
||||
Rotation3D::around_z(Radians::new(FRAC_PI_2)),
|
||||
Rotation3D::around_z(Radians::new(-FRAC_PI_2)),
|
||||
Rotation3D::around_z(Radians::new(PI)),
|
||||
];
|
||||
|
||||
let points = [
|
||||
point3(0.0, 0.0, 0.0),
|
||||
point3(1.0, 2.0, 3.0),
|
||||
point3(-5.0, 3.0, -1.0),
|
||||
point3(-0.5, -1.0, 1.5),
|
||||
];
|
||||
|
||||
for rotation in &rotations {
|
||||
for point in &points {
|
||||
let p1 = rotation.rotate_point3d(point);
|
||||
let p2 = rotation.to_transform().transform_point3d(point);
|
||||
assert!(p1.approx_eq(&p2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slerp() {
|
||||
let q1 = Rotation3D::quaternion(1.0, 0.0, 0.0, 0.0);
|
||||
let q2 = Rotation3D::quaternion(0.0, 1.0, 0.0, 0.0);
|
||||
let q3 = Rotation3D::quaternion(0.0, 0.0, -1.0, 0.0);
|
||||
|
||||
// The values below can be obtained with a python program:
|
||||
// import numpy
|
||||
// import quaternion
|
||||
// q1 = numpy.quaternion(1, 0, 0, 0)
|
||||
// q2 = numpy.quaternion(0, 1, 0, 0)
|
||||
// quaternion.slerp_evaluate(q1, q2, 0.2)
|
||||
|
||||
assert!(q1.slerp(&q2, 0.0).approx_eq(&q1));
|
||||
assert!(q1.slerp(&q2, 0.2).approx_eq(&Rotation3D::quaternion(0.951056516295154, 0.309016994374947, 0.0, 0.0)));
|
||||
assert!(q1.slerp(&q2, 0.4).approx_eq(&Rotation3D::quaternion(0.809016994374947, 0.587785252292473, 0.0, 0.0)));
|
||||
assert!(q1.slerp(&q2, 0.6).approx_eq(&Rotation3D::quaternion(0.587785252292473, 0.809016994374947, 0.0, 0.0)));
|
||||
assert!(q1.slerp(&q2, 0.8).approx_eq(&Rotation3D::quaternion(0.309016994374947, 0.951056516295154, 0.0, 0.0)));
|
||||
assert!(q1.slerp(&q2, 1.0).approx_eq(&q2));
|
||||
|
||||
assert!(q1.slerp(&q3, 0.0).approx_eq(&q1));
|
||||
assert!(q1.slerp(&q3, 0.2).approx_eq(&Rotation3D::quaternion(0.951056516295154, 0.0, -0.309016994374947, 0.0)));
|
||||
assert!(q1.slerp(&q3, 0.4).approx_eq(&Rotation3D::quaternion(0.809016994374947, 0.0, -0.587785252292473, 0.0)));
|
||||
assert!(q1.slerp(&q3, 0.6).approx_eq(&Rotation3D::quaternion(0.587785252292473, 0.0, -0.809016994374947, 0.0)));
|
||||
assert!(q1.slerp(&q3, 0.8).approx_eq(&Rotation3D::quaternion(0.309016994374947, 0.0, -0.951056516295154, 0.0)));
|
||||
assert!(q1.slerp(&q3, 1.0).approx_eq(&q3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn around_axis() {
|
||||
use std::f32::consts::{PI, FRAC_PI_2};
|
||||
|
||||
// Two sort of trivial cases:
|
||||
let r1 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Radians::new(PI));
|
||||
let r2 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Radians::new(FRAC_PI_2));
|
||||
assert!(r1.rotate_point3d(&point3(1.0, 2.0, 0.0)).approx_eq(&point3(2.0, 1.0, 0.0)));
|
||||
assert!(r2.rotate_point3d(&point3(1.0, 0.0, 0.0)).approx_eq(&point3(0.5, 0.5, -0.5.sqrt())));
|
||||
|
||||
// A more arbitray test (made up with numpy):
|
||||
let r3 = Rotation3D::around_axis(vec3(0.5, 1.0, 2.0), Radians::new(2.291288));
|
||||
assert!(r3.rotate_point3d(&point3(1.0, 0.0, 0.0)).approx_eq(&point3(-0.58071821, 0.81401868, -0.01182979)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_euler() {
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
// First test simple separate yaw pitch and roll rotations, because it is easy to come
|
||||
// up with the corresponding quaternion.
|
||||
// Since several quaternions can represent the same transformation we compare the result
|
||||
// of transforming a point rather than the values of each qauetrnions.
|
||||
let p = point3(1.0, 2.0, 3.0);
|
||||
|
||||
let angle = Radians::new(FRAC_PI_2);
|
||||
let zero = Radians::new(0.0);
|
||||
|
||||
// roll
|
||||
let roll_re = Rotation3D::euler(angle, zero, zero);
|
||||
let roll_rq = Rotation3D::around_x(angle);
|
||||
let roll_pe = roll_re.rotate_point3d(&p);
|
||||
let roll_pq = roll_rq.rotate_point3d(&p);
|
||||
|
||||
// pitch
|
||||
let pitch_re = Rotation3D::euler(zero, angle, zero);
|
||||
let pitch_rq = Rotation3D::around_y(angle);
|
||||
let pitch_pe = pitch_re.rotate_point3d(&p);
|
||||
let pitch_pq = pitch_rq.rotate_point3d(&p);
|
||||
|
||||
// yaw
|
||||
let yaw_re = Rotation3D::euler(zero, zero, angle);
|
||||
let yaw_rq = Rotation3D::around_z(angle);
|
||||
let yaw_pe = yaw_re.rotate_point3d(&p);
|
||||
let yaw_pq = yaw_rq.rotate_point3d(&p);
|
||||
|
||||
assert!(roll_pe.approx_eq(&roll_pq));
|
||||
assert!(pitch_pe.approx_eq(&pitch_pq));
|
||||
assert!(yaw_pe.approx_eq(&yaw_pq));
|
||||
|
||||
// Now check that the yaw pitch and roll transformations when compined are applied in
|
||||
// the proper order: roll -> pitch -> yaw.
|
||||
let ypr_e = Rotation3D::euler(angle, angle, angle);
|
||||
let ypr_q = roll_rq.post_rotate(&pitch_rq).post_rotate(&yaw_rq);
|
||||
let ypr_pe = ypr_e.rotate_point3d(&p);
|
||||
let ypr_pq = ypr_q.rotate_point3d(&p);
|
||||
|
||||
assert!(ypr_pe.approx_eq(&ypr_pq));
|
||||
}
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
// 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 heapsize::HeapSizeOf;
|
||||
use num_traits::NumCast;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Mul, Sub, Div};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// 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 `ScaleFactor`. They
|
||||
/// may be types without values, such as empty enums. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::ScaleFactor;
|
||||
/// use euclid::Length;
|
||||
/// enum Mm {};
|
||||
/// enum Inch {};
|
||||
///
|
||||
/// let mm_per_inch: ScaleFactor<f32, Inch, Mm> = ScaleFactor::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 ScaleFactor<T, Src, Dst>(pub T, PhantomData<(Src, Dst)>);
|
||||
|
||||
impl<T: HeapSizeOf, Src, Dst> HeapSizeOf for ScaleFactor<T, Src, Dst> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T, Src, Dst> Deserialize<'de> for ScaleFactor<T, Src, Dst> where T: Deserialize<'de> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<ScaleFactor<T, Src, Dst>, D::Error>
|
||||
where D: Deserializer<'de> {
|
||||
Ok(ScaleFactor(try!(Deserialize::deserialize(deserializer)), PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Serialize for ScaleFactor<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> ScaleFactor<T, Src, Dst> {
|
||||
pub fn new(x: T) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> ScaleFactor<T, Src, Dst> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + One + Div<T, Output=T>, Src, Dst> ScaleFactor<T, Src, Dst> {
|
||||
/// The inverse ScaleFactor (1.0 / self).
|
||||
pub fn inv(&self) -> ScaleFactor<T, Dst, Src> {
|
||||
let one: T = One::one();
|
||||
ScaleFactor::new(one / self.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 * scale1
|
||||
impl<T: Clone + Mul<T, Output=T>, A, B, C>
|
||||
Mul<ScaleFactor<T, B, C>> for ScaleFactor<T, A, B> {
|
||||
type Output = ScaleFactor<T, A, C>;
|
||||
#[inline]
|
||||
fn mul(self, other: ScaleFactor<T, B, C>) -> ScaleFactor<T, A, C> {
|
||||
ScaleFactor::new(self.get() * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 + scale1
|
||||
impl<T: Clone + Add<T, Output=T>, Src, Dst> Add for ScaleFactor<T, Src, Dst> {
|
||||
type Output = ScaleFactor<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn add(self, other: ScaleFactor<T, Src, Dst>) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 - scale1
|
||||
impl<T: Clone + Sub<T, Output=T>, Src, Dst> Sub for ScaleFactor<T, Src, Dst> {
|
||||
type Output = ScaleFactor<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn sub(self, other: ScaleFactor<T, Src, Dst>) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, Src, Dst0> ScaleFactor<T, Src, Dst0> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> Option<ScaleFactor<T1, Src, Dst0>> {
|
||||
NumCast::from(self.get()).map(ScaleFactor::new)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ScaleFactor<T, Src, Dst> {
|
||||
fn eq(&self, other: &ScaleFactor<T, Src, Dst>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for ScaleFactor<T, Src, Dst> {
|
||||
fn clone(&self) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for ScaleFactor<T, Src, Dst> {}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for ScaleFactor<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 ScaleFactor<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ScaleFactor;
|
||||
|
||||
enum Inch {}
|
||||
enum Cm {}
|
||||
enum Mm {}
|
||||
|
||||
#[test]
|
||||
fn test_scale_factor() {
|
||||
let mm_per_inch: ScaleFactor<f32, Inch, Mm> = ScaleFactor::new(25.4);
|
||||
let cm_per_mm: ScaleFactor<f32, Mm, Cm> = ScaleFactor::new(0.1);
|
||||
|
||||
let mm_per_cm: ScaleFactor<f32, Cm, Mm> = cm_per_mm.inv();
|
||||
assert_eq!(mm_per_cm.get(), 10.0);
|
||||
|
||||
let cm_per_inch: ScaleFactor<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
|
||||
assert_eq!(cm_per_inch, ScaleFactor::new(2.54));
|
||||
|
||||
let a: ScaleFactor<isize, Inch, Inch> = ScaleFactor::new(2);
|
||||
let b: ScaleFactor<isize, Inch, Inch> = ScaleFactor::new(3);
|
||||
assert!(a != b);
|
||||
assert_eq!(a, a.clone());
|
||||
assert_eq!(a.clone() + b.clone(), ScaleFactor::new(5));
|
||||
assert_eq!(a - b, ScaleFactor::new(-1));
|
||||
}
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
// 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 std::fmt;
|
||||
use std::ops::Add;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
/// 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: top,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
left: 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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A SIMD enabled version of TypedSideOffsets2D specialized for i32.
|
||||
#[cfg(feature = "unstable")]
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(simd)]
|
||||
pub struct SideOffsets2DSimdI32 {
|
||||
pub top: i32,
|
||||
pub bottom: i32,
|
||||
pub right: i32,
|
||||
pub left: i32,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl HeapSizeOf for SideOffsets2DSimdI32 {
|
||||
fn heap_size_of_children(&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn new(top: i32, right: i32, bottom: i32, left: i32) -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32 {
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
right: right,
|
||||
left: left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn new_all_same(all: i32) -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32::new(all.clone(), all.clone(), all.clone(), all.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn horizontal(&self) -> i32 {
|
||||
self.left + self.right
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn vertical(&self) -> i32 {
|
||||
self.top + self.bottom
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Add for SideOffsets2DSimdI32 {
|
||||
type Output = SideOffsets2DSimdI32;
|
||||
#[inline]
|
||||
fn add(self, other: SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
|
||||
self + other // Use SIMD addition
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn zero() -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32 {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.top == 0 && self.right == 0 && self.bottom == 0 && self.left == 0
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
let is_zero: bool;
|
||||
unsafe {
|
||||
asm! {
|
||||
"ptest $1, $1
|
||||
setz $0"
|
||||
: "=r"(is_zero)
|
||||
: "x"(*self)
|
||||
:
|
||||
: "intel"
|
||||
};
|
||||
}
|
||||
is_zero
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SideOffsets2DSimdI32;
|
||||
|
||||
#[test]
|
||||
fn test_is_zero() {
|
||||
assert!(SideOffsets2DSimdI32::new_all_same(0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new_all_same(1).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(1, 0, 0, 0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(0, 1, 0, 0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(0, 0, 1, 0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(0, 0, 0, 1).is_zero());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg(bench)]
|
||||
mod bench {
|
||||
use test::BenchHarness;
|
||||
use std::num::Zero;
|
||||
use rand::{XorShiftRng, Rng};
|
||||
use super::SideOffsets2DSimdI32;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[bench]
|
||||
fn bench_naive_is_zero(bh: &mut BenchHarness) {
|
||||
fn is_zero(x: &SideOffsets2DSimdI32) -> bool {
|
||||
x.top.is_zero() && x.right.is_zero() && x.bottom.is_zero() && x.left.is_zero()
|
||||
}
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| is_zero(&rng.gen::<SideOffsets2DSimdI32>()))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_zero(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| rng.gen::<SideOffsets2DSimdI32>().is_zero())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_naive_add(bh: &mut BenchHarness) {
|
||||
fn add(x: &SideOffsets2DSimdI32, y: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32 {
|
||||
top: x.top + y.top,
|
||||
right: x.right + y.right,
|
||||
bottom: x.bottom + y.bottom,
|
||||
left: x.left + y.left,
|
||||
}
|
||||
}
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| add(&rng.gen::<SideOffsets2DSimdI32>(), &rng.gen::<SideOffsets2DSimdI32>()))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_add(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| rng.gen::<SideOffsets2DSimdI32>() + rng.gen::<SideOffsets2DSimdI32>())
|
||||
}
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
// 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_factor::ScaleFactor;
|
||||
use vector::{TypedVector2D, vec2};
|
||||
use num::*;
|
||||
|
||||
use num_traits::{NumCast, Signed};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::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: width,
|
||||
height: 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, 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<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U1> {
|
||||
type Output = TypedSize2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<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<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U2> {
|
||||
type Output = TypedSize2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<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) -> 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
|
||||
/// 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().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
|
@ -1,526 +0,0 @@
|
|||
// 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, Radians};
|
||||
use num::{One, Zero};
|
||||
use point::TypedPoint2D;
|
||||
use vector::{TypedVector2D, vec2};
|
||||
use rect::TypedRect;
|
||||
use transform3d::TypedTransform3D;
|
||||
use std::ops::{Add, Mul, Div, Sub, Neg};
|
||||
use std::marker::PhantomData;
|
||||
use approxeq::ApproxEq;
|
||||
use trig::Trig;
|
||||
use std::fmt;
|
||||
use num_traits::NumCast;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d transform stored as a 2 by 3 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, WordSpace, ScreenSpace>::transform_point4d`
|
||||
/// takes a `TypedPoint2D<f32, WordSpace>` 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: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: 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: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: 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) -> 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: Radians<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: Radians<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: Radians<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> +
|
||||
ApproxEq<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: 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 Radians;
|
||||
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
type Mat = Transform2D<f32>;
|
||||
|
||||
fn rad(v: f32) -> Radians<f32> { Radians::new(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 std::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));
|
||||
}
|
||||
}
|
|
@ -1,945 +0,0 @@
|
|||
// 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, Radians};
|
||||
use approxeq::ApproxEq;
|
||||
use trig::Trig;
|
||||
use point::{TypedPoint2D, TypedPoint3D, point2, point3};
|
||||
use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
|
||||
use rect::TypedRect;
|
||||
use transform2d::TypedTransform2D;
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::{One, Zero};
|
||||
use std::ops::{Add, Mul, Sub, Div, Neg};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
use num_traits::NumCast;
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d transform stored as a 4 by 4 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, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
|
||||
/// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<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 TypedTransform3D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T, pub m13: T, pub m14: T,
|
||||
pub m21: T, pub m22: T, pub m23: T, pub m24: T,
|
||||
pub m31: T, pub m32: T, pub m33: T, pub m34: T,
|
||||
pub m41: T, pub m42: T, pub m43: T, pub m44: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 4d transform type with no units.
|
||||
pub type Transform3D<T> = TypedTransform3D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T, Src, Dst> TypedTransform3D<T, Src, Dst> {
|
||||
/// Create a transform specifying its components in row-major order.
|
||||
///
|
||||
/// For example, the translation terms m41, m42, m43 on the last row with the
|
||||
/// row-major convention) are the 13rd, 14th and 15th parameters.
|
||||
#[inline]
|
||||
pub fn row_major(
|
||||
m11: T, m12: T, m13: T, m14: T,
|
||||
m21: T, m22: T, m23: T, m24: T,
|
||||
m31: T, m32: T, m33: T, m34: T,
|
||||
m41: T, m42: T, m43: T, m44: T)
|
||||
-> Self {
|
||||
TypedTransform3D {
|
||||
m11: m11, m12: m12, m13: m13, m14: m14,
|
||||
m21: m21, m22: m22, m23: m23, m24: m24,
|
||||
m31: m31, m32: m32, m33: m33, m34: m34,
|
||||
m41: m41, m42: m42, m43: m43, m44: m44,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its components in column-major order.
|
||||
///
|
||||
/// For example, the translation terms m41, m42, m43 on the last column with the
|
||||
/// column-major convention) are the 4th, 8th and 12nd parameters.
|
||||
#[inline]
|
||||
pub fn column_major(
|
||||
m11: T, m21: T, m31: T, m41: T,
|
||||
m12: T, m22: T, m32: T, m42: T,
|
||||
m13: T, m23: T, m33: T, m43: T,
|
||||
m14: T, m24: T, m34: T, m44: T)
|
||||
-> Self {
|
||||
TypedTransform3D {
|
||||
m11: m11, m12: m12, m13: m13, m14: m14,
|
||||
m21: m21, m22: m22, m23: m23, m24: m24,
|
||||
m31: m31, m32: m32, m33: m33, m34: m34,
|
||||
m41: m41, m42: m42, m43: m43, m44: m44,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
// 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.
|
||||
#[inline]
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform3D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform3D<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> +
|
||||
ApproxEq<T> +
|
||||
PartialOrd +
|
||||
Trig +
|
||||
One + Zero {
|
||||
|
||||
/// Create a 4 by 4 transform representing a 2d transformation, specifying its components
|
||||
/// in row-major order.
|
||||
#[inline]
|
||||
pub fn row_major_2d(m11: T, m12: T, m21: T, m22: T, m41: T, m42: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
m11, m12, _0, _0,
|
||||
m21, m22, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
m41, m42, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Create an orthogonal projection transform.
|
||||
pub fn ortho(left: T, right: T,
|
||||
bottom: T, top: T,
|
||||
near: T, far: T) -> Self {
|
||||
let tx = -((right + left) / (right - left));
|
||||
let ty = -((top + bottom) / (top - bottom));
|
||||
let tz = -((far + near) / (far - near));
|
||||
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let _2 = _1 + _1;
|
||||
TypedTransform3D::row_major(
|
||||
_2 / (right - left), _0 , _0 , _0,
|
||||
_0 , _2 / (top - bottom), _0 , _0,
|
||||
_0 , _0 , -_2 / (far - near), _0,
|
||||
tx , ty , tz , _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this transform can be represented with a TypedTransform2D.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-transforms/#2d-transform
|
||||
#[inline]
|
||||
pub fn is_2d(&self) -> bool {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
self.m31 == _0 && self.m32 == _0 &&
|
||||
self.m13 == _0 && self.m23 == _0 &&
|
||||
self.m43 == _0 && self.m14 == _0 &&
|
||||
self.m24 == _0 && self.m34 == _0 &&
|
||||
self.m33 == _1 && self.m44 == _1
|
||||
}
|
||||
|
||||
/// Create a 2D transform picking the relevent terms from this transform.
|
||||
///
|
||||
/// This method assumes that self represents a 2d transformation, callers
|
||||
/// should check that self.is_2d() returns true beforehand.
|
||||
pub fn to_2d(&self) -> TypedTransform2D<T, Src, Dst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m41, self.m42
|
||||
)
|
||||
}
|
||||
|
||||
/// Check whether shapes on the XY plane with Z pointing towards the
|
||||
/// screen transformed by this matrix would be facing back.
|
||||
pub fn is_backface_visible(&self) -> bool {
|
||||
// inverse().m33 < 0;
|
||||
let det = self.determinant();
|
||||
let m33 = self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 +
|
||||
self.m14 * self.m21 * self.m42 - self.m11 * self.m24 * self.m42 -
|
||||
self.m12 * self.m21 * self.m44 + self.m11 * self.m22 * self.m44;
|
||||
let _0: T = Zero::zero();
|
||||
(m33 * det) < _0
|
||||
}
|
||||
|
||||
pub fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
|
||||
self.m13.approx_eq(&other.m13) && self.m14.approx_eq(&other.m14) &&
|
||||
self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
|
||||
self.m23.approx_eq(&other.m23) && self.m24.approx_eq(&other.m24) &&
|
||||
self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32) &&
|
||||
self.m33.approx_eq(&other.m33) && self.m34.approx_eq(&other.m34) &&
|
||||
self.m41.approx_eq(&other.m41) && self.m42.approx_eq(&other.m42) &&
|
||||
self.m43.approx_eq(&other.m43) && self.m44.approx_eq(&other.m44)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> TypedTransform3D<T, Src, NewDst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> TypedTransform3D<T, NewSrc, Dst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Transform3D<T> {
|
||||
Transform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(m: &Transform3D<T>) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
m.m11, m.m12, m.m13, m.m14,
|
||||
m.m21, m.m22, m.m23, m.m24,
|
||||
m.m31, m.m32, m.m33, m.m34,
|
||||
m.m41, m.m42, m.m43, m.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
pub fn post_mul<NewDst>(&self, mat: &TypedTransform3D<T, Dst, NewDst>) -> TypedTransform3D<T, Src, NewDst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21 + self.m13 * mat.m31 + self.m14 * mat.m41,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22 + self.m13 * mat.m32 + self.m14 * mat.m42,
|
||||
self.m11 * mat.m13 + self.m12 * mat.m23 + self.m13 * mat.m33 + self.m14 * mat.m43,
|
||||
self.m11 * mat.m14 + self.m12 * mat.m24 + self.m13 * mat.m34 + self.m14 * mat.m44,
|
||||
self.m21 * mat.m11 + self.m22 * mat.m21 + self.m23 * mat.m31 + self.m24 * mat.m41,
|
||||
self.m21 * mat.m12 + self.m22 * mat.m22 + self.m23 * mat.m32 + self.m24 * mat.m42,
|
||||
self.m21 * mat.m13 + self.m22 * mat.m23 + self.m23 * mat.m33 + self.m24 * mat.m43,
|
||||
self.m21 * mat.m14 + self.m22 * mat.m24 + self.m23 * mat.m34 + self.m24 * mat.m44,
|
||||
self.m31 * mat.m11 + self.m32 * mat.m21 + self.m33 * mat.m31 + self.m34 * mat.m41,
|
||||
self.m31 * mat.m12 + self.m32 * mat.m22 + self.m33 * mat.m32 + self.m34 * mat.m42,
|
||||
self.m31 * mat.m13 + self.m32 * mat.m23 + self.m33 * mat.m33 + self.m34 * mat.m43,
|
||||
self.m31 * mat.m14 + self.m32 * mat.m24 + self.m33 * mat.m34 + self.m34 * mat.m44,
|
||||
self.m41 * mat.m11 + self.m42 * mat.m21 + self.m43 * mat.m31 + self.m44 * mat.m41,
|
||||
self.m41 * mat.m12 + self.m42 * mat.m22 + self.m43 * mat.m32 + self.m44 * mat.m42,
|
||||
self.m41 * mat.m13 + self.m42 * mat.m23 + self.m43 * mat.m33 + self.m44 * mat.m43,
|
||||
self.m41 * mat.m14 + self.m42 * mat.m24 + self.m43 * mat.m34 + self.m44 * mat.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies before self's transformation.
|
||||
pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform3D<T, NewSrc, Src>) -> TypedTransform3D<T, NewSrc, Dst> {
|
||||
mat.post_mul(self)
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
pub fn inverse(&self) -> Option<TypedTransform3D<T, Dst, Src>> {
|
||||
let det = self.determinant();
|
||||
|
||||
if det == Zero::zero() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// todo(gw): this could be made faster by special casing
|
||||
// for simpler transform types.
|
||||
let m = TypedTransform3D::row_major(
|
||||
self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 +
|
||||
self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 -
|
||||
self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44,
|
||||
|
||||
self.m14*self.m33*self.m42 - self.m13*self.m34*self.m42 -
|
||||
self.m14*self.m32*self.m43 + self.m12*self.m34*self.m43 +
|
||||
self.m13*self.m32*self.m44 - self.m12*self.m33*self.m44,
|
||||
|
||||
self.m13*self.m24*self.m42 - self.m14*self.m23*self.m42 +
|
||||
self.m14*self.m22*self.m43 - self.m12*self.m24*self.m43 -
|
||||
self.m13*self.m22*self.m44 + self.m12*self.m23*self.m44,
|
||||
|
||||
self.m14*self.m23*self.m32 - self.m13*self.m24*self.m32 -
|
||||
self.m14*self.m22*self.m33 + self.m12*self.m24*self.m33 +
|
||||
self.m13*self.m22*self.m34 - self.m12*self.m23*self.m34,
|
||||
|
||||
self.m24*self.m33*self.m41 - self.m23*self.m34*self.m41 -
|
||||
self.m24*self.m31*self.m43 + self.m21*self.m34*self.m43 +
|
||||
self.m23*self.m31*self.m44 - self.m21*self.m33*self.m44,
|
||||
|
||||
self.m13*self.m34*self.m41 - self.m14*self.m33*self.m41 +
|
||||
self.m14*self.m31*self.m43 - self.m11*self.m34*self.m43 -
|
||||
self.m13*self.m31*self.m44 + self.m11*self.m33*self.m44,
|
||||
|
||||
self.m14*self.m23*self.m41 - self.m13*self.m24*self.m41 -
|
||||
self.m14*self.m21*self.m43 + self.m11*self.m24*self.m43 +
|
||||
self.m13*self.m21*self.m44 - self.m11*self.m23*self.m44,
|
||||
|
||||
self.m13*self.m24*self.m31 - self.m14*self.m23*self.m31 +
|
||||
self.m14*self.m21*self.m33 - self.m11*self.m24*self.m33 -
|
||||
self.m13*self.m21*self.m34 + self.m11*self.m23*self.m34,
|
||||
|
||||
self.m22*self.m34*self.m41 - self.m24*self.m32*self.m41 +
|
||||
self.m24*self.m31*self.m42 - self.m21*self.m34*self.m42 -
|
||||
self.m22*self.m31*self.m44 + self.m21*self.m32*self.m44,
|
||||
|
||||
self.m14*self.m32*self.m41 - self.m12*self.m34*self.m41 -
|
||||
self.m14*self.m31*self.m42 + self.m11*self.m34*self.m42 +
|
||||
self.m12*self.m31*self.m44 - self.m11*self.m32*self.m44,
|
||||
|
||||
self.m12*self.m24*self.m41 - self.m14*self.m22*self.m41 +
|
||||
self.m14*self.m21*self.m42 - self.m11*self.m24*self.m42 -
|
||||
self.m12*self.m21*self.m44 + self.m11*self.m22*self.m44,
|
||||
|
||||
self.m14*self.m22*self.m31 - self.m12*self.m24*self.m31 -
|
||||
self.m14*self.m21*self.m32 + self.m11*self.m24*self.m32 +
|
||||
self.m12*self.m21*self.m34 - self.m11*self.m22*self.m34,
|
||||
|
||||
self.m23*self.m32*self.m41 - self.m22*self.m33*self.m41 -
|
||||
self.m23*self.m31*self.m42 + self.m21*self.m33*self.m42 +
|
||||
self.m22*self.m31*self.m43 - self.m21*self.m32*self.m43,
|
||||
|
||||
self.m12*self.m33*self.m41 - self.m13*self.m32*self.m41 +
|
||||
self.m13*self.m31*self.m42 - self.m11*self.m33*self.m42 -
|
||||
self.m12*self.m31*self.m43 + self.m11*self.m32*self.m43,
|
||||
|
||||
self.m13*self.m22*self.m41 - self.m12*self.m23*self.m41 -
|
||||
self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 +
|
||||
self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43,
|
||||
|
||||
self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 +
|
||||
self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 -
|
||||
self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33
|
||||
);
|
||||
|
||||
let _1: T = One::one();
|
||||
Some(m.mul_s(_1 / det))
|
||||
}
|
||||
|
||||
/// Compute the determinant of the transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m14 * self.m23 * self.m32 * self.m41 -
|
||||
self.m13 * self.m24 * self.m32 * self.m41 -
|
||||
self.m14 * self.m22 * self.m33 * self.m41 +
|
||||
self.m12 * self.m24 * self.m33 * self.m41 +
|
||||
self.m13 * self.m22 * self.m34 * self.m41 -
|
||||
self.m12 * self.m23 * self.m34 * self.m41 -
|
||||
self.m14 * self.m23 * self.m31 * self.m42 +
|
||||
self.m13 * self.m24 * self.m31 * self.m42 +
|
||||
self.m14 * self.m21 * self.m33 * self.m42 -
|
||||
self.m11 * self.m24 * self.m33 * self.m42 -
|
||||
self.m13 * self.m21 * self.m34 * self.m42 +
|
||||
self.m11 * self.m23 * self.m34 * self.m42 +
|
||||
self.m14 * self.m22 * self.m31 * self.m43 -
|
||||
self.m12 * self.m24 * self.m31 * self.m43 -
|
||||
self.m14 * self.m21 * self.m32 * self.m43 +
|
||||
self.m11 * self.m24 * self.m32 * self.m43 +
|
||||
self.m12 * self.m21 * self.m34 * self.m43 -
|
||||
self.m11 * self.m22 * self.m34 * self.m43 -
|
||||
self.m13 * self.m22 * self.m31 * self.m44 +
|
||||
self.m12 * self.m23 * self.m31 * self.m44 +
|
||||
self.m13 * self.m21 * self.m32 * self.m44 -
|
||||
self.m11 * self.m23 * self.m32 * self.m44 -
|
||||
self.m12 * self.m21 * self.m33 * self.m44 +
|
||||
self.m11 * self.m22 * self.m33 * self.m44
|
||||
}
|
||||
|
||||
/// Multiplies all of the transform's component by a scalar and returns the result.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn mul_s(&self, x: T) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * x, self.m12 * x, self.m13 * x, self.m14 * x,
|
||||
self.m21 * x, self.m22 * x, self.m23 * x, self.m24 * x,
|
||||
self.m31 * x, self.m32 * x, self.m33 * x, self.m34 * x,
|
||||
self.m41 * x, self.m42 * x, self.m43 * x, self.m44 * x
|
||||
)
|
||||
}
|
||||
|
||||
/// Convenience function to create a scale transform from a ScaleFactor.
|
||||
pub fn from_scale_factor(scale: ScaleFactor<T, Src, Dst>) -> Self {
|
||||
TypedTransform3D::create_scale(scale.get(), scale.get(), scale.get())
|
||||
}
|
||||
|
||||
/// Returns the given 2d point transformed by this transform.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point2d(&self, p: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
let x = p.x * self.m11 + p.y * self.m21 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + self.m42;
|
||||
|
||||
let w = p.x * self.m14 + p.y * self.m24 + self.m44;
|
||||
|
||||
point2(x/w, y/w)
|
||||
}
|
||||
|
||||
/// Returns the given 2d vector transformed by this matrix.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector2d(&self, v: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
vec2(
|
||||
v.x * self.m11 + v.y * self.m21,
|
||||
v.x * self.m12 + v.y * self.m22,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the given 3d point transformed by this transform.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point3d(&self, p: &TypedPoint3D<T, Src>) -> TypedPoint3D<T, Dst> {
|
||||
let x = p.x * self.m11 + p.y * self.m21 + p.z * self.m31 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + p.z * self.m32 + self.m42;
|
||||
let z = p.x * self.m13 + p.y * self.m23 + p.z * self.m33 + self.m43;
|
||||
let w = p.x * self.m14 + p.y * self.m24 + p.z * self.m34 + self.m44;
|
||||
|
||||
point3(x/w, y/w, z/w)
|
||||
}
|
||||
|
||||
/// Returns the given 3d vector transformed by this matrix.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector3d(&self, v: &TypedVector3D<T, Src>) -> TypedVector3D<T, Dst> {
|
||||
vec3(
|
||||
v.x * self.m11 + v.y * self.m21 + v.z * self.m31,
|
||||
v.x * self.m12 + v.y * self.m22 + v.z * self.m32,
|
||||
v.x * self.m13 + v.y * self.m23 + v.z * self.m33,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
|
||||
/// transform.
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::from_points(&[
|
||||
self.transform_point2d(&rect.origin),
|
||||
self.transform_point2d(&rect.top_right()),
|
||||
self.transform_point2d(&rect.bottom_left()),
|
||||
self.transform_point2d(&rect.bottom_right()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Create a 3d translation transform
|
||||
pub fn create_translation(x: T, y: T, z: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
x, y, z, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a translation applied before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_translate(&self, v: TypedVector3D<T, Src>) -> Self {
|
||||
self.pre_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
|
||||
}
|
||||
|
||||
/// Returns a transform with a translation applied after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_translate(&self, v: TypedVector3D<T, Dst>) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
|
||||
}
|
||||
|
||||
/// Create a 3d scale transform
|
||||
pub fn create_scale(x: T, y: T, z: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
x, _0, _0, _0,
|
||||
_0, y, _0, _0,
|
||||
_0, _0, z, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a scale applied before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_scale(&self, x: T, y: T, z: T) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * x, self.m12, self.m13, self.m14,
|
||||
self.m21 , self.m22 * y, self.m23, self.m24,
|
||||
self.m31 , self.m32, self.m33 * z, self.m34,
|
||||
self.m41 , self.m42, self.m43, self.m44
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a scale applied after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_scale(&self, x: T, y: T, z: T) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_scale(x, y, z))
|
||||
}
|
||||
|
||||
/// Create a 3d rotation transform from an angle / axis.
|
||||
/// The supplied axis must be normalized.
|
||||
pub fn create_rotation(x: T, y: T, z: T, theta: Radians<T>) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let _2 = _1 + _1;
|
||||
|
||||
let xx = x * x;
|
||||
let yy = y * y;
|
||||
let zz = z * z;
|
||||
|
||||
let half_theta = theta.get() / _2;
|
||||
let sc = half_theta.sin() * half_theta.cos();
|
||||
let sq = half_theta.sin() * half_theta.sin();
|
||||
|
||||
TypedTransform3D::row_major(
|
||||
_1 - _2 * (yy + zz) * sq,
|
||||
_2 * (x * y * sq - z * sc),
|
||||
_2 * (x * z * sq + y * sc),
|
||||
_0,
|
||||
|
||||
_2 * (x * y * sq + z * sc),
|
||||
_1 - _2 * (xx + zz) * sq,
|
||||
_2 * (y * z * sq - x * sc),
|
||||
_0,
|
||||
|
||||
_2 * (x * z * sq - y * sc),
|
||||
_2 * (y * z * sq + x * sc),
|
||||
_1 - _2 * (xx + yy) * sq,
|
||||
_0,
|
||||
|
||||
_0,
|
||||
_0,
|
||||
_0,
|
||||
_1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a rotation applied after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_rotate(&self, x: T, y: T, z: T, theta: Radians<T>) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
|
||||
}
|
||||
|
||||
/// Returns a transform with a rotation applied before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_rotate(&self, x: T, y: T, z: T, theta: Radians<T>) -> Self {
|
||||
self.pre_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
|
||||
}
|
||||
|
||||
/// Create a 2d skew transform.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-transforms/#funcdef-skew
|
||||
pub fn create_skew(alpha: Radians<T>, beta: Radians<T>) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let (sx, sy) = (beta.get().tan(), alpha.get().tan());
|
||||
TypedTransform3D::row_major(
|
||||
_1, sx, _0, _0,
|
||||
sy, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a simple perspective projection transform
|
||||
pub fn create_perspective(d: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, -_1 / d,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> TypedTransform3D<T, Src, Dst> {
|
||||
/// 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; 16] {
|
||||
[
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
pub fn to_column_major_array(&self) -> [T; 16] {
|
||||
[
|
||||
self.m11, self.m21, self.m31, self.m41,
|
||||
self.m12, self.m22, self.m32, self.m42,
|
||||
self.m13, self.m23, self.m33, self.m43,
|
||||
self.m14, self.m24, self.m34, self.m44
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 4 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; 4]; 4] {
|
||||
[
|
||||
[self.m11, self.m12, self.m13, self.m14],
|
||||
[self.m21, self.m22, self.m23, self.m24],
|
||||
[self.m31, self.m32, self.m33, self.m34],
|
||||
[self.m41, self.m42, self.m43, self.m44]
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 4 columns in (in row-major order,
|
||||
/// or 4 rows in column-major order) as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_column_arrays(&self) -> [[T; 4]; 4] {
|
||||
[
|
||||
[self.m11, self.m21, self.m31, self.m41],
|
||||
[self.m12, self.m22, self.m32, self.m42],
|
||||
[self.m13, self.m23, self.m33, self.m43],
|
||||
[self.m14, self.m24, self.m34, self.m44]
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 16 elements in row-major order.
|
||||
pub fn from_array(array: [T; 16]) -> Self {
|
||||
Self::row_major(
|
||||
array[0], array[1], array[2], array[3],
|
||||
array[4], array[5], array[6], array[7],
|
||||
array[8], array[9], array[10], array[11],
|
||||
array[12], array[13], array[14], array[15],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 4 rows of 4 elements (row-major order).
|
||||
pub fn from_row_arrays(array: [[T; 4]; 4]) -> Self {
|
||||
Self::row_major(
|
||||
array[0][0], array[0][1], array[0][2], array[0][3],
|
||||
array[1][0], array[1][1], array[1][2], array[1][3],
|
||||
array[2][0], array[2][1], array[2][2], array[2][3],
|
||||
array[3][0], array[3][1], array[3][2], array[3][3],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Src, Dst> TypedTransform3D<T0, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> Option<TypedTransform3D<T1, Src, Dst>> {
|
||||
match (NumCast::from(self.m11), NumCast::from(self.m12),
|
||||
NumCast::from(self.m13), NumCast::from(self.m14),
|
||||
NumCast::from(self.m21), NumCast::from(self.m22),
|
||||
NumCast::from(self.m23), NumCast::from(self.m24),
|
||||
NumCast::from(self.m31), NumCast::from(self.m32),
|
||||
NumCast::from(self.m33), NumCast::from(self.m34),
|
||||
NumCast::from(self.m41), NumCast::from(self.m42),
|
||||
NumCast::from(self.m43), NumCast::from(self.m44)) {
|
||||
(Some(m11), Some(m12), Some(m13), Some(m14),
|
||||
Some(m21), Some(m22), Some(m23), Some(m24),
|
||||
Some(m31), Some(m32), Some(m33), Some(m34),
|
||||
Some(m41), Some(m42), Some(m43), Some(m44)) => {
|
||||
Some(TypedTransform3D::row_major(m11, m12, m13, m14,
|
||||
m21, m22, m23, m24,
|
||||
m31, m32, m33, m34,
|
||||
m41, m42, m43, m44))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> fmt::Debug for TypedTransform3D<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 tests {
|
||||
use approxeq::ApproxEq;
|
||||
use transform2d::Transform2D;
|
||||
use point::{Point2D, Point3D};
|
||||
use Radians;
|
||||
use super::*;
|
||||
|
||||
use std::f32::consts::{FRAC_PI_2, PI};
|
||||
|
||||
type Mf32 = Transform3D<f32>;
|
||||
|
||||
// For convenience.
|
||||
fn rad(v: f32) -> Radians<f32> { Radians::new(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mf32::create_translation(1.0, 2.0, 3.0);
|
||||
let t2 = Mf32::identity().pre_translate(vec3(1.0, 2.0, 3.0));
|
||||
let t3 = Mf32::identity().post_translate(vec3(1.0, 2.0, 3.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point3d(&Point3D::new(1.0, 1.0, 1.0)), Point3D::new(2.0, 3.0, 4.0));
|
||||
assert_eq!(t1.transform_point2d(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_mul(&t1), Mf32::create_translation(2.0, 4.0, 6.0));
|
||||
|
||||
assert!(!t1.is_2d());
|
||||
assert_eq!(Mf32::create_translation(1.0, 2.0, 3.0).to_2d(), Transform2D::create_translation(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let r2 = Mf32::identity().pre_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let r3 = Mf32::identity().post_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r1, r3);
|
||||
|
||||
assert!(r1.transform_point3d(&Point3D::new(1.0, 2.0, 3.0)).approx_eq(&Point3D::new(2.0, -1.0, 3.0)));
|
||||
assert!(r1.transform_point2d(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
|
||||
|
||||
assert!(r1.post_mul(&r1).approx_eq(&Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2*2.0))));
|
||||
|
||||
assert!(r1.is_2d());
|
||||
assert!(r1.to_2d().approx_eq(&Transform2D::create_rotation(rad(FRAC_PI_2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mf32::create_scale(2.0, 3.0, 4.0);
|
||||
let s2 = Mf32::identity().pre_scale(2.0, 3.0, 4.0);
|
||||
let s3 = Mf32::identity().post_scale(2.0, 3.0, 4.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
assert!(s1.transform_point3d(&Point3D::new(2.0, 2.0, 2.0)).approx_eq(&Point3D::new(4.0, 6.0, 8.0)));
|
||||
assert!(s1.transform_point2d(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
|
||||
|
||||
assert_eq!(s1.post_mul(&s1), Mf32::create_scale(4.0, 9.0, 16.0));
|
||||
|
||||
assert!(!s1.is_2d());
|
||||
assert_eq!(Mf32::create_scale(2.0, 3.0, 0.0).to_2d(), Transform2D::create_scale(2.0, 3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_ortho() {
|
||||
let (left, right, bottom, top) = (0.0f32, 1.0f32, 0.1f32, 1.0f32);
|
||||
let (near, far) = (-1.0f32, 1.0f32);
|
||||
let result = Mf32::ortho(left, right, bottom, top, near, far);
|
||||
let expected = Mf32::row_major(
|
||||
2.0, 0.0, 0.0, 0.0,
|
||||
0.0, 2.22222222, 0.0, 0.0,
|
||||
0.0, 0.0, -1.0, 0.0,
|
||||
-1.0, -1.22222222, -0.0, 1.0
|
||||
);
|
||||
debug!("result={:?} expected={:?}", result, expected);
|
||||
assert!(result.approx_eq(&expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_2d() {
|
||||
assert!(Mf32::identity().is_2d());
|
||||
assert!(Mf32::create_rotation(0.0, 0.0, 1.0, rad(0.7854)).is_2d());
|
||||
assert!(!Mf32::create_rotation(0.0, 1.0, 0.0, rad(0.7854)).is_2d());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_row_major_2d() {
|
||||
let m1 = Mf32::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
|
||||
let m2 = Mf32::row_major(
|
||||
1.0, 2.0, 0.0, 0.0,
|
||||
3.0, 4.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
5.0, 6.0, 0.0, 1.0
|
||||
);
|
||||
assert_eq!(m1, m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_major() {
|
||||
assert_eq!(
|
||||
Mf32::row_major(
|
||||
1.0, 2.0, 3.0, 4.0,
|
||||
5.0, 6.0, 7.0, 8.0,
|
||||
9.0, 10.0, 11.0, 12.0,
|
||||
13.0, 14.0, 15.0, 16.0,
|
||||
),
|
||||
Mf32::column_major(
|
||||
1.0, 5.0, 9.0, 13.0,
|
||||
2.0, 6.0, 10.0, 14.0,
|
||||
3.0, 7.0, 11.0, 15.0,
|
||||
4.0, 8.0, 12.0, 16.0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_simple() {
|
||||
let m1 = Mf32::identity();
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.approx_eq(&m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mf32::create_scale(1.5, 0.3, 2.1);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mf32::create_translation(-132.0, 0.3, 493.0);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_rotate() {
|
||||
let m1 = Mf32::create_rotation(0.0, 1.0, 0.0, rad(1.57));
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_transform_point_2d() {
|
||||
let m1 = Mf32::create_translation(100.0, 200.0, 0.0);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
|
||||
let p1 = Point2D::new(1000.0, 2000.0);
|
||||
let p2 = m1.transform_point2d(&p1);
|
||||
assert!(p2.eq(&Point2D::new(1100.0, 2200.0)));
|
||||
|
||||
let p3 = m2.transform_point2d(&p2);
|
||||
assert!(p3.eq(&p1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inverse_none() {
|
||||
assert!(Mf32::create_scale(2.0, 0.0, 2.0).inverse().is_none());
|
||||
assert!(Mf32::create_scale(2.0, 2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = Transform3D::identity().post_scale(1.0, 2.0, 3.0).post_translate(vec3(1.0, 2.0, 3.0));
|
||||
let m2 = Transform3D::identity().pre_translate(vec3(1.0, 2.0, 3.0)).pre_scale(1.0, 2.0, 3.0);
|
||||
assert!(m1.approx_eq(&m2));
|
||||
|
||||
let r = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let t = Mf32::create_translation(2.0, 3.0, 0.0);
|
||||
|
||||
let a = Point3D::new(1.0, 1.0, 1.0);
|
||||
|
||||
assert!(r.post_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0)));
|
||||
assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0)));
|
||||
assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&r.transform_point3d(&t.transform_point3d(&a))));
|
||||
|
||||
assert!(r.pre_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0)));
|
||||
assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0)));
|
||||
assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&t.transform_point3d(&r.transform_point3d(&a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use std::mem::size_of;
|
||||
assert_eq!(size_of::<Transform3D<f32>>(), 16*size_of::<f32>());
|
||||
assert_eq!(size_of::<Transform3D<f64>>(), 16*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_associativity() {
|
||||
let m1 = Mf32::row_major(3.0, 2.0, 1.5, 1.0,
|
||||
0.0, 4.5, -1.0, -4.0,
|
||||
0.0, 3.5, 2.5, 40.0,
|
||||
0.0, 3.0, 0.0, 1.0);
|
||||
let m2 = Mf32::row_major(1.0, -1.0, 3.0, 0.0,
|
||||
-1.0, 0.5, 0.0, 2.0,
|
||||
1.5, -2.0, 6.0, 0.0,
|
||||
-2.5, 6.0, 1.0, 1.0);
|
||||
|
||||
let p = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p1 = m2.pre_mul(&m1).transform_point3d(&p);
|
||||
let p2 = m2.transform_point3d(&m1.transform_point3d(&p));
|
||||
assert!(p1.approx_eq(&p2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = Transform3D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec3(0.1, 0.0, 0.0));
|
||||
assert!(!m2.is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_vector() {
|
||||
// Translation does not apply to vectors.
|
||||
let m = Mf32::create_translation(1.0, 2.0, 3.0);
|
||||
let v1 = vec3(10.0, -10.0, 3.0);
|
||||
assert_eq!(v1, m.transform_vector3d(&v1));
|
||||
// While it does apply to points.
|
||||
assert!(v1.to_point() != m.transform_point3d(&v1.to_point()));
|
||||
|
||||
// same thing with 2d vectors/points
|
||||
let v2 = vec2(10.0, -5.0);
|
||||
assert_eq!(v2, m.transform_vector2d(&v2));
|
||||
assert!(v2.to_point() != m.transform_point2d(&v2.to_point()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_backface_visible() {
|
||||
// backface is not visible for rotate-x 0 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(0.0));
|
||||
assert!(!r1.is_backface_visible());
|
||||
// backface is not visible for rotate-x 45 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI * 0.25));
|
||||
assert!(!r1.is_backface_visible());
|
||||
// backface is visible for rotate-x 180 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI));
|
||||
assert!(r1.is_backface_visible());
|
||||
// backface is visible for rotate-x 225 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI * 1.25));
|
||||
assert!(r1.is_backface_visible());
|
||||
// backface is not visible for non-inverseable matrix
|
||||
let r1 = Mf32::create_scale(2.0, 0.0, 2.0);
|
||||
assert!(!r1.is_backface_visible());
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
|
||||
macro_rules! trig {
|
||||
($ty:ty) => (
|
||||
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() }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
trig!(f32);
|
||||
trig!(f64);
|
|
@ -1,949 +0,0 @@
|
|||
// 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 point::{TypedPoint2D, TypedPoint3D, point2, point3};
|
||||
use size::{TypedSize2D, size2};
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::*;
|
||||
use num_traits::{Float, NumCast, Signed};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Neg, Mul, Sub, Div, AddAssign, SubAssign, MulAssign, DivAssign};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d Vector tagged with a unit.
|
||||
pub struct TypedVector2D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d vector type with no unit.
|
||||
///
|
||||
/// `Vector2D` provides the same methods as `TypedVector2D`.
|
||||
pub type Vector2D<T> = TypedVector2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedVector2D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
TypedVector2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
/// Convert into a 3d vector.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> TypedVector3D<T, U> {
|
||||
vec3(self.x, self.y, Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedVector2D<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 TypedVector2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector2D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
TypedVector2D { x: x, y: y, _unit: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedVector2D<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 {
|
||||
vec2(x.0, y.0)
|
||||
}
|
||||
|
||||
/// Create a 3d vector from this one, using the specified z value.
|
||||
#[inline]
|
||||
pub fn extend(&self, z: T) -> TypedVector3D<T, U> {
|
||||
vec3(self.x, self.y, z)
|
||||
}
|
||||
|
||||
/// Cast this vector into a point.
|
||||
///
|
||||
/// Equivalent to adding this vector to the origin.
|
||||
#[inline]
|
||||
pub fn to_point(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Swap x and y.
|
||||
#[inline]
|
||||
pub fn yx(&self) -> Self {
|
||||
vec2(self.y, self.x)
|
||||
}
|
||||
|
||||
/// Cast this vector into a size.
|
||||
#[inline]
|
||||
pub fn to_size(&self) -> TypedSize2D<T, U> {
|
||||
size2(self.x, self.y)
|
||||
}
|
||||
|
||||
|
||||
/// 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) -> Vector2D<T> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Vector2D<T>) -> Self {
|
||||
vec2(p.x, p.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector2D<T, U>
|
||||
where T: Copy + Mul<T, Output=T> + Add<T, Output=T> + Sub<T, Output=T> {
|
||||
/// Dot product.
|
||||
#[inline]
|
||||
pub fn dot(self, other: Self) -> T {
|
||||
self.x * other.x + self.y * other.y
|
||||
}
|
||||
|
||||
/// Returns the norm of the cross product [self.x, self.y, 0] x [other.x, other.y, 0]..
|
||||
#[inline]
|
||||
pub fn cross(self, other: Self) -> T {
|
||||
self.x * other.y - self.y * other.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize(self) -> Self where T: Float + ApproxEq<T> {
|
||||
let dot = self.dot(self);
|
||||
if dot.approx_eq(&T::zero()) {
|
||||
self
|
||||
} else {
|
||||
self / dot.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square_length(&self) -> T {
|
||||
self.x * self.x + self.y * self.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn length(&self) -> T where T: Float + ApproxEq<T> {
|
||||
self.square_length().sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector2D<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this vector and another vector.
|
||||
///
|
||||
/// `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;
|
||||
(*self) * one_t + other * t
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
TypedVector2D::new(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> AddAssign for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> SubAssign<TypedVector2D<T, U>> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self {
|
||||
vec2(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
vec2(-self.x, -self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
vec2(self.x.min(other.x), self.y.min(other.y))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
vec2(self.x.max(other.x), self.y.max(other.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
vec2(self.x * scale, self.y * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
vec2(self.x / scale, self.y / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> MulAssign<T> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> DivAssign<T> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedVector2D<T, U1> {
|
||||
type Output = TypedVector2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedVector2D<T, U2> {
|
||||
vec2(self.x * scale.get(), self.y * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedVector2D<T, U2> {
|
||||
type Output = TypedVector2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedVector2D<T, U1> {
|
||||
vec2(self.x / scale.get(), self.y / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedVector2D<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 {
|
||||
vec2(self.x.round(), self.y.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedVector2D<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 {
|
||||
vec2(self.x.ceil(), self.y.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedVector2D<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 {
|
||||
vec2(self.x.floor(), self.y.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedVector2D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating vector 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) -> Option<TypedVector2D<NewT, U>> {
|
||||
match (NumCast::from(self.x), NumCast::from(self.y)) {
|
||||
(Some(x), Some(y)) => Some(TypedVector2D::new(x, y)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` vector.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedVector2D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, 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) -> TypedVector2D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i32 vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, 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) -> TypedVector2D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i64 vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, 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) -> TypedVector2D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy+ApproxEq<T>, U> ApproxEq<TypedVector2D<T, U>> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
vec2(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 TypedVector2D<T, U> {
|
||||
fn into(self) -> [T; 2] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 2]> for TypedVector2D<T, U> {
|
||||
fn from(array: [T; 2]) -> Self {
|
||||
vec2(array[0], array[1])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector2D<T, U>
|
||||
where T: Signed {
|
||||
pub fn abs(&self) -> Self {
|
||||
vec2(self.x.abs(), self.y.abs())
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d Vector tagged with a unit.
|
||||
pub struct TypedVector3D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 3d vector type with no unit.
|
||||
///
|
||||
/// `Vector3D` provides the same methods as `TypedVector3D`.
|
||||
pub type Vector3D<T> = TypedVector3D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedVector3D<T, U> {
|
||||
/// Constructor, setting all copmonents to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
vec3(Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array_4d(&self) -> [T; 4] {
|
||||
[self.x, self.y, self.z, Zero::zero()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedVector3D<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 TypedVector3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector3D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T) -> Self {
|
||||
TypedVector3D { x: x, y: y, z: z, _unit: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedVector3D<T, U> {
|
||||
/// 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>) -> TypedVector3D<T, U> {
|
||||
vec3(x.0, y.0, z.0)
|
||||
}
|
||||
|
||||
/// Cast this vector into a point.
|
||||
///
|
||||
/// Equivalent to adding this vector to the origin.
|
||||
#[inline]
|
||||
pub fn to_point(&self) -> TypedPoint3D<T, U> {
|
||||
point3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d vector using this vector's x and y coordinates
|
||||
#[inline]
|
||||
pub fn xy(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Returns a 2d vector using this vector's x and z coordinates
|
||||
#[inline]
|
||||
pub fn xz(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d vector using this vector's x and z coordinates
|
||||
#[inline]
|
||||
pub fn yz(&self) -> TypedVector2D<T, U> {
|
||||
vec2(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) -> Vector3D<T> {
|
||||
vec3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Vector3D<T>) -> Self {
|
||||
vec3(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
/// Convert into a 2d vector.
|
||||
#[inline]
|
||||
pub fn to_2d(&self) -> TypedVector2D<T, U> {
|
||||
self.xy()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mul<T, Output=T> +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Copy, U> TypedVector3D<T, U> {
|
||||
|
||||
// Dot product.
|
||||
#[inline]
|
||||
pub fn dot(self, other: Self) -> T {
|
||||
self.x * other.x +
|
||||
self.y * other.y +
|
||||
self.z * other.z
|
||||
}
|
||||
|
||||
// Cross product.
|
||||
#[inline]
|
||||
pub fn cross(self, other: Self) -> Self {
|
||||
vec3(
|
||||
self.y * other.z - self.z * other.y,
|
||||
self.z * other.x - self.x * other.z,
|
||||
self.x * other.y - self.y * other.x
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize(self) -> Self where T: Float + ApproxEq<T> {
|
||||
let dot = self.dot(self);
|
||||
if dot.approx_eq(&T::zero()) {
|
||||
self
|
||||
} else {
|
||||
self / dot.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square_length(&self) -> T {
|
||||
self.x * self.x + self.y * self.y + self.z * self.z
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn length(&self) -> T where T: Float + ApproxEq<T> {
|
||||
self.square_length().sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector3D<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this vector and another vector.
|
||||
///
|
||||
/// `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;
|
||||
(*self) * one_t + other * t
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: Self) -> Self {
|
||||
vec3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self {
|
||||
vec3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> AddAssign for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> SubAssign<TypedVector3D<T, U>> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
vec3(-self.x, -self.y, -self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
Self::new(self.x * scale, self.y * scale, self.z * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
Self::new(self.x / scale, self.y / scale, self.z / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> MulAssign<T> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> DivAssign<T> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
vec3(self.x.min(other.x), self.y.min(other.y), self.z.min(other.z))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
vec3(self.x.max(other.x), self.y.max(other.y), self.z.max(other.z))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedVector3D<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 {
|
||||
vec3(self.x.round(), self.y.round(), self.z.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedVector3D<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 {
|
||||
vec3(self.x.ceil(), self.y.ceil(), self.z.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedVector3D<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 {
|
||||
vec3(self.x.floor(), self.y.floor(), self.z.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedVector3D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating vector 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) -> Option<TypedVector3D<NewT, U>> {
|
||||
match (NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
NumCast::from(self.z)) {
|
||||
(Some(x), Some(y), Some(z)) => Some(vec3(x, y, z)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` vector.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedVector3D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, 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) -> TypedVector3D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, 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) -> TypedVector3D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, 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) -> TypedVector3D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy+ApproxEq<T>, U> ApproxEq<TypedVector3D<T, U>> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
vec3(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 TypedVector3D<T, U> {
|
||||
fn into(self) -> [T; 3] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 3]> for TypedVector3D<T, U> {
|
||||
fn from(array: [T; 3]) -> Self {
|
||||
vec3(array[0], array[1], array[2])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector3D<T, U>
|
||||
where T: Signed {
|
||||
pub fn abs(&self) -> Self {
|
||||
vec3(self.x.abs(), self.y.abs(), self.z.abs())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience constructor.
|
||||
#[inline]
|
||||
pub fn vec2<T, U>(x: T, y: T) -> TypedVector2D<T, U> {
|
||||
TypedVector2D::new(x, y)
|
||||
}
|
||||
|
||||
/// Convenience constructor.
|
||||
#[inline]
|
||||
pub fn vec3<T, U>(x: T, y: T, z: T) -> TypedVector3D<T, U> {
|
||||
TypedVector3D::new(x, y, z)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod vector2d {
|
||||
use super::{Vector2D, vec2};
|
||||
type Vec2 = Vector2D<f32>;
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1: Vec2 = vec2(3.0, 5.0);
|
||||
|
||||
let result = p1 * 5.0;
|
||||
|
||||
assert_eq!(result, Vector2D::new(15.0, 25.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let p1: Vec2 = vec2(2.0, 7.0);
|
||||
let p2: Vec2 = vec2(13.0, 11.0);
|
||||
assert_eq!(p1.dot(p2), 103.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross() {
|
||||
let p1: Vec2 = vec2(4.0, 7.0);
|
||||
let p2: Vec2 = vec2(13.0, 8.0);
|
||||
let r = p1.cross(p2);
|
||||
assert_eq!(r, -59.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let p0: Vec2 = Vec2::zero();
|
||||
let p1: Vec2 = vec2(4.0, 0.0);
|
||||
let p2: Vec2 = vec2(3.0, -4.0);
|
||||
assert_eq!(p0.normalize(), p0);
|
||||
assert_eq!(p1.normalize(), vec2(1.0, 0.0));
|
||||
assert_eq!(p2.normalize(), vec2(0.6, -0.8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1: Vec2 = vec2(1.0, 3.0);
|
||||
let p2: Vec2 = vec2(2.0, 2.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, vec2(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1: Vec2 = vec2(1.0, 3.0);
|
||||
let p2: Vec2 = vec2(2.0, 2.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, vec2(2.0, 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod typedvector2d {
|
||||
use super::{TypedVector2D, Vector2D, vec2};
|
||||
use scale_factor::ScaleFactor;
|
||||
|
||||
pub enum Mm {}
|
||||
pub enum Cm {}
|
||||
|
||||
pub type Vector2DMm<T> = TypedVector2D<T, Mm>;
|
||||
pub type Vector2DCm<T> = TypedVector2D<T, Cm>;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Vector2DMm::new(1.0, 2.0);
|
||||
let p2 = Vector2DMm::new(3.0, 4.0);
|
||||
|
||||
let result = p1 + p2;
|
||||
|
||||
assert_eq!(result, vec2(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_assign() {
|
||||
let mut p1 = Vector2DMm::new(1.0, 2.0);
|
||||
p1 += vec2(3.0, 4.0);
|
||||
|
||||
assert_eq!(p1, vec2(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1 = Vector2DMm::new(1.0, 2.0);
|
||||
let cm_per_mm: ScaleFactor<f32, Mm, Cm> = ScaleFactor::new(0.1);
|
||||
|
||||
let result: Vector2DCm<f32> = p1 * cm_per_mm;
|
||||
|
||||
assert_eq!(result, vec2(0.1, 0.2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_swizzling() {
|
||||
let p: Vector2D<i32> = vec2(1, 2);
|
||||
assert_eq!(p.yx(), vec2(2, 1));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod vector3d {
|
||||
use super::{Vector3D, vec2, vec3};
|
||||
type Vec3 = Vector3D<f32>;
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let p1: Vec3 = vec3(7.0, 21.0, 32.0);
|
||||
let p2: Vec3 = vec3(43.0, 5.0, 16.0);
|
||||
assert_eq!(p1.dot(p2), 918.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross() {
|
||||
let p1: Vec3 = vec3(4.0, 7.0, 9.0);
|
||||
let p2: Vec3 = vec3(13.0, 8.0, 3.0);
|
||||
let p3 = p1.cross(p2);
|
||||
assert_eq!(p3, vec3(-51.0, 105.0, -59.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let p0: Vec3 = Vec3::zero();
|
||||
let p1: Vec3 = vec3(0.0, -6.0, 0.0);
|
||||
let p2: Vec3 = vec3(1.0, 2.0, -2.0);
|
||||
assert_eq!(p0.normalize(), p0);
|
||||
assert_eq!(p1.normalize(), vec3(0.0, -1.0, 0.0));
|
||||
assert_eq!(p2.normalize(), vec3(1.0/3.0, 2.0/3.0, -2.0/3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1: Vec3 = vec3(1.0, 3.0, 5.0);
|
||||
let p2: Vec3 = vec3(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, vec3(1.0, 2.0, -1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1: Vec3 = vec3(1.0, 3.0, 5.0);
|
||||
let p2: Vec3 = vec3(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, vec3(2.0, 3.0, 5.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_swizzling() {
|
||||
let p: Vector3D<i32> = vec3(1, 2, 3);
|
||||
assert_eq!(p.xy(), vec2(1, 2));
|
||||
assert_eq!(p.xz(), vec2(1, 3));
|
||||
assert_eq!(p.yz(), vec2(2, 3));
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"dafb727ecf8ce1c097737e0fc3c82a047591ac34c1c04362cd489c1e1fb1f91e","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"62f99334c17b451342fcea70eb1cc27b26612616b7c1a58fab50dd493f766f32","benches/split.rs":"dfe01759652e2098f20547e0ddcc1b2937e88c6d6ddb025353c037a46b7ef85d","src/bsp.rs":"66e1690aa8540f744ee013ac0e550ecdee84633727cb3a2d8239db3597ad25d6","src/lib.rs":"21d6135c10dd820c2b9ac484cc018e1149f2bf44c315d27134edd3ecb8a7f3d2","src/naive.rs":"444d3298224009209ae329458fe8df953193b15a04da29cdd6f498572a6471bf","tests/main.rs":"d65d7fe01ff3091a9b470a2f26b28108968ca5d32a5a14defba4336df31c7d7f","tests/split.rs":"19d5bfaaf93115ddecdac0f720893c61b2ed73a0bcb4711534ac7e4500cc06ae"},"package":"e57800a97ca52c556db6b6184a3201f05366ad5e11876f7d17e234589ca2fa26"}
|
||||
{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"74145a361386c636f61349e165ef59dd1631a4a5f7e1c16c96b0163184e6ccaa","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"62f99334c17b451342fcea70eb1cc27b26612616b7c1a58fab50dd493f766f32","benches/split.rs":"dfe01759652e2098f20547e0ddcc1b2937e88c6d6ddb025353c037a46b7ef85d","src/bsp.rs":"66e1690aa8540f744ee013ac0e550ecdee84633727cb3a2d8239db3597ad25d6","src/lib.rs":"21d6135c10dd820c2b9ac484cc018e1149f2bf44c315d27134edd3ecb8a7f3d2","src/naive.rs":"444d3298224009209ae329458fe8df953193b15a04da29cdd6f498572a6471bf","tests/main.rs":"54104b672128ae623e1ef6000c30110c2b713482b81bc5f18ac1f86088813cb1","tests/split.rs":"67259be206c2e9eb77b0ff285dc5c5d912f7e539c15fc9e278e5ec9959bc24af"},"package":"d2adb8d1523b2ddcd98275613e9bc04eef75b47a39e252e63733a3218ae3c1b7"}
|
|
@ -1,15 +1,33 @@
|
|||
# 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 = "plane-split"
|
||||
version = "0.6.0"
|
||||
description = "Plane splitting"
|
||||
version = "0.7.0"
|
||||
authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
|
||||
description = "Plane splitting"
|
||||
documentation = "https://docs.rs/plane-split"
|
||||
keywords = ["geometry", "math"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/kvark/plane-split"
|
||||
keywords = ["geometry", "math"]
|
||||
documentation = "https://docs.rs/plane-split"
|
||||
[dependencies.binary-space-partition]
|
||||
version = "0.1.2"
|
||||
|
||||
[dependencies]
|
||||
binary-space-partition = "0.1.2"
|
||||
euclid = "0.15"
|
||||
log = "0.3"
|
||||
num-traits = {version = "0.1.37", default-features = false}
|
||||
[dependencies.euclid]
|
||||
version = "0.16"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.37"
|
||||
default-features = false
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extern crate euclid;
|
||||
extern crate plane_split;
|
||||
|
||||
use euclid::{Radians, TypedRect, TypedSize2D, TypedTransform3D, point2, point3, vec3};
|
||||
use euclid::{Angle, TypedRect, TypedSize2D, TypedTransform3D, point2, point3, vec3};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use plane_split::{Intersection, Line, LineProjection, Polygon};
|
||||
|
||||
|
@ -56,7 +56,7 @@ fn valid() {
|
|||
fn from_transformed_rect() {
|
||||
let rect: TypedRect<f32, ()> = TypedRect::new(point2(10.0, 10.0), TypedSize2D::new(20.0, 30.0));
|
||||
let transform: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Radians::new(5.0))
|
||||
TypedTransform3D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
|
||||
.pre_translate(vec3(0.0, 0.0, 10.0));
|
||||
let poly = Polygon::from_transformed_rect(rect, transform, 0);
|
||||
assert!(poly.is_valid());
|
||||
|
|
|
@ -2,7 +2,7 @@ extern crate euclid;
|
|||
extern crate plane_split;
|
||||
|
||||
use std::f32::consts::FRAC_PI_4;
|
||||
use euclid::{Radians, TypedTransform3D, TypedRect, vec3};
|
||||
use euclid::{Angle, TypedTransform3D, TypedRect, vec3};
|
||||
use plane_split::{BspSplitter, NaiveSplitter, Polygon, Splitter, _make_grid};
|
||||
|
||||
|
||||
|
@ -25,11 +25,11 @@ fn grid_bsp() {
|
|||
|
||||
fn sort_rotation(splitter: &mut Splitter<f32, ()>) {
|
||||
let transform0: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(-FRAC_PI_4));
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
|
||||
let transform1: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(0.0));
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(0.0));
|
||||
let transform2: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(FRAC_PI_4));
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(FRAC_PI_4));
|
||||
|
||||
let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let polys = [
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"0537077146c955003ccbb274e748e727b66447fc4f07b585935a407e9ecee73a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"eedee04bddb61e99bc816656bb3b8ae2fa50ff00045ecdb5212682f3592d9ab2","src/de/from_primitive.rs":"28ec3ab1c430cf27d632b642ccfccb6d055eeb9fb576e7e446ba24c66f507fb4","src/de/ignored_any.rs":"1b5ee592f5ae58d69e321144d4397f149c047e327529d0b880e1a5285e781a35","src/de/impls.rs":"8088c7cb2427c9c32bb7104b6d473c9242e98b630b95949543b322348dba4969","src/de/mod.rs":"761d9bd018fe75b8dbd9ec78e2882e533e2488a7cb7980805c1939143eb7a5af","src/de/utf8.rs":"956b124b7ce98353cb781b56e43a6fed2e67f1389d35b7a468d5be75b1485853","src/de/value.rs":"26eec47336c3d31ecbc2b7c131d71954a68ae62b8f13574d85d118eb69177ed1","src/export.rs":"a3e62187f57748cc12109b109cc850343adfab83b74b6b60123fd531c68d5d8d","src/lib.rs":"7659af7266821d1aca648ae48146216dfed0415f134f3989fa25a00ed6f265e2","src/macros.rs":"e1d542b1dac2c1d1f9d5ada7cc5b6639767fc67851421cc3adfb942a7cf750b6","src/private/de.rs":"598f6736d3c23b2e1481932df3636701f36cc5943428647742335081c5c7a650","src/private/macros.rs":"6861a4f332ea24d0ed5db1c28fe3105d2716523902f045c0bbbd439ebf9e44de","src/private/mod.rs":"bcd7c54838e139475c23a323678e20eccbe88c0be93f7977f7675cead4d3b6ed","src/private/ser.rs":"12e686fa3bf7f10ca21642c6308c6ef0b2158ee66ebda412b5d5c388019e7cd7","src/ser/impls.rs":"534f0f94757b0e87304bdbe6f2440b51cb4fc2933d23a62ac61b8c382d80b4c0","src/ser/impossible.rs":"35bd09bb517b28eda0048b0622eb5a0313d5aebf37c03b5a44dbca200d0a9ac8","src/ser/mod.rs":"fc6d1d8dc5e1e1459fda4349634a955c3e59a9bc6d98978be5f8a37bc9c1b02c"},"package":"c2f530d36fb84ec48fb7146936881f026cdbf4892028835fd9398475f82c1bb4"}
|
||||
{"files":{"Cargo.toml":"f98dcc6eedbfb307be98a2d8ca49d3cc10a76a08e7d7dfa2f6ec69987f0c679d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7dc6376a88195594cd6b43cb891aad10f57144ff737178b55d046aa04739b43a","src/de/from_primitive.rs":"28ec3ab1c430cf27d632b642ccfccb6d055eeb9fb576e7e446ba24c66f507fb4","src/de/ignored_any.rs":"1b5ee592f5ae58d69e321144d4397f149c047e327529d0b880e1a5285e781a35","src/de/impls.rs":"d220ded8bebc3367c3a0ac1360b59be1b435df40be65a5b688a6ba8108e96bea","src/de/mod.rs":"b8fd19e4a2d40369cb85e3ed6bc5197800c06b2271a846ed6e9b0cb7af41c5f0","src/de/utf8.rs":"956b124b7ce98353cb781b56e43a6fed2e67f1389d35b7a468d5be75b1485853","src/de/value.rs":"fe31174cc41035a1b53d4657b6cae6f3bcb660be39eccaae281a6e3705655578","src/export.rs":"7477f5bd345ca9e0b8d56bccdc62484e42a92fc6cd909bf17fb6e05cd1eb7946","src/lib.rs":"f549fbbd92b2e5e946a327eadf18216b9fe076f569bd623a32872797dc9eb4bc","src/macros.rs":"e1d542b1dac2c1d1f9d5ada7cc5b6639767fc67851421cc3adfb942a7cf750b6","src/private/de.rs":"15b82edcadaa60e748bf6ee0e0e14464cf945f39cb7493d3c1684cc34f6594b6","src/private/macros.rs":"6861a4f332ea24d0ed5db1c28fe3105d2716523902f045c0bbbd439ebf9e44de","src/private/mod.rs":"bcd7c54838e139475c23a323678e20eccbe88c0be93f7977f7675cead4d3b6ed","src/private/ser.rs":"eec5aecf077cebf4dc3fdbe83780f716bf33d311806ee59ce42a0c53c2f92211","src/ser/impls.rs":"dc5219e898d67c9422fef81d1f36d25ea92341c5ad3d976c0017886f371a8d51","src/ser/impossible.rs":"35bd09bb517b28eda0048b0622eb5a0313d5aebf37c03b5a44dbca200d0a9ac8","src/ser/mod.rs":"3d92794f13c8d90c2ebc33716bd6e4d4dde552c6f7de42dfaf9967d8a9ab7d10"},"package":null}
|
|
@ -1,45 +1,68 @@
|
|||
# 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 = "serde"
|
||||
version = "1.0.8"
|
||||
version = "1.0.23" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "A generic serialization/deserialization framework"
|
||||
homepage = "https://serde.rs"
|
||||
repository = "https://github.com/serde-rs/serde"
|
||||
documentation = "https://docs.serde.rs/serde/"
|
||||
readme = "README.md"
|
||||
keywords = ["serde", "serialization", "no_std"]
|
||||
categories = ["encoding"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/serde-rs/serde"
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
[dev-dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
readme = "README.md"
|
||||
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "serde-rs/serde" }
|
||||
appveyor = { repository = "serde-rs/serde" }
|
||||
|
||||
[dependencies]
|
||||
serde_derive = { version = "1.0", optional = true, path = "../serde_derive" }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_derive = { version = "1.0", path = "../serde_derive" }
|
||||
|
||||
|
||||
### FEATURES #################################################################
|
||||
|
||||
[features]
|
||||
playground = ["serde_derive"]
|
||||
derive = ["serde_derive"]
|
||||
alloc = ["unstable"]
|
||||
unstable = []
|
||||
std = []
|
||||
collections = ["alloc"]
|
||||
rc = []
|
||||
default = ["std"]
|
||||
[badges.appveyor]
|
||||
repository = "serde-rs/serde"
|
||||
|
||||
[badges.travis-ci]
|
||||
repository = "serde-rs/serde"
|
||||
# Re-export the derive(Serialize, Deserialize) macros. This is specifically
|
||||
# intended for library crates that provide optional Serde impls behind a Cargo
|
||||
# cfg of their own. All other crates should depend on serde_derive directly.
|
||||
#
|
||||
# Please refer to the long comment above the line `pub use serde_derive::*` in
|
||||
# src/lib.rs before enabling this feature. If you think you need this feature
|
||||
# and your use case does not precisely match the one described in the comment,
|
||||
# please open an issue to let us know about your use case.
|
||||
derive = ["serde_derive"]
|
||||
|
||||
# Provide impls for common standard library types like Vec<T> and HashMap<K, V>.
|
||||
# Requires a dependency on the Rust standard library.
|
||||
std = []
|
||||
|
||||
# Provide impls for types that require unstable functionality. For tracking and
|
||||
# discussion of unstable functionality please refer to this issue:
|
||||
#
|
||||
# https://github.com/serde-rs/serde/issues/812
|
||||
unstable = []
|
||||
|
||||
# Provide impls for types in the Rust core allocation and collections library
|
||||
# including String, Box<T>, Vec<T>, and Cow<T>. This is a subset of std but may
|
||||
# be enabled without depending on all of std.
|
||||
#
|
||||
# Requires a dependency on the unstable core allocation library:
|
||||
#
|
||||
# https://doc.rust-lang.org/alloc/
|
||||
alloc = ["unstable"]
|
||||
|
||||
# Opt into impls for Rc<T> and Arc<T>. Serializing and deserializing these types
|
||||
# does not preserve identity and may result in multiple copies of the same data.
|
||||
# Be sure that this is what you want before enabling this feature.
|
||||
rc = []
|
||||
|
||||
# Get serde_derive picked up by the Integer 32 playground. Not public API.
|
||||
#
|
||||
# http://play.integer32.com/
|
||||
playground = ["serde_derive"]
|
||||
|
|
|
@ -20,9 +20,30 @@ You may be looking for:
|
|||
|
||||
## Serde in action
|
||||
|
||||
<a href="http://play.integer32.com/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
<details>
|
||||
<summary>
|
||||
Click to show Cargo.toml.
|
||||
<a href="http://play.integer32.com/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">Run this code in the playground.</a>
|
||||
</summary>
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
|
||||
# The core APIs, including the Serialize and Deserialize traits. Always
|
||||
# required when using Serde.
|
||||
serde = "1.0"
|
||||
|
||||
# Support for #[derive(Serialize, Deserialize)]. Required if you want Serde
|
||||
# to work for structs and enums defined in your crate.
|
||||
serde_derive = "1.0"
|
||||
|
||||
# Each data format lives in its own crate; the sample code below uses JSON
|
||||
# but you may be using a different one.
|
||||
serde_json = "1.0"
|
||||
```
|
||||
|
||||
</details>
|
||||
<p></p>
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -94,6 +94,7 @@
|
|||
//! - OsString
|
||||
//! - **Miscellaneous standard library types**:
|
||||
//! - Duration
|
||||
//! - SystemTime
|
||||
//! - Path
|
||||
//! - PathBuf
|
||||
//! - Range\<T\>
|
||||
|
@ -503,6 +504,35 @@ pub trait Deserialize<'de>: Sized {
|
|||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>;
|
||||
|
||||
/// Deserializes a value into `self` from the given Deserializer.
|
||||
///
|
||||
/// The purpose of this method is to allow the deserializer to reuse
|
||||
/// resources and avoid copies. As such, if this method returns an error,
|
||||
/// `self` will be in an indeterminate state where some parts of the struct
|
||||
/// have been overwritten. Although whatever state that is will be
|
||||
/// memory-safe.
|
||||
///
|
||||
/// This is generally useful when repeateadly deserializing values that
|
||||
/// are processed one at a time, where the value of `self` doesn't matter
|
||||
/// when the next deserialization occurs.
|
||||
///
|
||||
/// If you manually implement this, your recursive deserializations should
|
||||
/// use `deserialize_from`.
|
||||
///
|
||||
/// TODO: example
|
||||
///
|
||||
/// ```
|
||||
/// // Something with a loop that returns on error.
|
||||
///
|
||||
/// ```
|
||||
fn deserialize_from<D>(&mut self, deserializer: D) -> Result<(), D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
// Default implementation just delegates to `deserialize` impl.
|
||||
*self = Deserialize::deserialize(deserializer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A data structure that can be deserialized without borrowing any data from
|
||||
|
@ -1010,6 +1040,74 @@ pub trait Deserializer<'de>: Sized {
|
|||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>;
|
||||
|
||||
/// Determine whether `Deserialize` implementations should expect to
|
||||
/// deserialize their human-readable form.
|
||||
///
|
||||
/// Some types have a human-readable form that may be somewhat expensive to
|
||||
/// construct, as well as a binary form that is compact and efficient.
|
||||
/// Generally text-based formats like JSON and YAML will prefer to use the
|
||||
/// human-readable one and binary formats like Bincode will prefer the
|
||||
/// compact one.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::ops::Add;
|
||||
/// # use std::str::FromStr;
|
||||
/// #
|
||||
/// # struct Timestamp;
|
||||
/// #
|
||||
/// # impl Timestamp {
|
||||
/// # const EPOCH: Timestamp = Timestamp;
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl FromStr for Timestamp {
|
||||
/// # type Err = String;
|
||||
/// # fn from_str(_: &str) -> Result<Self, Self::Err> {
|
||||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # struct Duration;
|
||||
/// #
|
||||
/// # impl Duration {
|
||||
/// # fn seconds(_: u64) -> Self { unimplemented!() }
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl Add<Duration> for Timestamp {
|
||||
/// # type Output = Timestamp;
|
||||
/// # fn add(self, _: Duration) -> Self::Output {
|
||||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// use serde::de::{self, Deserialize, Deserializer};
|
||||
///
|
||||
/// impl<'de> Deserialize<'de> for Timestamp {
|
||||
/// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
/// where D: Deserializer<'de>
|
||||
/// {
|
||||
/// if deserializer.is_human_readable() {
|
||||
/// // Deserialize from a human-readable string like "2015-05-15T17:01:00Z".
|
||||
/// let s = String::deserialize(deserializer)?;
|
||||
/// Timestamp::from_str(&s).map_err(de::Error::custom)
|
||||
/// } else {
|
||||
/// // Deserialize from a compact binary representation, seconds since
|
||||
/// // the Unix epoch.
|
||||
/// let n = u64::deserialize(deserializer)?;
|
||||
/// Ok(Timestamp::EPOCH + Duration::seconds(n))
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The default implementation of this method returns `true`. Data formats
|
||||
/// may override this to `false` to request a compact form for types that
|
||||
/// support one. Note that modifying this method to change a format from
|
||||
/// human-readable to compact or vice versa should be regarded as a breaking
|
||||
/// change, as a value serialized in human-readable mode is not required to
|
||||
/// deserialize from the same data in compact mode.
|
||||
#[inline]
|
||||
fn is_human_readable(&self) -> bool { true }
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1119,7 +1217,7 @@ pub trait Visitor<'de>: Sized {
|
|||
self.visit_i64(v as i64)
|
||||
}
|
||||
|
||||
/// The input contains an `i32`.
|
||||
/// The input contains an `i64`.
|
||||
///
|
||||
/// The default implementation fails with a type error.
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
|
@ -1262,7 +1360,7 @@ pub trait Visitor<'de>: Sized {
|
|||
/// The default implementation forwards to `visit_str` and then drops the
|
||||
/// `String`.
|
||||
#[inline]
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
|
@ -1321,7 +1419,7 @@ pub trait Visitor<'de>: Sized {
|
|||
///
|
||||
/// The default implementation forwards to `visit_bytes` and then drops the
|
||||
/// `Vec<u8>`.
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
|
@ -1423,7 +1521,7 @@ pub trait SeqAccess<'de> {
|
|||
/// `Ok(None)` if there are no more remaining items.
|
||||
///
|
||||
/// `Deserialize` implementations should typically use
|
||||
/// `SeqAcccess::next_element` instead.
|
||||
/// `SeqAccess::next_element` instead.
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
||||
where
|
||||
T: DeserializeSeed<'de>;
|
||||
|
|
|
@ -51,13 +51,13 @@ pub struct Error {
|
|||
err: ErrorImpl,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
type ErrorImpl = Box<str>;
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
type ErrorImpl = ();
|
||||
|
||||
impl de::Error for Error {
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
|
@ -65,7 +65,7 @@ impl de::Error for Error {
|
|||
Error { err: msg.to_string().into_boxed_str() }
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
|
@ -85,12 +85,12 @@ impl ser::Error for Error {
|
|||
}
|
||||
|
||||
impl Display for Error {
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
formatter.write_str(&self.err)
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
formatter.write_str("Serde deserialization error")
|
||||
}
|
||||
|
@ -425,14 +425,14 @@ where
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// A deserializer holding a `String`.
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StringDeserializer<E> {
|
||||
value: String,
|
||||
marker: PhantomData<E>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, E> IntoDeserializer<'de, E> for String
|
||||
where
|
||||
E: de::Error,
|
||||
|
@ -447,7 +447,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, E> de::Deserializer<'de> for StringDeserializer<E>
|
||||
where
|
||||
E: de::Error,
|
||||
|
@ -482,7 +482,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, 'a, E> de::EnumAccess<'de> for StringDeserializer<E>
|
||||
where
|
||||
E: de::Error,
|
||||
|
@ -501,14 +501,14 @@ where
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// A deserializer holding a `Cow<str>`.
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CowStrDeserializer<'a, E> {
|
||||
value: Cow<'a, str>,
|
||||
marker: PhantomData<E>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, 'a, E> IntoDeserializer<'de, E> for Cow<'a, str>
|
||||
where
|
||||
E: de::Error,
|
||||
|
@ -523,7 +523,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, 'a, E> de::Deserializer<'de> for CowStrDeserializer<'a, E>
|
||||
where
|
||||
E: de::Error,
|
||||
|
@ -561,7 +561,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, 'a, E> de::EnumAccess<'de> for CowStrDeserializer<'a, E>
|
||||
where
|
||||
E: de::Error,
|
||||
|
@ -727,26 +727,26 @@ impl Expected for ExpectedInSeq {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, T, E> IntoDeserializer<'de, E> for Vec<T>
|
||||
where
|
||||
T: IntoDeserializer<'de, E>,
|
||||
E: de::Error,
|
||||
{
|
||||
type Deserializer = SeqDeserializer<<Vec<T> as IntoIterator>::IntoIter, E>;
|
||||
type Deserializer = SeqDeserializer<<Self as IntoIterator>::IntoIter, E>;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
SeqDeserializer::new(self.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, T, E> IntoDeserializer<'de, E> for BTreeSet<T>
|
||||
where
|
||||
T: IntoDeserializer<'de, E> + Eq + Ord,
|
||||
E: de::Error,
|
||||
{
|
||||
type Deserializer = SeqDeserializer<<BTreeSet<T> as IntoIterator>::IntoIter, E>;
|
||||
type Deserializer = SeqDeserializer<<Self as IntoIterator>::IntoIter, E>;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
SeqDeserializer::new(self.into_iter())
|
||||
|
@ -754,12 +754,13 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'de, T, E> IntoDeserializer<'de, E> for HashSet<T>
|
||||
impl<'de, T, S, E> IntoDeserializer<'de, E> for HashSet<T, S>
|
||||
where
|
||||
T: IntoDeserializer<'de, E> + Eq + Hash,
|
||||
S: BuildHasher,
|
||||
E: de::Error,
|
||||
{
|
||||
type Deserializer = SeqDeserializer<<HashSet<T> as IntoIterator>::IntoIter, E>;
|
||||
type Deserializer = SeqDeserializer<<Self as IntoIterator>::IntoIter, E>;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
SeqDeserializer::new(self.into_iter())
|
||||
|
@ -1145,14 +1146,14 @@ impl Expected for ExpectedInMap {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'de, K, V, E> IntoDeserializer<'de, E> for BTreeMap<K, V>
|
||||
where
|
||||
K: IntoDeserializer<'de, E> + Eq + Ord,
|
||||
V: IntoDeserializer<'de, E>,
|
||||
E: de::Error,
|
||||
{
|
||||
type Deserializer = MapDeserializer<'de, <BTreeMap<K, V> as IntoIterator>::IntoIter, E>;
|
||||
type Deserializer = MapDeserializer<'de, <Self as IntoIterator>::IntoIter, E>;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
MapDeserializer::new(self.into_iter())
|
||||
|
@ -1160,13 +1161,14 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'de, K, V, E> IntoDeserializer<'de, E> for HashMap<K, V>
|
||||
impl<'de, K, V, S, E> IntoDeserializer<'de, E> for HashMap<K, V, S>
|
||||
where
|
||||
K: IntoDeserializer<'de, E> + Eq + Hash,
|
||||
V: IntoDeserializer<'de, E>,
|
||||
S: BuildHasher,
|
||||
E: de::Error,
|
||||
{
|
||||
type Deserializer = MapDeserializer<'de, <HashMap<K, V> as IntoIterator>::IntoIter, E>;
|
||||
type Deserializer = MapDeserializer<'de, <Self as IntoIterator>::IntoIter, E>;
|
||||
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
MapDeserializer::new(self.into_iter())
|
||||
|
|
|
@ -19,7 +19,7 @@ pub use self::string::from_utf8_lossy;
|
|||
mod string {
|
||||
use lib::*;
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
pub fn from_utf8_lossy(bytes: &[u8]) -> Cow<str> {
|
||||
String::from_utf8_lossy(bytes)
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ mod string {
|
|||
//
|
||||
// so it is okay for the return type to be different from the std case as long
|
||||
// as the above works.
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
pub fn from_utf8_lossy(bytes: &[u8]) -> &str {
|
||||
// Three unicode replacement characters if it fails. They look like a
|
||||
// white-on-black question mark. The user will recognize it as invalid
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Serde types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.8")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.23")]
|
||||
|
||||
// Support using Serde without the standard library!
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
@ -89,33 +89,57 @@
|
|||
//
|
||||
// https://github.com/serde-rs/serde/issues/812
|
||||
#![cfg_attr(feature = "unstable", feature(nonzero, specialization))]
|
||||
#![cfg_attr(all(feature = "std", feature = "unstable"), feature(into_boxed_c_str))]
|
||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||
#![cfg_attr(feature = "collections", feature(collections))]
|
||||
|
||||
// Whitelisted clippy lints.
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(linkedlist))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(zero_prefixed_literal))]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(
|
||||
cast_lossless,
|
||||
const_static_lifetime,
|
||||
doc_markdown,
|
||||
linkedlist,
|
||||
needless_pass_by_value,
|
||||
type_complexity,
|
||||
unreadable_literal,
|
||||
zero_prefixed_literal,
|
||||
))]
|
||||
// Whitelisted clippy_pedantic lints
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(
|
||||
// integer and float ser/de requires these sorts of casts
|
||||
cast_possible_truncation,
|
||||
cast_possible_wrap,
|
||||
cast_precision_loss,
|
||||
cast_sign_loss,
|
||||
// simplifies some macros
|
||||
invalid_upcast_comparisons,
|
||||
// things are often more readable this way
|
||||
option_unwrap_used,
|
||||
result_unwrap_used,
|
||||
shadow_reuse,
|
||||
single_match_else,
|
||||
stutter,
|
||||
use_self,
|
||||
// not practical
|
||||
missing_docs_in_private_items,
|
||||
// alternative is not stable
|
||||
empty_enum,
|
||||
use_debug,
|
||||
))]
|
||||
|
||||
// Blacklisted Rust lints.
|
||||
#![deny(missing_docs, unused_imports)]
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(feature = "collections")]
|
||||
extern crate collections;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(all(feature = "unstable", feature = "std"))]
|
||||
extern crate core;
|
||||
|
||||
/// A facade around all the types we need from the `std`, `core`, `alloc`, and
|
||||
/// `collections` crates. This avoids elaborate import wrangling having to
|
||||
/// happen in every module.
|
||||
/// A facade around all the types we need from the `std`, `core`, and `alloc`
|
||||
/// crates. This avoids elaborate import wrangling having to happen in every
|
||||
/// module.
|
||||
mod lib {
|
||||
mod core {
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -140,18 +164,18 @@ mod lib {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::borrow::{Cow, ToOwned};
|
||||
#[cfg(all(feature = "collections", not(feature = "std")))]
|
||||
pub use collections::borrow::{Cow, ToOwned};
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
pub use alloc::borrow::{Cow, ToOwned};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::string::String;
|
||||
#[cfg(all(feature = "collections", not(feature = "std")))]
|
||||
pub use collections::string::{String, ToString};
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
pub use alloc::string::{String, ToString};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::vec::Vec;
|
||||
#[cfg(all(feature = "collections", not(feature = "std")))]
|
||||
pub use collections::vec::Vec;
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
pub use alloc::vec::Vec;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::boxed::Box;
|
||||
|
@ -170,8 +194,8 @@ mod lib {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::collections::{BinaryHeap, BTreeMap, BTreeSet, LinkedList, VecDeque};
|
||||
#[cfg(all(feature = "collections", not(feature = "std")))]
|
||||
pub use collections::{BinaryHeap, BTreeMap, BTreeSet, LinkedList, VecDeque};
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
pub use alloc::{BinaryHeap, BTreeMap, BTreeSet, LinkedList, VecDeque};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::{error, net};
|
||||
|
@ -185,9 +209,11 @@ mod lib {
|
|||
#[cfg(feature = "std")]
|
||||
pub use std::io::Write;
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::num::Wrapping;
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::path::{Path, PathBuf};
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::time::Duration;
|
||||
pub use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::sync::{Mutex, RwLock};
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
use lib::*;
|
||||
|
||||
use de::{Deserialize, Deserializer, IntoDeserializer, Error, Visitor};
|
||||
use de::{Deserialize, Deserializer, DeserializeSeed, IntoDeserializer, Error, Visitor};
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
use de::Unexpected;
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
pub use self::content::{Content, ContentRefDeserializer, ContentDeserializer,
|
||||
TaggedContentVisitor, TagOrContentField, TagOrContentFieldVisitor,
|
||||
TagContentOtherField, TagContentOtherFieldVisitor,
|
||||
|
@ -59,7 +59,7 @@ where
|
|||
Deserialize::deserialize(deserializer)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
pub fn borrow_cow_str<'de: 'a, 'a, D>(deserializer: D) -> Result<Cow<'a, str>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
@ -128,7 +128,7 @@ where
|
|||
deserializer.deserialize_str(CowStrVisitor)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
pub fn borrow_cow_bytes<'de: 'a, 'a, D>(deserializer: D) -> Result<Cow<'a, [u8]>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
@ -198,6 +198,7 @@ pub mod size_hint {
|
|||
helper(iter.size_hint())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cautious(hint: Option<usize>) -> usize {
|
||||
cmp::min(hint.unwrap_or(0), 4096)
|
||||
}
|
||||
|
@ -210,7 +211,7 @@ pub mod size_hint {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
mod content {
|
||||
// This module is private and nothing here should be used outside of
|
||||
// generated code.
|
||||
|
@ -834,26 +835,43 @@ mod content {
|
|||
type Value = TaggedContent<'de, T>;
|
||||
|
||||
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("any value")
|
||||
fmt.write_str("internally tagged enum")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
|
||||
where
|
||||
V: MapAccess<'de>,
|
||||
S: SeqAccess<'de>,
|
||||
{
|
||||
let tag = match try!(seq.next_element()) {
|
||||
Some(tag) => tag,
|
||||
None => {
|
||||
return Err(de::Error::missing_field(self.tag_name));
|
||||
}
|
||||
};
|
||||
let rest = de::value::SeqAccessDeserializer::new(seq);
|
||||
Ok(TaggedContent {
|
||||
tag: tag,
|
||||
content: try!(Content::deserialize(rest)),
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let mut tag = None;
|
||||
let mut vec = Vec::with_capacity(size_hint::cautious(visitor.size_hint()));
|
||||
let mut vec = Vec::with_capacity(size_hint::cautious(map.size_hint()));
|
||||
while let Some(k) =
|
||||
try!(visitor.next_key_seed(TagOrContentVisitor::new(self.tag_name))) {
|
||||
try!(map.next_key_seed(TagOrContentVisitor::new(self.tag_name))) {
|
||||
match k {
|
||||
TagOrContent::Tag => {
|
||||
if tag.is_some() {
|
||||
return Err(de::Error::duplicate_field(self.tag_name));
|
||||
}
|
||||
tag = Some(try!(visitor.next_value()));
|
||||
tag = Some(try!(map.next_value()));
|
||||
}
|
||||
TagOrContent::Content(k) => {
|
||||
let v = try!(visitor.next_value());
|
||||
let v = try!(map.next_value());
|
||||
vec.push((k, v));
|
||||
}
|
||||
}
|
||||
|
@ -1097,10 +1115,38 @@ mod content {
|
|||
)
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.content {
|
||||
// As a special case, allow deserializing untagged newtype
|
||||
// variant containing unit struct.
|
||||
//
|
||||
// #[derive(Deserialize)]
|
||||
// struct Info;
|
||||
//
|
||||
// #[derive(Deserialize)]
|
||||
// #[serde(tag = "topic")]
|
||||
// enum Message {
|
||||
// Info(Info),
|
||||
// }
|
||||
//
|
||||
// We want {"topic":"Info"} to deserialize even though
|
||||
// ordinarily unit structs do not deserialize from empty map.
|
||||
Content::Map(ref v) if v.is_empty() => visitor.visit_unit(),
|
||||
_ => self.deserialize_any(visitor),
|
||||
}
|
||||
}
|
||||
|
||||
forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
|
||||
byte_buf unit unit_struct seq tuple tuple_struct map struct
|
||||
identifier ignored_any
|
||||
byte_buf unit seq tuple tuple_struct map struct identifier
|
||||
ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1201,6 +1247,9 @@ mod content {
|
|||
Some(Content::Map(v)) => {
|
||||
de::Deserializer::deserialize_any(MapDeserializer::new(v), visitor)
|
||||
}
|
||||
Some(Content::Seq(v)) => {
|
||||
de::Deserializer::deserialize_any(SeqDeserializer::new(v), visitor)
|
||||
}
|
||||
Some(other) => Err(de::Error::invalid_type(other.unexpected(), &"struct variant"),),
|
||||
_ => Err(de::Error::invalid_type(de::Unexpected::UnitVariant, &"struct variant"),),
|
||||
}
|
||||
|
@ -1595,6 +1644,9 @@ mod content {
|
|||
Some(&Content::Map(ref v)) => {
|
||||
de::Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor)
|
||||
}
|
||||
Some(&Content::Seq(ref v)) => {
|
||||
de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor)
|
||||
}
|
||||
Some(other) => Err(de::Error::invalid_type(other.unexpected(), &"struct variant"),),
|
||||
_ => Err(de::Error::invalid_type(de::Unexpected::UnitVariant, &"struct variant"),),
|
||||
}
|
||||
|
@ -1802,9 +1854,16 @@ mod content {
|
|||
write!(formatter, "unit variant {}::{}", self.type_name, self.variant_name)
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, _: V) -> Result<(), V::Error>
|
||||
fn visit_seq<S>(self, _: S) -> Result<(), S::Error>
|
||||
where
|
||||
V: MapAccess<'de>,
|
||||
S: SeqAccess<'de>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, _: M) -> Result<(), M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1950,3 +2009,20 @@ where
|
|||
map struct enum identifier ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
/// A DeserializeSeed helper for implementing deserialize_from Visitors.
|
||||
///
|
||||
/// Wraps a mutable reference and calls deserialize_from on it.
|
||||
pub struct DeserializeFromSeed<'a, T: 'a>(pub &'a mut T);
|
||||
|
||||
impl<'a, 'de, T> DeserializeSeed<'de> for DeserializeFromSeed<'a, T>
|
||||
where T: Deserialize<'de>,
|
||||
{
|
||||
type Value = ();
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
self.0.deserialize_from(deserializer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use lib::*;
|
|||
|
||||
use ser::{self, Serialize, Serializer, SerializeMap, SerializeStruct, Impossible};
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
use self::content::{SerializeTupleVariantAsMapValue, SerializeStructVariantAsMapValue};
|
||||
|
||||
/// Used to check that serde(getter) attributes return the expected type.
|
||||
|
@ -60,11 +60,10 @@ enum Unsupported {
|
|||
ByteArray,
|
||||
Optional,
|
||||
Unit,
|
||||
UnitStruct,
|
||||
Sequence,
|
||||
Tuple,
|
||||
TupleStruct,
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
Enum,
|
||||
}
|
||||
|
||||
|
@ -79,11 +78,10 @@ impl Display for Unsupported {
|
|||
Unsupported::ByteArray => formatter.write_str("a byte array"),
|
||||
Unsupported::Optional => formatter.write_str("an optional"),
|
||||
Unsupported::Unit => formatter.write_str("unit"),
|
||||
Unsupported::UnitStruct => formatter.write_str("a unit struct"),
|
||||
Unsupported::Sequence => formatter.write_str("a sequence"),
|
||||
Unsupported::Tuple => formatter.write_str("a tuple"),
|
||||
Unsupported::TupleStruct => formatter.write_str("a tuple struct"),
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
Unsupported::Enum => formatter.write_str("an enum"),
|
||||
}
|
||||
}
|
||||
|
@ -117,14 +115,14 @@ where
|
|||
type SerializeMap = S::SerializeMap;
|
||||
type SerializeStruct = S::SerializeStruct;
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
type SerializeTupleVariant = Impossible<S::Ok, S::Error>;
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
type SerializeTupleVariant = SerializeTupleVariantAsMapValue<S::SerializeMap>;
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
type SerializeStructVariant = Impossible<S::Ok, S::Error>;
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
type SerializeStructVariant = SerializeStructVariantAsMapValue<S::SerializeMap>;
|
||||
|
||||
fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
|
||||
|
@ -199,7 +197,9 @@ where
|
|||
}
|
||||
|
||||
fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(self.bad_type(Unsupported::UnitStruct))
|
||||
let mut map = try!(self.delegate.serialize_map(Some(1)));
|
||||
try!(map.serialize_entry(self.tag, self.variant_name));
|
||||
map.end()
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
|
@ -257,7 +257,7 @@ where
|
|||
Err(self.bad_type(Unsupported::TupleStruct))
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
|
@ -270,7 +270,7 @@ where
|
|||
Err(self.bad_type(Unsupported::Enum))
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
|
@ -300,7 +300,7 @@ where
|
|||
Ok(state)
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
|
@ -313,7 +313,7 @@ where
|
|||
Err(self.bad_type(Unsupported::Enum))
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
|
@ -327,7 +327,7 @@ where
|
|||
Ok(SerializeStructVariantAsMapValue::new(map, inner_variant, len),)
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
fn collect_str<T: ?Sized>(self, _: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Display,
|
||||
|
@ -363,7 +363,7 @@ impl Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
mod content {
|
||||
use lib::*;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ impl Serialize for str {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl Serialize for String {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
@ -177,6 +177,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
macro_rules! seq_impl {
|
||||
($ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)* $(, $typaram:ident : $bound:ident)* >) => {
|
||||
impl<T $(, $typaram)*> Serialize for $ty<T $(, $typaram)*>
|
||||
|
@ -195,22 +196,22 @@ macro_rules! seq_impl {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
seq_impl!(BinaryHeap<T: Ord>);
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
seq_impl!(BTreeSet<T: Ord>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
seq_impl!(HashSet<T: Eq + Hash, H: BuildHasher>);
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
seq_impl!(LinkedList<T>);
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
seq_impl!(Vec<T>);
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
seq_impl!(VecDeque<T>);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -290,6 +291,7 @@ tuple_impls! {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
macro_rules! map_impl {
|
||||
($ty:ident < K $(: $kbound1:ident $(+ $kbound2:ident)*)*, V $(, $typaram:ident : $bound:ident)* >) => {
|
||||
impl<K, V $(, $typaram)*> Serialize for $ty<K, V $(, $typaram)*>
|
||||
|
@ -309,7 +311,7 @@ macro_rules! map_impl {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
map_impl!(BTreeMap<K: Ord, V>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -338,12 +340,12 @@ deref_impl!(<'a, T: ?Sized> Serialize for &'a mut T where T: Serialize);
|
|||
deref_impl!(<T: ?Sized> Serialize for Box<T> where T: Serialize);
|
||||
|
||||
#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))]
|
||||
deref_impl!(<T> Serialize for Rc<T> where T: Serialize);
|
||||
deref_impl!(<T: ?Sized> Serialize for Rc<T> where T: Serialize);
|
||||
|
||||
#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))]
|
||||
deref_impl!(<T> Serialize for Arc<T> where T: Serialize);
|
||||
deref_impl!(<T: ?Sized> Serialize for Arc<T> where T: Serialize);
|
||||
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
deref_impl!(<'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwned);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -455,6 +457,23 @@ impl Serialize for Duration {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Serialize for SystemTime {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use super::SerializeStruct;
|
||||
let duration_since_epoch = self.duration_since(UNIX_EPOCH).expect("SystemTime must be later than UNIX_EPOCH");
|
||||
let mut state = try!(serializer.serialize_struct("SystemTime", 2));
|
||||
try!(state.serialize_field("secs_since_epoch", &duration_since_epoch.as_secs()));
|
||||
try!(state.serialize_field("nanos_since_epoch", &duration_since_epoch.subsec_nanos()));
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Serialize a value that implements `Display` as a string, when that string is
|
||||
/// statically known to never have more than a constant `MAX_LEN` bytes.
|
||||
///
|
||||
|
@ -487,9 +506,18 @@ impl Serialize for net::IpAddr {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
net::IpAddr::V4(ref a) => a.serialize(serializer),
|
||||
net::IpAddr::V6(ref a) => a.serialize(serializer),
|
||||
if serializer.is_human_readable() {
|
||||
match *self {
|
||||
net::IpAddr::V4(ref a) => a.serialize(serializer),
|
||||
net::IpAddr::V6(ref a) => a.serialize(serializer),
|
||||
}
|
||||
} else {
|
||||
match *self {
|
||||
net::IpAddr::V4(ref a) =>
|
||||
serializer.serialize_newtype_variant("IpAddr", 0, "V4", a),
|
||||
net::IpAddr::V6(ref a) =>
|
||||
serializer.serialize_newtype_variant("IpAddr", 1, "V6", a),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -500,9 +528,13 @@ impl Serialize for net::Ipv4Addr {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
/// "101.102.103.104".len()
|
||||
const MAX_LEN: usize = 15;
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
if serializer.is_human_readable() {
|
||||
const MAX_LEN: usize = 15;
|
||||
debug_assert_eq!(MAX_LEN, "101.102.103.104".len());
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
} else {
|
||||
self.octets().serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,9 +544,13 @@ impl Serialize for net::Ipv6Addr {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
/// "1000:1002:1003:1004:1005:1006:1007:1008".len()
|
||||
const MAX_LEN: usize = 39;
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
if serializer.is_human_readable() {
|
||||
const MAX_LEN: usize = 39;
|
||||
debug_assert_eq!(MAX_LEN, "1001:1002:1003:1004:1005:1006:1007:1008".len());
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
} else {
|
||||
self.octets().serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -524,9 +560,18 @@ impl Serialize for net::SocketAddr {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
net::SocketAddr::V4(ref addr) => addr.serialize(serializer),
|
||||
net::SocketAddr::V6(ref addr) => addr.serialize(serializer),
|
||||
if serializer.is_human_readable() {
|
||||
match *self {
|
||||
net::SocketAddr::V4(ref addr) => addr.serialize(serializer),
|
||||
net::SocketAddr::V6(ref addr) => addr.serialize(serializer),
|
||||
}
|
||||
} else {
|
||||
match *self {
|
||||
net::SocketAddr::V4(ref addr) =>
|
||||
serializer.serialize_newtype_variant("SocketAddr", 0, "V4", addr),
|
||||
net::SocketAddr::V6(ref addr) =>
|
||||
serializer.serialize_newtype_variant("SocketAddr", 1, "V6", addr),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -537,9 +582,13 @@ impl Serialize for net::SocketAddrV4 {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
/// "101.102.103.104:65000".len()
|
||||
const MAX_LEN: usize = 21;
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
if serializer.is_human_readable() {
|
||||
const MAX_LEN: usize = 21;
|
||||
debug_assert_eq!(MAX_LEN, "101.102.103.104:65000".len());
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
} else {
|
||||
(self.ip(), self.port()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -549,9 +598,13 @@ impl Serialize for net::SocketAddrV6 {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
/// "[1000:1002:1003:1004:1005:1006:1007:1008]:65000".len()
|
||||
const MAX_LEN: usize = 47;
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
if serializer.is_human_readable() {
|
||||
const MAX_LEN: usize = 47;
|
||||
debug_assert_eq!(MAX_LEN, "[1001:1002:1003:1004:1005:1006:1007:1008]:65000".len());
|
||||
serialize_display_bounded_length!(self, MAX_LEN, serializer)
|
||||
} else {
|
||||
(self.ip(), self.port()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,3 +664,19 @@ impl Serialize for OsString {
|
|||
self.as_os_str().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> Serialize for Wrapping<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
//! - OsString
|
||||
//! - **Miscellaneous standard library types**:
|
||||
//! - Duration
|
||||
//! - SystemTime
|
||||
//! - Path
|
||||
//! - PathBuf
|
||||
//! - Range\<T\>
|
||||
|
@ -1321,7 +1322,7 @@ pub trait Serializer: Sized {
|
|||
///
|
||||
/// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
|
||||
/// [`serialize_str`]: #tymethod.serialize_str
|
||||
#[cfg(any(feature = "std", feature = "collections"))]
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Display,
|
||||
|
@ -1358,10 +1359,60 @@ pub trait Serializer: Sized {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(not(any(feature = "std", feature = "collections")))]
|
||||
#[cfg(not(any(feature = "std", feature = "alloc")))]
|
||||
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Display;
|
||||
|
||||
/// Determine whether `Serialize` implementations should serialize in
|
||||
/// human-readable form.
|
||||
///
|
||||
/// Some types have a human-readable form that may be somewhat expensive to
|
||||
/// construct, as well as a binary form that is compact and efficient.
|
||||
/// Generally text-based formats like JSON and YAML will prefer to use the
|
||||
/// human-readable one and binary formats like Bincode will prefer the
|
||||
/// compact one.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::fmt::{self, Display};
|
||||
/// #
|
||||
/// # struct Timestamp;
|
||||
/// #
|
||||
/// # impl Timestamp {
|
||||
/// # fn seconds_since_epoch(&self) -> u64 { unimplemented!() }
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl Display for Timestamp {
|
||||
/// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// use serde::{Serialize, Serializer};
|
||||
///
|
||||
/// impl Serialize for Timestamp {
|
||||
/// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
/// where S: Serializer
|
||||
/// {
|
||||
/// if serializer.is_human_readable() {
|
||||
/// // Serialize to a human-readable string "2015-05-15T17:01:00Z".
|
||||
/// self.to_string().serialize(serializer)
|
||||
/// } else {
|
||||
/// // Serialize to a compact binary representation.
|
||||
/// self.seconds_since_epoch().serialize(serializer)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The default implementation of this method returns `true`. Data formats
|
||||
/// may override this to `false` to request a compact form for types that
|
||||
/// support one. Note that modifying this method to change a format from
|
||||
/// human-readable to compact or vice versa should be regarded as a breaking
|
||||
/// change, as a value serialized in human-readable mode is not required to
|
||||
/// deserialize from the same data in compact mode.
|
||||
#[inline]
|
||||
fn is_human_readable(&self) -> bool { true }
|
||||
}
|
||||
|
||||
/// Returned from `Serializer::serialize_seq`.
|
||||
|
@ -1726,6 +1777,13 @@ pub trait SerializeStruct {
|
|||
where
|
||||
T: Serialize;
|
||||
|
||||
/// Indicate that a struct field has been skipped.
|
||||
#[inline]
|
||||
fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
|
||||
let _ = key;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Finish serializing a struct.
|
||||
fn end(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
@ -1771,6 +1829,13 @@ pub trait SerializeStructVariant {
|
|||
where
|
||||
T: Serialize;
|
||||
|
||||
/// Indicate that a struct variant field has been skipped.
|
||||
#[inline]
|
||||
fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
|
||||
let _ = key;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Finish serializing a struct variant.
|
||||
fn end(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"fc1c90305d1d8d6debda370913068486b2465b4ac548651f9122169def6da94c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"eedee04bddb61e99bc816656bb3b8ae2fa50ff00045ecdb5212682f3592d9ab2","src/bound.rs":"c01b1ec82b830b01a840d27654f2b1d3354e62a2b404227fccbb84d80cc6a593","src/de.rs":"518d74b38a7c254383fa6b2e5bfd71137e3abfbeda8b2577d25b7d8acdb03df5","src/fragment.rs":"f1642a1c2abbc36191206a5ec8077e314bdc20a420d7649e4bec3a69d555f78d","src/lib.rs":"fda8bd7ae031370f004d2672c12bfb9a5ae4314f499b5c7cbf9947e810d1d255","src/ser.rs":"b573af63ecfc51849b1157e2c3ae55b3bc256c3ce4b2712a639c5750e42eddfa"},"package":"10552fad5500771f3902d0c5ba187c5881942b811b7ba0d8fbbfbf84d80806d3"}
|
||||
{"files":{"Cargo.toml":"972517c9f00568bfc6806f09dad62aa94ed9f86afe35b4ab0e8d30f1600a04c5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7dc6376a88195594cd6b43cb891aad10f57144ff737178b55d046aa04739b43a","src/bound.rs":"4999df0c898ae4d5f0fdcd7a6365edabd5101027d67224ce54901c1653060f15","src/de.rs":"6783ba15e53f5d494c9af609474fa15d99d5e76bb0fea71e4d85829f3eeec2d0","src/fragment.rs":"f1642a1c2abbc36191206a5ec8077e314bdc20a420d7649e4bec3a69d555f78d","src/lib.rs":"49832577d7d7451cb6aedab7fdb1c9013e4ad0b8962d0d5d0936e69c9c9fc7ce","src/ser.rs":"e10042683c58f5eb833d9811ed3c2115e93f412188ac6f116c063851584fbec5"},"package":null}
|
|
@ -1,27 +1,22 @@
|
|||
# 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 = "serde_derive"
|
||||
version = "1.0.8"
|
||||
version = "1.0.23" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
||||
homepage = "https://serde.rs"
|
||||
documentation = "https://serde.rs/codegen.html"
|
||||
readme = "README.md"
|
||||
keywords = ["serde", "serialization", "no_std"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/serde-rs/serde"
|
||||
documentation = "https://serde.rs/codegen.html"
|
||||
keywords = ["serde", "serialization", "no_std"]
|
||||
readme = "README.md"
|
||||
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "serde-rs/serde" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
deserialize_from = []
|
||||
|
||||
[lib]
|
||||
name = "serde_derive"
|
||||
|
@ -29,13 +24,8 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
quote = "0.3.8"
|
||||
serde_derive_internals = { version = "=0.17.0", default-features = false, path = "../serde_derive_internals" }
|
||||
syn = { version = "0.11", features = ["visit"] }
|
||||
|
||||
[dependencies.syn]
|
||||
version = "0.11"
|
||||
features = ["visit"]
|
||||
|
||||
[dependencies.serde_derive_internals]
|
||||
version = "=0.15.1"
|
||||
default-features = false
|
||||
[badges.travis-ci]
|
||||
repository = "serde-rs/serde"
|
||||
[dev-dependencies]
|
||||
serde = { version = "1.0", path = "../serde" }
|
||||
|
|
|
@ -20,9 +20,30 @@ You may be looking for:
|
|||
|
||||
## Serde in action
|
||||
|
||||
<a href="http://play.integer32.com/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
<details>
|
||||
<summary>
|
||||
Click to show Cargo.toml.
|
||||
<a href="http://play.integer32.com/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">Run this code in the playground.</a>
|
||||
</summary>
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
|
||||
# The core APIs, including the Serialize and Deserialize traits. Always
|
||||
# required when using Serde.
|
||||
serde = "1.0"
|
||||
|
||||
# Support for #[derive(Serialize, Deserialize)]. Required if you want Serde
|
||||
# to work for structs and enums defined in your crate.
|
||||
serde_derive = "1.0"
|
||||
|
||||
# Each data format lives in its own crate; the sample code below uses JSON
|
||||
# but you may be using a different one.
|
||||
serde_json = "1.0"
|
||||
```
|
||||
|
||||
</details>
|
||||
<p></p>
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
|
|
|
@ -10,12 +10,12 @@ use std::collections::HashSet;
|
|||
|
||||
use syn::{self, visit};
|
||||
|
||||
use internals::ast::Container;
|
||||
use internals::ast::{Body, Container};
|
||||
use internals::attr;
|
||||
|
||||
macro_rules! path {
|
||||
($($path:tt)+) => {
|
||||
syn::parse_path(stringify!($($path)+)).unwrap()
|
||||
syn::parse_path(quote!($($path)+).as_str()).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ pub fn with_bound<F>(
|
|||
bound: &syn::Path,
|
||||
) -> syn::Generics
|
||||
where
|
||||
F: Fn(&attr::Field) -> bool,
|
||||
F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
|
||||
{
|
||||
struct FindTyParams {
|
||||
// Set of all generic type parameters on the current struct (A, B, C in
|
||||
|
@ -116,6 +116,14 @@ where
|
|||
}
|
||||
visit::walk_path(self, path);
|
||||
}
|
||||
|
||||
// Type parameter should not be considered used by a macro path.
|
||||
//
|
||||
// struct TypeMacro<T> {
|
||||
// mac: T!(),
|
||||
// marker: PhantomData<T>,
|
||||
// }
|
||||
fn visit_mac(&mut self, _mac: &syn::Mac) {}
|
||||
}
|
||||
|
||||
let all_ty_params: HashSet<_> = generics
|
||||
|
@ -124,17 +132,27 @@ where
|
|||
.map(|ty_param| ty_param.ident.clone())
|
||||
.collect();
|
||||
|
||||
let relevant_tys = cont.body
|
||||
.all_fields()
|
||||
.filter(|&field| filter(&field.attrs))
|
||||
.map(|field| &field.ty);
|
||||
|
||||
let mut visitor = FindTyParams {
|
||||
all_ty_params: all_ty_params,
|
||||
relevant_ty_params: HashSet::new(),
|
||||
};
|
||||
for ty in relevant_tys {
|
||||
visit::walk_ty(&mut visitor, ty);
|
||||
match cont.body {
|
||||
Body::Enum(_, ref variants) => {
|
||||
for variant in variants.iter() {
|
||||
let relevant_fields = variant
|
||||
.fields
|
||||
.iter()
|
||||
.filter(|field| filter(&field.attrs, Some(&variant.attrs)));
|
||||
for field in relevant_fields {
|
||||
visit::walk_ty(&mut visitor, field.ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
Body::Struct(_, ref fields) => {
|
||||
for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
|
||||
visit::walk_ty(&mut visitor, field.ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let new_predicates = generics
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,15 +8,21 @@
|
|||
|
||||
//! This crate provides Serde's two derive macros.
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! ```rust
|
||||
//! # #[macro_use]
|
||||
//! # extern crate serde_derive;
|
||||
//! #
|
||||
//! #[derive(Serialize, Deserialize)]
|
||||
//! # struct S;
|
||||
//! #
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
|
||||
//!
|
||||
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.8")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.23")]
|
||||
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(used_underscore_binding))]
|
||||
|
|
|
@ -29,9 +29,10 @@ pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<Tokens, Strin
|
|||
let body = Stmts(serialize_body(&cont, ¶ms));
|
||||
|
||||
let impl_block = if let Some(remote) = cont.attrs.remote() {
|
||||
let vis = &input.vis;
|
||||
quote! {
|
||||
impl #impl_generics #ident #ty_generics #where_clause {
|
||||
fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
|
||||
#vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
|
||||
where __S: _serde::Serializer
|
||||
{
|
||||
#body
|
||||
|
@ -142,12 +143,16 @@ fn build_generics(cont: &Container) -> syn::Generics {
|
|||
}
|
||||
}
|
||||
|
||||
// Fields with a `skip_serializing` or `serialize_with` attribute are not
|
||||
// serialized by us so we do not generate a bound. Fields with a `bound`
|
||||
// attribute specify their own bound so we do not generate one. All other fields
|
||||
// may need a `T: Serialize` bound where T is the type of the field.
|
||||
fn needs_serialize_bound(attrs: &attr::Field) -> bool {
|
||||
!attrs.skip_serializing() && attrs.serialize_with().is_none() && attrs.ser_bound().is_none()
|
||||
// Fields with a `skip_serializing` or `serialize_with` attribute, or which
|
||||
// belong to a variant with a `serialize_with` attribute, are not serialized by
|
||||
// us so we do not generate a bound. Fields with a `bound` attribute specify
|
||||
// their own bound so we do not generate one. All other fields may need a `T:
|
||||
// Serialize` bound where T is the type of the field.
|
||||
fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
|
||||
!field.skip_serializing() &&
|
||||
field.serialize_with().is_none() &&
|
||||
field.ser_bound().is_none() &&
|
||||
variant.map_or(true, |variant| variant.serialize_with().is_none())
|
||||
}
|
||||
|
||||
fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
||||
|
@ -155,7 +160,7 @@ fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
|||
serialize_into(params, into_type)
|
||||
} else {
|
||||
match cont.body {
|
||||
Body::Enum(ref variants) => serialize_enum(params, variants, &cont.attrs),
|
||||
Body::Enum(_, ref variants) => serialize_enum(params, variants, &cont.attrs),
|
||||
Body::Struct(Style::Struct, ref fields) => {
|
||||
if fields.iter().any(|field| field.ident.is_none()) {
|
||||
panic!("struct has unnamed fields");
|
||||
|
@ -202,7 +207,7 @@ fn serialize_newtype_struct(
|
|||
|
||||
let mut field_expr = get_field(params, field, 0);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote_expr! {
|
||||
|
@ -241,6 +246,7 @@ fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Contai
|
|||
params,
|
||||
false,
|
||||
quote!(_serde::ser::SerializeStruct::serialize_field),
|
||||
quote!(_serde::ser::SerializeStruct::skip_field),
|
||||
);
|
||||
|
||||
let type_name = cattrs.name().serialize_name();
|
||||
|
@ -394,6 +400,19 @@ fn serialize_externally_tagged_variant(
|
|||
let type_name = cattrs.name().serialize_name();
|
||||
let variant_name = variant.attrs.name().serialize_name();
|
||||
|
||||
if let Some(path) = variant.attrs.serialize_with() {
|
||||
let ser = wrap_serialize_variant_with(params, path, &variant);
|
||||
return quote_expr! {
|
||||
_serde::Serializer::serialize_newtype_variant(
|
||||
__serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#ser,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
quote_expr! {
|
||||
|
@ -409,7 +428,7 @@ fn serialize_externally_tagged_variant(
|
|||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__field0);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote_expr! {
|
||||
|
@ -459,6 +478,20 @@ fn serialize_internally_tagged_variant(
|
|||
let enum_ident_str = params.type_name();
|
||||
let variant_ident_str = variant.ident.as_ref();
|
||||
|
||||
if let Some(path) = variant.attrs.serialize_with() {
|
||||
let ser = wrap_serialize_variant_with(params, path, &variant);
|
||||
return quote_expr! {
|
||||
_serde::private::ser::serialize_tagged_newtype(
|
||||
__serializer,
|
||||
#enum_ident_str,
|
||||
#variant_ident_str,
|
||||
#tag,
|
||||
#variant_name,
|
||||
#ser,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
quote_block! {
|
||||
|
@ -473,7 +506,7 @@ fn serialize_internally_tagged_variant(
|
|||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__field0);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote_expr! {
|
||||
|
@ -514,44 +547,57 @@ fn serialize_adjacently_tagged_variant(
|
|||
let variant_name = variant.attrs.name().serialize_name();
|
||||
|
||||
let inner = Stmts(
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
return quote_block! {
|
||||
let mut __struct = try!(_serde::Serializer::serialize_struct(
|
||||
__serializer, #type_name, 1));
|
||||
try!(_serde::ser::SerializeStruct::serialize_field(
|
||||
&mut __struct, #tag, #variant_name));
|
||||
_serde::ser::SerializeStruct::end(__struct)
|
||||
};
|
||||
if let Some(path) = variant.attrs.serialize_with() {
|
||||
let ser = wrap_serialize_variant_with(params, path, &variant);
|
||||
quote_expr! {
|
||||
_serde::Serialize::serialize(#ser, __serializer)
|
||||
}
|
||||
Style::Newtype => {
|
||||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__field0);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
|
||||
} else {
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
return quote_block! {
|
||||
let mut __struct = try!(_serde::Serializer::serialize_struct(
|
||||
__serializer, #type_name, 1));
|
||||
try!(_serde::ser::SerializeStruct::serialize_field(
|
||||
&mut __struct, #tag, #variant_name));
|
||||
_serde::ser::SerializeStruct::end(__struct)
|
||||
};
|
||||
}
|
||||
Style::Newtype => {
|
||||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__field0);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote_expr! {
|
||||
_serde::Serialize::serialize(#field_expr, __serializer)
|
||||
quote_expr! {
|
||||
_serde::Serialize::serialize(#field_expr, __serializer)
|
||||
}
|
||||
}
|
||||
Style::Tuple => {
|
||||
serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
|
||||
}
|
||||
Style::Struct => {
|
||||
serialize_struct_variant(
|
||||
StructVariant::Untagged,
|
||||
params,
|
||||
&variant.fields,
|
||||
&variant_name,
|
||||
)
|
||||
}
|
||||
}
|
||||
Style::Tuple => {
|
||||
serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
|
||||
}
|
||||
Style::Struct => {
|
||||
serialize_struct_variant(
|
||||
StructVariant::Untagged,
|
||||
params,
|
||||
&variant.fields,
|
||||
&variant_name,
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let fields_ty = variant.fields.iter().map(|f| &f.ty);
|
||||
let ref fields_ident: Vec<_> = match variant.style {
|
||||
Style::Unit => unreachable!(),
|
||||
Style::Unit => {
|
||||
if variant.attrs.serialize_with().is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
Style::Newtype => vec![Ident::new("__field0")],
|
||||
Style::Tuple => {
|
||||
(0..variant.fields.len())
|
||||
|
@ -575,7 +621,11 @@ fn serialize_adjacently_tagged_variant(
|
|||
|
||||
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
|
||||
let wrapper_generics = bound::with_lifetime_bound(¶ms.generics, "'__a");
|
||||
let wrapper_generics = if let Style::Unit = variant.style {
|
||||
params.generics.clone()
|
||||
} else {
|
||||
bound::with_lifetime_bound(¶ms.generics, "'__a")
|
||||
};
|
||||
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
|
||||
|
||||
quote_block! {
|
||||
|
@ -611,6 +661,13 @@ fn serialize_untagged_variant(
|
|||
variant: &Variant,
|
||||
cattrs: &attr::Container,
|
||||
) -> Fragment {
|
||||
if let Some(path) = variant.attrs.serialize_with() {
|
||||
let ser = wrap_serialize_variant_with(params, path, &variant);
|
||||
return quote_expr! {
|
||||
_serde::Serialize::serialize(#ser, __serializer)
|
||||
};
|
||||
}
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
quote_expr! {
|
||||
|
@ -621,7 +678,7 @@ fn serialize_untagged_variant(
|
|||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__field0);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote_expr! {
|
||||
|
@ -706,15 +763,23 @@ fn serialize_struct_variant<'a>(
|
|||
fields: &[Field],
|
||||
name: &str,
|
||||
) -> Fragment {
|
||||
let method = match context {
|
||||
let (method, skip_method) = match context {
|
||||
StructVariant::ExternallyTagged { .. } => {
|
||||
quote!(_serde::ser::SerializeStructVariant::serialize_field)
|
||||
(
|
||||
quote!(_serde::ser::SerializeStructVariant::serialize_field),
|
||||
quote!(_serde::ser::SerializeStructVariant::skip_field),
|
||||
)
|
||||
}
|
||||
StructVariant::InternallyTagged { .. } |
|
||||
StructVariant::Untagged => quote!(_serde::ser::SerializeStruct::serialize_field),
|
||||
StructVariant::Untagged => {
|
||||
(
|
||||
quote!(_serde::ser::SerializeStruct::serialize_field),
|
||||
quote!(_serde::ser::SerializeStruct::skip_field),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let serialize_fields = serialize_struct_visitor(fields, params, true, method);
|
||||
let serialize_fields = serialize_struct_visitor(fields, params, true, method, skip_method);
|
||||
|
||||
let mut serialized_fields = fields
|
||||
.iter()
|
||||
|
@ -807,7 +872,7 @@ fn serialize_tuple_struct_visitor(
|
|||
.map(|path| quote!(#path(#field_expr)));
|
||||
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
let ser = quote! {
|
||||
|
@ -828,6 +893,7 @@ fn serialize_struct_visitor(
|
|||
params: &Parameters,
|
||||
is_enum: bool,
|
||||
func: Tokens,
|
||||
skip_func: Tokens,
|
||||
) -> Vec<Tokens> {
|
||||
fields
|
||||
.iter()
|
||||
|
@ -849,7 +915,7 @@ fn serialize_struct_visitor(
|
|||
.map(|path| quote!(#path(#field_expr)));
|
||||
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(params, field.ty, path, field_expr)
|
||||
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
let ser = quote! {
|
||||
|
@ -858,28 +924,71 @@ fn serialize_struct_visitor(
|
|||
|
||||
match skip {
|
||||
None => ser,
|
||||
Some(skip) => quote!(if !#skip { #ser }),
|
||||
Some(skip) => {
|
||||
quote! {
|
||||
if !#skip {
|
||||
#ser
|
||||
} else {
|
||||
try!(#skip_func(&mut __serde_state, #key_expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn wrap_serialize_with(
|
||||
fn wrap_serialize_field_with(
|
||||
params: &Parameters,
|
||||
field_ty: &syn::Ty,
|
||||
serialize_with: &syn::Path,
|
||||
value: Tokens,
|
||||
field_expr: Tokens,
|
||||
) -> Tokens {
|
||||
wrap_serialize_with(params,
|
||||
serialize_with,
|
||||
&[field_ty],
|
||||
&[quote!(#field_expr)])
|
||||
}
|
||||
|
||||
fn wrap_serialize_variant_with(
|
||||
params: &Parameters,
|
||||
serialize_with: &syn::Path,
|
||||
variant: &Variant,
|
||||
) -> Tokens {
|
||||
let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect();
|
||||
let field_exprs: Vec<_> = variant.fields.iter()
|
||||
.enumerate()
|
||||
.map(|(i, field)| {
|
||||
let id = field.ident.as_ref().map_or_else(|| Ident::new(format!("__field{}", i)),
|
||||
|id| id.clone());
|
||||
quote!(#id)
|
||||
})
|
||||
.collect();
|
||||
wrap_serialize_with(params, serialize_with, field_tys.as_slice(), field_exprs.as_slice())
|
||||
}
|
||||
|
||||
fn wrap_serialize_with(
|
||||
params: &Parameters,
|
||||
serialize_with: &syn::Path,
|
||||
field_tys: &[&syn::Ty],
|
||||
field_exprs: &[Tokens],
|
||||
) -> Tokens {
|
||||
let this = ¶ms.this;
|
||||
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
|
||||
let wrapper_generics = bound::with_lifetime_bound(¶ms.generics, "'__a");
|
||||
let wrapper_generics = if field_exprs.len() == 0 {
|
||||
params.generics.clone()
|
||||
} else {
|
||||
bound::with_lifetime_bound(¶ms.generics, "'__a")
|
||||
};
|
||||
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
|
||||
|
||||
let field_access = (0..field_exprs.len()).map(|n| Ident::new(format!("{}", n)));
|
||||
|
||||
quote!({
|
||||
struct __SerializeWith #wrapper_impl_generics #where_clause {
|
||||
value: &'__a #field_ty,
|
||||
values: (#(&'__a #field_tys, )*),
|
||||
phantom: _serde::export::PhantomData<#this #ty_generics>,
|
||||
}
|
||||
|
||||
|
@ -887,12 +996,12 @@ fn wrap_serialize_with(
|
|||
fn serialize<__S>(&self, __s: __S) -> _serde::export::Result<__S::Ok, __S::Error>
|
||||
where __S: _serde::Serializer
|
||||
{
|
||||
#serialize_with(self.value, __s)
|
||||
#serialize_with(#(self.values.#field_access, )* __s)
|
||||
}
|
||||
}
|
||||
|
||||
&__SerializeWith {
|
||||
value: #value,
|
||||
values: (#(#field_exprs, )*),
|
||||
phantom: _serde::export::PhantomData::<#this #ty_generics>,
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"511194979a2e34ebea7b27d4abe9350a6d6d2beb4ee074e296c5b95f4c8fc6e9","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"eedee04bddb61e99bc816656bb3b8ae2fa50ff00045ecdb5212682f3592d9ab2","src/ast.rs":"6d41e5055cc012474f72a2fb25874d8f59b872ccccddc2dee83ceaaa08135cd9","src/attr.rs":"bfd6120502807f8a5531a846fd76128ce133b9f9d162a6ea35e471afb73a3b20","src/case.rs":"036711fc550a405ab86d9470c94bac1d58dcdcad4e3a672c73cc0c5a0ecc138b","src/check.rs":"69ecc07d0916ae33ba868eb697ca35f7cd2e97127187f1e95efdcf7ad8e5e2f8","src/ctxt.rs":"98eab1acd3de8617b516d1de7d5d870bb1d2a1c1aca258a4cca2bde3f8df88e4","src/lib.rs":"7fef231c76b77486d813fa43a5dc7394fdeb8b1e95e02f5c1205213725ed400b"},"package":"37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"}
|
||||
{"files":{"Cargo.toml":"0435204d95404e25ee07fa3169ddf5eef26bde3fce03f4e63e9e55774d620199","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7dc6376a88195594cd6b43cb891aad10f57144ff737178b55d046aa04739b43a","src/ast.rs":"bc9936766227f78bf34cf003d271ffd70ad056e1272caafc1b185bdd32cc46bb","src/attr.rs":"51c92750f7b490a2c46950a68b5491d83181815d98e0fe3db102369b1ca9fe6e","src/case.rs":"be3288491dae43ca53643cb8409431e5e65ccf39c69c8047676e89c997827b0c","src/check.rs":"e976c9bac0daa334b2c82c6a91ac472235c7a1d91c5fe0deb2a83fe71b7c0663","src/ctxt.rs":"98eab1acd3de8617b516d1de7d5d870bb1d2a1c1aca258a4cca2bde3f8df88e4","src/lib.rs":"f242433bcb42c2360b8887d6e9f1b286a332080f1f0a081de06b0c061b563348"},"package":null}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.15.1" # remember to update html_root_url
|
||||
version = "0.17.0" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "AST representation used by Serde derive macros. Unstable."
|
||||
|
|
|
@ -20,9 +20,30 @@ You may be looking for:
|
|||
|
||||
## Serde in action
|
||||
|
||||
<a href="http://play.integer32.com/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
<details>
|
||||
<summary>
|
||||
Click to show Cargo.toml.
|
||||
<a href="http://play.integer32.com/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">Run this code in the playground.</a>
|
||||
</summary>
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
|
||||
# The core APIs, including the Serialize and Deserialize traits. Always
|
||||
# required when using Serde.
|
||||
serde = "1.0"
|
||||
|
||||
# Support for #[derive(Serialize, Deserialize)]. Required if you want Serde
|
||||
# to work for structs and enums defined in your crate.
|
||||
serde_derive = "1.0"
|
||||
|
||||
# Each data format lives in its own crate; the sample code below uses JSON
|
||||
# but you may be using a different one.
|
||||
serde_json = "1.0"
|
||||
```
|
||||
|
||||
</details>
|
||||
<p></p>
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
|
|
|
@ -19,7 +19,7 @@ pub struct Container<'a> {
|
|||
}
|
||||
|
||||
pub enum Body<'a> {
|
||||
Enum(Vec<Variant<'a>>),
|
||||
Enum(Repr, Vec<Variant<'a>>),
|
||||
Struct(Style, Vec<Field<'a>>),
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,12 @@ pub struct Field<'a> {
|
|||
pub ty: &'a syn::Ty,
|
||||
}
|
||||
|
||||
pub struct Repr {
|
||||
pub int_repr: Option<&'static str>,
|
||||
pub c_repr: bool,
|
||||
pub other_repr: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Style {
|
||||
Struct,
|
||||
|
@ -49,21 +55,23 @@ impl<'a> Container<'a> {
|
|||
let attrs = attr::Container::from_ast(cx, item);
|
||||
|
||||
let mut body = match item.body {
|
||||
syn::Body::Enum(ref variants) => Body::Enum(enum_from_ast(cx, variants)),
|
||||
syn::Body::Enum(ref variants) => enum_from_ast(cx, item, variants),
|
||||
syn::Body::Struct(ref variant_data) => {
|
||||
let (style, fields) = struct_from_ast(cx, variant_data);
|
||||
let (style, fields) = struct_from_ast(cx, variant_data, None);
|
||||
Body::Struct(style, fields)
|
||||
}
|
||||
};
|
||||
|
||||
match body {
|
||||
Body::Enum(ref mut variants) => {
|
||||
Body::Enum(_, ref mut variants) => {
|
||||
for ref mut variant in variants {
|
||||
variant.attrs.rename_by_rule(attrs.rename_all());
|
||||
for ref mut field in &mut variant.fields {
|
||||
field.attrs.rename_by_rule(variant.attrs.rename_all());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Body::Struct(_, ref mut fields) => {
|
||||
for field in fields {
|
||||
|
@ -86,7 +94,7 @@ impl<'a> Container<'a> {
|
|||
impl<'a> Body<'a> {
|
||||
pub fn all_fields(&'a self) -> Box<Iterator<Item = &'a Field<'a>> + 'a> {
|
||||
match *self {
|
||||
Body::Enum(ref variants) => {
|
||||
Body::Enum(_, ref variants) => {
|
||||
Box::new(variants.iter().flat_map(|variant| variant.fields.iter()))
|
||||
}
|
||||
Body::Struct(_, ref fields) => Box::new(fields.iter()),
|
||||
|
@ -98,35 +106,97 @@ impl<'a> Body<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enum_from_ast<'a>(cx: &Ctxt, variants: &'a [syn::Variant]) -> Vec<Variant<'a>> {
|
||||
variants
|
||||
impl Repr {
|
||||
/// Gives the int type to use for the `repr(int)` enum layout
|
||||
pub fn get_stable_rust_enum_layout(&self) -> Option<&'static str> {
|
||||
if self.c_repr || self.other_repr {
|
||||
None
|
||||
} else {
|
||||
self.int_repr
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives the int type to use for the `repr(C, int)` enum layout
|
||||
pub fn get_stable_c_enum_layout(&self) -> Option<&'static str> {
|
||||
if !self.c_repr && self.other_repr {
|
||||
None
|
||||
} else {
|
||||
self.int_repr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enum_from_ast<'a>(cx: &Ctxt, item: &'a syn::DeriveInput, variants: &'a [syn::Variant]) -> Body<'a> {
|
||||
let variants = variants
|
||||
.iter()
|
||||
.map(
|
||||
|variant| {
|
||||
let (style, fields) = struct_from_ast(cx, &variant.data);
|
||||
let attrs = attr::Variant::from_ast(cx, variant);
|
||||
let (style, fields) = struct_from_ast(cx, &variant.data, Some(&attrs));
|
||||
Variant {
|
||||
ident: variant.ident.clone(),
|
||||
attrs: attr::Variant::from_ast(cx, variant),
|
||||
attrs: attrs,
|
||||
style: style,
|
||||
fields: fields,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
// Compute repr info for enum optimizations
|
||||
static INT_TYPES: [&'static str; 12] = [
|
||||
"u8", "u16", "u32", "u64", "u128", "usize",
|
||||
"i8", "i16", "i32", "i64", "i128", "isize",
|
||||
];
|
||||
|
||||
let mut int_repr = None;
|
||||
let mut c_repr = false;
|
||||
let mut other_repr = false;
|
||||
|
||||
for attr in &item.attrs {
|
||||
if let syn::MetaItem::List(ref ident, ref vals) = attr.value {
|
||||
if *ident == "repr" {
|
||||
// has_repr = true;
|
||||
for repr in vals {
|
||||
if let syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref repr)) = *repr {
|
||||
if repr == "C" {
|
||||
c_repr = true;
|
||||
} else if let Some(int_type) = INT_TYPES.iter().cloned().find(|int_type| repr == int_type) {
|
||||
if int_repr.is_some() {
|
||||
// This shouldn't happen, but we shouldn't crash if we see it.
|
||||
// So just treat the enum as having a mysterious other repr,
|
||||
// which makes us discard any attempt to optimize based on layout.
|
||||
other_repr = true;
|
||||
}
|
||||
int_repr = Some(int_type);
|
||||
} else {
|
||||
other_repr = true;
|
||||
}
|
||||
} else {
|
||||
panic!("impossible repr? {:?}", repr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let repr = Repr { int_repr, c_repr, other_repr };
|
||||
|
||||
Body::Enum(repr, variants)
|
||||
}
|
||||
|
||||
fn struct_from_ast<'a>(cx: &Ctxt, data: &'a syn::VariantData) -> (Style, Vec<Field<'a>>) {
|
||||
fn struct_from_ast<'a>(cx: &Ctxt, data: &'a syn::VariantData, attrs: Option<&attr::Variant>) -> (Style, Vec<Field<'a>>) {
|
||||
match *data {
|
||||
syn::VariantData::Struct(ref fields) => (Style::Struct, fields_from_ast(cx, fields)),
|
||||
syn::VariantData::Struct(ref fields) => (Style::Struct, fields_from_ast(cx, fields, attrs)),
|
||||
syn::VariantData::Tuple(ref fields) if fields.len() == 1 => {
|
||||
(Style::Newtype, fields_from_ast(cx, fields))
|
||||
(Style::Newtype, fields_from_ast(cx, fields, attrs))
|
||||
}
|
||||
syn::VariantData::Tuple(ref fields) => (Style::Tuple, fields_from_ast(cx, fields)),
|
||||
syn::VariantData::Tuple(ref fields) => (Style::Tuple, fields_from_ast(cx, fields, attrs)),
|
||||
syn::VariantData::Unit => (Style::Unit, Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field]) -> Vec<Field<'a>> {
|
||||
fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field], attrs: Option<&attr::Variant>) -> Vec<Field<'a>> {
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
@ -134,7 +204,7 @@ fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field]) -> Vec<Field<'a>> {
|
|||
|(i, field)| {
|
||||
Field {
|
||||
ident: field.ident.clone(),
|
||||
attrs: attr::Field::from_ast(cx, i, field),
|
||||
attrs: attr::Field::from_ast(cx, i, field, attrs),
|
||||
ty: &field.ty,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -510,6 +510,9 @@ pub struct Variant {
|
|||
skip_deserializing: bool,
|
||||
skip_serializing: bool,
|
||||
other: bool,
|
||||
serialize_with: Option<syn::Path>,
|
||||
deserialize_with: Option<syn::Path>,
|
||||
borrow: Option<syn::MetaItem>,
|
||||
}
|
||||
|
||||
impl Variant {
|
||||
|
@ -520,6 +523,9 @@ impl Variant {
|
|||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||
let mut rename_all = Attr::none(cx, "rename_all");
|
||||
let mut other = BoolAttr::none(cx, "other");
|
||||
let mut serialize_with = Attr::none(cx, "serialize_with");
|
||||
let mut deserialize_with = Attr::none(cx, "deserialize_with");
|
||||
let mut borrow = Attr::none(cx, "borrow");
|
||||
|
||||
for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
|
@ -569,6 +575,44 @@ impl Variant {
|
|||
other.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(with = "...")]`
|
||||
MetaItem(NameValue(ref name, ref lit)) if name == "with" => {
|
||||
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
|
||||
let mut ser_path = path.clone();
|
||||
ser_path.segments.push("serialize".into());
|
||||
serialize_with.set(ser_path);
|
||||
let mut de_path = path;
|
||||
de_path.segments.push("deserialize".into());
|
||||
deserialize_with.set(de_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(serialize_with = "...")]`
|
||||
MetaItem(NameValue(ref name, ref lit)) if name == "serialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
|
||||
serialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(deserialize_with = "...")]`
|
||||
MetaItem(NameValue(ref name, ref lit)) if name == "deserialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
|
||||
deserialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]`
|
||||
MetaItem(ref mi) if mi.name() == "borrow" => {
|
||||
match variant.data {
|
||||
syn::VariantData::Tuple(ref fields) if fields.len() == 1 => {
|
||||
borrow.set(mi.clone());
|
||||
}
|
||||
_ => {
|
||||
cx.error("#[serde(borrow)] may only be used on newtype variants");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MetaItem(ref meta_item) => {
|
||||
cx.error(format!("unknown serde variant attribute `{}`", meta_item.name()));
|
||||
}
|
||||
|
@ -595,6 +639,9 @@ impl Variant {
|
|||
skip_deserializing: skip_deserializing.get(),
|
||||
skip_serializing: skip_serializing.get(),
|
||||
other: other.get(),
|
||||
serialize_with: serialize_with.get(),
|
||||
deserialize_with: deserialize_with.get(),
|
||||
borrow: borrow.get(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,6 +673,14 @@ impl Variant {
|
|||
pub fn other(&self) -> bool {
|
||||
self.other
|
||||
}
|
||||
|
||||
pub fn serialize_with(&self) -> Option<&syn::Path> {
|
||||
self.serialize_with.as_ref()
|
||||
}
|
||||
|
||||
pub fn deserialize_with(&self) -> Option<&syn::Path> {
|
||||
self.deserialize_with.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents field attribute information
|
||||
|
@ -659,7 +714,7 @@ pub enum Default {
|
|||
|
||||
impl Field {
|
||||
/// Extract out the `#[serde(...)]` attributes from a struct field.
|
||||
pub fn from_ast(cx: &Ctxt, index: usize, field: &syn::Field) -> Self {
|
||||
pub fn from_ast(cx: &Ctxt, index: usize, field: &syn::Field, attrs: Option<&Variant>) -> Self {
|
||||
let mut ser_name = Attr::none(cx, "rename");
|
||||
let mut de_name = Attr::none(cx, "rename");
|
||||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||
|
@ -678,7 +733,13 @@ impl Field {
|
|||
None => index.to_string(),
|
||||
};
|
||||
|
||||
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
|
||||
let variant_borrow = attrs
|
||||
.map(|variant| &variant.borrow)
|
||||
.unwrap_or(&None)
|
||||
.as_ref()
|
||||
.map(|borrow| vec![MetaItem(borrow.clone())]);
|
||||
|
||||
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items).chain(variant_borrow) {
|
||||
for meta_item in meta_items {
|
||||
match meta_item {
|
||||
// Parse `#[serde(rename = "foo")]`
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// See https://users.rust-lang.org/t/psa-dealing-with-warning-unused-import-std-ascii-asciiext-in-today-s-nightly/13726
|
||||
#[allow(unused_imports)]
|
||||
use std::ascii::AsciiExt;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use self::RenameRule::*;
|
||||
|
@ -27,6 +30,8 @@ pub enum RenameRule {
|
|||
ScreamingSnakeCase,
|
||||
/// Rename direct children to "kebab-case" style.
|
||||
KebabCase,
|
||||
/// Rename direct children to "SCREAMING-KEBAB-CASE" style.
|
||||
ScreamingKebabCase
|
||||
}
|
||||
|
||||
impl RenameRule {
|
||||
|
@ -47,6 +52,7 @@ impl RenameRule {
|
|||
}
|
||||
ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
|
||||
KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
|
||||
ScreamingKebabCase => ScreamingSnakeCase.apply_to_variant(variant).replace('_', "-")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,6 +80,7 @@ impl RenameRule {
|
|||
}
|
||||
ScreamingSnakeCase => field.to_ascii_uppercase(),
|
||||
KebabCase => field.replace('_', "-"),
|
||||
ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +96,7 @@ impl FromStr for RenameRule {
|
|||
"snake_case" => Ok(SnakeCase),
|
||||
"SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
|
||||
"kebab-case" => Ok(KebabCase),
|
||||
"SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -96,12 +104,12 @@ impl FromStr for RenameRule {
|
|||
|
||||
#[test]
|
||||
fn rename_variants() {
|
||||
for &(original, lower, camel, snake, screaming, kebab) in
|
||||
for &(original, lower, camel, snake, screaming, kebab, screaming_kebab) in
|
||||
&[
|
||||
("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome"),
|
||||
("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty"),
|
||||
("A", "a", "a", "a", "A", "a"),
|
||||
("Z42", "z42", "z42", "z42", "Z42", "z42"),
|
||||
("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
|
||||
("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
|
||||
("A", "a", "a", "a", "A", "a", "A"),
|
||||
("Z42", "z42", "z42", "z42", "Z42", "z42", "Z42"),
|
||||
] {
|
||||
assert_eq!(None.apply_to_variant(original), original);
|
||||
assert_eq!(LowerCase.apply_to_variant(original), lower);
|
||||
|
@ -110,17 +118,18 @@ fn rename_variants() {
|
|||
assert_eq!(SnakeCase.apply_to_variant(original), snake);
|
||||
assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
|
||||
assert_eq!(KebabCase.apply_to_variant(original), kebab);
|
||||
assert_eq!(ScreamingKebabCase.apply_to_variant(original), screaming_kebab);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_fields() {
|
||||
for &(original, pascal, camel, screaming, kebab) in
|
||||
for &(original, pascal, camel, screaming, kebab, screaming_kebab) in
|
||||
&[
|
||||
("outcome", "Outcome", "outcome", "OUTCOME", "outcome"),
|
||||
("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty"),
|
||||
("a", "A", "a", "A", "a"),
|
||||
("z42", "Z42", "z42", "Z42", "z42"),
|
||||
("outcome", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
|
||||
("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
|
||||
("a", "A", "a", "A", "a", "A"),
|
||||
("z42", "Z42", "z42", "Z42", "z42", "Z42"),
|
||||
] {
|
||||
assert_eq!(None.apply_to_field(original), original);
|
||||
assert_eq!(PascalCase.apply_to_field(original), pascal);
|
||||
|
@ -128,5 +137,6 @@ fn rename_fields() {
|
|||
assert_eq!(SnakeCase.apply_to_field(original), original);
|
||||
assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
|
||||
assert_eq!(KebabCase.apply_to_field(original), kebab);
|
||||
assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,14 @@ use Ctxt;
|
|||
pub fn check(cx: &Ctxt, cont: &Container) {
|
||||
check_getter(cx, cont);
|
||||
check_identifier(cx, cont);
|
||||
check_variant_skip_attrs(cx, cont);
|
||||
}
|
||||
|
||||
/// Getters are only allowed inside structs (not enums) with the `remote`
|
||||
/// attribute.
|
||||
fn check_getter(cx: &Ctxt, cont: &Container) {
|
||||
match cont.body {
|
||||
Body::Enum(_) => {
|
||||
Body::Enum(_, _) => {
|
||||
if cont.body.has_getter() {
|
||||
cx.error("#[serde(getter = \"...\")] is not allowed in an enum");
|
||||
}
|
||||
|
@ -45,7 +46,7 @@ fn check_getter(cx: &Ctxt, cont: &Container) {
|
|||
/// last variant may be a newtype variant which is an implicit "other" case.
|
||||
fn check_identifier(cx: &Ctxt, cont: &Container) {
|
||||
let variants = match cont.body {
|
||||
Body::Enum(ref variants) => variants,
|
||||
Body::Enum(_, ref variants) => variants,
|
||||
Body::Struct(_, _) => {
|
||||
return;
|
||||
}
|
||||
|
@ -94,3 +95,58 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip-(de)serializing attributes are not allowed on variants marked
|
||||
/// (de)serialize_with.
|
||||
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
|
||||
let variants = match cont.body {
|
||||
Body::Enum(_, ref variants) => variants,
|
||||
Body::Struct(_, _) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for variant in variants.iter() {
|
||||
if variant.attrs.serialize_with().is_some() {
|
||||
if variant.attrs.skip_serializing() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
#[serde(skip_serializing)]", variant.ident));
|
||||
}
|
||||
|
||||
for (i, field) in variant.fields.iter().enumerate() {
|
||||
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|
||||
|ident| format!("`{}`", ident));
|
||||
|
||||
if field.attrs.skip_serializing() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
a field {} marked with #[serde(skip_serializing)]",
|
||||
variant.ident, ident));
|
||||
}
|
||||
|
||||
if field.attrs.skip_serializing_if().is_some() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
a field {} marked with #[serde(skip_serializing_if)]",
|
||||
variant.ident, ident));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if variant.attrs.deserialize_with().is_some() {
|
||||
if variant.attrs.skip_deserializing() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(deserialize_with)] and \
|
||||
#[serde(skip_deserializing)]", variant.ident));
|
||||
}
|
||||
|
||||
for (i, field) in variant.fields.iter().enumerate() {
|
||||
if field.attrs.skip_deserializing() {
|
||||
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|
||||
|ident| format!("`{}`", ident));
|
||||
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(deserialize_with)] \
|
||||
and a field {} marked with #[serde(skip_deserializing)]",
|
||||
variant.ident, ident));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.15.1")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.17.0")]
|
||||
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"91edce5ea2a1956399db4b17f580c8b7995af3aa9801c4314865f560c55d6d09","Cargo.toml":"bc42bda24309b1a1d289458aad00fc1e1b354586c5e7f69ab0c83a16b66d4b61","README.md":"ecca7edfce86fe7b219535e3c14721d1de838de7035de077a4d497959260bccc","benches/bench.rs":"54cf4879d36ba2a9f3423af91bb93227b70849200e5bf74e384a166d6aa09893","lib.rs":"517f626b3d4649ae03cee12d6dd26d4baf8a6e1fac9bf410796f08a3e784007f"},"package":"872c0ff227000041c520cca51e883b858d388ab0ecf646bab76f065cebaec025"}
|
|
@ -1,14 +0,0 @@
|
|||
language: rust
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
script: |
|
||||
cargo build --verbose &&
|
||||
cargo build --all-features --verbose &&
|
||||
cargo test --verbose &&
|
||||
cargo test --all-features --verbose &&
|
||||
([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --no-default-features) &&
|
||||
([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench)
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
|
@ -1,35 +0,0 @@
|
|||
# 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 = "smallvec"
|
||||
version = "0.5.0"
|
||||
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
|
||||
description = "'Small vector' optimization: store up to a small number of items on the stack"
|
||||
documentation = "http://doc.servo.org/smallvec/"
|
||||
readme = "README.md"
|
||||
keywords = ["small", "vec", "vector", "stack", "no_std"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/rust-smallvec"
|
||||
|
||||
[lib]
|
||||
name = "smallvec"
|
||||
path = "lib.rs"
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
optional = true
|
||||
[dev-dependencies.bincode]
|
||||
version = "0.8"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
|
@ -1,6 +0,0 @@
|
|||
rust-smallvec
|
||||
=============
|
||||
|
||||
[Documentation](http://docs.rs/smallvec/)
|
||||
|
||||
"Small vector" optimization for Rust: store up to a small number of items on the stack
|
|
@ -1,111 +0,0 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate smallvec;
|
||||
extern crate test;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_push(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn push_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
|
||||
vec.push(x)
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
for x in 0..100 {
|
||||
push_noinline(&mut vec, x);
|
||||
}
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn insert_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
|
||||
vec.insert(0, x)
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
for x in 0..100 {
|
||||
insert_noinline(&mut vec, x);
|
||||
}
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert_many(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn insert_many_noinline<I: IntoIterator<Item=u64>>(
|
||||
vec: &mut SmallVec<[u64; 16]>, index: usize, iterable: I) {
|
||||
vec.insert_many(index, iterable)
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
insert_many_noinline(&mut vec, 0, 0..100);
|
||||
insert_many_noinline(&mut vec, 0, 0..100);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_extend(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
vec.extend(0..100);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_from_slice(b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..100).collect();
|
||||
b.iter(|| {
|
||||
let vec: SmallVec<[u64; 16]> = SmallVec::from_slice(&v);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_extend_from_slice(b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..100).collect();
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
vec.extend_from_slice(&v);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert_from_slice(b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..100).collect();
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
vec.insert_from_slice(0, &v);
|
||||
vec.insert_from_slice(0, &v);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pushpop(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn pushpop_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
|
||||
vec.push(x);
|
||||
vec.pop();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
for x in 0..100 {
|
||||
pushpop_noinline(&mut vec, x);
|
||||
}
|
||||
vec
|
||||
});
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -11,24 +11,13 @@ name = "ansi_term"
|
|||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "app_units"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "app_units"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -67,8 +56,8 @@ dependencies = [
|
|||
"memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -111,7 +100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -120,7 +109,7 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -404,8 +393,8 @@ dependencies = [
|
|||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -454,17 +443,6 @@ name = "error-chain"
|
|||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.16.0"
|
||||
|
@ -472,7 +450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -633,14 +611,6 @@ dependencies = [
|
|||
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapsize"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.0"
|
||||
|
@ -1022,11 +992,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.6.0"
|
||||
version = "0.7.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.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1128,11 +1098,6 @@ dependencies = [
|
|||
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "0.1.3"
|
||||
|
@ -1180,26 +1145,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "1.0.23"
|
||||
source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums3#fc6117367ef974fb2d3b2017c21c375d34823415"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "1.0.23"
|
||||
source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums3#fc6117367ef974fb2d3b2017c21c375d34823415"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive_internals 0.17.0 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums3#fc6117367ef974fb2d3b2017c21c375d34823415"
|
||||
dependencies = [
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synom 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1233,11 +1198,6 @@ name = "smallbitvec"
|
|||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.0"
|
||||
|
@ -1496,9 +1456,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webrender"
|
||||
version = "0.55.0"
|
||||
version = "0.56.1"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1506,34 +1466,35 @@ dependencies = [
|
|||
"core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 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.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_api 0.55.0",
|
||||
"webrender_api 0.56.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webrender_api"
|
||||
version = "0.55.0"
|
||||
version = "0.56.1"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.0 (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.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1541,17 +1502,17 @@ dependencies = [
|
|||
name = "webrender_bindings"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender 0.55.0",
|
||||
"webrender 0.56.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1592,7 +1553,6 @@ dependencies = [
|
|||
[metadata]
|
||||
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
|
||||
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
||||
"checksum app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ed0a4de09a3b8449515e649f3bb84f72ea15fc2d10639beb0776a09b7d308074"
|
||||
"checksum app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"
|
||||
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
|
||||
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
|
||||
|
@ -1633,7 +1593,6 @@ dependencies = [
|
|||
"checksum encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5215aabf22b83153be3ee44dfe3f940214541b2ce13d419c55e7a115c8c51a9"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8"
|
||||
"checksum euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ed7d77e46f6600f490463ad7b6349c3ebb2d2319af56e679e279e4c66495d9"
|
||||
"checksum euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "926c639bfdff1f3063f76bb66245f6d2b691aa20fdbaabecc38b2947a13a4eba"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ebc04f19019fff1f2d627b5581574ead502f80c48c88900575a46e0840fe5d0"
|
||||
|
@ -1647,7 +1606,6 @@ dependencies = [
|
|||
"checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d"
|
||||
"checksum gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dff613336334932baaa2759d001f14e06ea1a08a247c05962d1423aa0e89ee99"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
|
||||
"checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
|
||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
|
||||
|
@ -1685,7 +1643,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.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e57800a97ca52c556db6b6184a3201f05366ad5e11876f7d17e234589ca2fa26"
|
||||
"checksum plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2adb8d1523b2ddcd98275613e9bc04eef75b47a39e252e63733a3218ae3c1b7"
|
||||
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
|
@ -1696,19 +1654,17 @@ dependencies = [
|
|||
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
|
||||
"checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918"
|
||||
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f530d36fb84ec48fb7146936881f026cdbf4892028835fd9398475f82c1bb4"
|
||||
"checksum serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "10552fad5500771f3902d0c5ba187c5881942b811b7ba0d8fbbfbf84d80806d3"
|
||||
"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
|
||||
"checksum serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)" = "<none>"
|
||||
"checksum serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)" = "<none>"
|
||||
"checksum serde_derive_internals 0.17.0 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)" = "<none>"
|
||||
"checksum simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a94d14a2ae1f1f110937de5fb69e494372560181c7e1739a097fcc2cee37ba0"
|
||||
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79b776f00dfe01df905fa3b2eaa1659522e99e3fc4a7b1334171622205c4bdcf"
|
||||
"checksum smallvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "872c0ff227000041c520cca51e883b858d388ab0ecf646bab76f065cebaec025"
|
||||
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
|
||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
|
|
|
@ -11,24 +11,13 @@ name = "ansi_term"
|
|||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "app_units"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "app_units"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -67,8 +56,8 @@ dependencies = [
|
|||
"memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -111,7 +100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -120,7 +109,7 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -404,8 +393,8 @@ dependencies = [
|
|||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -454,17 +443,6 @@ name = "error-chain"
|
|||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.16.0"
|
||||
|
@ -472,7 +450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -632,14 +610,6 @@ dependencies = [
|
|||
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapsize"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.0"
|
||||
|
@ -1010,11 +980,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.6.0"
|
||||
version = "0.7.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.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1116,11 +1086,6 @@ dependencies = [
|
|||
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "0.1.3"
|
||||
|
@ -1168,26 +1133,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "1.0.23"
|
||||
source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums3#fc6117367ef974fb2d3b2017c21c375d34823415"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "1.0.23"
|
||||
source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums3#fc6117367ef974fb2d3b2017c21c375d34823415"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive_internals 0.17.0 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums3#fc6117367ef974fb2d3b2017c21c375d34823415"
|
||||
dependencies = [
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synom 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1225,11 +1190,6 @@ name = "smallbitvec"
|
|||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.0"
|
||||
|
@ -1508,9 +1468,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webrender"
|
||||
version = "0.55.0"
|
||||
version = "0.56.1"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1518,34 +1478,35 @@ dependencies = [
|
|||
"core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 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.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_api 0.55.0",
|
||||
"webrender_api 0.56.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webrender_api"
|
||||
version = "0.55.0"
|
||||
version = "0.56.1"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.0 (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.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1553,17 +1514,17 @@ dependencies = [
|
|||
name = "webrender_bindings"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender 0.55.0",
|
||||
"webrender 0.56.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1604,7 +1565,6 @@ dependencies = [
|
|||
[metadata]
|
||||
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
|
||||
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
||||
"checksum app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ed0a4de09a3b8449515e649f3bb84f72ea15fc2d10639beb0776a09b7d308074"
|
||||
"checksum app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"
|
||||
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
|
||||
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
|
||||
|
@ -1645,7 +1605,6 @@ dependencies = [
|
|||
"checksum encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5215aabf22b83153be3ee44dfe3f940214541b2ce13d419c55e7a115c8c51a9"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8"
|
||||
"checksum euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ed7d77e46f6600f490463ad7b6349c3ebb2d2319af56e679e279e4c66495d9"
|
||||
"checksum euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "926c639bfdff1f3063f76bb66245f6d2b691aa20fdbaabecc38b2947a13a4eba"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ebc04f19019fff1f2d627b5581574ead502f80c48c88900575a46e0840fe5d0"
|
||||
|
@ -1659,7 +1618,6 @@ dependencies = [
|
|||
"checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d"
|
||||
"checksum gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dff613336334932baaa2759d001f14e06ea1a08a247c05962d1423aa0e89ee99"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
|
||||
"checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
|
||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
|
||||
|
@ -1697,7 +1655,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.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e57800a97ca52c556db6b6184a3201f05366ad5e11876f7d17e234589ca2fa26"
|
||||
"checksum plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2adb8d1523b2ddcd98275613e9bc04eef75b47a39e252e63733a3218ae3c1b7"
|
||||
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
|
@ -1708,19 +1666,17 @@ dependencies = [
|
|||
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
|
||||
"checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918"
|
||||
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f530d36fb84ec48fb7146936881f026cdbf4892028835fd9398475f82c1bb4"
|
||||
"checksum serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "10552fad5500771f3902d0c5ba187c5881942b811b7ba0d8fbbfbf84d80806d3"
|
||||
"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
|
||||
"checksum serde 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)" = "<none>"
|
||||
"checksum serde_derive 1.0.23 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)" = "<none>"
|
||||
"checksum serde_derive_internals 0.17.0 (git+https://github.com/gankro/serde?branch=deserialize_from_enums3)" = "<none>"
|
||||
"checksum simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a94d14a2ae1f1f110937de5fb69e494372560181c7e1739a097fcc2cee37ba0"
|
||||
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79b776f00dfe01df905fa3b2eaa1659522e99e3fc4a7b1334171622205c4bdcf"
|
||||
"checksum smallvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "872c0ff227000041c520cca51e883b858d388ab0ecf646bab76f065cebaec025"
|
||||
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
|
||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
|
|
Загрузка…
Ссылка в новой задаче