зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1656236 - Vendor euclid update. r=gw
Differential Revision: https://phabricator.services.mozilla.com/D85545
This commit is contained in:
Родитель
31d1e467eb
Коммит
61179d460f
|
@ -1299,6 +1299,15 @@ name = "euclid"
|
|||
version = "0.20.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bb7ef65b3777a325d1eeefefab5b6d4959da54747e33bd6258e789640f307ad"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab0e07e345fb061928646949fdf5fb888e5d75a57385e7f5856e45be289e745"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
|
@ -2780,7 +2789,7 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"app_units",
|
||||
"cssparser",
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
"hashglobe",
|
||||
"selectors",
|
||||
"servo_arc",
|
||||
|
@ -3529,7 +3538,7 @@ dependencies = [
|
|||
name = "peek-poke"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
"peek-poke-derive 0.2.1",
|
||||
]
|
||||
|
||||
|
@ -3688,12 +3697,12 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.15.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe16a646a08f4b4dd74035b9ff8e378eb1a4012a74f14f5889e7001cdbece33"
|
||||
checksum = "2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654"
|
||||
dependencies = [
|
||||
"binary-space-partition",
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
"log",
|
||||
"num-traits",
|
||||
]
|
||||
|
@ -4621,7 +4630,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"cssparser",
|
||||
"derive_more",
|
||||
"euclid",
|
||||
"euclid 0.20.14",
|
||||
"fallible",
|
||||
"fxhash",
|
||||
"hashbrown",
|
||||
|
@ -4684,7 +4693,7 @@ dependencies = [
|
|||
"app_units",
|
||||
"bitflags",
|
||||
"cssparser",
|
||||
"euclid",
|
||||
"euclid 0.20.14",
|
||||
"lazy_static",
|
||||
"malloc_size_of",
|
||||
"malloc_size_of_derive",
|
||||
|
@ -5540,7 +5549,7 @@ dependencies = [
|
|||
"core-text",
|
||||
"cstr",
|
||||
"dwrote",
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
"freetype",
|
||||
"fxhash",
|
||||
"gleam",
|
||||
|
@ -5578,7 +5587,7 @@ dependencies = [
|
|||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"derive_more",
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
"malloc_size_of_derive",
|
||||
"peek-poke 0.2.0",
|
||||
"serde",
|
||||
|
@ -5598,7 +5607,7 @@ dependencies = [
|
|||
"core-graphics",
|
||||
"dirs",
|
||||
"dwrote",
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
"foreign-types",
|
||||
"fxhash",
|
||||
"gleam",
|
||||
|
@ -5766,7 +5775,7 @@ name = "wr_malloc_size_of"
|
|||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"euclid",
|
||||
"euclid 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"430f53fbae7381f12155805f713f9b9bb08a9e9ffff3a3f4c887163fb4521e7d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/angle.rs":"b4b17c16bd135914a6e2ebad54b9cbb8ea475d5992bcb75c397b7813b6f7b8dd","src/approxeq.rs":"8ad9ab7336e1924b0543eb46f975367f4388a4dda91839b7cda7d21eec8991fb","src/approxord.rs":"f1b11ea7603b3dddb0002c7ded934816f5a8bb108e150028b49595b8e3382ac1","src/box2d.rs":"0f7ef877183e25e6b769fc91a0d814e0992b0e6d1c16df415ebbd364d2fd2cf7","src/box3d.rs":"54d6e59622957e784e611a23da9612671a530c61cd364313024d750f953a7564","src/homogen.rs":"3aa960fae9b817057013183f543cf2adc042f5867ece1ee78607e2c46bdbcfed","src/length.rs":"67ef01cfba3f70143c57afda39c8bf9990f29111a031a238de328753ccc2c8a4","src/lib.rs":"d0f1ddda70d8bf3913d9c2c49949dd21ced500cbc0c09d7d08c0aa1570f63df5","src/macros.rs":"3b475e84d00cceee6c7e96e9f2c97ba15d8dc7f4094efb82c5ed10bd60d86a64","src/nonempty.rs":"be5ac81321390d71447607de01811cc6420bcd48aa5274f7a298a99674621846","src/num.rs":"2478501dbe545a73a8b964f4713987b86df8ff0af3d1aba0571cbd00a4e9cbaa","src/point.rs":"6d07046f27dd7a18ba8db8988de82c67cfcdd1b8895a70730123338574e47d39","src/rect.rs":"1391359581ba891a35f390ec3900f1e34ce1e4dc5608fe27662655d3bf443d45","src/rigid.rs":"a9717aac4d254299dc3a60a2a32c5c5309044136702d96030533f7639adeb467","src/rotation.rs":"6cfc7f74a90236c53fa55b83eae373146f7b37a8c400f8f820ebfdbcca245245","src/scale.rs":"197d2d4ba5f6a4fe8d5f0b2b6684499a66727129ad27474b85b0d1ffecd38f05","src/side_offsets.rs":"11b84a7dc620cdf2f1877c82b534947ae337734b9075d34e61306be7f458d127","src/size.rs":"5d5703b464207ff362da650008972ca67fbbc79f7193d0d2a1c7a7873221c24c","src/transform2d.rs":"6b2ff21021d7aa1628585650cc66f18f1a5a6aff5f335ae1ec4f63616c54b3cf","src/transform3d.rs":"83ac57ab5a077127fa7eba401dae2e67f2c129a05081b5b5ed09a2166fa9703f","src/translation.rs":"7847306a3a081559f2839baff9d622e58c63fdcb3981c5ef410b6a8eb8e13ab8","src/trig.rs":"ab09a8503d04ac1d0d6848d074ac18a22d324184e5bb186bbd4287c977894886","src/vector.rs":"f895e029e273defac88b47fef4fb27f627553fb05b8d173cf8f75e28818cd580"},"package":"2bb7ef65b3777a325d1eeefefab5b6d4959da54747e33bd6258e789640f307ad"}
|
|
@ -0,0 +1,5 @@
|
|||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
option. All files in the project carrying such notice may not be
|
||||
copied, modified, or distributed except according to those terms.
|
|
@ -0,0 +1,44 @@
|
|||
# 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]
|
||||
edition = "2018"
|
||||
name = "euclid"
|
||||
version = "0.20.14"
|
||||
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.mint]
|
||||
version = "0.5.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2.10"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["serde_derive"]
|
||||
optional = true
|
||||
default-features = false
|
||||
[dev-dependencies.serde_test]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
libm = ["num-traits/libm"]
|
||||
std = ["num-traits/std"]
|
||||
unstable = []
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2012-2013 Mozilla Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,8 @@
|
|||
# euclid
|
||||
|
||||
This is a small library for geometric types with a focus on 2d graphics and
|
||||
layout.
|
||||
|
||||
* [Documentation](https://docs.rs/euclid/)
|
||||
* [Release notes](https://github.com/servo/euclid/releases)
|
||||
* [crates.io](https://crates.io/crates/euclid)
|
|
@ -0,0 +1,316 @@
|
|||
// 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 crate::approxeq::ApproxEq;
|
||||
use crate::trig::Trig;
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
use core::hash::Hash;
|
||||
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
|
||||
use num_traits::{Float, FloatConst, NumCast, One, Zero};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An angle in radians
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Angle<T> {
|
||||
pub radians: T,
|
||||
}
|
||||
|
||||
impl<T> Angle<T> {
|
||||
#[inline]
|
||||
pub fn radians(radians: T) -> Self {
|
||||
Angle { radians }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(self) -> T {
|
||||
self.radians
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: Trig,
|
||||
{
|
||||
#[inline]
|
||||
pub fn degrees(deg: T) -> Self {
|
||||
Angle {
|
||||
radians: T::degrees_to_radians(deg),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_degrees(self) -> T {
|
||||
T::radians_to_degrees(self.radians)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: Rem<Output = T> + Sub<Output = T> + Add<Output = T> + Zero + FloatConst + PartialOrd + Copy,
|
||||
{
|
||||
/// Returns this angle in the [0..2*PI[ range.
|
||||
pub fn positive(&self) -> Self {
|
||||
let two_pi = T::PI() + T::PI();
|
||||
let mut a = self.radians % two_pi;
|
||||
if a < T::zero() {
|
||||
a = a + two_pi;
|
||||
}
|
||||
Angle::radians(a)
|
||||
}
|
||||
|
||||
/// Returns this angle in the ]-PI..PI] range.
|
||||
pub fn signed(&self) -> Self {
|
||||
Angle::pi() - (Angle::pi() - *self).positive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: Rem<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Add<Output = T>
|
||||
+ One
|
||||
+ FloatConst
|
||||
+ Copy,
|
||||
{
|
||||
/// Returns the shortest signed angle between two angles.
|
||||
///
|
||||
/// Takes wrapping and signs into account.
|
||||
pub fn angle_to(&self, to: Self) -> Self {
|
||||
let two = T::one() + T::one();
|
||||
let max = T::PI() * two;
|
||||
let d = (to.radians - self.radians) % max;
|
||||
|
||||
Angle::radians(two * d % max - d)
|
||||
}
|
||||
|
||||
/// Linear interpolation between two angles, using the shortest path.
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
*self + self.angle_to(other) * t
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: Float,
|
||||
{
|
||||
/// Returns (sin(self), cos(self)).
|
||||
pub fn sin_cos(self) -> (T, T) {
|
||||
self.radians.sin_cos()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
pub fn zero() -> Self {
|
||||
Angle::radians(T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: FloatConst + Add<Output = T>,
|
||||
{
|
||||
pub fn pi() -> Self {
|
||||
Angle::radians(T::PI())
|
||||
}
|
||||
|
||||
pub fn two_pi() -> Self {
|
||||
Angle::radians(T::PI() + T::PI())
|
||||
}
|
||||
|
||||
pub fn frac_pi_2() -> Self {
|
||||
Angle::radians(T::FRAC_PI_2())
|
||||
}
|
||||
|
||||
pub fn frac_pi_3() -> Self {
|
||||
Angle::radians(T::FRAC_PI_3())
|
||||
}
|
||||
|
||||
pub fn frac_pi_4() -> Self {
|
||||
Angle::radians(T::FRAC_PI_4())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Angle<T>
|
||||
where
|
||||
T: NumCast + Copy,
|
||||
{
|
||||
/// Cast from one numeric representation to another.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Angle<NewT> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Angle<NewT>> {
|
||||
NumCast::from(self.radians).map(|radians| Angle { radians })
|
||||
}
|
||||
|
||||
// Convenience functions for common casts.
|
||||
|
||||
/// Cast angle to `f32`.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Angle<f32> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast angle `f64`.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Angle<f64> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Add<T, Output = T>> Add for Angle<T> {
|
||||
type Output = Angle<T>;
|
||||
fn add(self, other: Angle<T>) -> Angle<T> {
|
||||
Angle::radians(self.radians + other.radians)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AddAssign<T>> AddAssign for Angle<T> {
|
||||
fn add_assign(&mut self, other: Angle<T>) {
|
||||
self.radians += other.radians;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sub<T, Output = T>> Sub<Angle<T>> for Angle<T> {
|
||||
type Output = Angle<T>;
|
||||
fn sub(self, other: Angle<T>) -> <Self as Sub>::Output {
|
||||
Angle::radians(self.radians - other.radians)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SubAssign<T>> SubAssign for Angle<T> {
|
||||
fn sub_assign(&mut self, other: Angle<T>) {
|
||||
self.radians -= other.radians;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Div<T, Output = T>> Div<Angle<T>> for Angle<T> {
|
||||
type Output = T;
|
||||
#[inline]
|
||||
fn div(self, other: Angle<T>) -> T {
|
||||
self.radians / other.radians
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Div<T, Output = T>> Div<T> for Angle<T> {
|
||||
type Output = Angle<T>;
|
||||
#[inline]
|
||||
fn div(self, factor: T) -> Angle<T> {
|
||||
Angle::radians(self.radians / factor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DivAssign<T>> DivAssign<T> for Angle<T> {
|
||||
fn div_assign(&mut self, factor: T) {
|
||||
self.radians /= factor;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mul<T, Output = T>> Mul<T> for Angle<T> {
|
||||
type Output = Angle<T>;
|
||||
#[inline]
|
||||
fn mul(self, factor: T) -> Angle<T> {
|
||||
Angle::radians(self.radians * factor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MulAssign<T>> MulAssign<T> for Angle<T> {
|
||||
fn mul_assign(&mut self, factor: T) {
|
||||
self.radians *= factor;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Neg<Output = T>> Neg for Angle<T> {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self {
|
||||
Angle::radians(-self.radians)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApproxEq<T>> ApproxEq<T> for Angle<T> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> T {
|
||||
T::approx_epsilon()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Angle<T>, approx_epsilon: &T) -> bool {
|
||||
self.radians.approx_eq_eps(&other.radians, approx_epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrap_angles() {
|
||||
use core::f32::consts::{FRAC_PI_2, PI};
|
||||
|
||||
assert!(Angle::radians(0.0).positive().approx_eq(&Angle::zero()));
|
||||
assert!(Angle::radians(FRAC_PI_2)
|
||||
.positive()
|
||||
.approx_eq(&Angle::frac_pi_2()));
|
||||
assert!(Angle::radians(-FRAC_PI_2)
|
||||
.positive()
|
||||
.approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
|
||||
assert!(Angle::radians(3.0 * FRAC_PI_2)
|
||||
.positive()
|
||||
.approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
|
||||
assert!(Angle::radians(5.0 * FRAC_PI_2)
|
||||
.positive()
|
||||
.approx_eq(&Angle::frac_pi_2()));
|
||||
assert!(Angle::radians(2.0 * PI)
|
||||
.positive()
|
||||
.approx_eq(&Angle::zero()));
|
||||
assert!(Angle::radians(-2.0 * PI)
|
||||
.positive()
|
||||
.approx_eq(&Angle::zero()));
|
||||
assert!(Angle::radians(PI).positive().approx_eq(&Angle::pi()));
|
||||
assert!(Angle::radians(-PI).positive().approx_eq(&Angle::pi()));
|
||||
|
||||
assert!(Angle::radians(FRAC_PI_2)
|
||||
.signed()
|
||||
.approx_eq(&Angle::frac_pi_2()));
|
||||
assert!(Angle::radians(3.0 * FRAC_PI_2)
|
||||
.signed()
|
||||
.approx_eq(&-Angle::frac_pi_2()));
|
||||
assert!(Angle::radians(5.0 * FRAC_PI_2)
|
||||
.signed()
|
||||
.approx_eq(&Angle::frac_pi_2()));
|
||||
assert!(Angle::radians(2.0 * PI).signed().approx_eq(&Angle::zero()));
|
||||
assert!(Angle::radians(-2.0 * PI).signed().approx_eq(&Angle::zero()));
|
||||
assert!(Angle::radians(-PI).signed().approx_eq(&Angle::pi()));
|
||||
assert!(Angle::radians(PI).signed().approx_eq(&Angle::pi()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lerp() {
|
||||
type A = Angle<f32>;
|
||||
|
||||
let a = A::radians(1.0);
|
||||
let b = A::radians(2.0);
|
||||
assert!(a.lerp(b, 0.25).approx_eq(&Angle::radians(1.25)));
|
||||
assert!(a.lerp(b, 0.5).approx_eq(&Angle::radians(1.5)));
|
||||
assert!(a.lerp(b, 0.75).approx_eq(&Angle::radians(1.75)));
|
||||
assert!(a
|
||||
.lerp(b + A::two_pi(), 0.75)
|
||||
.approx_eq(&Angle::radians(1.75)));
|
||||
assert!(a
|
||||
.lerp(b - A::two_pi(), 0.75)
|
||||
.approx_eq(&Angle::radians(1.75)));
|
||||
assert!(a
|
||||
.lerp(b + A::two_pi() * 5.0, 0.75)
|
||||
.approx_eq(&Angle::radians(1.75)));
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// 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> {
|
||||
/// Default epsilon value
|
||||
fn approx_epsilon() -> Eps;
|
||||
|
||||
/// Returns `true` is this object is approximately equal to the other one, using
|
||||
/// a provided epsilon value.
|
||||
fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool;
|
||||
|
||||
/// Returns `true` is this object is approximately equal to the other one, using
|
||||
/// the `approx_epsilon()` epsilon value.
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.approx_eq_eps(other, &Self::approx_epsilon())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! approx_eq {
|
||||
($ty:ty, $eps:expr) => {
|
||||
impl ApproxEq<$ty> for $ty {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> $ty {
|
||||
$eps
|
||||
}
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &$ty, approx_epsilon: &$ty) -> bool {
|
||||
num_traits::Float::abs(*self - *other) < *approx_epsilon
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
approx_eq!(f32, 1.0e-6);
|
||||
approx_eq!(f64, 1.0e-6);
|
|
@ -0,0 +1,44 @@
|
|||
// 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.
|
||||
|
||||
//! Utilities for testing approximate ordering - especially true for
|
||||
//! floating point types, where NaN's cannot be ordered.
|
||||
|
||||
pub fn min<T: PartialOrd>(x: T, y: T) -> T {
|
||||
if x <= y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max<T: PartialOrd>(x: T, y: T) -> T {
|
||||
if x >= y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_min() {
|
||||
assert!(min(0u32, 1u32) == 0u32);
|
||||
assert!(min(-1.0f32, 0.0f32) == -1.0f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max() {
|
||||
assert!(max(0u32, 1u32) == 1u32);
|
||||
assert!(max(-1.0f32, 0.0f32) == 0.0f32);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,828 @@
|
|||
// 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 crate::approxord::{max, min};
|
||||
use crate::nonempty::NonEmpty;
|
||||
use crate::num::*;
|
||||
use crate::point::{point2, Point2D};
|
||||
use crate::rect::Rect;
|
||||
use crate::scale::Scale;
|
||||
use crate::side_offsets::SideOffsets2D;
|
||||
use crate::size::Size2D;
|
||||
use crate::vector::{vec2, Vector2D};
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::PartialOrd;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Sub};
|
||||
|
||||
/// An axis aligned rectangle represented by its minimum and maximum coordinates.
|
||||
///
|
||||
/// That struct is similar to the [`Rect`] struct, but stores rectangle as two corners
|
||||
/// instead of origin point and size. Such representation has several advantages over
|
||||
/// [`Rect`] representation:
|
||||
/// - Several operations are more efficient with `Box2D`, including [`intersection`],
|
||||
/// [`union`], and point-in-rect.
|
||||
/// - The representation is more symmetric, since it stores two quantities of the
|
||||
/// same kind (two points) rather than a point and a dimension (width/height).
|
||||
/// - The representation is less susceptible to overflow. With [`Rect`], computation
|
||||
/// of second point can overflow for a large range of values of origin and size.
|
||||
/// However, with `Box2D`, computation of [`size`] cannot overflow if the coordinates
|
||||
/// are signed and the resulting size is unsigned.
|
||||
///
|
||||
/// A known disadvantage of `Box2D` is that translating the rectangle requires translating
|
||||
/// both points, whereas translating [`Rect`] only requires translating one point.
|
||||
///
|
||||
/// [`Rect`]: struct.Rect.html
|
||||
/// [`intersection`]: #method.intersection
|
||||
/// [`union`]: #method.union
|
||||
/// [`size`]: #method.size
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
|
||||
)]
|
||||
pub struct Box2D<T, U> {
|
||||
pub min: Point2D<T, U>,
|
||||
pub max: Point2D<T, U>,
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for Box2D<T, U> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.min.hash(h);
|
||||
self.max.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for Box2D<T, U> {}
|
||||
|
||||
impl<T: Clone, U> Clone for Box2D<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.min.clone(), self.max.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq for Box2D<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.min.eq(&other.min) && self.max.eq(&other.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for Box2D<T, U> {}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for Box2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("Box2D")
|
||||
.field(&self.min)
|
||||
.field(&self.max)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Box2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Box2D(")?;
|
||||
fmt::Display::fmt(&self.min, f)?;
|
||||
write!(f, ", ")?;
|
||||
fmt::Display::fmt(&self.max, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U> {
|
||||
/// Constructor.
|
||||
#[inline]
|
||||
pub const fn new(min: Point2D<T, U>, max: Point2D<T, U>) -> Self {
|
||||
Box2D { min, max }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
/// Returns true if the box has a negative area.
|
||||
///
|
||||
/// The common interpretation for a negative box is to consider it empty. It can be obtained
|
||||
/// by calculating the intersection of two boxes that do not intersect.
|
||||
#[inline]
|
||||
pub fn is_negative(&self) -> bool {
|
||||
self.max.x < self.min.x || self.max.y < self.min.y
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero or negative.
|
||||
#[inline]
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
!(self.max.x > self.min.x && self.max.y > self.min.y)
|
||||
}
|
||||
|
||||
/// Returns `true` if the two boxes intersect.
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
self.min.x < other.max.x
|
||||
&& self.max.x > other.min.x
|
||||
&& self.min.y < other.max.y
|
||||
&& self.max.y > other.min.y
|
||||
}
|
||||
|
||||
/// Returns `true` if this box contains the point. Points are considered
|
||||
/// in the box if they are on the front, left or top faces, but outside if they
|
||||
/// are on the back, right or bottom faces.
|
||||
#[inline]
|
||||
pub fn contains(&self, p: Point2D<T, U>) -> bool {
|
||||
self.min.x <= p.x && p.x < self.max.x && self.min.y <= p.y && p.y < self.max.y
|
||||
}
|
||||
|
||||
/// Returns `true` if this box contains the interior of the other box. Always
|
||||
/// returns `true` if other is empty, and always returns `false` if other is
|
||||
/// nonempty but this box is empty.
|
||||
#[inline]
|
||||
pub fn contains_box(&self, other: &Self) -> bool {
|
||||
other.is_empty_or_negative()
|
||||
|| (self.min.x <= other.min.x
|
||||
&& other.max.x <= self.max.x
|
||||
&& self.min.y <= other.min.y
|
||||
&& other.max.y <= self.max.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + PartialOrd,
|
||||
{
|
||||
#[inline]
|
||||
pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
|
||||
if self.is_empty_or_negative() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(*self))
|
||||
}
|
||||
/// Computes the intersection of two boxes.
|
||||
///
|
||||
/// The result is a negative box if the boxes do not intersect.
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Self {
|
||||
Box2D {
|
||||
min: point2(max(self.min.x, other.min.x), max(self.min.y, other.min.y)),
|
||||
max: point2(min(self.max.x, other.max.x), min(self.max.y, other.max.y)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the intersection of two boxes, returning `None` if the boxes do not intersect.
|
||||
#[inline]
|
||||
pub fn try_intersection(&self, other: &Self) -> Option<NonEmpty<Self>> {
|
||||
let intersection = self.intersection(other);
|
||||
|
||||
if intersection.is_negative() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(intersection))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
Box2D {
|
||||
min: point2(min(self.min.x, other.min.x), min(self.min.y, other.min.y)),
|
||||
max: point2(max(self.max.x, other.max.x), max(self.max.y, other.max.y)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T>,
|
||||
{
|
||||
/// Returns the same box, translated by a vector.
|
||||
#[inline]
|
||||
pub fn translate(&self, by: Vector2D<T, U>) -> Self {
|
||||
Box2D {
|
||||
min: self.min + by,
|
||||
max: self.max + by,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn size(&self) -> Size2D<T, U> {
|
||||
(self.max - self.min).to_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> T {
|
||||
self.max.x - self.min.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> T {
|
||||
self.max.y - self.min.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_rect(&self) -> Rect<T, U> {
|
||||
Rect {
|
||||
origin: self.min,
|
||||
size: self.size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
/// Inflates the box by the specified sizes on each side respectively.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inflate(&self, width: T, height: T) -> Self {
|
||||
Box2D {
|
||||
min: point2(self.min.x - width, self.min.y - height),
|
||||
max: point2(self.max.x + width, self.max.y + height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the size and position of an inner box.
|
||||
///
|
||||
/// Subtracts the side offsets from all sides. The horizontal, vertical
|
||||
/// and applicate offsets must not be larger than the original side length.
|
||||
pub fn inner_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
|
||||
Box2D {
|
||||
min: self.min + vec2(offsets.left, offsets.top),
|
||||
max: self.max - vec2(offsets.right, offsets.bottom),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the b and position of an outer box.
|
||||
///
|
||||
/// Add the offsets to all sides. The expanded box is returned.
|
||||
pub fn outer_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
|
||||
Box2D {
|
||||
min: self.min - vec2(offsets.left, offsets.top),
|
||||
max: self.max + vec2(offsets.right, offsets.bottom),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd,
|
||||
{
|
||||
/// Creates a Box2D of the given size, at offset zero.
|
||||
#[inline]
|
||||
pub fn from_size(size: Size2D<T, U>) -> Self {
|
||||
let zero = Point2D::zero();
|
||||
let point = size.to_vector().to_point();
|
||||
Box2D::from_points(&[zero, point])
|
||||
}
|
||||
|
||||
/// Returns the smallest box containing all of the provided points.
|
||||
pub fn from_points<I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Point2D<T, U>>,
|
||||
{
|
||||
let mut points = points.into_iter();
|
||||
|
||||
let (mut min_x, mut min_y) = match points.next() {
|
||||
Some(first) => first.borrow().to_tuple(),
|
||||
None => return Box2D::zero(),
|
||||
};
|
||||
|
||||
let (mut max_x, mut max_y) = (min_x, min_y);
|
||||
for point in points {
|
||||
let p = point.borrow();
|
||||
if p.x < min_x {
|
||||
min_x = p.x
|
||||
}
|
||||
if p.x > max_x {
|
||||
max_x = p.x
|
||||
}
|
||||
if p.y < min_y {
|
||||
min_y = p.y
|
||||
}
|
||||
if p.y > max_y {
|
||||
max_y = p.y
|
||||
}
|
||||
}
|
||||
|
||||
Box2D {
|
||||
min: point2(min_x, min_y),
|
||||
max: point2(max_x, max_y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this box and another box.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Div<Output = T>,
|
||||
{
|
||||
pub fn center(&self) -> Point2D<T, U> {
|
||||
let two = T::one() + T::one();
|
||||
(self.min + self.max.to_vector()) / two
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn area(&self) -> T {
|
||||
let size = self.size();
|
||||
size.width * size.height
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
Box2D::new(Point2D::zero(), Point2D::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
/// Returns true if the size is zero.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.min.x == self.max.x || self.min.y == self.max.y
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Box2D<T, U> {
|
||||
type Output = Box2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Box2D::new(self.min * scale.clone(), self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self *= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Box2D<T, U> {
|
||||
type Output = Box2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Box2D::new(self.min / scale.clone(), self.max / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self /= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1> {
|
||||
type Output = Box2D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Box2D::new(self.min * scale.clone(), self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min *= scale.clone();
|
||||
self.max *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2> {
|
||||
type Output = Box2D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Box2D::new(self.min / scale.clone(), self.max / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min /= scale.clone();
|
||||
self.max /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Box2D<T, UnknownUnit> {
|
||||
Box2D::new(self.min.to_untyped(), self.max.to_untyped())
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(c: &Box2D<T, UnknownUnit>) -> Box2D<T, U> {
|
||||
Box2D::new(Point2D::from_untyped(c.min), Point2D::from_untyped(c.max))
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Box2D<T, V> {
|
||||
Box2D::new(self.min.cast_unit(), self.max.cast_unit())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
|
||||
where
|
||||
T: Mul<S, Output = T>,
|
||||
{
|
||||
Box2D {
|
||||
min: point2(self.min.x * x, self.min.y * y),
|
||||
max: point2(self.max.x * x, self.max.y * y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> Box2D<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(), round_in or round_out() before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Box2D<NewT, U> {
|
||||
Box2D::new(self.min.cast(), self.max.cast())
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Box2D<NewT, U>> {
|
||||
match (self.min.try_cast(), self.max.try_cast()) {
|
||||
(Some(a), Some(b)) => Some(Box2D::new(a, b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` box.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Box2D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` box.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Box2D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` box, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point boxes, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Box2D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` box, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point boxes, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Box2D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` box, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point boxes, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Box2D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` box, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point boxes, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Box2D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Round,
|
||||
{
|
||||
/// Return a box with edges rounded to integer coordinates, such that
|
||||
/// the returned box has the same set of pixel centers as the original
|
||||
/// one.
|
||||
/// Values equal to 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).
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self {
|
||||
Box2D::new(self.min.round(), self.max.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: Floor + Ceil,
|
||||
{
|
||||
/// Return a box with faces/edges rounded to integer coordinates, such that
|
||||
/// the original box contains the resulting box.
|
||||
#[must_use]
|
||||
pub fn round_in(&self) -> Self {
|
||||
let min = self.min.ceil();
|
||||
let max = self.max.floor();
|
||||
Box2D { min, max }
|
||||
}
|
||||
|
||||
/// Return a box with faces/edges rounded to integer coordinates, such that
|
||||
/// the original box is contained in the resulting box.
|
||||
#[must_use]
|
||||
pub fn round_out(&self) -> Self {
|
||||
let min = self.min.floor();
|
||||
let max = self.max.ceil();
|
||||
Box2D { min, max }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> From<Size2D<T, U>> for Box2D<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd,
|
||||
{
|
||||
fn from(b: Size2D<T, U>) -> Self {
|
||||
Self::from_size(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::default::Box2D;
|
||||
use crate::side_offsets::SideOffsets2D;
|
||||
use crate::{point2, size2, vec2, Point2D};
|
||||
//use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
|
||||
assert_eq!(b.size().width, 20.0);
|
||||
assert_eq!(b.size().height, 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_width_height() {
|
||||
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
|
||||
assert!(b.width() == 20.0);
|
||||
assert!(b.height() == 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_center() {
|
||||
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
|
||||
assert_eq!(b.center(), Point2D::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_area() {
|
||||
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
|
||||
assert_eq!(b.area(), 400.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_points() {
|
||||
let b = Box2D::from_points(&[point2(50.0, 160.0), point2(100.0, 25.0)]);
|
||||
assert_eq!(b.min, point2(50.0, 25.0));
|
||||
assert_eq!(b.max, point2(100.0, 160.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round_in() {
|
||||
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_in();
|
||||
assert_eq!(b.min.x, -25.0);
|
||||
assert_eq!(b.min.y, -40.0);
|
||||
assert_eq!(b.max.x, 60.0);
|
||||
assert_eq!(b.max.y, 36.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round_out() {
|
||||
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_out();
|
||||
assert_eq!(b.min.x, -26.0);
|
||||
assert_eq!(b.min.y, -41.0);
|
||||
assert_eq!(b.max.x, 61.0);
|
||||
assert_eq!(b.max.y, 37.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round() {
|
||||
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
|
||||
assert_eq!(b.min.x, -26.0);
|
||||
assert_eq!(b.min.y, -40.0);
|
||||
assert_eq!(b.max.x, 60.0);
|
||||
assert_eq!(b.max.y, 37.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_size() {
|
||||
let b = Box2D::from_size(size2(30.0, 40.0));
|
||||
assert!(b.min == Point2D::zero());
|
||||
assert!(b.size().width == 30.0);
|
||||
assert!(b.size().height == 40.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inner_box() {
|
||||
let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
|
||||
let b = b.inner_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
|
||||
assert_eq!(b.max.x, 80.0);
|
||||
assert_eq!(b.max.y, 155.0);
|
||||
assert_eq!(b.min.x, 60.0);
|
||||
assert_eq!(b.min.y, 35.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_outer_box() {
|
||||
let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
|
||||
let b = b.outer_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
|
||||
assert_eq!(b.max.x, 120.0);
|
||||
assert_eq!(b.max.y, 165.0);
|
||||
assert_eq!(b.min.x, 40.0);
|
||||
assert_eq!(b.min.y, 15.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate() {
|
||||
let size = size2(15.0, 15.0);
|
||||
let mut center = (size / 2.0).to_vector().to_point();
|
||||
let b = Box2D::from_size(size);
|
||||
assert_eq!(b.center(), center);
|
||||
let translation = vec2(10.0, 2.5);
|
||||
let b = b.translate(translation);
|
||||
center += translation;
|
||||
assert_eq!(b.center(), center);
|
||||
assert_eq!(b.max.x, 25.0);
|
||||
assert_eq!(b.max.y, 17.5);
|
||||
assert_eq!(b.min.x, 10.0);
|
||||
assert_eq!(b.min.y, 2.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(0.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(0.0, 20.0), point2(20.0, -20.0)]);
|
||||
let b = b1.union(&b2);
|
||||
assert_eq!(b.max.x, 20.0);
|
||||
assert_eq!(b.max.y, 20.0);
|
||||
assert_eq!(b.min.x, -20.0);
|
||||
assert_eq!(b.min.y, -20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersects() {
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
|
||||
assert!(b1.intersects(&b2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
|
||||
let b = b1.intersection(&b2);
|
||||
assert_eq!(b.max.x, 10.0);
|
||||
assert_eq!(b.max.y, 20.0);
|
||||
assert_eq!(b.min.x, -10.0);
|
||||
assert_eq!(b.min.y, -20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_intersection() {
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_some());
|
||||
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let b = Box2D::from_points(&[point2(-10.0, -10.0), point2(10.0, 10.0)]);
|
||||
let b = b.scale(0.5, 0.5);
|
||||
assert_eq!(b.max.x, 5.0);
|
||||
assert_eq!(b.max.y, 5.0);
|
||||
assert_eq!(b.min.x, -5.0);
|
||||
assert_eq!(b.min.y, -5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lerp() {
|
||||
let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(-10.0, -10.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(10.0, 10.0), point2(20.0, 20.0)]);
|
||||
let b = b1.lerp(b2, 0.5);
|
||||
assert_eq!(b.center(), Point2D::zero());
|
||||
assert_eq!(b.size().width, 10.0);
|
||||
assert_eq!(b.size().height, 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
|
||||
assert!(b.contains(point2(-15.3, 10.5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_box() {
|
||||
let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(-14.3, -16.5), point2(6.7, 17.6)]);
|
||||
assert!(b1.contains_box(&b2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inflate() {
|
||||
let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
|
||||
let b = b.inflate(10.0, 5.0);
|
||||
assert_eq!(b.size().width, 60.0);
|
||||
assert_eq!(b.size().height, 50.0);
|
||||
assert_eq!(b.center(), Point2D::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
for i in 0..2 {
|
||||
let mut coords_neg = [-20.0, -20.0];
|
||||
let mut coords_pos = [20.0, 20.0];
|
||||
coords_neg[i] = 0.0;
|
||||
coords_pos[i] = 0.0;
|
||||
let b = Box2D::from_points(&[Point2D::from(coords_neg), Point2D::from(coords_pos)]);
|
||||
assert!(b.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nan_empty_or_negative() {
|
||||
use std::f32::NAN;
|
||||
assert!(Box2D { min: point2(NAN, 2.0), max: point2(1.0, 3.0) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(0.0, NAN), max: point2(1.0, 2.0) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(1.0, -2.0), max: point2(NAN, 2.0) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(1.0, -2.0), max: point2(0.0, NAN) }.is_empty_or_negative());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,901 @@
|
|||
// 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 crate::approxord::{max, min};
|
||||
use crate::nonempty::NonEmpty;
|
||||
use crate::num::*;
|
||||
use crate::point::{point3, Point3D};
|
||||
use crate::scale::Scale;
|
||||
use crate::size::Size3D;
|
||||
use crate::vector::Vector3D;
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::PartialOrd;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Sub};
|
||||
|
||||
/// An axis aligned 3D box represented by its minimum and maximum coordinates.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
|
||||
)]
|
||||
pub struct Box3D<T, U> {
|
||||
pub min: Point3D<T, U>,
|
||||
pub max: Point3D<T, U>,
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for Box3D<T, U> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.min.hash(h);
|
||||
self.max.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for Box3D<T, U> {}
|
||||
|
||||
impl<T: Clone, U> Clone for Box3D<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.min.clone(), self.max.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq for Box3D<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.min.eq(&other.min) && self.max.eq(&other.max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for Box3D<T, U> {}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for Box3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("Box3D")
|
||||
.field(&self.min)
|
||||
.field(&self.max)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Box3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Box3D(")?;
|
||||
fmt::Display::fmt(&self.min, f)?;
|
||||
write!(f, ", ")?;
|
||||
fmt::Display::fmt(&self.max, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U> {
|
||||
/// Constructor.
|
||||
#[inline]
|
||||
pub const fn new(min: Point3D<T, U>, max: Point3D<T, U>) -> Self {
|
||||
Box3D { min, max }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
/// Returns true if the box has a negative volume.
|
||||
///
|
||||
/// The common interpretation for a negative box is to consider it empty. It can be obtained
|
||||
/// by calculating the intersection of two boxes that do not intersect.
|
||||
#[inline]
|
||||
pub fn is_negative(&self) -> bool {
|
||||
self.max.x < self.min.x || self.max.y < self.min.y || self.max.z < self.min.z
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero or negative.
|
||||
#[inline]
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
!(self.max.x > self.min.x && self.max.y > self.min.y && self.max.z > self.min.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
self.min.x < other.max.x
|
||||
&& self.max.x > other.min.x
|
||||
&& self.min.y < other.max.y
|
||||
&& self.max.y > other.min.y
|
||||
&& self.min.z < other.max.z
|
||||
&& self.max.z > other.min.z
|
||||
}
|
||||
|
||||
/// Returns `true` if this box3d contains the point. Points are considered
|
||||
/// in the box3d if they are on the front, left or top faces, but outside if they
|
||||
/// are on the back, right or bottom faces.
|
||||
#[inline]
|
||||
pub fn contains(&self, other: Point3D<T, U>) -> bool {
|
||||
self.min.x <= other.x
|
||||
&& other.x < self.max.x
|
||||
&& self.min.y <= other.y
|
||||
&& other.y < self.max.y
|
||||
&& self.min.z <= other.z
|
||||
&& other.z < self.max.z
|
||||
}
|
||||
|
||||
/// Returns `true` if this box3d contains the interior of the other box3d. Always
|
||||
/// returns `true` if other is empty, and always returns `false` if other is
|
||||
/// nonempty but this box3d is empty.
|
||||
#[inline]
|
||||
pub fn contains_box(&self, other: &Self) -> bool {
|
||||
other.is_empty_or_negative()
|
||||
|| (self.min.x <= other.min.x
|
||||
&& other.max.x <= self.max.x
|
||||
&& self.min.y <= other.min.y
|
||||
&& other.max.y <= self.max.y
|
||||
&& self.min.z <= other.min.z
|
||||
&& other.max.z <= self.max.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + PartialOrd,
|
||||
{
|
||||
#[inline]
|
||||
pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
|
||||
if self.is_empty_or_negative() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(*self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_intersection(&self, other: &Self) -> Option<NonEmpty<Self>> {
|
||||
if !self.intersects(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(self.intersection(other)))
|
||||
}
|
||||
|
||||
pub fn intersection(&self, other: &Self) -> Self {
|
||||
let intersection_min = Point3D::new(
|
||||
max(self.min.x, other.min.x),
|
||||
max(self.min.y, other.min.y),
|
||||
max(self.min.z, other.min.z),
|
||||
);
|
||||
|
||||
let intersection_max = Point3D::new(
|
||||
min(self.max.x, other.max.x),
|
||||
min(self.max.y, other.max.y),
|
||||
min(self.max.z, other.max.z),
|
||||
);
|
||||
|
||||
Box3D::new(intersection_min, intersection_max)
|
||||
}
|
||||
|
||||
/// Returns the smallest box containing both of the provided boxes.
|
||||
#[inline]
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
Box3D::new(
|
||||
Point3D::new(
|
||||
min(self.min.x, other.min.x),
|
||||
min(self.min.y, other.min.y),
|
||||
min(self.min.z, other.min.z),
|
||||
),
|
||||
Point3D::new(
|
||||
max(self.max.x, other.max.x),
|
||||
max(self.max.y, other.max.y),
|
||||
max(self.max.z, other.max.z),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T>,
|
||||
{
|
||||
/// Returns the same box3d, translated by a vector.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn translate(&self, by: Vector3D<T, U>) -> Self {
|
||||
Box3D {
|
||||
min: self.min + by,
|
||||
max: self.max + by,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn size(&self) -> Size3D<T, U> {
|
||||
Size3D::new(
|
||||
self.max.x - self.min.x,
|
||||
self.max.y - self.min.y,
|
||||
self.max.z - self.min.z,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> T {
|
||||
self.max.x - self.min.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> T {
|
||||
self.max.y - self.min.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn depth(&self) -> T {
|
||||
self.max.z - self.min.z
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
/// Inflates the box by the specified sizes on each side respectively.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inflate(&self, width: T, height: T, depth: T) -> Self {
|
||||
Box3D::new(
|
||||
Point3D::new(self.min.x - width, self.min.y - height, self.min.z - depth),
|
||||
Point3D::new(self.max.x + width, self.max.y + height, self.max.z + depth),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd,
|
||||
{
|
||||
/// Creates a Box3D of the given size, at offset zero.
|
||||
#[inline]
|
||||
pub fn from_size(size: Size3D<T, U>) -> Self {
|
||||
let zero = Point3D::zero();
|
||||
let point = size.to_vector().to_point();
|
||||
Box3D::from_points(&[zero, point])
|
||||
}
|
||||
|
||||
/// Returns the smallest box containing all of the provided points.
|
||||
pub fn from_points<I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Point3D<T, U>>,
|
||||
{
|
||||
let mut points = points.into_iter();
|
||||
|
||||
let (mut min_x, mut min_y, mut min_z) = match points.next() {
|
||||
Some(first) => first.borrow().to_tuple(),
|
||||
None => return Box3D::zero(),
|
||||
};
|
||||
let (mut max_x, mut max_y, mut max_z) = (min_x, min_y, min_z);
|
||||
|
||||
for point in points {
|
||||
let p = point.borrow();
|
||||
if p.x < min_x {
|
||||
min_x = p.x
|
||||
}
|
||||
if p.x > max_x {
|
||||
max_x = p.x
|
||||
}
|
||||
if p.y < min_y {
|
||||
min_y = p.y
|
||||
}
|
||||
if p.y > max_y {
|
||||
max_y = p.y
|
||||
}
|
||||
if p.z < min_z {
|
||||
min_z = p.z
|
||||
}
|
||||
if p.z > max_z {
|
||||
max_z = p.z
|
||||
}
|
||||
}
|
||||
|
||||
Box3D {
|
||||
min: point3(min_x, min_y, min_z),
|
||||
max: point3(max_x, max_y, max_z),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this box3d and another box3d.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Div<Output = T>,
|
||||
{
|
||||
pub fn center(&self) -> Point3D<T, U> {
|
||||
let two = T::one() + T::one();
|
||||
(self.min + self.max.to_vector()) / two
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn volume(&self) -> T {
|
||||
let size = self.size();
|
||||
size.width * size.height * size.depth
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn xy_area(&self) -> T {
|
||||
let size = self.size();
|
||||
size.width * size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn yz_area(&self) -> T {
|
||||
let size = self.size();
|
||||
size.depth * size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn xz_area(&self) -> T {
|
||||
let size = self.size();
|
||||
size.depth * size.width
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
Box3D::new(Point3D::zero(), Point3D::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
/// Returns true if the volume is zero.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.min.x == self.max.x || self.min.y == self.max.y || self.min.z == self.max.z
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Box3D<T, U> {
|
||||
type Output = Box3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Box3D::new(self.min * scale.clone(), self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
self.min *= scale.clone();
|
||||
self.max *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Box3D<T, U> {
|
||||
type Output = Box3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Box3D::new(self.min / scale.clone(), self.max / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
self.min /= scale.clone();
|
||||
self.max /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
|
||||
type Output = Box3D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Box3D::new(self.min * scale.clone(), self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min *= scale.clone();
|
||||
self.max *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
|
||||
type Output = Box3D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Box3D::new(self.min / scale.clone(), self.max / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min /= scale.clone();
|
||||
self.max /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Box3D<T, UnknownUnit> {
|
||||
Box3D {
|
||||
min: self.min.to_untyped(),
|
||||
max: self.max.to_untyped(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(c: &Box3D<T, UnknownUnit>) -> Box3D<T, U> {
|
||||
Box3D {
|
||||
min: Point3D::from_untyped(c.min),
|
||||
max: Point3D::from_untyped(c.max),
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Box3D<T, V> {
|
||||
Box3D::new(self.min.cast_unit(), self.max.cast_unit())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale<S: Copy>(&self, x: S, y: S, z: S) -> Self
|
||||
where
|
||||
T: Mul<S, Output = T>,
|
||||
{
|
||||
Box3D::new(
|
||||
Point3D::new(self.min.x * x, self.min.y * y, self.min.z * z),
|
||||
Point3D::new(self.max.x * x, self.max.y * y, self.max.z * z),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> Box3D<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(), round_in or round_out() before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Box3D<NewT, U> {
|
||||
Box3D::new(self.min.cast(), self.max.cast())
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Box3D<NewT, U>> {
|
||||
match (self.min.try_cast(), self.max.try_cast()) {
|
||||
(Some(a), Some(b)) => Some(Box3D::new(a, b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` box3d.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Box3D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` box3d.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Box3D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` box3d, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point cuboids, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Box3D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` box3d, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point cuboids, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Box3D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` box3d, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point cuboids, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Box3D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` box3d, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point cuboids, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Box3D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Round,
|
||||
{
|
||||
/// Return a box3d with edges rounded to integer coordinates, such that
|
||||
/// the returned box3d has the same set of pixel centers as the original
|
||||
/// one.
|
||||
/// Values equal to 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).
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self {
|
||||
Box3D::new(self.min.round(), self.max.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: Floor + Ceil,
|
||||
{
|
||||
/// Return a box3d with faces/edges rounded to integer coordinates, such that
|
||||
/// the original box3d contains the resulting box3d.
|
||||
#[must_use]
|
||||
pub fn round_in(&self) -> Self {
|
||||
Box3D {
|
||||
min: self.min.ceil(),
|
||||
max: self.max.floor(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a box3d with faces/edges rounded to integer coordinates, such that
|
||||
/// the original box3d is contained in the resulting box3d.
|
||||
#[must_use]
|
||||
pub fn round_out(&self) -> Self {
|
||||
Box3D {
|
||||
min: self.min.floor(),
|
||||
max: self.max.ceil(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> From<Size3D<T, U>> for Box3D<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd,
|
||||
{
|
||||
fn from(b: Size3D<T, U>) -> Self {
|
||||
Self::from_size(b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `Box3D::new(Point3D::new(x1, y1, z1), Point3D::new(x2, y2, z2))`.
|
||||
pub fn box3d<T: Copy, U>(
|
||||
min_x: T,
|
||||
min_y: T,
|
||||
min_z: T,
|
||||
max_x: T,
|
||||
max_y: T,
|
||||
max_z: T,
|
||||
) -> Box3D<T, U> {
|
||||
Box3D::new(
|
||||
Point3D::new(min_x, min_y, min_z),
|
||||
Point3D::new(max_x, max_y, max_z),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::default::{Box3D, Point3D};
|
||||
use crate::{point3, size3, vec3};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let b = Box3D::new(point3(-1.0, -1.0, -1.0), point3(1.0, 1.0, 1.0));
|
||||
assert!(b.min.x == -1.0);
|
||||
assert!(b.min.y == -1.0);
|
||||
assert!(b.min.z == -1.0);
|
||||
assert!(b.max.x == 1.0);
|
||||
assert!(b.max.y == 1.0);
|
||||
assert!(b.max.z == 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
|
||||
assert!(b.size().width == 20.0);
|
||||
assert!(b.size().height == 20.0);
|
||||
assert!(b.size().depth == 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_width_height_depth() {
|
||||
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
|
||||
assert!(b.width() == 20.0);
|
||||
assert!(b.height() == 20.0);
|
||||
assert!(b.depth() == 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_center() {
|
||||
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
|
||||
assert!(b.center() == Point3D::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_volume() {
|
||||
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
|
||||
assert!(b.volume() == 8000.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_area() {
|
||||
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
|
||||
assert!(b.xy_area() == 400.0);
|
||||
assert!(b.yz_area() == 400.0);
|
||||
assert!(b.xz_area() == 400.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_points() {
|
||||
let b = Box3D::from_points(&[point3(50.0, 160.0, 12.5), point3(100.0, 25.0, 200.0)]);
|
||||
assert!(b.min == point3(50.0, 25.0, 12.5));
|
||||
assert!(b.max == point3(100.0, 160.0, 200.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max() {
|
||||
let b = Box3D::from_points(&[point3(50.0, 25.0, 12.5), point3(100.0, 160.0, 200.0)]);
|
||||
assert!(b.min.x == 50.0);
|
||||
assert!(b.min.y == 25.0);
|
||||
assert!(b.min.z == 12.5);
|
||||
assert!(b.max.x == 100.0);
|
||||
assert!(b.max.y == 160.0);
|
||||
assert!(b.max.z == 200.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round_in() {
|
||||
let b =
|
||||
Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round_in();
|
||||
assert!(b.min.x == -25.0);
|
||||
assert!(b.min.y == -40.0);
|
||||
assert!(b.min.z == -70.0);
|
||||
assert!(b.max.x == 60.0);
|
||||
assert!(b.max.y == 36.0);
|
||||
assert!(b.max.z == 89.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round_out() {
|
||||
let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)])
|
||||
.round_out();
|
||||
assert!(b.min.x == -26.0);
|
||||
assert!(b.min.y == -41.0);
|
||||
assert!(b.min.z == -71.0);
|
||||
assert!(b.max.x == 61.0);
|
||||
assert!(b.max.y == 37.0);
|
||||
assert!(b.max.z == 90.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round() {
|
||||
let b =
|
||||
Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round();
|
||||
assert!(b.min.x == -26.0);
|
||||
assert!(b.min.y == -40.0);
|
||||
assert!(b.min.z == -71.0);
|
||||
assert!(b.max.x == 60.0);
|
||||
assert!(b.max.y == 37.0);
|
||||
assert!(b.max.z == 90.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_size() {
|
||||
let b = Box3D::from_size(size3(30.0, 40.0, 50.0));
|
||||
assert!(b.min == Point3D::zero());
|
||||
assert!(b.size().width == 30.0);
|
||||
assert!(b.size().height == 40.0);
|
||||
assert!(b.size().depth == 50.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate() {
|
||||
let size = size3(15.0, 15.0, 200.0);
|
||||
let mut center = (size / 2.0).to_vector().to_point();
|
||||
let b = Box3D::from_size(size);
|
||||
assert!(b.center() == center);
|
||||
let translation = vec3(10.0, 2.5, 9.5);
|
||||
let b = b.translate(translation);
|
||||
center += translation;
|
||||
assert!(b.center() == center);
|
||||
assert!(b.max.x == 25.0);
|
||||
assert!(b.max.y == 17.5);
|
||||
assert!(b.max.z == 209.5);
|
||||
assert!(b.min.x == 10.0);
|
||||
assert!(b.min.y == 2.5);
|
||||
assert!(b.min.z == 9.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(0.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(0.0, 20.0, 20.0), point3(20.0, -20.0, -20.0)]);
|
||||
let b = b1.union(&b2);
|
||||
assert!(b.max.x == 20.0);
|
||||
assert!(b.max.y == 20.0);
|
||||
assert!(b.max.z == 20.0);
|
||||
assert!(b.min.x == -20.0);
|
||||
assert!(b.min.y == -20.0);
|
||||
assert!(b.min.z == -20.0);
|
||||
assert!(b.volume() == (40.0 * 40.0 * 40.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersects() {
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
assert!(b1.intersects(&b2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
let b = b1.intersection(&b2);
|
||||
assert!(b.max.x == 10.0);
|
||||
assert!(b.max.y == 20.0);
|
||||
assert!(b.max.z == 20.0);
|
||||
assert!(b.min.x == -10.0);
|
||||
assert!(b.min.y == -20.0);
|
||||
assert!(b.min.z == -20.0);
|
||||
assert!(b.volume() == (20.0 * 40.0 * 40.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_intersection() {
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_some());
|
||||
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(-10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let b = Box3D::from_points(&[point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0)]);
|
||||
let b = b.scale(0.5, 0.5, 0.5);
|
||||
assert!(b.max.x == 5.0);
|
||||
assert!(b.max.y == 5.0);
|
||||
assert!(b.max.z == 5.0);
|
||||
assert!(b.min.x == -5.0);
|
||||
assert!(b.min.y == -5.0);
|
||||
assert!(b.min.z == -5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
let b = Box3D::<f64>::zero();
|
||||
assert!(b.max.x == 0.0);
|
||||
assert!(b.max.y == 0.0);
|
||||
assert!(b.max.z == 0.0);
|
||||
assert!(b.min.x == 0.0);
|
||||
assert!(b.min.y == 0.0);
|
||||
assert!(b.min.z == 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lerp() {
|
||||
let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(-10.0, -10.0, -10.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(10.0, 10.0, 10.0), point3(20.0, 20.0, 20.0)]);
|
||||
let b = b1.lerp(b2, 0.5);
|
||||
assert!(b.center() == Point3D::zero());
|
||||
assert!(b.size().width == 10.0);
|
||||
assert!(b.size().height == 10.0);
|
||||
assert!(b.size().depth == 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
|
||||
assert!(b.contains(point3(-15.3, 10.5, 18.4)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_box() {
|
||||
let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(-14.3, -16.5, -19.3), point3(6.7, 17.6, 2.5)]);
|
||||
assert!(b1.contains_box(&b2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inflate() {
|
||||
let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
|
||||
let b = b.inflate(10.0, 5.0, 2.0);
|
||||
assert!(b.size().width == 60.0);
|
||||
assert!(b.size().height == 50.0);
|
||||
assert!(b.size().depth == 44.0);
|
||||
assert!(b.center() == Point3D::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
for i in 0..3 {
|
||||
let mut coords_neg = [-20.0, -20.0, -20.0];
|
||||
let mut coords_pos = [20.0, 20.0, 20.0];
|
||||
coords_neg[i] = 0.0;
|
||||
coords_pos[i] = 0.0;
|
||||
let b = Box3D::from_points(&[Point3D::from(coords_neg), Point3D::from(coords_pos)]);
|
||||
assert!(b.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nan_empty_or_negative() {
|
||||
use std::f32::NAN;
|
||||
assert!(Box3D { min: point3(NAN, 2.0, 1.0), max: point3(1.0, 3.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(0.0, NAN, 1.0), max: point3(1.0, 2.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, NAN), max: point3(3.0, 2.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(NAN, 2.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, NAN, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, 1.0, NAN) }.is_empty_or_negative());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright 2018 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::point::{Point2D, Point3D};
|
||||
use crate::vector::{Vector2D, Vector3D};
|
||||
|
||||
use crate::num::{One, Zero};
|
||||
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
use core::fmt;
|
||||
use core::hash::Hash;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Div;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde;
|
||||
|
||||
/// Homogeneous vector in 3D space.
|
||||
#[repr(C)]
|
||||
pub struct HomogeneousVector<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
pub w: T,
|
||||
#[doc(hidden)]
|
||||
pub _unit: PhantomData<U>,
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for HomogeneousVector<T, U> {}
|
||||
|
||||
impl<T: Clone, U> Clone for HomogeneousVector<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
HomogeneousVector {
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
z: self.z.clone(),
|
||||
w: self.w.clone(),
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, U> serde::Deserialize<'de> for HomogeneousVector<T, U>
|
||||
where
|
||||
T: serde::Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let (x, y, z, w) = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(HomogeneousVector {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
w,
|
||||
_unit: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, U> serde::Serialize for HomogeneousVector<T, U>
|
||||
where
|
||||
T: serde::Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
(&self.x, &self.y, &self.z, &self.w).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {}
|
||||
|
||||
impl<T, U> PartialEq for HomogeneousVector<T, U>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Hash for HomogeneousVector<T, U>
|
||||
where
|
||||
T: Hash,
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
|
||||
self.x.hash(h);
|
||||
self.y.hash(h);
|
||||
self.z.hash(h);
|
||||
self.w.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> HomogeneousVector<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub const fn new(x: T, y: T, z: T, w: T) -> Self {
|
||||
HomogeneousVector {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
w,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U> {
|
||||
/// Convert into Cartesian 2D point.
|
||||
///
|
||||
/// Returns None if the point is on or behind the W=0 hemisphere.
|
||||
#[inline]
|
||||
pub fn to_point2d(&self) -> Option<Point2D<T, U>> {
|
||||
if self.w > T::zero() {
|
||||
Some(Point2D::new(self.x / self.w, self.y / self.w))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert into Cartesian 3D point.
|
||||
///
|
||||
/// Returns None if the point is on or behind the W=0 hemisphere.
|
||||
#[inline]
|
||||
pub fn to_point3d(&self) -> Option<Point3D<T, U>> {
|
||||
if self.w > T::zero() {
|
||||
Some(Point3D::new(
|
||||
self.x / self.w,
|
||||
self.y / self.w,
|
||||
self.z / self.w,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> From<Vector2D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(v: Vector2D<T, U>) -> Self {
|
||||
HomogeneousVector::new(v.x, v.y, T::zero(), T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> From<Vector3D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(v: Vector3D<T, U>) -> Self {
|
||||
HomogeneousVector::new(v.x, v.y, v.z, T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + One, U> From<Point2D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(p: Point2D<T, U>) -> Self {
|
||||
HomogeneousVector::new(p.x, p.y, T::zero(), T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: One, U> From<Point3D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(p: Point3D<T, U>) -> Self {
|
||||
HomogeneousVector::new(p.x, p.y, p.z, T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("")
|
||||
.field(&self.x)
|
||||
.field(&self.y)
|
||||
.field(&self.z)
|
||||
.field(&self.w)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
fmt::Display::fmt(&self.x, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.y, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.z, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.w, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod homogeneous {
|
||||
use super::HomogeneousVector;
|
||||
use crate::default::{Point2D, Point3D};
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
assert_eq!(
|
||||
Some(Point2D::new(1.0, 2.0)),
|
||||
HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d()
|
||||
);
|
||||
assert_eq!(
|
||||
Some(Point3D::new(1.0, -2.0, 0.1)),
|
||||
HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative() {
|
||||
assert_eq!(
|
||||
None,
|
||||
HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d()
|
||||
);
|
||||
assert_eq!(
|
||||
None,
|
||||
HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,574 @@
|
|||
// 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 crate::approxeq::ApproxEq;
|
||||
use crate::num::Zero;
|
||||
use crate::scale::Scale;
|
||||
|
||||
use crate::num::One;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::ops::{AddAssign, DivAssign, MulAssign, SubAssign};
|
||||
use num_traits::{NumCast, Saturating};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
/// 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::Scale` to convert it from one unit to
|
||||
/// another. See the [`Scale`] docs for an example.
|
||||
///
|
||||
/// [`Scale`]: struct.Scale.html
|
||||
#[repr(C)]
|
||||
pub struct Length<T, Unit>(pub T, #[doc(hidden)] pub PhantomData<Unit>);
|
||||
|
||||
impl<T: Clone, U> Clone for Length<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Length(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for Length<T, U> {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, U> Deserialize<'de> for Length<T, U>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Length(Deserialize::deserialize(deserializer)?, PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, U> Serialize for Length<T, U>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Length<T, U> {
|
||||
/// Associate a value with a unit of measure.
|
||||
#[inline]
|
||||
pub const fn new(x: T) -> Self {
|
||||
Length(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, U> Length<T, U> {
|
||||
/// Unpack the underlying value from the wrapper, cloning it.
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Length<T, V> {
|
||||
Length::new(self.0.clone())
|
||||
}
|
||||
|
||||
/// Linearly interpolate between this length and another length.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::default::Length;
|
||||
///
|
||||
/// let from = Length::new(0.0);
|
||||
/// let to = Length::new(8.0);
|
||||
///
|
||||
/// assert_eq!(from.lerp(to, -1.0), Length::new(-8.0));
|
||||
/// assert_eq!(from.lerp(to, 0.0), Length::new( 0.0));
|
||||
/// assert_eq!(from.lerp(to, 0.5), Length::new( 4.0));
|
||||
/// assert_eq!(from.lerp(to, 1.0), Length::new( 8.0));
|
||||
/// assert_eq!(from.lerp(to, 2.0), Length::new(16.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self
|
||||
where
|
||||
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
|
||||
{
|
||||
let one_t = T::one() - t.clone();
|
||||
Length::new(one_t * self.0.clone() + t * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, U> Length<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Length<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Length<NewT, U>> {
|
||||
NumCast::from(self.get()).map(Length::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Length<T, U> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Length::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for Length<T, U> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.0.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
// length + length
|
||||
impl<T: Add, U> Add for Length<T, U> {
|
||||
type Output = Length<T::Output, U>;
|
||||
|
||||
fn add(self, other: Self) -> Self::Output {
|
||||
Length::new(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// length += length
|
||||
impl<T: AddAssign, U> AddAssign for Length<T, U> {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.0 += other.0;
|
||||
}
|
||||
}
|
||||
|
||||
// length - length
|
||||
impl<T: Sub, U> Sub for Length<T, U> {
|
||||
type Output = Length<T::Output, U>;
|
||||
|
||||
fn sub(self, other: Length<T, U>) -> Self::Output {
|
||||
Length::new(self.0 - other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// length -= length
|
||||
impl<T: SubAssign, U> SubAssign for Length<T, U> {
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
self.0 -= other.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Saturating length + length and length - length.
|
||||
impl<T: Saturating, U> Saturating for Length<T, U> {
|
||||
fn saturating_add(self, other: Self) -> Self {
|
||||
Length::new(self.0.saturating_add(other.0))
|
||||
}
|
||||
|
||||
fn saturating_sub(self, other: Self) -> Self {
|
||||
Length::new(self.0.saturating_sub(other.0))
|
||||
}
|
||||
}
|
||||
|
||||
// length / length
|
||||
impl<Src, Dst, T: Div> Div<Length<T, Src>> for Length<T, Dst> {
|
||||
type Output = Scale<T::Output, Src, Dst>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Length<T, Src>) -> Self::Output {
|
||||
Scale::new(self.0 / other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// length * scalar
|
||||
impl<T: Mul, U> Mul<T> for Length<T, U> {
|
||||
type Output = Length<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Length::new(self.0 * scale)
|
||||
}
|
||||
}
|
||||
|
||||
// length *= scalar
|
||||
impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
// length / scalar
|
||||
impl<T: Div, U> Div<T> for Length<T, U> {
|
||||
type Output = Length<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Length::new(self.0 / scale)
|
||||
}
|
||||
}
|
||||
|
||||
// length /= scalar
|
||||
impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
// length * scaleFactor
|
||||
impl<Src, Dst, T: Mul> Mul<Scale<T, Src, Dst>> for Length<T, Src> {
|
||||
type Output = Length<T::Output, Dst>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, Src, Dst>) -> Self::Output {
|
||||
Length::new(self.0 * scale.0)
|
||||
}
|
||||
}
|
||||
|
||||
// length / scaleFactor
|
||||
impl<Src, Dst, T: Div> Div<Scale<T, Src, Dst>> for Length<T, Dst> {
|
||||
type Output = Length<T::Output, Src>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, Src, Dst>) -> Self::Output {
|
||||
Length::new(self.0 / scale.0)
|
||||
}
|
||||
}
|
||||
|
||||
// -length
|
||||
impl<U, T: Neg> Neg for Length<T, U> {
|
||||
type Output = Length<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Length::new(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq for Length<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, U> PartialOrd for Length<T, U> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for Length<T, U> {}
|
||||
|
||||
impl<T: Ord, U> Ord for Length<T, U> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> Zero for Length<T, U> {
|
||||
#[inline]
|
||||
fn zero() -> Self {
|
||||
Length::new(Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, T: ApproxEq<T>> ApproxEq<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> T {
|
||||
T::approx_epsilon()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Length<T, U>, approx_epsilon: &T) -> bool {
|
||||
self.0.approx_eq_eps(&other.0, approx_epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Length;
|
||||
use crate::num::Zero;
|
||||
|
||||
use crate::scale::Scale;
|
||||
use core::f32::INFINITY;
|
||||
use num_traits::Saturating;
|
||||
|
||||
enum Inch {}
|
||||
enum Mm {}
|
||||
enum Cm {}
|
||||
enum Second {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde {
|
||||
use super::*;
|
||||
|
||||
extern crate serde_test;
|
||||
use self::serde_test::assert_tokens;
|
||||
use self::serde_test::Token;
|
||||
|
||||
#[test]
|
||||
fn test_length_serde() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
assert_tokens(&one_cm, &[Token::F32(10.0)]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
// A cloned Length is a separate length with the state matching the
|
||||
// original Length at the point it was cloned.
|
||||
let mut variable_length: Length<f32, Inch> = Length::new(12.0);
|
||||
|
||||
let one_foot = variable_length.clone();
|
||||
variable_length.0 = 24.0;
|
||||
|
||||
assert_eq!(one_foot.get(), 12.0);
|
||||
assert_eq!(variable_length.get(), 24.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_clones_length_value() {
|
||||
// Calling get returns a clone of the Length's value.
|
||||
// To test this, we need something clone-able - hence a vector.
|
||||
let mut length: Length<Vec<i32>, Inch> = Length::new(vec![1, 2, 3]);
|
||||
|
||||
let value = length.get();
|
||||
length.0.push(4);
|
||||
|
||||
assert_eq!(value, vec![1, 2, 3]);
|
||||
assert_eq!(length.get(), vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 + length2;
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement += one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), 15.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 - length2;
|
||||
|
||||
assert_eq!(result.get(), 245);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement -= one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), -5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(6);
|
||||
|
||||
let result = length1.saturating_add(length2);
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(5);
|
||||
let length2: Length<u8, Mm> = Length::new(10);
|
||||
|
||||
let result = length1.saturating_sub(length2);
|
||||
|
||||
assert_eq!(result.get(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_length() {
|
||||
// Division results in a Scale 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: Scale<f32, Second, Cm> = Scale::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: Scale<f32, Mm, Cm> = Scale::new(0.1);
|
||||
|
||||
let result = length_mm * cm_per_mm;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(1.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication_with_scalar() {
|
||||
let length_mm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = length_mm * 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(20.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication_assignment() {
|
||||
let mut length: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
length *= 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(20.0);
|
||||
assert_eq!(length, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalefactor() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let cm_per_second: Scale<f32, Second, Cm> = Scale::new(10.0);
|
||||
|
||||
let result = length / cm_per_second;
|
||||
|
||||
let expected: Length<f32, Second> = Length::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalar() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = length / 2.0;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(2.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_assignment() {
|
||||
let mut length: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
length /= 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(5.0);
|
||||
assert_eq!(length, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = -length;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(-5.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
let length_as_i32: Length<i32, Cm> = Length::new(5);
|
||||
|
||||
let result: Length<f32, Cm> = length_as_i32.cast();
|
||||
|
||||
let length_as_f32: Length<f32, Cm> = Length::new(5.0);
|
||||
assert_eq!(result, length_as_f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 == length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_0 != length_5_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 < length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_1 > length_5_point_0);
|
||||
assert!(length_5_point_1 >= length_5_point_0);
|
||||
assert!(length_5_point_0 >= length_5_point_1 - length_0_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_add() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
|
||||
let result = length - LengthCm::zero();
|
||||
|
||||
assert_eq!(result, length);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_division() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
let length_zero: LengthCm = Length::zero();
|
||||
|
||||
let result = length / length_zero;
|
||||
|
||||
let expected: Scale<f32, Cm, Cm> = Scale::new(INFINITY);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// 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(not(test), no_std)]
|
||||
|
||||
//! A collection of strongly typed math tools for computer graphics with an inclination
|
||||
//! towards 2d graphics and layout.
|
||||
//!
|
||||
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
|
||||
//! and tagged with a generic Unit parameter which is useful to prevent mixing
|
||||
//! values from different spaces. For example it should not be legal to translate
|
||||
//! a screen-space position by a world-space vector and this can be expressed using
|
||||
//! the generic Unit parameter.
|
||||
//!
|
||||
//! This unit system is not mandatory and all structures have an alias
|
||||
//! with the default unit: `UnknownUnit`.
|
||||
//! for example ```default::Point2D<T>``` is equivalent to ```Point2D<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 = Point2D<f32, ScreenSpace>;
|
||||
//! pub type ScreenSize = Size2D<f32, ScreenSpace>;
|
||||
//! pub struct WorldSpace;
|
||||
//! pub type WorldPoint = Point3D<f32, WorldSpace>;
|
||||
//! pub type ProjectionMatrix = Transform3D<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)`).
|
||||
//!
|
||||
#![deny(unconditional_recursion)]
|
||||
|
||||
pub use crate::angle::Angle;
|
||||
pub use crate::box2d::Box2D;
|
||||
pub use crate::homogen::HomogeneousVector;
|
||||
pub use crate::length::Length;
|
||||
pub use crate::nonempty::NonEmpty;
|
||||
pub use crate::point::{point2, point3, Point2D, Point3D};
|
||||
pub use crate::scale::Scale;
|
||||
pub use crate::transform2d::Transform2D;
|
||||
pub use crate::transform3d::Transform3D;
|
||||
pub use crate::vector::{bvec2, bvec3, BoolVector2D, BoolVector3D};
|
||||
pub use crate::vector::{vec2, vec3, Vector2D, Vector3D};
|
||||
|
||||
pub use crate::box3d::{box3d, Box3D};
|
||||
pub use crate::rect::{rect, Rect};
|
||||
pub use crate::rigid::RigidTransform3D;
|
||||
pub use crate::rotation::{Rotation2D, Rotation3D};
|
||||
pub use crate::side_offsets::SideOffsets2D;
|
||||
pub use crate::size::{size2, size3, Size2D, Size3D};
|
||||
pub use crate::translation::{Translation2D, Translation3D};
|
||||
pub use crate::trig::Trig;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod angle;
|
||||
pub mod approxeq;
|
||||
pub mod approxord;
|
||||
mod box2d;
|
||||
mod box3d;
|
||||
mod homogen;
|
||||
mod length;
|
||||
mod nonempty;
|
||||
pub mod num;
|
||||
mod point;
|
||||
mod rect;
|
||||
mod rigid;
|
||||
mod rotation;
|
||||
mod scale;
|
||||
mod side_offsets;
|
||||
mod size;
|
||||
mod transform2d;
|
||||
mod transform3d;
|
||||
mod translation;
|
||||
mod trig;
|
||||
mod vector;
|
||||
|
||||
/// The default unit.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct UnknownUnit;
|
||||
|
||||
pub mod default {
|
||||
//! A set of aliases for all types, tagged with the default unknown unit.
|
||||
|
||||
use super::UnknownUnit;
|
||||
pub type Length<T> = super::Length<T, UnknownUnit>;
|
||||
pub type Point2D<T> = super::Point2D<T, UnknownUnit>;
|
||||
pub type Point3D<T> = super::Point3D<T, UnknownUnit>;
|
||||
pub type Vector2D<T> = super::Vector2D<T, UnknownUnit>;
|
||||
pub type Vector3D<T> = super::Vector3D<T, UnknownUnit>;
|
||||
pub type HomogeneousVector<T> = super::HomogeneousVector<T, UnknownUnit>;
|
||||
pub type Size2D<T> = super::Size2D<T, UnknownUnit>;
|
||||
pub type Size3D<T> = super::Size3D<T, UnknownUnit>;
|
||||
pub type Rect<T> = super::Rect<T, UnknownUnit>;
|
||||
pub type Box2D<T> = super::Box2D<T, UnknownUnit>;
|
||||
pub type Box3D<T> = super::Box3D<T, UnknownUnit>;
|
||||
pub type SideOffsets2D<T> = super::SideOffsets2D<T, UnknownUnit>;
|
||||
pub type Transform2D<T> = super::Transform2D<T, UnknownUnit, UnknownUnit>;
|
||||
pub type Transform3D<T> = super::Transform3D<T, UnknownUnit, UnknownUnit>;
|
||||
pub type Rotation2D<T> = super::Rotation2D<T, UnknownUnit, UnknownUnit>;
|
||||
pub type Rotation3D<T> = super::Rotation3D<T, UnknownUnit, UnknownUnit>;
|
||||
pub type Translation2D<T> = super::Translation2D<T, UnknownUnit, UnknownUnit>;
|
||||
pub type Translation3D<T> = super::Translation3D<T, UnknownUnit, UnknownUnit>;
|
||||
pub type Scale<T> = super::Scale<T, UnknownUnit, UnknownUnit>;
|
||||
pub type RigidTransform3D<T> = super::RigidTransform3D<T, UnknownUnit, UnknownUnit>;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// 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! mint_vec {
|
||||
($name:ident [ $($field:ident),* ] = $std_name:ident) => {
|
||||
#[cfg(feature = "mint")]
|
||||
impl<T, U> From<mint::$std_name<T>> for $name<T, U> {
|
||||
fn from(v: mint::$std_name<T>) -> Self {
|
||||
$name {
|
||||
$( $field: v.$field, )*
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "mint")]
|
||||
impl<T, U> Into<mint::$std_name<T>> for $name<T, U> {
|
||||
fn into(self) -> mint::$std_name<T> {
|
||||
mint::$std_name {
|
||||
$( $field: self.$field, )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
// 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;
|
||||
|
||||
// Euclid has its own Zero and One traits instead of of using the num_traits equivalents.
|
||||
// Unfortunately, num_traits::Zero requires Add, which opens a bag of sad things:
|
||||
// - Most importantly, for Point2D to implement Zero it would need to implement Add<Self> which we
|
||||
// don't want (we allow "Point + Vector" and "Vector + Vector" semantics and purposefully disallow
|
||||
// "Point + Point".
|
||||
// - Some operations that require, say, One and Div (for example Scale::inv) currently return a
|
||||
// type parameterized over T::Output which is ambiguous with num_traits::One because it inherits
|
||||
// Mul which also has an Output associated type. To fix it need to complicate type signatures
|
||||
// by using <T as Trait>::Output which makes the code and documentation harder to read.
|
||||
//
|
||||
// On the other hand, euclid::num::Zero/One are automatically implemented for all types that
|
||||
// implement their num_traits counterpart. Euclid users never need to explicitly use
|
||||
// euclid::num::Zero/One and can/should only manipulate the num_traits equivalents without risk
|
||||
// of compatibility issues with euclid.
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the nearest integer value to the original value.
|
||||
pub trait Round: Copy {
|
||||
/// Rounds to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[must_use]
|
||||
fn round(self) -> Self;
|
||||
}
|
||||
/// Defines the biggest integer equal or lower than the original value.
|
||||
pub trait Floor: Copy {
|
||||
/// Rounds to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[must_use]
|
||||
fn floor(self) -> Self;
|
||||
}
|
||||
/// Defines the smallest integer equal or greater than the original value.
|
||||
pub trait Ceil: Copy {
|
||||
/// Rounds to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[must_use]
|
||||
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 {
|
||||
num_traits::Float::round(self)
|
||||
}
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty {
|
||||
num_traits::Float::floor(self)
|
||||
}
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty {
|
||||
num_traits::Float::ceil(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
num_int!(i16);
|
||||
num_int!(u16);
|
||||
num_int!(i32);
|
||||
num_int!(u32);
|
||||
num_int!(i64);
|
||||
num_int!(u64);
|
||||
num_int!(isize);
|
||||
num_int!(usize);
|
||||
num_float!(f32);
|
||||
num_float!(f64);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,908 @@
|
|||
// 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 crate::box2d::Box2D;
|
||||
use crate::nonempty::NonEmpty;
|
||||
use crate::num::*;
|
||||
use crate::point::Point2D;
|
||||
use crate::scale::Scale;
|
||||
use crate::side_offsets::SideOffsets2D;
|
||||
use crate::size::Size2D;
|
||||
use crate::vector::Vector2D;
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::PartialOrd;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
|
||||
|
||||
/// A 2d Rectangle optionally tagged with a unit.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
|
||||
)]
|
||||
pub struct Rect<T, U> {
|
||||
pub origin: Point2D<T, U>,
|
||||
pub size: Size2D<T, U>,
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for Rect<T, U> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.origin.hash(h);
|
||||
self.size.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for Rect<T, U> {}
|
||||
|
||||
impl<T: Clone, U> Clone for Rect<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.origin.clone(), self.size.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq for Rect<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.origin.eq(&other.origin) && self.size.eq(&other.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for Rect<T, U> {}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for Rect<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Rect(")?;
|
||||
fmt::Debug::fmt(&self.size, f)?;
|
||||
write!(f, " at ")?;
|
||||
fmt::Debug::fmt(&self.origin, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Rect<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Rect(")?;
|
||||
fmt::Display::fmt(&self.size, f)?;
|
||||
write!(f, " at ")?;
|
||||
fmt::Display::fmt(&self.origin, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Rect<T, U> {
|
||||
fn default() -> Self {
|
||||
Rect::new(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U> {
|
||||
/// Constructor.
|
||||
#[inline]
|
||||
pub const fn new(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self {
|
||||
Rect { origin, size }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
/// Constructor, setting all sides to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Rect::new(Point2D::origin(), Size2D::zero())
|
||||
}
|
||||
|
||||
/// Creates a rect of the given size, at offset zero.
|
||||
#[inline]
|
||||
pub fn from_size(size: Size2D<T, U>) -> Self {
|
||||
Rect {
|
||||
origin: Point2D::zero(),
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn min(&self) -> Point2D<T, U> {
|
||||
self.origin
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(&self) -> Point2D<T, U> {
|
||||
self.origin + self.size
|
||||
}
|
||||
|
||||
#[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 width(&self) -> T {
|
||||
self.size.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> T {
|
||||
self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x_range(&self) -> Range<T> {
|
||||
self.min_x()..self.max_x()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn y_range(&self) -> Range<T> {
|
||||
self.min_y()..self.max_y()
|
||||
}
|
||||
|
||||
/// Returns the same rectangle, translated by a vector.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn translate(&self, by: Vector2D<T, U>) -> Self {
|
||||
Self::new(self.origin + by, self.size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_box2d(&self) -> Box2D<T, U> {
|
||||
Box2D {
|
||||
min: self.min(),
|
||||
max: self.max(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + PartialOrd + Add<T, Output = T>,
|
||||
{
|
||||
/// 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, p: Point2D<T, U>) -> bool {
|
||||
self.to_box2d().contains(p)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
self.to_box2d().intersects(&other.to_box2d())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let box2d = self.to_box2d().intersection(&other.to_box2d());
|
||||
if box2d.is_empty_or_negative() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(box2d.to_rect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inflate(&self, width: T, height: T) -> Self {
|
||||
Rect::new(
|
||||
Point2D::new(self.origin.x - width, self.origin.y - height),
|
||||
Size2D::new(
|
||||
self.size.width + width + width,
|
||||
self.size.height + height + height,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd + Add<T, Output = T>,
|
||||
{
|
||||
/// 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_or_negative()
|
||||
|| (self.min_x() <= rect.min_x()
|
||||
&& rect.max_x() <= self.max_x()
|
||||
&& self.min_y() <= rect.min_y()
|
||||
&& rect.max_y() <= self.max_y())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
/// Calculate the size and position of an inner rectangle.
|
||||
///
|
||||
/// Subtracts the side offsets from all sides. The horizontal and vertical
|
||||
/// offsets must not be larger than the original side length.
|
||||
/// This method assumes y oriented downward.
|
||||
pub fn inner_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
|
||||
let rect = Rect::new(
|
||||
Point2D::new(self.origin.x + offsets.left, self.origin.y + offsets.top),
|
||||
Size2D::new(
|
||||
self.size.width - offsets.horizontal(),
|
||||
self.size.height - offsets.vertical(),
|
||||
),
|
||||
);
|
||||
debug_assert!(rect.size.width >= Zero::zero());
|
||||
debug_assert!(rect.size.height >= Zero::zero());
|
||||
rect
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
/// Calculate the size and position of an outer rectangle.
|
||||
///
|
||||
/// Add the offsets to all sides. The expanded rectangle is returned.
|
||||
/// This method assumes y oriented downward.
|
||||
pub fn outer_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
|
||||
Rect::new(
|
||||
Point2D::new(self.origin.x - offsets.left, self.origin.y - offsets.top),
|
||||
Size2D::new(
|
||||
self.size.width + offsets.horizontal(),
|
||||
self.size.height + offsets.vertical(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + Zero + PartialOrd + Sub<T, Output = T>,
|
||||
{
|
||||
/// Returns the smallest rectangle defined by the top/bottom/left/right-most
|
||||
/// points provided as parameter.
|
||||
///
|
||||
/// Note: This function has a behavior that can be surprising because
|
||||
/// the right-most and bottom-most points are exactly on the edge
|
||||
/// of the rectangle while the `contains` function is has exclusive
|
||||
/// semantic on these edges. This means that the right-most and bottom-most
|
||||
/// points provided to `from_points` will count as not contained by the rect.
|
||||
/// This behavior may change in the future.
|
||||
pub fn from_points<I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Point2D<T, U>>,
|
||||
{
|
||||
Box2D::from_points(points).to_rect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this rectangle and another rectangle.
|
||||
#[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> Rect<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Div<Output = T>,
|
||||
{
|
||||
pub fn center(&self) -> Point2D<T, U> {
|
||||
let two = T::one() + T::one();
|
||||
self.origin + self.size.to_vector() / two
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U>
|
||||
where
|
||||
T: Copy + 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;
|
||||
}
|
||||
|
||||
self.to_box2d().union(&other.to_box2d()).to_rect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Rect<T, U> {
|
||||
#[inline]
|
||||
pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
|
||||
where
|
||||
T: Copy + Mul<S, Output = T>,
|
||||
{
|
||||
Rect::new(
|
||||
Point2D::new(self.origin.x * x, self.origin.y * y),
|
||||
Size2D::new(self.size.width * x, self.size.height * y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Rect<T, U> {
|
||||
#[inline]
|
||||
pub fn area(&self) -> T {
|
||||
self.size.area()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + PartialEq, U> Rect<T, U> {
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + PartialOrd, U> Rect<T, U> {
|
||||
#[inline]
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
self.size.is_empty_or_negative()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
|
||||
#[inline]
|
||||
pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
|
||||
if self.is_empty_or_negative() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Rect<T, U> {
|
||||
type Output = Rect<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Rect::new(self.origin * scale.clone(), self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self *= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Rect<T, U> {
|
||||
type Output = Rect<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Rect::new(self.origin / scale.clone(), self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self /= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
|
||||
type Output = Rect<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Rect::new(self.origin * scale.clone(), self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.origin *= scale.clone();
|
||||
self.size *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
|
||||
type Output = Rect<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Rect::new(self.origin / scale.clone(), self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.origin /= scale.clone();
|
||||
self.size /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Rect<T, U> {
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Rect<T, UnknownUnit> {
|
||||
Rect::new(self.origin.to_untyped(), self.size.to_untyped())
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(r: &Rect<T, UnknownUnit>) -> Rect<T, U> {
|
||||
Rect::new(
|
||||
Point2D::from_untyped(r.origin),
|
||||
Size2D::from_untyped(r.size),
|
||||
)
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Rect<T, V> {
|
||||
Rect::new(self.origin.cast_unit(), self.size.cast_unit())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> Rect<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(), round_in or round_out() before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Rect<NewT, U> {
|
||||
Rect::new(self.origin.cast(), self.size.cast())
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Rect<NewT, U>> {
|
||||
match (self.origin.try_cast(), self.size.try_cast()) {
|
||||
(Some(origin), Some(size)) => Some(Rect::new(origin, size)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` rectangle.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Rect<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` rectangle.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Rect<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Rect<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Rect<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `u64` 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.
|
||||
#[inline]
|
||||
pub fn to_u64(&self) -> Rect<u64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Rect<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Rect<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor + Ceil + Round + Add<T, Output = T> + Sub<T, Output = T>, U> Rect<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).
|
||||
///
|
||||
/// # Usage notes
|
||||
/// Note, that when using with floating-point `T` types that method can significantly
|
||||
/// loose precision for large values, so if you need to call this method very often it
|
||||
/// is better to use [`Box2D`].
|
||||
///
|
||||
/// [`Box2D`]: struct.Box2D.html
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self {
|
||||
self.to_box2d().round().to_rect()
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle contains the resulting rectangle.
|
||||
///
|
||||
/// # Usage notes
|
||||
/// Note, that when using with floating-point `T` types that method can significantly
|
||||
/// loose precision for large values, so if you need to call this method very often it
|
||||
/// is better to use [`Box2D`].
|
||||
///
|
||||
/// [`Box2D`]: struct.Box2D.html
|
||||
#[must_use]
|
||||
pub fn round_in(&self) -> Self {
|
||||
self.to_box2d().round_in().to_rect()
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle is contained in the resulting rectangle.
|
||||
///
|
||||
/// # Usage notes
|
||||
/// Note, that when using with floating-point `T` types that method can significantly
|
||||
/// loose precision for large values, so if you need to call this method very often it
|
||||
/// is better to use [`Box2D`].
|
||||
///
|
||||
/// [`Box2D`]: struct.Box2D.html
|
||||
#[must_use]
|
||||
pub fn round_out(&self) -> Self {
|
||||
self.to_box2d().round_out().to_rect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> From<Size2D<T, U>> for Rect<T, U>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
fn from(size: Size2D<T, U>) -> Self {
|
||||
Self::from_size(size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `Rect::new(Point2D::new(x, y), Size2D::new(w, h))`.
|
||||
pub const fn rect<T, U>(x: T, y: T, w: T, h: T) -> Rect<T, U> {
|
||||
Rect::new(Point2D::new(x, y), Size2D::new(w, h))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::default::{Point2D, Rect, Size2D};
|
||||
use crate::side_offsets::SideOffsets2D;
|
||||
use crate::{point2, rect, size2, vec2};
|
||||
|
||||
#[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_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_intersection_overflow() {
|
||||
// test some scenarios where the intersection can overflow but
|
||||
// the min_x() and max_x() don't. Gecko currently fails these cases
|
||||
let p = Rect::new(Point2D::new(-2147483648, -2147483648), Size2D::new(0, 0));
|
||||
let q = Rect::new(
|
||||
Point2D::new(2136893440, 2136893440),
|
||||
Size2D::new(279552, 279552),
|
||||
);
|
||||
let r = Rect::new(Point2D::new(-2147483648, -2147483648), Size2D::new(1, 1));
|
||||
|
||||
assert!(p.is_empty());
|
||||
let pq = p.intersection(&q);
|
||||
assert!(pq.is_none());
|
||||
|
||||
let qr = q.intersection(&r);
|
||||
assert!(qr.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
|
||||
|
||||
assert!(r.contains(Point2D::new(0, 50)));
|
||||
assert!(r.contains(Point2D::new(-10, 200)));
|
||||
|
||||
// The `contains` method is inclusive of the top/left edges, but not the
|
||||
// bottom/right edges.
|
||||
assert!(r.contains(Point2D::new(-20, 15)));
|
||||
assert!(!r.contains(Point2D::new(80, 15)));
|
||||
assert!(!r.contains(Point2D::new(80, 215)));
|
||||
assert!(!r.contains(Point2D::new(-20, 215)));
|
||||
|
||||
// Points beyond the top-left corner.
|
||||
assert!(!r.contains(Point2D::new(-25, 15)));
|
||||
assert!(!r.contains(Point2D::new(-15, 10)));
|
||||
|
||||
// Points beyond the top-right corner.
|
||||
assert!(!r.contains(Point2D::new(85, 20)));
|
||||
assert!(!r.contains(Point2D::new(75, 10)));
|
||||
|
||||
// Points beyond the bottom-right corner.
|
||||
assert!(!r.contains(Point2D::new(85, 210)));
|
||||
assert!(!r.contains(Point2D::new(75, 220)));
|
||||
|
||||
// Points beyond the bottom-left corner.
|
||||
assert!(!r.contains(Point2D::new(-25, 210)));
|
||||
assert!(!r.contains(Point2D::new(-15, 220)));
|
||||
|
||||
let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
|
||||
assert!(r.contains_rect(&r));
|
||||
assert!(!r.contains_rect(&r.translate(vec2(0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(vec2(-0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(vec2(0.0, 0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(vec2(0.0, -0.1))));
|
||||
// Empty rectangles are always considered as contained in other rectangles,
|
||||
// even if their origin is not.
|
||||
let p = Point2D::new(1.0, 1.0);
|
||||
assert!(!r.contains(p));
|
||||
assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.scale(10, 15);
|
||||
|
||||
assert!(pp.size.width == 500);
|
||||
assert!(pp.size.height == 600);
|
||||
assert!(pp.origin.x == 0);
|
||||
assert!(pp.origin.y == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.scale(1, 20);
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 800);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inflate() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
|
||||
let pp = p.inflate(10, 20);
|
||||
|
||||
assert!(pp.size.width == 30);
|
||||
assert!(pp.size.height == 50);
|
||||
assert!(pp.origin.x == -10);
|
||||
assert!(pp.origin.y == -20);
|
||||
|
||||
let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let rr = r.inflate(-2, -5);
|
||||
|
||||
assert!(rr.size.width == 6);
|
||||
assert!(rr.size.height == 10);
|
||||
assert!(rr.origin.x == 2);
|
||||
assert!(rr.origin.y == 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inner_outer_rect() {
|
||||
let inner_rect = Rect::new(point2(20, 40), size2(80, 100));
|
||||
let offsets = SideOffsets2D::new(20, 10, 10, 10);
|
||||
let outer_rect = inner_rect.outer_rect(offsets);
|
||||
assert_eq!(outer_rect.origin.x, 10);
|
||||
assert_eq!(outer_rect.origin.y, 20);
|
||||
assert_eq!(outer_rect.size.width, 100);
|
||||
assert_eq!(outer_rect.size.height, 130);
|
||||
assert_eq!(outer_rect.inner_rect(offsets), inner_rect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max_x_y() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
assert!(p.max_y() == 40);
|
||||
assert!(p.min_y() == 0);
|
||||
assert!(p.max_x() == 50);
|
||||
assert!(p.min_x() == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
assert!(r.max_y() == 35);
|
||||
assert!(r.min_y() == -5);
|
||||
assert!(r.max_x() == 40);
|
||||
assert!(r.min_x() == -10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_width_height() {
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
assert!(r.width() == 50);
|
||||
assert!(r.height() == 40);
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_center() {
|
||||
let r: Rect<i32> = rect(-2, 5, 4, 10);
|
||||
assert_eq!(r.center(), point2(0, 10));
|
||||
|
||||
let r: Rect<f32> = rect(1.0, 2.0, 3.0, 4.0);
|
||||
assert_eq!(r.center(), point2(2.5, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nan() {
|
||||
let r1: Rect<f32> = rect(-2.0, 5.0, 4.0, std::f32::NAN);
|
||||
let r2: Rect<f32> = rect(std::f32::NAN, -1.0, 3.0, 10.0);
|
||||
|
||||
assert_eq!(r1.intersection(&r2), None);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
//! All matrix multiplication in this module is in row-vector notation,
|
||||
//! i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
|
||||
//! before `T2` you use `T1 * T2`
|
||||
|
||||
use crate::approxeq::ApproxEq;
|
||||
use crate::trig::Trig;
|
||||
use crate::{Rotation3D, Transform3D, UnknownUnit, Vector3D};
|
||||
use num_traits::Float;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A rigid transformation. All lengths are preserved under such a transformation.
|
||||
///
|
||||
///
|
||||
/// Internally, this is a rotation and a translation, with the rotation
|
||||
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
|
||||
///
|
||||
/// This can be more efficient to use over full matrices, especially if you
|
||||
/// have to deal with the decomposed quantities often.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[repr(C)]
|
||||
pub struct RigidTransform3D<T, Src, Dst> {
|
||||
pub rotation: Rotation3D<T, Src, Dst>,
|
||||
pub translation: Vector3D<T, Dst>,
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
||||
/// Construct a new rigid transformation, where the `rotation` applies first
|
||||
#[inline]
|
||||
pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
|
||||
Self {
|
||||
rotation,
|
||||
translation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
||||
pub fn cast_unit<Src2, Dst2>(&self) -> RigidTransform3D<T, Src2, Dst2> {
|
||||
RigidTransform3D {
|
||||
rotation: self.rotation.cast_unit(),
|
||||
translation: self.translation.cast_unit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
||||
/// Construct an identity transform
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
Self {
|
||||
rotation: Rotation3D::identity(),
|
||||
translation: Vector3D::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new rigid transformation, where the `translation` applies first
|
||||
#[inline]
|
||||
pub fn new_from_reversed(
|
||||
translation: Vector3D<T, Src>,
|
||||
rotation: Rotation3D<T, Src, Dst>,
|
||||
) -> Self {
|
||||
// T * R
|
||||
// = (R * R^-1) * T * R
|
||||
// = R * (R^-1 * T * R)
|
||||
// = R * T'
|
||||
//
|
||||
// T' = (R^-1 * T * R) is also a translation matrix
|
||||
// It is equivalent to the translation matrix obtained by rotating the
|
||||
// translation by R
|
||||
|
||||
let translation = rotation.transform_vector3d(translation);
|
||||
Self {
|
||||
rotation,
|
||||
translation,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
|
||||
Self {
|
||||
rotation,
|
||||
translation: Vector3D::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
|
||||
Self {
|
||||
translation,
|
||||
rotation: Rotation3D::identity(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompose this into a translation and an rotation to be applied in the opposite order
|
||||
///
|
||||
/// i.e., the translation is applied _first_
|
||||
#[inline]
|
||||
pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
|
||||
// self = R * T
|
||||
// = R * T * (R^-1 * R)
|
||||
// = (R * T * R^-1) * R)
|
||||
// = T' * R
|
||||
//
|
||||
// T' = (R^ * T * R^-1) is T rotated by R^-1
|
||||
|
||||
let translation = self.rotation.inverse().transform_vector3d(self.translation);
|
||||
(translation, self.rotation)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two transforms such that
|
||||
/// other's transformation applies after self's transformation.
|
||||
///
|
||||
/// i.e., this produces `self * other` in row-vector notation
|
||||
#[inline]
|
||||
pub fn post_transform<Dst2>(
|
||||
&self,
|
||||
other: &RigidTransform3D<T, Dst, Dst2>,
|
||||
) -> RigidTransform3D<T, Src, Dst2> {
|
||||
// self = R1 * T1
|
||||
// other = R2 * T2
|
||||
// result = R1 * T1 * R2 * T2
|
||||
// = R1 * (R2 * R2^-1) * T1 * R2 * T2
|
||||
// = (R1 * R2) * (R2^-1 * T1 * R2) * T2
|
||||
// = R' * T' * T2
|
||||
// = R' * T''
|
||||
//
|
||||
// (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
|
||||
// R1 * R2 = R'
|
||||
// T' * T2 = T'' = vector addition of translations T2 and T'
|
||||
|
||||
let t_prime = other.rotation.transform_vector3d(self.translation);
|
||||
let r_prime = self.rotation.post_rotate(&other.rotation);
|
||||
let t_prime2 = t_prime + other.translation;
|
||||
RigidTransform3D {
|
||||
rotation: r_prime,
|
||||
translation: t_prime2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two transforms such that
|
||||
/// self's transformation applies after other's transformation.
|
||||
///
|
||||
/// i.e., this produces `other * self` in row-vector notation
|
||||
#[inline]
|
||||
pub fn pre_transform<Src2>(
|
||||
&self,
|
||||
other: &RigidTransform3D<T, Src2, Src>,
|
||||
) -> RigidTransform3D<T, Src2, Dst> {
|
||||
other.post_transform(&self)
|
||||
}
|
||||
|
||||
/// Inverts the transformation
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
|
||||
// result = (self)^-1
|
||||
// = (R * T)^-1
|
||||
// = T^-1 * R^-1
|
||||
// = (R^-1 * R) * T^-1 * R^-1
|
||||
// = R^-1 * (R * T^-1 * R^-1)
|
||||
// = R' * T'
|
||||
//
|
||||
// T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
|
||||
// R' = R^-1
|
||||
//
|
||||
// An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1
|
||||
|
||||
RigidTransform3D::new_from_reversed(-self.translation, self.rotation.inverse())
|
||||
}
|
||||
|
||||
pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
|
||||
where
|
||||
T: Trig,
|
||||
{
|
||||
self.translation
|
||||
.to_transform()
|
||||
.pre_transform(&self.rotation.to_transform())
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
|
||||
RigidTransform3D {
|
||||
rotation: self.rotation.to_untyped(),
|
||||
translation: self.translation.to_untyped(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
|
||||
RigidTransform3D {
|
||||
rotation: Rotation3D::from_untyped(&transform.rotation),
|
||||
translation: Vector3D::from_untyped(transform.translation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
|
||||
for RigidTransform3D<T, Src, Dst>
|
||||
{
|
||||
fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
|
||||
Self::from_rotation(rot)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>> for RigidTransform3D<T, Src, Dst> {
|
||||
fn from(t: Vector3D<T, Dst>) -> Self {
|
||||
Self::from_translation(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::RigidTransform3D;
|
||||
use crate::default::{Rotation3D, Transform3D, Vector3D};
|
||||
|
||||
#[test]
|
||||
fn test_rigid_construction() {
|
||||
let translation = Vector3D::new(12.1, 17.8, -5.5);
|
||||
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
|
||||
|
||||
let rigid = RigidTransform3D::new(rotation, translation);
|
||||
assert!(rigid.to_transform().approx_eq(
|
||||
&translation
|
||||
.to_transform()
|
||||
.pre_transform(&rotation.to_transform())
|
||||
));
|
||||
|
||||
let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
|
||||
assert!(rigid.to_transform().approx_eq(
|
||||
&translation
|
||||
.to_transform()
|
||||
.post_transform(&rotation.to_transform())
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rigid_decomposition() {
|
||||
let translation = Vector3D::new(12.1, 17.8, -5.5);
|
||||
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
|
||||
|
||||
let rigid = RigidTransform3D::new(rotation, translation);
|
||||
let (t2, r2) = rigid.decompose_reversed();
|
||||
assert!(rigid
|
||||
.to_transform()
|
||||
.approx_eq(&t2.to_transform().post_transform(&r2.to_transform())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rigid_inverse() {
|
||||
let translation = Vector3D::new(12.1, 17.8, -5.5);
|
||||
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
|
||||
|
||||
let rigid = RigidTransform3D::new(rotation, translation);
|
||||
let inverse = rigid.inverse();
|
||||
assert!(rigid
|
||||
.post_transform(&inverse)
|
||||
.to_transform()
|
||||
.approx_eq(&Transform3D::identity()));
|
||||
assert!(inverse
|
||||
.to_transform()
|
||||
.approx_eq(&rigid.to_transform().inverse().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rigid_multiply() {
|
||||
let translation = Vector3D::new(12.1, 17.8, -5.5);
|
||||
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
|
||||
let translation2 = Vector3D::new(9.3, -3.9, 1.1);
|
||||
let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
|
||||
let rigid = RigidTransform3D::new(rotation, translation);
|
||||
let rigid2 = RigidTransform3D::new(rotation2, translation2);
|
||||
|
||||
assert!(rigid
|
||||
.post_transform(&rigid2)
|
||||
.to_transform()
|
||||
.approx_eq(&rigid.to_transform().post_transform(&rigid2.to_transform())));
|
||||
assert!(rigid
|
||||
.pre_transform(&rigid2)
|
||||
.to_transform()
|
||||
.approx_eq(&rigid.to_transform().pre_transform(&rigid2.to_transform())));
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,399 @@
|
|||
// 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 crate::num::One;
|
||||
|
||||
use crate::{Point2D, Rect, Size2D, Vector2D};
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// 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 `Scale`. They
|
||||
/// may be types without values, such as empty enums. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::Scale;
|
||||
/// use euclid::Length;
|
||||
/// enum Mm {};
|
||||
/// enum Inch {};
|
||||
///
|
||||
/// let mm_per_inch: Scale<f32, Inch, Mm> = Scale::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)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(
|
||||
serialize = "T: serde::Serialize",
|
||||
deserialize = "T: serde::Deserialize<'de>"
|
||||
))
|
||||
)]
|
||||
pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
|
||||
|
||||
impl<T, Src, Dst> Scale<T, Src, Dst> {
|
||||
#[inline]
|
||||
pub const fn new(x: T) -> Self {
|
||||
Scale(x, PhantomData)
|
||||
}
|
||||
|
||||
/// Returns the given point transformed by this scale.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::{Scale, point2};
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
|
||||
///
|
||||
/// assert_eq!(to_mm.transform_point(point2(42, -42)), point2(420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Mul,
|
||||
{
|
||||
Point2D::new(point.x * self.get(), point.y * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::{Scale, vec2};
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
|
||||
///
|
||||
/// assert_eq!(to_mm.transform_vector(vec2(42, -42)), vec2(420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Mul,
|
||||
{
|
||||
Vector2D::new(vec.x * self.get(), vec.y * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::{Scale, size2};
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
|
||||
///
|
||||
/// assert_eq!(to_mm.transform_size(size2(42, -42)), size2(420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_size(&self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Mul,
|
||||
{
|
||||
Size2D::new(size.width * self.get(), size.height * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given rect transformed by this scale.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::{Scale, rect};
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
|
||||
///
|
||||
/// assert_eq!(to_mm.transform_rect(&rect(1, 2, 42, -42)), rect(10, 20, 420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
|
||||
where
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Rect::new(
|
||||
self.transform_point(rect.origin),
|
||||
self.transform_size(rect.size),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the inverse of this scale.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> Scale<T::Output, Dst, Src>
|
||||
where
|
||||
T: Clone + Neg,
|
||||
{
|
||||
Scale::new(-self.get())
|
||||
}
|
||||
|
||||
/// Returns `true` if this scale has no effect.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::Scale;
|
||||
/// use euclid::num::One;
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
|
||||
/// let mm_per_mm: Scale<f32, Mm, Mm> = Scale::new(1.0);
|
||||
///
|
||||
/// assert_eq!(cm_per_mm.is_identity(), false);
|
||||
/// assert_eq!(mm_per_mm.is_identity(), true);
|
||||
/// assert_eq!(mm_per_mm, Scale::one());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> bool
|
||||
where
|
||||
T: PartialEq + One,
|
||||
{
|
||||
self.0 == T::one()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Scale<T, Src, Dst> {
|
||||
#[inline]
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
/// The inverse Scale (1.0 / self).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::Scale;
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let cm_per_mm: Scale<f32, Cm, Mm> = Scale::new(0.1);
|
||||
///
|
||||
/// assert_eq!(cm_per_mm.inv(), Scale::new(10.0));
|
||||
/// ```
|
||||
pub fn inv(&self) -> Scale<T::Output, Dst, Src>
|
||||
where
|
||||
T: One + Div,
|
||||
{
|
||||
let one: T = One::one();
|
||||
Scale::new(one / self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, Src, Dst> Scale<T, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the source value cannot be represented by the target type `NewT`, then
|
||||
/// method panics. Use `try_cast` if that must be case.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::Scale;
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
///
|
||||
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
|
||||
///
|
||||
/// assert_eq!(to_mm.cast::<f32>(), Scale::new(10.0));
|
||||
/// ```
|
||||
/// That conversion will panic, because `i32` not enough to store such big numbers:
|
||||
/// ```rust,should_panic
|
||||
/// use euclid::Scale;
|
||||
/// enum Mm {};// millimeter = 10^-2 meters
|
||||
/// enum Em {};// exameter = 10^18 meters
|
||||
///
|
||||
/// // Panics
|
||||
/// let to_em: Scale<i32, Mm, Em> = Scale::new(10e20).cast();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Scale<NewT, Src, Dst> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
/// If the source value cannot be represented by the target type `NewT`, then `None`
|
||||
/// is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::Scale;
|
||||
/// enum Mm {};
|
||||
/// enum Cm {};
|
||||
/// enum Em {};// Exameter = 10^18 meters
|
||||
///
|
||||
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
|
||||
/// let to_em: Scale<f32, Mm, Em> = Scale::new(10e20);
|
||||
///
|
||||
/// assert_eq!(to_mm.try_cast::<f32>(), Some(Scale::new(10.0)));
|
||||
/// // Integer to small to store that number
|
||||
/// assert_eq!(to_em.try_cast::<i32>(), None);
|
||||
/// ```
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Scale<NewT, Src, Dst>> {
|
||||
NumCast::from(self.get()).map(Scale::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Src, Dst> Scale<f32, Src, Dst> {
|
||||
/// Identity scaling, could be used to safely transit from one space to another.
|
||||
pub const ONE: Self = Scale(1.0, PhantomData);
|
||||
}
|
||||
|
||||
// scale0 * scale1
|
||||
// (A,B) * (B,C) = (A,C)
|
||||
impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
|
||||
type Output = Scale<T::Output, A, C>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: Scale<T, B, C>) -> Self::Output {
|
||||
Scale::new(self.0 * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 + scale1
|
||||
impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> {
|
||||
type Output = Scale<T::Output, Src, Dst>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Scale<T, Src, Dst>) -> Self::Output {
|
||||
Scale::new(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 - scale1
|
||||
impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> {
|
||||
type Output = Scale<T::Output, Src, Dst>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output {
|
||||
Scale::new(self.0 - other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
|
||||
// https://github.com/rust-lang/rust/issues/26925
|
||||
|
||||
impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> {
|
||||
fn eq(&self, other: &Scale<T, Src, Dst>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
|
||||
|
||||
impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
|
||||
fn clone(&self) -> Scale<T, Src, Dst> {
|
||||
Scale::new(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<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 Scale<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
|
||||
fn default() -> Self {
|
||||
Self::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: One, Src, Dst> One for Scale<T, Src, Dst> {
|
||||
#[inline]
|
||||
fn one() -> Self {
|
||||
Scale::new(T::one())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Scale;
|
||||
|
||||
enum Inch {}
|
||||
enum Cm {}
|
||||
enum Mm {}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
|
||||
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
|
||||
|
||||
let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inv();
|
||||
assert_eq!(mm_per_cm.get(), 10.0);
|
||||
|
||||
let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
|
||||
assert_eq!(one.get(), 1.0);
|
||||
|
||||
let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm;
|
||||
assert_eq!(one.get(), 1.0);
|
||||
|
||||
let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
|
||||
// mm cm cm
|
||||
// ---- x ---- = ----
|
||||
// inch mm inch
|
||||
assert_eq!(cm_per_inch, Scale::new(2.54));
|
||||
|
||||
let a: Scale<isize, Inch, Inch> = Scale::new(2);
|
||||
let b: Scale<isize, Inch, Inch> = Scale::new(3);
|
||||
assert_ne!(a, b);
|
||||
assert_eq!(a, a.clone());
|
||||
assert_eq!(a.clone() + b.clone(), Scale::new(5));
|
||||
assert_eq!(a - b, Scale::new(-1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,438 @@
|
|||
// 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 crate::length::Length;
|
||||
use crate::num::Zero;
|
||||
use crate::scale::Scale;
|
||||
use crate::Vector2D;
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
use core::fmt;
|
||||
use core::hash::Hash;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Neg};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding,
|
||||
/// and margins in CSS, optionally tagged with a unit.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
|
||||
)]
|
||||
pub struct SideOffsets2D<T, U> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
#[doc(hidden)]
|
||||
pub _unit: PhantomData<U>,
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
|
||||
|
||||
impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
SideOffsets2D {
|
||||
top: self.top.clone(),
|
||||
right: self.right.clone(),
|
||||
bottom: self.bottom.clone(),
|
||||
left: self.left.clone(),
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
|
||||
|
||||
impl<T, U> PartialEq for SideOffsets2D<T, U>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.top == other.top
|
||||
&& self.right == other.right
|
||||
&& self.bottom == other.bottom
|
||||
&& self.left == other.left
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Hash for SideOffsets2D<T, U>
|
||||
where
|
||||
T: Hash,
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
|
||||
self.top.hash(h);
|
||||
self.right.hash(h);
|
||||
self.bottom.hash(h);
|
||||
self.left.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"({:?},{:?},{:?},{:?})",
|
||||
self.top, self.right, self.bottom, self.left
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for SideOffsets2D<T, U> {
|
||||
fn default() -> Self {
|
||||
SideOffsets2D {
|
||||
top: Default::default(),
|
||||
right: Default::default(),
|
||||
bottom: Default::default(),
|
||||
left: Default::default(),
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> SideOffsets2D<T, U> {
|
||||
/// Constructor taking a scalar for each side.
|
||||
///
|
||||
/// Sides are specified in top-right-bottom-left order following
|
||||
/// CSS's convention.
|
||||
pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
|
||||
SideOffsets2D {
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor taking a typed Length for each side.
|
||||
///
|
||||
/// Sides are specified in top-right-bottom-left order following
|
||||
/// CSS's convention.
|
||||
pub fn from_lengths(
|
||||
top: Length<T, U>,
|
||||
right: Length<T, U>,
|
||||
bottom: Length<T, U>,
|
||||
left: Length<T, U>,
|
||||
) -> Self {
|
||||
SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
|
||||
}
|
||||
|
||||
/// Construct side offsets from min and a max vector offsets.
|
||||
///
|
||||
/// The outer rect of the resulting side offsets is equivalent to translating
|
||||
/// a rectangle's upper-left corner with the min vector and translating the
|
||||
/// bottom-right corner with the max vector.
|
||||
pub fn from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
|
||||
where
|
||||
T: Neg<Output = T>,
|
||||
{
|
||||
SideOffsets2D {
|
||||
left: -min.x,
|
||||
top: -min.y,
|
||||
right: max.x,
|
||||
bottom: max.y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct side offsets from min and a max vector offsets.
|
||||
///
|
||||
/// The inner rect of the resulting side offsets is equivalent to translating
|
||||
/// a rectangle's upper-left corner with the min vector and translating the
|
||||
/// bottom-right corner with the max vector.
|
||||
pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
|
||||
where
|
||||
T: Neg<Output = T>,
|
||||
{
|
||||
SideOffsets2D {
|
||||
left: min.x,
|
||||
top: min.y,
|
||||
right: -max.x,
|
||||
bottom: -max.y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if all side offsets are zero.
|
||||
pub fn is_zero(&self) -> bool
|
||||
where
|
||||
T: Zero + PartialEq,
|
||||
{
|
||||
let zero = T::zero();
|
||||
self.top == zero && self.right == zero && self.bottom == zero && self.left == zero
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> SideOffsets2D<T, U> {
|
||||
/// Constructor setting the same value to all sides, taking a scalar value directly.
|
||||
pub fn new_all_same(all: T) -> Self {
|
||||
SideOffsets2D::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 {
|
||||
SideOffsets2D::new_all_same(all.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> SideOffsets2D<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
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Add for SideOffsets2D<T, U>
|
||||
where
|
||||
T: Add<T, Output = T>,
|
||||
{
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
SideOffsets2D::new(
|
||||
self.top + other.top,
|
||||
self.right + other.right,
|
||||
self.bottom + other.bottom,
|
||||
self.left + other.left,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> SideOffsets2D<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for SideOffsets2D<T, U> {
|
||||
type Output = SideOffsets2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top * scale.clone(),
|
||||
self.right * scale.clone(),
|
||||
self.bottom * scale.clone(),
|
||||
self.left * scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: T) {
|
||||
self.top *= other.clone();
|
||||
self.right *= other.clone();
|
||||
self.bottom *= other.clone();
|
||||
self.left *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
|
||||
type Output = SideOffsets2D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top * scale.0.clone(),
|
||||
self.right * scale.0.clone(),
|
||||
self.bottom * scale.0.clone(),
|
||||
self.left * scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self *= other.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for SideOffsets2D<T, U> {
|
||||
type Output = SideOffsets2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top / scale.clone(),
|
||||
self.right / scale.clone(),
|
||||
self.bottom / scale.clone(),
|
||||
self.left / scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: T) {
|
||||
self.top /= other.clone();
|
||||
self.right /= other.clone();
|
||||
self.bottom /= other.clone();
|
||||
self.left /= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
|
||||
type Output = SideOffsets2D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top / scale.0.clone(),
|
||||
self.right / scale.0.clone(),
|
||||
self.bottom / scale.0.clone(),
|
||||
self.left / scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
|
||||
fn div_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self /= other.0;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_vectors() {
|
||||
use crate::{point2, vec2};
|
||||
type Box2D = crate::default::Box2D<i32>;
|
||||
|
||||
let b = Box2D {
|
||||
min: point2(10, 10),
|
||||
max: point2(20, 20),
|
||||
};
|
||||
|
||||
let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
|
||||
let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
|
||||
|
||||
assert_eq!(
|
||||
outer,
|
||||
Box2D {
|
||||
min: point2(9, 8),
|
||||
max: point2(23, 24)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
inner,
|
||||
Box2D {
|
||||
min: point2(11, 12),
|
||||
max: point2(17, 16)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_zero() {
|
||||
let s1: SideOffsets2D<f32, ()> = SideOffsets2D::new_all_same(0.0);
|
||||
assert!(s1.is_zero());
|
||||
|
||||
let s2: SideOffsets2D<f32, ()> = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
|
||||
assert!(!s2.is_zero());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ops {
|
||||
use crate::Scale;
|
||||
|
||||
pub enum Mm {}
|
||||
pub enum Cm {}
|
||||
|
||||
type SideOffsets2D<T> = crate::default::SideOffsets2D<T>;
|
||||
type SideOffsets2DMm<T> = crate::SideOffsets2D<T, Mm>;
|
||||
type SideOffsets2DCm<T> = crate::SideOffsets2D<T, Cm>;
|
||||
|
||||
#[test]
|
||||
fn test_mul_scalar() {
|
||||
let s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
|
||||
|
||||
let result = s * 3.0;
|
||||
|
||||
assert_eq!(result, SideOffsets2D::new(3.0, 6.0, 9.0, 12.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_assign_scalar() {
|
||||
let mut s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
|
||||
|
||||
s *= 2.0;
|
||||
|
||||
assert_eq!(s, SideOffsets2D::new(2.0, 4.0, 6.0, 8.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_scale() {
|
||||
let s = SideOffsets2DMm::new(0.0, 1.0, 3.0, 2.0);
|
||||
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
|
||||
|
||||
let result = s * cm_per_mm;
|
||||
|
||||
assert_eq!(result, SideOffsets2DCm::new(0.0, 0.1, 0.3, 0.2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_assign_scale() {
|
||||
let mut s = SideOffsets2DMm::new(2.0, 4.0, 6.0, 8.0);
|
||||
let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
|
||||
|
||||
s *= scale;
|
||||
|
||||
assert_eq!(s, SideOffsets2DMm::new(0.2, 0.4, 0.6, 0.8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_scalar() {
|
||||
let s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
|
||||
|
||||
let result = s / 10.0;
|
||||
|
||||
assert_eq!(result, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_assign_scalar() {
|
||||
let mut s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
|
||||
|
||||
s /= 10.0;
|
||||
|
||||
assert_eq!(s, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_scale() {
|
||||
let s = SideOffsets2DCm::new(0.1, 0.2, 0.3, 0.4);
|
||||
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
|
||||
|
||||
let result = s / cm_per_mm;
|
||||
|
||||
assert_eq!(result, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_assign_scale() {
|
||||
let mut s = SideOffsets2DMm::new(0.1, 0.2, 0.3, 0.4);
|
||||
let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
|
||||
|
||||
s /= scale;
|
||||
|
||||
assert_eq!(s, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,827 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(just_underscores_and_digits))]
|
||||
|
||||
use super::{UnknownUnit, Angle};
|
||||
#[cfg(feature = "mint")]
|
||||
use mint;
|
||||
use crate::num::{One, Zero};
|
||||
use crate::point::{Point2D, point2};
|
||||
use crate::vector::{Vector2D, vec2};
|
||||
use crate::rect::Rect;
|
||||
use crate::transform3d::Transform3D;
|
||||
use core::ops::{Add, Mul, Div, Sub};
|
||||
use core::marker::PhantomData;
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
use core::hash::{Hash};
|
||||
use crate::approxeq::ApproxEq;
|
||||
use crate::trig::Trig;
|
||||
use core::fmt;
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde;
|
||||
|
||||
/// A 2d transform stored as a 3 by 2 matrix in row-major order in memory.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
/// For example, `Transform2D<f32, WorldSpace, ScreenSpace>::transform_point4d`
|
||||
/// takes a `Point2D<f32, WorldSpace>` and returns a `Point2D<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.
|
||||
///
|
||||
/// These transforms are for working with _row vectors_, so the matrix math for transforming
|
||||
/// a vector is `v * T`. If your library is using column vectors, use `row_major` functions when you
|
||||
/// are asked for `column_major` representations and vice versa.
|
||||
#[repr(C)]
|
||||
pub struct Transform2D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T,
|
||||
pub m21: T, pub m22: T,
|
||||
pub m31: T, pub m32: T,
|
||||
#[doc(hidden)]
|
||||
pub _unit: PhantomData<(Src, Dst)>,
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for Transform2D<T, Src, Dst> {}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for Transform2D<T, Src, Dst> {
|
||||
fn clone(&self) -> Self {
|
||||
Transform2D {
|
||||
m11: self.m11.clone(),
|
||||
m12: self.m12.clone(),
|
||||
m21: self.m21.clone(),
|
||||
m22: self.m22.clone(),
|
||||
m31: self.m31.clone(),
|
||||
m32: self.m32.clone(),
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, Src, Dst> serde::Deserialize<'de> for Transform2D<T, Src, Dst>
|
||||
where T: serde::Deserialize<'de>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: serde::Deserializer<'de>
|
||||
{
|
||||
let (
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
) = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(Transform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Src, Dst> serde::Serialize for Transform2D<T, Src, Dst>
|
||||
where T: serde::Serialize
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer
|
||||
{
|
||||
(
|
||||
&self.m11, &self.m12,
|
||||
&self.m21, &self.m22,
|
||||
&self.m31, &self.m32,
|
||||
).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Eq for Transform2D<T, Src, Dst> where T: Eq {}
|
||||
|
||||
impl<T, Src, Dst> PartialEq for Transform2D<T, Src, Dst>
|
||||
where T: PartialEq
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.m11 == other.m11 &&
|
||||
self.m12 == other.m12 &&
|
||||
self.m21 == other.m21 &&
|
||||
self.m22 == other.m22 &&
|
||||
self.m31 == other.m31 &&
|
||||
self.m32 == other.m32
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Hash for Transform2D<T, Src, Dst>
|
||||
where T: Hash
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
|
||||
self.m11.hash(h);
|
||||
self.m12.hash(h);
|
||||
self.m21.hash(h);
|
||||
self.m22.hash(h);
|
||||
self.m31.hash(h);
|
||||
self.m32.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
||||
/// Create a transform specifying its matrix elements in row-major order.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `column_major`
|
||||
pub const fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
|
||||
Transform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its matrix elements in column-major order.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `row_major`
|
||||
pub const fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> Self {
|
||||
Transform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns true is this transform is approximately equal to the other one, using
|
||||
/// T's default epsilon value.
|
||||
///
|
||||
/// The same as [`ApproxEq::approx_eq()`] but available without importing trait.
|
||||
///
|
||||
/// [`ApproxEq::approx_eq()`]: ./approxeq/trait.ApproxEq.html#method.approx_eq
|
||||
#[inline]
|
||||
pub fn approx_eq(&self, other: &Self) -> bool
|
||||
where T : ApproxEq<T> {
|
||||
<Self as ApproxEq<T>>::approx_eq(&self, &other)
|
||||
}
|
||||
|
||||
/// Returns true is this transform is approximately equal to the other one, using
|
||||
/// a provided epsilon value.
|
||||
///
|
||||
/// The same as [`ApproxEq::approx_eq_eps()`] but available without importing trait.
|
||||
///
|
||||
/// [`ApproxEq::approx_eq_eps()`]: ./approxeq/trait.ApproxEq.html#method.approx_eq_eps
|
||||
#[inline]
|
||||
pub fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool
|
||||
where T : ApproxEq<T> {
|
||||
<Self as ApproxEq<T>>::approx_eq_eps(&self, &other, &eps)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Transform2D<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).
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `to_column_major_array`
|
||||
#[inline]
|
||||
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.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `to_row_major_array`
|
||||
#[inline]
|
||||
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.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), this will return column major arrays.
|
||||
#[inline]
|
||||
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.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), please provide a column major array.
|
||||
#[inline]
|
||||
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).
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), please provide a column major array.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Transform2D<T, UnknownUnit, UnknownUnit> {
|
||||
Transform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Transform2D<T, UnknownUnit, UnknownUnit>) -> Self {
|
||||
Transform2D::row_major(
|
||||
p.m11, p.m12,
|
||||
p.m21, p.m22,
|
||||
p.m31, p.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> Transform2D<T, NewSrc, Dst> {
|
||||
Transform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> Transform2D<T, Src, NewDst> {
|
||||
Transform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a 3D transform from the current transform
|
||||
pub fn to_3d(&self) -> Transform3D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
Transform3D::row_major_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Transform2D<NewT, Src, Dst> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Transform2D<NewT, 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(Transform2D::row_major(
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32
|
||||
))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
/// Create an identity matrix:
|
||||
///
|
||||
/// ```text
|
||||
/// 1 0
|
||||
/// 0 1
|
||||
/// 0 0
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
Self::create_translation(T::zero(), T::zero())
|
||||
}
|
||||
|
||||
/// 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
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
*self == Self::identity()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Methods for combining generic transformations
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to self * mat
|
||||
#[must_use]
|
||||
pub fn post_transform<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> {
|
||||
Transform2D::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.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to mat * self
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pre_transform<NewSrc>(&self, mat: &Transform2D<T, NewSrc, Src>) -> Transform2D<T, NewSrc, Dst> {
|
||||
mat.post_transform(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for creating and combining translation transformations
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
/// Create a 2d translation transform:
|
||||
///
|
||||
/// ```text
|
||||
/// 1 0
|
||||
/// 0 1
|
||||
/// x y
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn create_translation(x: T, y: T) -> Self {
|
||||
let _0 = || T::zero();
|
||||
let _1 = || T::one();
|
||||
|
||||
Self::row_major(
|
||||
_1(), _0(),
|
||||
_0(), _1(),
|
||||
x, y,
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a translation after self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn post_translate(&self, v: Vector2D<T, Dst>) -> Self
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
self.post_transform(&Transform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Applies a translation before self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pre_translate(&self, v: Vector2D<T, Src>) -> Self
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
self.pre_transform(&Transform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for creating and combining rotation transformations
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig,
|
||||
{
|
||||
/// Returns a rotation transform.
|
||||
#[inline]
|
||||
pub fn create_rotation(theta: Angle<T>) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
let cos = theta.get().cos();
|
||||
let sin = theta.get().sin();
|
||||
Transform2D::row_major(
|
||||
cos, _0 - sin,
|
||||
sin, cos,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn post_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.post_transform(&Transform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Applies a rotation before self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.pre_transform(&Transform2D::create_rotation(theta))
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for creating and combining scale transformations
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
||||
/// Create a 2d scale transform:
|
||||
///
|
||||
/// ```text
|
||||
/// x 0
|
||||
/// 0 y
|
||||
/// 0 0
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn create_scale(x: T, y: T) -> Self
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
let _0 = || Zero::zero();
|
||||
|
||||
Self::row_major(
|
||||
x, _0(),
|
||||
_0(), y,
|
||||
_0(), _0(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a scale after self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn post_scale(&self, x: T, y: T) -> Self
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
|
||||
{
|
||||
self.post_transform(&Transform2D::create_scale(x, y))
|
||||
}
|
||||
|
||||
/// Applies a scale before self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pre_scale(&self, x: T, y: T) -> Self
|
||||
where
|
||||
T: Copy + Mul<Output = T>,
|
||||
{
|
||||
Transform2D::row_major(
|
||||
self.m11 * x, self.m12 * x,
|
||||
self.m21 * y, self.m22 * y,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for apply transformations to objects
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Returns the given point transformed by this transform.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to `p * self`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> {
|
||||
Point2D::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.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to `v * self`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<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]
|
||||
#[must_use]
|
||||
pub fn transform_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst>
|
||||
where
|
||||
T: Sub<Output = T> + Zero + PartialOrd,
|
||||
{
|
||||
let min = rect.min();
|
||||
let max = rect.max();
|
||||
Rect::from_points(&[
|
||||
self.transform_point(min),
|
||||
self.transform_point(max),
|
||||
self.transform_point(point2(max.x, min.y)),
|
||||
self.transform_point(point2(min.x, max.y)),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + PartialEq + Zero + One,
|
||||
{
|
||||
/// Computes and returns the determinant of this transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m11 * self.m22 - self.m12 * self.m21
|
||||
}
|
||||
|
||||
/// Returns whether it is possible to compute the inverse transform.
|
||||
#[inline]
|
||||
pub fn is_invertible(&self) -> bool {
|
||||
self.determinant() != Zero::zero()
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
#[must_use]
|
||||
pub fn inverse(&self) -> Option<Transform2D<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(Transform2D::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),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl <T, Src, Dst> Default for Transform2D<T, Src, Dst>
|
||||
where T: Zero + One
|
||||
{
|
||||
/// Returns the [identity transform](#method.identity).
|
||||
fn default() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApproxEq<T>, Src, Dst> ApproxEq<T> for Transform2D<T, Src, Dst> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> T { T::approx_epsilon() }
|
||||
|
||||
/// Returns true is this transform is approximately equal to the other one, using
|
||||
/// a provided epsilon value.
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
|
||||
self.m11.approx_eq_eps(&other.m11, eps) && self.m12.approx_eq_eps(&other.m12, eps) &&
|
||||
self.m21.approx_eq_eps(&other.m21, eps) && self.m22.approx_eq_eps(&other.m22, eps) &&
|
||||
self.m31.approx_eq_eps(&other.m31, eps) && self.m32.approx_eq_eps(&other.m32, eps)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> fmt::Debug for Transform2D<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(feature = "mint")]
|
||||
impl<T, Src, Dst> From<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> {
|
||||
fn from(m: mint::RowMatrix3x2<T>) -> Self {
|
||||
Transform2D {
|
||||
m11: m.x.x, m12: m.x.y,
|
||||
m21: m.y.x, m22: m.y.y,
|
||||
m31: m.z.x, m32: m.z.y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "mint")]
|
||||
impl<T, Src, Dst> Into<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> {
|
||||
fn into(self) -> mint::RowMatrix3x2<T> {
|
||||
mint::RowMatrix3x2 {
|
||||
x: mint::Vector2 { x: self.m11, y: self.m12 },
|
||||
y: mint::Vector2 { x: self.m21, y: self.m22 },
|
||||
z: mint::Vector2 { x: self.m31, y: self.m32 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::default;
|
||||
use crate::approxeq::ApproxEq;
|
||||
#[cfg(feature = "mint")]
|
||||
use mint;
|
||||
|
||||
use core::f32::consts::FRAC_PI_2;
|
||||
|
||||
type Mat = default::Transform2D<f32>;
|
||||
|
||||
fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mat::create_translation(1.0, 2.0);
|
||||
let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
|
||||
let t3 = Mat::identity().post_translate(vec2(1.0, 2.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point(Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_transform(&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_transform(&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]
|
||||
pub fn test_pre_post_scale() {
|
||||
let m = Mat::create_rotation(rad(FRAC_PI_2)).post_translate(vec2(6.0, 7.0));
|
||||
let s = Mat::create_scale(2.0, 3.0);
|
||||
assert_eq!(m.post_transform(&s), m.post_scale(2.0, 3.0));
|
||||
assert_eq!(m.pre_transform(&s), m.pre_scale(2.0, 3.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_transform(&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_transform(&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 = default::Transform2D::identity().post_scale(1.0, 2.0).post_translate(vec2(1.0, 2.0));
|
||||
let m2 = default::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_transform(&t).transform_point(a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.post_transform(&r).transform_point(a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.post_transform(&r).transform_point(a).approx_eq(&r.transform_point(t.transform_point(a))));
|
||||
|
||||
assert!(r.pre_transform(&t).transform_point(a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.pre_transform(&r).transform_point(a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.pre_transform(&r).transform_point(a).approx_eq(&t.transform_point(r.transform_point(a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use core::mem::size_of;
|
||||
assert_eq!(size_of::<default::Transform2D<f32>>(), 6*size_of::<f32>());
|
||||
assert_eq!(size_of::<default::Transform2D<f64>>(), 6*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = default::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));
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
#[test]
|
||||
pub fn test_mint() {
|
||||
let m1 = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let mm: mint::RowMatrix3x2<_> = m1.into();
|
||||
let m2 = Mat::from(mm);
|
||||
|
||||
assert_eq!(m1, m2);
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,824 @@
|
|||
// Copyright 2018 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::num::*;
|
||||
use crate::UnknownUnit;
|
||||
use crate::{point2, point3, vec2, vec3, Box2D, Box3D, Rect, Size2D};
|
||||
use crate::{Point2D, Point3D, Transform2D, Transform3D, Vector2D, Vector3D};
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
use core::fmt;
|
||||
use core::hash::Hash;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A 2d transformation from a space to another that can only express translations.
|
||||
///
|
||||
/// The main benefit of this type over a Vector2D is the ability to cast
|
||||
/// between a source and a destination spaces.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// use euclid::{Translation2D, Point2D, point2};
|
||||
/// struct ParentSpace;
|
||||
/// struct ChildSpace;
|
||||
/// type ScrollOffset = Translation2D<i32, ParentSpace, ChildSpace>;
|
||||
/// type ParentPoint = Point2D<i32, ParentSpace>;
|
||||
/// type ChildPoint = Point2D<i32, ChildSpace>;
|
||||
///
|
||||
/// let scrolling = ScrollOffset::new(0, 100);
|
||||
/// let p1: ParentPoint = point2(0, 0);
|
||||
/// let p2: ChildPoint = scrolling.transform_point(p1);
|
||||
/// ```
|
||||
///
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(
|
||||
serialize = "T: serde::Serialize",
|
||||
deserialize = "T: serde::Deserialize<'de>"
|
||||
))
|
||||
)]
|
||||
pub struct Translation2D<T, Src, Dst> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
#[doc(hidden)]
|
||||
pub _unit: PhantomData<(Src, Dst)>,
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for Translation2D<T, Src, Dst> {}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for Translation2D<T, Src, Dst> {
|
||||
fn clone(&self) -> Self {
|
||||
Translation2D {
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Eq for Translation2D<T, Src, Dst> where T: Eq {}
|
||||
|
||||
impl<T, Src, Dst> PartialEq for Translation2D<T, Src, Dst>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.x == other.x && self.y == other.y
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Hash for Translation2D<T, Src, Dst>
|
||||
where
|
||||
T: Hash,
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
|
||||
self.x.hash(h);
|
||||
self.y.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Translation2D<T, Src, Dst> {
|
||||
#[inline]
|
||||
pub const fn new(x: T, y: T) -> Self {
|
||||
Translation2D {
|
||||
x,
|
||||
y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates no-op translation (both `x` and `y` is `zero()`).
|
||||
#[inline]
|
||||
pub fn identity() -> Self
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
Self::new(T::zero(), T::zero())
|
||||
}
|
||||
|
||||
/// Check if translation does nothing (both x and y is `zero()`).
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::default::Translation2D;
|
||||
///
|
||||
/// assert_eq!(Translation2D::<f32>::identity().is_identity(), true);
|
||||
/// assert_eq!(Translation2D::new(0, 0).is_identity(), true);
|
||||
/// assert_eq!(Translation2D::new(1, 0).is_identity(), false);
|
||||
/// assert_eq!(Translation2D::new(0, 1).is_identity(), false);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> bool
|
||||
where
|
||||
T: Zero + PartialEq,
|
||||
{
|
||||
let _0 = T::zero();
|
||||
self.x == _0 && self.y == _0
|
||||
}
|
||||
|
||||
/// No-op, just cast the unit.
|
||||
#[inline]
|
||||
pub fn transform_size(&self, s: Size2D<T, Src>) -> Size2D<T, Dst> {
|
||||
Size2D::new(s.width, s.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Translation2D<T, Src, Dst> {
|
||||
/// Cast into a 2D vector.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> Vector2D<T, Src> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Cast into an array with x and y.
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
|
||||
/// Cast into a tuple with x and y.
|
||||
#[inline]
|
||||
pub fn to_tuple(&self) -> (T, T) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Translation2D<T, UnknownUnit, UnknownUnit> {
|
||||
Translation2D {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(t: &Translation2D<T, UnknownUnit, UnknownUnit>) -> Self {
|
||||
Translation2D {
|
||||
x: t.x,
|
||||
y: t.y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the matrix representation of this translation.
|
||||
#[inline]
|
||||
pub fn to_transform(&self) -> Transform2D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
/// Translate a point and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_point(&self, p: Point2D<T, Src>) -> Point2D<T::Output, Dst>
|
||||
where
|
||||
T: Add,
|
||||
{
|
||||
point2(p.x + self.x, p.y + self.y)
|
||||
}
|
||||
|
||||
/// Translate a rectangle and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T::Output, Dst>
|
||||
where
|
||||
T: Add<Output = T>,
|
||||
{
|
||||
Rect {
|
||||
origin: self.transform_point(r.origin),
|
||||
size: self.transform_size(r.size),
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a 2D box and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_box(&self, r: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
|
||||
where
|
||||
T: Add,
|
||||
{
|
||||
Box2D {
|
||||
min: self.transform_point(r.min),
|
||||
max: self.transform_point(r.max),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the inverse transformation.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> Translation2D<T::Output, Dst, Src>
|
||||
where
|
||||
T: Neg,
|
||||
{
|
||||
Translation2D::new(-self.x, -self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Add, Src, Dst1, Dst2> Add<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst1> {
|
||||
type Output = Translation2D<T::Output, Src, Dst2>;
|
||||
|
||||
fn add(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output {
|
||||
Translation2D::new(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AddAssign, Src, Dst> AddAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> {
|
||||
fn add_assign(&mut self, other: Translation2D<T, Dst, Dst>) {
|
||||
self.x += other.x;
|
||||
self.y += other.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sub, Src, Dst1, Dst2> Sub<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst2> {
|
||||
type Output = Translation2D<T::Output, Src, Dst1>;
|
||||
|
||||
fn sub(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output {
|
||||
Translation2D::new(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SubAssign, Src, Dst> SubAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> {
|
||||
fn sub_assign(&mut self, other: Translation2D<T, Dst, Dst>) {
|
||||
self.x -= other.x;
|
||||
self.y -= other.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> From<Vector2D<T, Src>> for Translation2D<T, Src, Dst> {
|
||||
fn from(v: Vector2D<T, Src>) -> Self {
|
||||
Translation2D::new(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Into<Vector2D<T, Src>> for Translation2D<T, Src, Dst> {
|
||||
fn into(self) -> Vector2D<T, Src> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Into<Transform2D<T, Src, Dst>> for Translation2D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
fn into(self) -> Transform2D<T, Src, Dst> {
|
||||
Transform2D::create_translation(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Default for Translation2D<T, Src, Dst>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation2D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Translation({:?},{:?})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for Translation2D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
/// A 3d transformation from a space to another that can only express translations.
|
||||
///
|
||||
/// The main benefit of this type over a Vector3D is the ability to cast
|
||||
/// between a source and a destination spaces.
|
||||
#[repr(C)]
|
||||
pub struct Translation3D<T, Src, Dst> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
#[doc(hidden)]
|
||||
pub _unit: PhantomData<(Src, Dst)>,
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for Translation3D<T, Src, Dst> {}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for Translation3D<T, Src, Dst> {
|
||||
fn clone(&self) -> Self {
|
||||
Translation3D {
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
z: self.z.clone(),
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, Src, Dst> serde::Deserialize<'de> for Translation3D<T, Src, Dst>
|
||||
where
|
||||
T: serde::Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let (x, y, z) = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(Translation3D {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
_unit: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Src, Dst> serde::Serialize for Translation3D<T, Src, Dst>
|
||||
where
|
||||
T: serde::Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
(&self.x, &self.y, &self.z).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Eq for Translation3D<T, Src, Dst> where T: Eq {}
|
||||
|
||||
impl<T, Src, Dst> PartialEq for Translation3D<T, Src, Dst>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.x == other.x && self.y == other.y && self.z == other.z
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Hash for Translation3D<T, Src, Dst>
|
||||
where
|
||||
T: Hash,
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
|
||||
self.x.hash(h);
|
||||
self.y.hash(h);
|
||||
self.z.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Translation3D<T, Src, Dst> {
|
||||
#[inline]
|
||||
pub const fn new(x: T, y: T, z: T) -> Self {
|
||||
Translation3D {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates no-op translation (`x`, `y` and `z` is `zero()`).
|
||||
#[inline]
|
||||
pub fn identity() -> Self
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
Translation3D::new(T::zero(), T::zero(), T::zero())
|
||||
}
|
||||
|
||||
/// Check if translation does nothing (`x`, `y` and `z` is `zero()`).
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::default::Translation3D;
|
||||
///
|
||||
/// assert_eq!(Translation3D::<f32>::identity().is_identity(), true);
|
||||
/// assert_eq!(Translation3D::new(0, 0, 0).is_identity(), true);
|
||||
/// assert_eq!(Translation3D::new(1, 0, 0).is_identity(), false);
|
||||
/// assert_eq!(Translation3D::new(0, 1, 0).is_identity(), false);
|
||||
/// assert_eq!(Translation3D::new(0, 0, 1).is_identity(), false);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> bool
|
||||
where
|
||||
T: Zero + PartialEq,
|
||||
{
|
||||
let _0 = T::zero();
|
||||
self.x == _0 && self.y == _0 && self.z == _0
|
||||
}
|
||||
|
||||
/// No-op, just cast the unit.
|
||||
#[inline]
|
||||
pub fn transform_size(self, s: Size2D<T, Src>) -> Size2D<T, Dst> {
|
||||
Size2D::new(s.width, s.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Translation3D<T, Src, Dst> {
|
||||
/// Cast into a 3D vector.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> Vector3D<T, Src> {
|
||||
vec3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Cast into an array with x, y and z.
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] {
|
||||
[self.x, self.y, self.z]
|
||||
}
|
||||
|
||||
/// Cast into a tuple with x, y and z.
|
||||
#[inline]
|
||||
pub fn to_tuple(&self) -> (T, T, T) {
|
||||
(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Translation3D<T, UnknownUnit, UnknownUnit> {
|
||||
Translation3D {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
z: self.z,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(t: &Translation3D<T, UnknownUnit, UnknownUnit>) -> Self {
|
||||
Translation3D {
|
||||
x: t.x,
|
||||
y: t.y,
|
||||
z: t.z,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the matrix representation of this translation.
|
||||
#[inline]
|
||||
pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
/// Translate a point and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_point3d(&self, p: &Point3D<T, Src>) -> Point3D<T::Output, Dst>
|
||||
where
|
||||
T: Add,
|
||||
{
|
||||
point3(p.x + self.x, p.y + self.y, p.z + self.z)
|
||||
}
|
||||
|
||||
/// Translate a point and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_point2d(&self, p: &Point2D<T, Src>) -> Point2D<T::Output, Dst>
|
||||
where
|
||||
T: Add,
|
||||
{
|
||||
point2(p.x + self.x, p.y + self.y)
|
||||
}
|
||||
|
||||
/// Translate a 2D box and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_box2d(&self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
|
||||
where
|
||||
T: Add,
|
||||
{
|
||||
Box2D {
|
||||
min: self.transform_point2d(&b.min),
|
||||
max: self.transform_point2d(&b.max),
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a 3D box and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_box3d(&self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
|
||||
where
|
||||
T: Add,
|
||||
{
|
||||
Box3D {
|
||||
min: self.transform_point3d(&b.min),
|
||||
max: self.transform_point3d(&b.max),
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a rectangle and cast its unit.
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T, Dst>
|
||||
where
|
||||
T: Add<Output = T>,
|
||||
{
|
||||
Rect {
|
||||
origin: self.transform_point2d(&r.origin),
|
||||
size: self.transform_size(r.size),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the inverse transformation.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> Translation3D<T::Output, Dst, Src>
|
||||
where
|
||||
T: Neg,
|
||||
{
|
||||
Translation3D::new(-self.x, -self.y, -self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Add, Src, Dst1, Dst2> Add<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst1> {
|
||||
type Output = Translation3D<T::Output, Src, Dst2>;
|
||||
|
||||
fn add(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output {
|
||||
Translation3D::new(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AddAssign, Src, Dst> AddAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> {
|
||||
fn add_assign(&mut self, other: Translation3D<T, Dst, Dst>) {
|
||||
self.x += other.x;
|
||||
self.y += other.y;
|
||||
self.z += other.z;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sub, Src, Dst1, Dst2> Sub<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst2> {
|
||||
type Output = Translation3D<T::Output, Src, Dst1>;
|
||||
|
||||
fn sub(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output {
|
||||
Translation3D::new(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SubAssign, Src, Dst> SubAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> {
|
||||
fn sub_assign(&mut self, other: Translation3D<T, Dst, Dst>) {
|
||||
self.x -= other.x;
|
||||
self.y -= other.y;
|
||||
self.z -= other.z;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> From<Vector3D<T, Src>> for Translation3D<T, Src, Dst> {
|
||||
fn from(v: Vector3D<T, Src>) -> Self {
|
||||
Translation3D::new(v.x, v.y, v.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Into<Vector3D<T, Src>> for Translation3D<T, Src, Dst> {
|
||||
fn into(self) -> Vector3D<T, Src> {
|
||||
vec3(self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Into<Transform3D<T, Src, Dst>> for Translation3D<T, Src, Dst>
|
||||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
fn into(self) -> Transform3D<T, Src, Dst> {
|
||||
Transform3D::create_translation(self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Default for Translation3D<T, Src, Dst>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation3D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Translation({:?},{:?},{:?})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for Translation3D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod _2d {
|
||||
#[test]
|
||||
fn simple() {
|
||||
use crate::{rect, Rect, Translation2D};
|
||||
|
||||
struct A;
|
||||
struct B;
|
||||
|
||||
type Translation = Translation2D<i32, A, B>;
|
||||
type SrcRect = Rect<i32, A>;
|
||||
type DstRect = Rect<i32, B>;
|
||||
|
||||
let tx = Translation::new(10, -10);
|
||||
let r1: SrcRect = rect(10, 20, 30, 40);
|
||||
let r2: DstRect = tx.transform_rect(&r1);
|
||||
assert_eq!(r2, rect(20, 10, 30, 40));
|
||||
|
||||
let inv_tx = tx.inverse();
|
||||
assert_eq!(inv_tx.transform_rect(&r2), r1);
|
||||
|
||||
assert!((tx + inv_tx).is_identity());
|
||||
}
|
||||
|
||||
/// Operation tests
|
||||
mod ops {
|
||||
use crate::default::Translation2D;
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let t1 = Translation2D::new(1.0, 2.0);
|
||||
let t2 = Translation2D::new(3.0, 4.0);
|
||||
assert_eq!(t1 + t2, Translation2D::new(4.0, 6.0));
|
||||
|
||||
let t1 = Translation2D::new(1.0, 2.0);
|
||||
let t2 = Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t1 + t2, Translation2D::new(1.0, 2.0));
|
||||
|
||||
let t1 = Translation2D::new(1.0, 2.0);
|
||||
let t2 = Translation2D::new(-3.0, -4.0);
|
||||
assert_eq!(t1 + t2, Translation2D::new(-2.0, -2.0));
|
||||
|
||||
let t1 = Translation2D::new(0.0, 0.0);
|
||||
let t2 = Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t1 + t2, Translation2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_assign() {
|
||||
let mut t = Translation2D::new(1.0, 2.0);
|
||||
t += Translation2D::new(3.0, 4.0);
|
||||
assert_eq!(t, Translation2D::new(4.0, 6.0));
|
||||
|
||||
let mut t = Translation2D::new(1.0, 2.0);
|
||||
t += Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t, Translation2D::new(1.0, 2.0));
|
||||
|
||||
let mut t = Translation2D::new(1.0, 2.0);
|
||||
t += Translation2D::new(-3.0, -4.0);
|
||||
assert_eq!(t, Translation2D::new(-2.0, -2.0));
|
||||
|
||||
let mut t = Translation2D::new(0.0, 0.0);
|
||||
t += Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t, Translation2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let t1 = Translation2D::new(1.0, 2.0);
|
||||
let t2 = Translation2D::new(3.0, 4.0);
|
||||
assert_eq!(t1 - t2, Translation2D::new(-2.0, -2.0));
|
||||
|
||||
let t1 = Translation2D::new(1.0, 2.0);
|
||||
let t2 = Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t1 - t2, Translation2D::new(1.0, 2.0));
|
||||
|
||||
let t1 = Translation2D::new(1.0, 2.0);
|
||||
let t2 = Translation2D::new(-3.0, -4.0);
|
||||
assert_eq!(t1 - t2, Translation2D::new(4.0, 6.0));
|
||||
|
||||
let t1 = Translation2D::new(0.0, 0.0);
|
||||
let t2 = Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t1 - t2, Translation2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub_assign() {
|
||||
let mut t = Translation2D::new(1.0, 2.0);
|
||||
t -= Translation2D::new(3.0, 4.0);
|
||||
assert_eq!(t, Translation2D::new(-2.0, -2.0));
|
||||
|
||||
let mut t = Translation2D::new(1.0, 2.0);
|
||||
t -= Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t, Translation2D::new(1.0, 2.0));
|
||||
|
||||
let mut t = Translation2D::new(1.0, 2.0);
|
||||
t -= Translation2D::new(-3.0, -4.0);
|
||||
assert_eq!(t, Translation2D::new(4.0, 6.0));
|
||||
|
||||
let mut t = Translation2D::new(0.0, 0.0);
|
||||
t -= Translation2D::new(0.0, 0.0);
|
||||
assert_eq!(t, Translation2D::new(0.0, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod _3d {
|
||||
#[test]
|
||||
fn simple() {
|
||||
use crate::{point3, Point3D, Translation3D};
|
||||
|
||||
struct A;
|
||||
struct B;
|
||||
|
||||
type Translation = Translation3D<i32, A, B>;
|
||||
type SrcPoint = Point3D<i32, A>;
|
||||
type DstPoint = Point3D<i32, B>;
|
||||
|
||||
let tx = Translation::new(10, -10, 100);
|
||||
let p1: SrcPoint = point3(10, 20, 30);
|
||||
let p2: DstPoint = tx.transform_point3d(&p1);
|
||||
assert_eq!(p2, point3(20, 10, 130));
|
||||
|
||||
let inv_tx = tx.inverse();
|
||||
assert_eq!(inv_tx.transform_point3d(&p2), p1);
|
||||
|
||||
assert!((tx + inv_tx).is_identity());
|
||||
}
|
||||
|
||||
/// Operation tests
|
||||
mod ops {
|
||||
use crate::default::Translation3D;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let t1 = Translation3D::new(1.0, 2.0, 3.0);
|
||||
let t2 = Translation3D::new(4.0, 5.0, 6.0);
|
||||
assert_eq!(t1 + t2, Translation3D::new(5.0, 7.0, 9.0));
|
||||
|
||||
let t1 = Translation3D::new(1.0, 2.0, 3.0);
|
||||
let t2 = Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t1 + t2, Translation3D::new(1.0, 2.0, 3.0));
|
||||
|
||||
let t1 = Translation3D::new(1.0, 2.0, 3.0);
|
||||
let t2 = Translation3D::new(-4.0, -5.0, -6.0);
|
||||
assert_eq!(t1 + t2, Translation3D::new(-3.0, -3.0, -3.0));
|
||||
|
||||
let t1 = Translation3D::new(0.0, 0.0, 0.0);
|
||||
let t2 = Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t1 + t2, Translation3D::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_assign() {
|
||||
let mut t = Translation3D::new(1.0, 2.0, 3.0);
|
||||
t += Translation3D::new(4.0, 5.0, 6.0);
|
||||
assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0));
|
||||
|
||||
let mut t = Translation3D::new(1.0, 2.0, 3.0);
|
||||
t += Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0));
|
||||
|
||||
let mut t = Translation3D::new(1.0, 2.0, 3.0);
|
||||
t += Translation3D::new(-4.0, -5.0, -6.0);
|
||||
assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0));
|
||||
|
||||
let mut t = Translation3D::new(0.0, 0.0, 0.0);
|
||||
t += Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let t1 = Translation3D::new(1.0, 2.0, 3.0);
|
||||
let t2 = Translation3D::new(4.0, 5.0, 6.0);
|
||||
assert_eq!(t1 - t2, Translation3D::new(-3.0, -3.0, -3.0));
|
||||
|
||||
let t1 = Translation3D::new(1.0, 2.0, 3.0);
|
||||
let t2 = Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t1 - t2, Translation3D::new(1.0, 2.0, 3.0));
|
||||
|
||||
let t1 = Translation3D::new(1.0, 2.0, 3.0);
|
||||
let t2 = Translation3D::new(-4.0, -5.0, -6.0);
|
||||
assert_eq!(t1 - t2, Translation3D::new(5.0, 7.0, 9.0));
|
||||
|
||||
let t1 = Translation3D::new(0.0, 0.0, 0.0);
|
||||
let t2 = Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t1 - t2, Translation3D::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub_assign() {
|
||||
let mut t = Translation3D::new(1.0, 2.0, 3.0);
|
||||
t -= Translation3D::new(4.0, 5.0, 6.0);
|
||||
assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0));
|
||||
|
||||
let mut t = Translation3D::new(1.0, 2.0, 3.0);
|
||||
t -= Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0));
|
||||
|
||||
let mut t = Translation3D::new(1.0, 2.0, 3.0);
|
||||
t -= Translation3D::new(-4.0, -5.0, -6.0);
|
||||
assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0));
|
||||
|
||||
let mut t = Translation3D::new(0.0, 0.0, 0.0);
|
||||
t -= Translation3D::new(0.0, 0.0, 0.0);
|
||||
assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Trait for basic trigonometry functions, so they can be used on generic numeric types
|
||||
pub trait Trig {
|
||||
fn sin(self) -> Self;
|
||||
fn cos(self) -> Self;
|
||||
fn tan(self) -> Self;
|
||||
fn fast_atan2(y: Self, x: Self) -> Self;
|
||||
fn degrees_to_radians(deg: Self) -> Self;
|
||||
fn radians_to_degrees(rad: Self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! trig {
|
||||
($ty:ident) => {
|
||||
impl Trig for $ty {
|
||||
#[inline]
|
||||
fn sin(self) -> $ty {
|
||||
num_traits::Float::sin(self)
|
||||
}
|
||||
#[inline]
|
||||
fn cos(self) -> $ty {
|
||||
num_traits::Float::cos(self)
|
||||
}
|
||||
#[inline]
|
||||
fn tan(self) -> $ty {
|
||||
num_traits::Float::tan(self)
|
||||
}
|
||||
|
||||
/// A slightly faster approximation of `atan2`.
|
||||
///
|
||||
/// Note that it does not deal with the case where both x and y are 0.
|
||||
#[inline]
|
||||
fn fast_atan2(y: $ty, x: $ty) -> $ty {
|
||||
// This macro is used with f32 and f64 and clippy warns about the extra
|
||||
// precision with f32.
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(excessive_precision))]
|
||||
|
||||
// See https://math.stackexchange.com/questions/1098487/atan2-faster-approximation#1105038
|
||||
use core::$ty::consts;
|
||||
let x_abs = num_traits::Float::abs(x);
|
||||
let y_abs = num_traits::Float::abs(y);
|
||||
let a = x_abs.min(y_abs) / x_abs.max(y_abs);
|
||||
let s = a * a;
|
||||
let mut result =
|
||||
((-0.046_496_474_9 * s + 0.159_314_22) * s - 0.327_622_764) * s * a + a;
|
||||
if y_abs > x_abs {
|
||||
result = consts::FRAC_PI_2 - result;
|
||||
}
|
||||
if x < 0.0 {
|
||||
result = consts::PI - result
|
||||
}
|
||||
if y < 0.0 {
|
||||
result = -result
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn degrees_to_radians(deg: Self) -> Self {
|
||||
deg.to_radians()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn radians_to_degrees(rad: Self) -> Self {
|
||||
rad.to_degrees()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
trig!(f32);
|
||||
trig!(f64);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +1 @@
|
|||
{"files":{"COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"430f53fbae7381f12155805f713f9b9bb08a9e9ffff3a3f4c887163fb4521e7d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/angle.rs":"b4b17c16bd135914a6e2ebad54b9cbb8ea475d5992bcb75c397b7813b6f7b8dd","src/approxeq.rs":"8ad9ab7336e1924b0543eb46f975367f4388a4dda91839b7cda7d21eec8991fb","src/approxord.rs":"f1b11ea7603b3dddb0002c7ded934816f5a8bb108e150028b49595b8e3382ac1","src/box2d.rs":"0f7ef877183e25e6b769fc91a0d814e0992b0e6d1c16df415ebbd364d2fd2cf7","src/box3d.rs":"54d6e59622957e784e611a23da9612671a530c61cd364313024d750f953a7564","src/homogen.rs":"3aa960fae9b817057013183f543cf2adc042f5867ece1ee78607e2c46bdbcfed","src/length.rs":"67ef01cfba3f70143c57afda39c8bf9990f29111a031a238de328753ccc2c8a4","src/lib.rs":"d0f1ddda70d8bf3913d9c2c49949dd21ced500cbc0c09d7d08c0aa1570f63df5","src/macros.rs":"3b475e84d00cceee6c7e96e9f2c97ba15d8dc7f4094efb82c5ed10bd60d86a64","src/nonempty.rs":"be5ac81321390d71447607de01811cc6420bcd48aa5274f7a298a99674621846","src/num.rs":"2478501dbe545a73a8b964f4713987b86df8ff0af3d1aba0571cbd00a4e9cbaa","src/point.rs":"6d07046f27dd7a18ba8db8988de82c67cfcdd1b8895a70730123338574e47d39","src/rect.rs":"1391359581ba891a35f390ec3900f1e34ce1e4dc5608fe27662655d3bf443d45","src/rigid.rs":"a9717aac4d254299dc3a60a2a32c5c5309044136702d96030533f7639adeb467","src/rotation.rs":"6cfc7f74a90236c53fa55b83eae373146f7b37a8c400f8f820ebfdbcca245245","src/scale.rs":"197d2d4ba5f6a4fe8d5f0b2b6684499a66727129ad27474b85b0d1ffecd38f05","src/side_offsets.rs":"11b84a7dc620cdf2f1877c82b534947ae337734b9075d34e61306be7f458d127","src/size.rs":"5d5703b464207ff362da650008972ca67fbbc79f7193d0d2a1c7a7873221c24c","src/transform2d.rs":"6b2ff21021d7aa1628585650cc66f18f1a5a6aff5f335ae1ec4f63616c54b3cf","src/transform3d.rs":"83ac57ab5a077127fa7eba401dae2e67f2c129a05081b5b5ed09a2166fa9703f","src/translation.rs":"7847306a3a081559f2839baff9d622e58c63fdcb3981c5ef410b6a8eb8e13ab8","src/trig.rs":"ab09a8503d04ac1d0d6848d074ac18a22d324184e5bb186bbd4287c977894886","src/vector.rs":"f895e029e273defac88b47fef4fb27f627553fb05b8d173cf8f75e28818cd580"},"package":"2bb7ef65b3777a325d1eeefefab5b6d4959da54747e33bd6258e789640f307ad"}
|
||||
{"files":{"COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"a86dfb641b12e15efe3e2b40a2061c27b9347b38a315700773d7af2c6acccc96","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/angle.rs":"b4b17c16bd135914a6e2ebad54b9cbb8ea475d5992bcb75c397b7813b6f7b8dd","src/approxeq.rs":"8ad9ab7336e1924b0543eb46f975367f4388a4dda91839b7cda7d21eec8991fb","src/approxord.rs":"f1b11ea7603b3dddb0002c7ded934816f5a8bb108e150028b49595b8e3382ac1","src/box2d.rs":"2566d13d8823c13eaab68bbeabe371fa507497d07b3638e5c501cd73cb2a5251","src/box3d.rs":"a96e6b4cbae217095fcbc997db9f7dfafe813d84856bc0a02abbbb6bc881eac7","src/homogen.rs":"57c08e6e5d0bf87c724eb074c624a6557170f9d695ef1ed1a4b2677069dbbaa4","src/length.rs":"6972f49203c0256287dbf5119124ad64e2cf001574995bfaed8cd21e4a19f17c","src/lib.rs":"ebf2023c1e1bd397a4ae135a14657d00757c9ad4094d32d65848a1d5e061df7a","src/macros.rs":"3b475e84d00cceee6c7e96e9f2c97ba15d8dc7f4094efb82c5ed10bd60d86a64","src/num.rs":"27ef5975c2149f4c1a4659ae1dd9fc86618579ec985361130ea9fdc891fcfae3","src/point.rs":"db1beb1e581261a8077669c0a69973c35100163e1b98e6b720b0af331a951345","src/rect.rs":"83ce34da75dc570d8f62700d283adfda419a98ea2d0fc691dedc4389c574dd46","src/rigid.rs":"e9b39039df2882d5b8d5a28305ddb40f7c59a9401af615b842f485a62af12217","src/rotation.rs":"6ed0b5793fdd3d557e98e9523e506786c337c98ae61a3f7773bbd934922803a9","src/scale.rs":"c26ad154ace1647196eb1a19c0c26c22c461322c643c22b9b260392443688299","src/side_offsets.rs":"b795816fe6cc027692bbb5704eebe48d8556619f837ce75de34ca6acf8502ef9","src/size.rs":"17002e23cc424c04d288f65e5f6493759ea7bfb2b0a749131c1a3561d2e3def4","src/transform2d.rs":"505f47b58a96a64804d027d21ebf28c75c80c7d2975760d6d2cadbefb7c4605c","src/transform3d.rs":"e8221391475208af1430ad3a432e972fe45cec68e10103a57af7197f11e034f8","src/translation.rs":"8500a10ed37447fbc41a4a1f554c08095f055c36b4e0a637d7c07505fe2570a2","src/trig.rs":"ab09a8503d04ac1d0d6848d074ac18a22d324184e5bb186bbd4287c977894886","src/vector.rs":"2f573f1587c8f119c45c8822c81d3fe5aa6113cad51be4d19eea73d8058b91dd"},"package":"7ab0e07e345fb061928646949fdf5fb888e5d75a57385e7f5856e45be289e745"}
|
|
@ -13,7 +13,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "euclid"
|
||||
version = "0.20.14"
|
||||
version = "0.22.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Geometry primitives"
|
||||
documentation = "https://docs.rs/euclid/"
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
use super::UnknownUnit;
|
||||
use crate::approxord::{max, min};
|
||||
use crate::nonempty::NonEmpty;
|
||||
use crate::num::*;
|
||||
use crate::point::{point2, Point2D};
|
||||
use crate::rect::Rect;
|
||||
|
@ -28,15 +27,15 @@ use core::fmt;
|
|||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Sub};
|
||||
|
||||
/// An axis aligned rectangle represented by its minimum and maximum coordinates.
|
||||
/// A 2d axis aligned rectangle represented by its minimum and maximum coordinates.
|
||||
///
|
||||
/// That struct is similar to the [`Rect`] struct, but stores rectangle as two corners
|
||||
/// # Representation
|
||||
///
|
||||
/// This struct is similar to [`Rect`], but stores rectangle as two endpoints
|
||||
/// instead of origin point and size. Such representation has several advantages over
|
||||
/// [`Rect`] representation:
|
||||
/// - Several operations are more efficient with `Box2D`, including [`intersection`],
|
||||
/// [`union`], and point-in-rect.
|
||||
/// - The representation is more symmetric, since it stores two quantities of the
|
||||
/// same kind (two points) rather than a point and a dimension (width/height).
|
||||
/// - The representation is less susceptible to overflow. With [`Rect`], computation
|
||||
/// of second point can overflow for a large range of values of origin and size.
|
||||
/// However, with `Box2D`, computation of [`size`] cannot overflow if the coordinates
|
||||
|
@ -45,8 +44,16 @@ use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Sub};
|
|||
/// A known disadvantage of `Box2D` is that translating the rectangle requires translating
|
||||
/// both points, whereas translating [`Rect`] only requires translating one point.
|
||||
///
|
||||
/// # Empty box
|
||||
///
|
||||
/// A box is considered empty (see [`is_empty`]) if any of the following is true:
|
||||
/// - it's area is empty,
|
||||
/// - it's area is negative (`min.x > max.x` or `min.y > max.y`),
|
||||
/// - it contains NaNs.
|
||||
///
|
||||
/// [`Rect`]: struct.Rect.html
|
||||
/// [`intersection`]: #method.intersection
|
||||
/// [`is_empty`]: #method.is_empty
|
||||
/// [`union`]: #method.union
|
||||
/// [`size`]: #method.size
|
||||
#[repr(C)]
|
||||
|
@ -92,16 +99,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Box2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Box2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Box2D(")?;
|
||||
fmt::Display::fmt(&self.min, f)?;
|
||||
write!(f, ", ")?;
|
||||
fmt::Display::fmt(&self.max, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U> {
|
||||
/// Constructor.
|
||||
#[inline]
|
||||
|
@ -123,9 +120,9 @@ where
|
|||
self.max.x < self.min.x || self.max.y < self.min.y
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero or negative.
|
||||
/// Returns true if the size is zero, negative or NaN.
|
||||
#[inline]
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
!(self.max.x > self.min.x && self.max.y > self.min.y)
|
||||
}
|
||||
|
||||
|
@ -151,7 +148,7 @@ where
|
|||
/// nonempty but this box is empty.
|
||||
#[inline]
|
||||
pub fn contains_box(&self, other: &Self) -> bool {
|
||||
other.is_empty_or_negative()
|
||||
other.is_empty()
|
||||
|| (self.min.x <= other.min.x
|
||||
&& other.max.x <= self.max.x
|
||||
&& self.min.y <= other.min.y
|
||||
|
@ -164,34 +161,38 @@ where
|
|||
T: Copy + PartialOrd,
|
||||
{
|
||||
#[inline]
|
||||
pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
|
||||
if self.is_empty_or_negative() {
|
||||
pub fn to_non_empty(&self) -> Option<Self> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(*self))
|
||||
}
|
||||
/// Computes the intersection of two boxes.
|
||||
///
|
||||
/// The result is a negative box if the boxes do not intersect.
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Self {
|
||||
Box2D {
|
||||
min: point2(max(self.min.x, other.min.x), max(self.min.y, other.min.y)),
|
||||
max: point2(min(self.max.x, other.max.x), min(self.max.y, other.max.y)),
|
||||
}
|
||||
Some(*self)
|
||||
}
|
||||
|
||||
/// Computes the intersection of two boxes, returning `None` if the boxes do not intersect.
|
||||
#[inline]
|
||||
pub fn try_intersection(&self, other: &Self) -> Option<NonEmpty<Self>> {
|
||||
let intersection = self.intersection(other);
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let b = self.intersection_unchecked(other);
|
||||
|
||||
if intersection.is_negative() {
|
||||
if b.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(intersection))
|
||||
Some(b)
|
||||
}
|
||||
|
||||
/// Computes the intersection of two boxes without check whether they do intersect.
|
||||
///
|
||||
/// The result is a negative box if the boxes do not intersect.
|
||||
/// This can be useful for computing the intersection of more than two boxes, as
|
||||
/// it is possible to chain multiple intersection_unchecked calls and check for
|
||||
/// empty/negative result at the end.
|
||||
#[inline]
|
||||
pub fn intersection_unchecked(&self, other: &Self) -> Self {
|
||||
Box2D {
|
||||
min: point2(max(self.min.x, other.min.x), max(self.min.y, other.min.y)),
|
||||
max: point2(min(self.max.x, other.max.x), min(self.max.y, other.max.y)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -372,79 +373,68 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box2D<T, U>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
/// Returns true if the size is zero.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.min.x == self.max.x || self.min.y == self.max.y
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Box2D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Box2D<T, U> {
|
||||
type Output = Box2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Box2D::new(self.min * scale.clone(), self.max * scale)
|
||||
Box2D::new(self.min * scale, self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Box2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self *= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Box2D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Box2D<T, U> {
|
||||
type Output = Box2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Box2D::new(self.min / scale.clone(), self.max / scale)
|
||||
Box2D::new(self.min / scale, self.max / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Box2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self /= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1> {
|
||||
type Output = Box2D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Box2D::new(self.min * scale.clone(), self.max * scale)
|
||||
Box2D::new(self.min * scale, self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Box2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min *= scale.clone();
|
||||
self.min *= scale;
|
||||
self.max *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2> {
|
||||
type Output = Box2D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Box2D::new(self.min / scale.clone(), self.max / scale)
|
||||
Box2D::new(self.min / scale, self.max / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Box2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min /= scale.clone();
|
||||
self.min /= scale;
|
||||
self.max /= scale;
|
||||
}
|
||||
}
|
||||
|
@ -673,7 +663,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_round() {
|
||||
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
|
||||
assert_eq!(b.min.x, -26.0);
|
||||
assert_eq!(b.min.x, -25.0);
|
||||
assert_eq!(b.min.y, -40.0);
|
||||
assert_eq!(b.max.x, 60.0);
|
||||
assert_eq!(b.max.y, 37.0);
|
||||
|
@ -742,10 +732,10 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
fn test_intersection_unchecked() {
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
|
||||
let b = b1.intersection(&b2);
|
||||
let b = b1.intersection_unchecked(&b2);
|
||||
assert_eq!(b.max.x, 10.0);
|
||||
assert_eq!(b.max.y, 20.0);
|
||||
assert_eq!(b.min.x, -10.0);
|
||||
|
@ -753,14 +743,14 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_intersection() {
|
||||
fn test_intersection() {
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_some());
|
||||
assert!(b1.intersection(&b2).is_some());
|
||||
|
||||
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
|
||||
let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_none());
|
||||
assert!(b1.intersection(&b2).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -818,11 +808,11 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_nan_empty_or_negative() {
|
||||
fn test_nan_empty() {
|
||||
use std::f32::NAN;
|
||||
assert!(Box2D { min: point2(NAN, 2.0), max: point2(1.0, 3.0) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(0.0, NAN), max: point2(1.0, 2.0) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(1.0, -2.0), max: point2(NAN, 2.0) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(1.0, -2.0), max: point2(0.0, NAN) }.is_empty_or_negative());
|
||||
assert!(Box2D { min: point2(NAN, 2.0), max: point2(1.0, 3.0) }.is_empty());
|
||||
assert!(Box2D { min: point2(0.0, NAN), max: point2(1.0, 2.0) }.is_empty());
|
||||
assert!(Box2D { min: point2(1.0, -2.0), max: point2(NAN, 2.0) }.is_empty());
|
||||
assert!(Box2D { min: point2(1.0, -2.0), max: point2(0.0, NAN) }.is_empty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
use super::UnknownUnit;
|
||||
use crate::approxord::{max, min};
|
||||
use crate::nonempty::NonEmpty;
|
||||
use crate::num::*;
|
||||
use crate::point::{point3, Point3D};
|
||||
use crate::scale::Scale;
|
||||
|
@ -70,16 +69,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Box3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Box3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Box3D(")?;
|
||||
fmt::Display::fmt(&self.min, f)?;
|
||||
write!(f, ", ")?;
|
||||
fmt::Display::fmt(&self.max, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U> {
|
||||
/// Constructor.
|
||||
#[inline]
|
||||
|
@ -101,9 +90,9 @@ where
|
|||
self.max.x < self.min.x || self.max.y < self.min.y || self.max.z < self.min.z
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero or negative.
|
||||
/// Returns true if the size is zero, negative or NaN.
|
||||
#[inline]
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
!(self.max.x > self.min.x && self.max.y > self.min.y && self.max.z > self.min.z)
|
||||
}
|
||||
|
||||
|
@ -135,7 +124,7 @@ where
|
|||
/// nonempty but this box3d is empty.
|
||||
#[inline]
|
||||
pub fn contains_box(&self, other: &Self) -> bool {
|
||||
other.is_empty_or_negative()
|
||||
other.is_empty()
|
||||
|| (self.min.x <= other.min.x
|
||||
&& other.max.x <= self.max.x
|
||||
&& self.min.y <= other.min.y
|
||||
|
@ -150,24 +139,26 @@ where
|
|||
T: Copy + PartialOrd,
|
||||
{
|
||||
#[inline]
|
||||
pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
|
||||
if self.is_empty_or_negative() {
|
||||
pub fn to_non_empty(&self) -> Option<Self> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(*self))
|
||||
Some(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_intersection(&self, other: &Self) -> Option<NonEmpty<Self>> {
|
||||
if !self.intersects(other) {
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let b = self.intersection_unchecked(other);
|
||||
|
||||
if b.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(self.intersection(other)))
|
||||
Some(b)
|
||||
}
|
||||
|
||||
pub fn intersection(&self, other: &Self) -> Self {
|
||||
pub fn intersection_unchecked(&self, other: &Self) -> Self {
|
||||
let intersection_min = Point3D::new(
|
||||
max(self.min.x, other.min.x),
|
||||
max(self.min.y, other.min.y),
|
||||
|
@ -375,35 +366,24 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, U> Box3D<T, U>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
/// Returns true if the volume is zero.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.min.x == self.max.x || self.min.y == self.max.y || self.min.z == self.max.z
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Box3D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Box3D<T, U> {
|
||||
type Output = Box3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Box3D::new(self.min * scale.clone(), self.max * scale)
|
||||
Box3D::new(self.min * scale, self.max * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Box3D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
self.min *= scale.clone();
|
||||
self.min *= scale;
|
||||
self.max *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Box3D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Box3D<T, U> {
|
||||
type Output = Box3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
|
@ -412,15 +392,15 @@ impl<T: Clone + Div, U> Div<T> for Box3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Box3D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
self.min /= scale.clone();
|
||||
self.min /= scale;
|
||||
self.max /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
|
||||
type Output = Box3D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
|
@ -429,7 +409,7 @@ impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min *= scale.clone();
|
||||
|
@ -437,7 +417,7 @@ impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
|
||||
type Output = Box3D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
|
@ -446,7 +426,7 @@ impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Box3D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.min /= scale.clone();
|
||||
|
@ -738,7 +718,7 @@ mod tests {
|
|||
fn test_round() {
|
||||
let b =
|
||||
Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round();
|
||||
assert!(b.min.x == -26.0);
|
||||
assert!(b.min.x == -25.0);
|
||||
assert!(b.min.y == -40.0);
|
||||
assert!(b.min.z == -71.0);
|
||||
assert!(b.max.x == 60.0);
|
||||
|
@ -795,10 +775,10 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
fn test_intersection_unchecked() {
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
let b = b1.intersection(&b2);
|
||||
let b = b1.intersection_unchecked(&b2);
|
||||
assert!(b.max.x == 10.0);
|
||||
assert!(b.max.y == 20.0);
|
||||
assert!(b.max.z == 20.0);
|
||||
|
@ -809,14 +789,14 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_intersection() {
|
||||
fn test_intersection() {
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_some());
|
||||
assert!(b1.intersection(&b2).is_some());
|
||||
|
||||
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(-10.0, 20.0, 20.0)]);
|
||||
let b2 = Box3D::from_points(&[point3(10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
|
||||
assert!(b1.try_intersection(&b2).is_none());
|
||||
assert!(b1.intersection(&b2).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -891,11 +871,11 @@ mod tests {
|
|||
#[test]
|
||||
fn test_nan_empty_or_negative() {
|
||||
use std::f32::NAN;
|
||||
assert!(Box3D { min: point3(NAN, 2.0, 1.0), max: point3(1.0, 3.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(0.0, NAN, 1.0), max: point3(1.0, 2.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, NAN), max: point3(3.0, 2.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(NAN, 2.0, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, NAN, 5.0) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, 1.0, NAN) }.is_empty_or_negative());
|
||||
assert!(Box3D { min: point3(NAN, 2.0, 1.0), max: point3(1.0, 3.0, 5.0) }.is_empty());
|
||||
assert!(Box3D { min: point3(0.0, NAN, 1.0), max: point3(1.0, 2.0, 5.0) }.is_empty());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, NAN), max: point3(3.0, 2.0, 5.0) }.is_empty());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(NAN, 2.0, 5.0) }.is_empty());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, NAN, 5.0) }.is_empty());
|
||||
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, 1.0, NAN) }.is_empty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U
|
|||
///
|
||||
/// Returns None if the point is on or behind the W=0 hemisphere.
|
||||
#[inline]
|
||||
pub fn to_point2d(&self) -> Option<Point2D<T, U>> {
|
||||
pub fn to_point2d(self) -> Option<Point2D<T, U>> {
|
||||
if self.w > T::zero() {
|
||||
Some(Point2D::new(self.x / self.w, self.y / self.w))
|
||||
} else {
|
||||
|
@ -132,7 +132,7 @@ impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U
|
|||
///
|
||||
/// Returns None if the point is on or behind the W=0 hemisphere.
|
||||
#[inline]
|
||||
pub fn to_point3d(&self) -> Option<Point3D<T, U>> {
|
||||
pub fn to_point3d(self) -> Option<Point3D<T, U>> {
|
||||
if self.w > T::zero() {
|
||||
Some(Point3D::new(
|
||||
self.x / self.w,
|
||||
|
@ -184,20 +184,6 @@ impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
fmt::Display::fmt(&self.x, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.y, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.z, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.w, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod homogeneous {
|
||||
use super::HomogeneousVector;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
use crate::approxeq::ApproxEq;
|
||||
use crate::num::Zero;
|
||||
use crate::scale::Scale;
|
||||
use crate::approxord::{max, min};
|
||||
|
||||
use crate::num::One;
|
||||
use core::cmp::Ordering;
|
||||
|
@ -82,15 +83,15 @@ impl<T, U> Length<T, U> {
|
|||
}
|
||||
|
||||
impl<T: Clone, U> Length<T, U> {
|
||||
/// Unpack the underlying value from the wrapper, cloning it.
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
/// Unpack the underlying value from the wrapper.
|
||||
pub fn get(self) -> T {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Length<T, V> {
|
||||
Length::new(self.0.clone())
|
||||
pub fn cast_unit<V>(self) -> Length<T, V> {
|
||||
Length::new(self.0)
|
||||
}
|
||||
|
||||
/// Linearly interpolate between this length and another length.
|
||||
|
@ -110,7 +111,7 @@ impl<T: Clone, U> Length<T, U> {
|
|||
/// assert_eq!(from.lerp(to, 2.0), Length::new(16.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self
|
||||
pub fn lerp(self, other: Self, t: T) -> Self
|
||||
where
|
||||
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
|
||||
{
|
||||
|
@ -119,16 +120,30 @@ impl<T: Clone, U> Length<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, U> Length<T, U> {
|
||||
/// Returns minimum between this length and another length.
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
min(self, other)
|
||||
}
|
||||
|
||||
/// Returns maximum between this length and another length.
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
max(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, U> Length<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Length<NewT, U> {
|
||||
pub fn cast<NewT: NumCast>(self) -> Length<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
/// Fallible cast from one numeric representation to another, preserving the units.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Length<NewT, U>> {
|
||||
NumCast::from(self.get()).map(Length::new)
|
||||
pub fn try_cast<NewT: NumCast>(self) -> Option<Length<NewT, U>> {
|
||||
NumCast::from(self.0).map(Length::new)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,12 +153,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Length<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Length<T, U> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
|
@ -358,19 +367,6 @@ mod tests {
|
|||
assert_eq!(variable_length.get(), 24.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_clones_length_value() {
|
||||
// Calling get returns a clone of the Length's value.
|
||||
// To test this, we need something clone-able - hence a vector.
|
||||
let mut length: Length<Vec<i32>, Inch> = Length::new(vec![1, 2, 3]);
|
||||
|
||||
let value = length.get();
|
||||
length.0.push(4);
|
||||
|
||||
assert_eq!(value, vec![1, 2, 3]);
|
||||
assert_eq!(length.get(), vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
|
|
|
@ -44,7 +44,6 @@ pub use crate::angle::Angle;
|
|||
pub use crate::box2d::Box2D;
|
||||
pub use crate::homogen::HomogeneousVector;
|
||||
pub use crate::length::Length;
|
||||
pub use crate::nonempty::NonEmpty;
|
||||
pub use crate::point::{point2, point3, Point2D, Point3D};
|
||||
pub use crate::scale::Scale;
|
||||
pub use crate::transform2d::Transform2D;
|
||||
|
@ -71,7 +70,6 @@ mod box2d;
|
|||
mod box3d;
|
||||
mod homogen;
|
||||
mod length;
|
||||
mod nonempty;
|
||||
pub mod num;
|
||||
mod point;
|
||||
mod rect;
|
||||
|
|
|
@ -70,34 +70,12 @@ 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 {
|
||||
num_traits::Float::round(self)
|
||||
(self + 0.5).floor()
|
||||
}
|
||||
}
|
||||
impl Floor for $ty {
|
||||
|
@ -114,14 +92,5 @@ macro_rules! num_float {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -107,16 +107,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Point2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Point2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "(")?;
|
||||
fmt::Display::fmt(&self.x, formatter)?;
|
||||
write!(formatter, ",")?;
|
||||
fmt::Display::fmt(&self.y, formatter)?;
|
||||
write!(formatter, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Point2D<T, U> {
|
||||
fn default() -> Self {
|
||||
Point2D::new(Default::default(), Default::default())
|
||||
|
@ -168,7 +158,7 @@ impl<T, U> Point2D<T, U> {
|
|||
impl<T: Copy, U> Point2D<T, U> {
|
||||
/// Create a 3d point from this one, using the specified z value.
|
||||
#[inline]
|
||||
pub fn extend(&self, z: T) -> Point3D<T, U> {
|
||||
pub fn extend(self, z: T) -> Point3D<T, U> {
|
||||
point3(self.x, self.y, z)
|
||||
}
|
||||
|
||||
|
@ -176,7 +166,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
///
|
||||
/// Equivalent to subtracting the origin from this point.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> Vector2D<T, U> {
|
||||
pub fn to_vector(self) -> Vector2D<T, U> {
|
||||
Vector2D {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
|
@ -197,7 +187,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// assert_eq!(point.yx(), point2(-8, 1));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn yx(&self) -> Self {
|
||||
pub fn yx(self) -> Self {
|
||||
point2(self.y, self.x)
|
||||
}
|
||||
|
||||
|
@ -215,7 +205,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// assert_eq!(point.y, point.to_untyped().y);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point2D<T, UnknownUnit> {
|
||||
pub fn to_untyped(self) -> Point2D<T, UnknownUnit> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
|
@ -234,7 +224,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// assert_eq!(point.y, point.cast_unit::<Cm>().y);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Point2D<T, V> {
|
||||
pub fn cast_unit<V>(self) -> Point2D<T, V> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
|
@ -251,7 +241,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// assert_eq!(point.to_array(), [1, -8]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
pub fn to_array(self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
|
||||
|
@ -268,13 +258,13 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// assert_eq!(point.to_tuple(), (1, -8));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_tuple(&self) -> (T, T) {
|
||||
pub fn to_tuple(self) -> (T, T) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Convert into a 3d point with z-coordinate equals to zero.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> Point3D<T, U>
|
||||
pub fn to_3d(self) -> Point3D<T, U>
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
|
@ -293,7 +283,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self
|
||||
pub fn round(self) -> Self
|
||||
where
|
||||
T: Round,
|
||||
{
|
||||
|
@ -312,7 +302,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ceil(&self) -> Self
|
||||
pub fn ceil(self) -> Self
|
||||
where
|
||||
T: Ceil,
|
||||
{
|
||||
|
@ -331,7 +321,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn floor(&self) -> Self
|
||||
pub fn floor(self) -> Self
|
||||
where
|
||||
T: Floor,
|
||||
{
|
||||
|
@ -356,7 +346,7 @@ impl<T: Copy, U> Point2D<T, U> {
|
|||
/// assert_eq!(from.lerp(to, 2.0), point2(16.0, -18.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self
|
||||
pub fn lerp(self, other: Self, t: T) -> Self
|
||||
where
|
||||
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
|
||||
{
|
||||
|
@ -381,7 +371,7 @@ impl<T: PartialOrd, U> Point2D<T, U> {
|
|||
///
|
||||
/// Shortcut for `self.max(start).min(end)`.
|
||||
#[inline]
|
||||
pub fn clamp(&self, start: Self, end: Self) -> Self
|
||||
pub fn clamp(self, start: Self, end: Self) -> Self
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
|
@ -396,7 +386,7 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
/// 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>(&self) -> Point2D<NewT, U> {
|
||||
pub fn cast<NewT: NumCast>(self) -> Point2D<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
|
@ -405,7 +395,7 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Point2D<NewT, U>> {
|
||||
pub fn try_cast<NewT: NumCast>(self) -> Option<Point2D<NewT, U>> {
|
||||
match (NumCast::from(self.x), NumCast::from(self.y)) {
|
||||
(Some(x), Some(y)) => Some(point2(x, y)),
|
||||
_ => None,
|
||||
|
@ -416,13 +406,13 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
|
||||
/// Cast into an `f32` point.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Point2D<f32, U> {
|
||||
pub fn to_f32(self) -> Point2D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` point.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Point2D<f64, U> {
|
||||
pub fn to_f64(self) -> Point2D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -432,7 +422,7 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Point2D<usize, U> {
|
||||
pub fn to_usize(self) -> Point2D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -442,7 +432,7 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Point2D<u32, U> {
|
||||
pub fn to_u32(self) -> Point2D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -452,7 +442,7 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Point2D<i32, U> {
|
||||
pub fn to_i32(self) -> Point2D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -462,14 +452,14 @@ impl<T: NumCast + Copy, U> Point2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Point2D<i64, U> {
|
||||
pub fn to_i64(self) -> Point2D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Point2D<T, U> {
|
||||
#[inline]
|
||||
pub fn add_size(&self, other: &Size2D<T, U>) -> Self {
|
||||
pub fn add_size(self, other: &Size2D<T, U>) -> Self {
|
||||
point2(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
@ -565,12 +555,12 @@ impl<T: Copy + Sub<T, Output = T>, U> SubAssign<Vector2D<T, U>> for Point2D<T, U
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Point2D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Point2D<T, U> {
|
||||
type Output = Point2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
point2(self.x * scale.clone(), self.y * scale)
|
||||
point2(self.x * scale, self.y * scale)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -581,29 +571,29 @@ impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for Point2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Point2D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Point2D<T, U1> {
|
||||
type Output = Point2D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
point2(self.x * scale.0.clone(), self.y * scale.0)
|
||||
point2(self.x * scale.0, self.y * scale.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Point2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Point2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.x *= scale.0.clone();
|
||||
self.x *= scale.0;
|
||||
self.y *= scale.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Point2D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Point2D<T, U> {
|
||||
type Output = Point2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
point2(self.x / scale.clone(), self.y / scale)
|
||||
point2(self.x / scale, self.y / scale)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,19 +604,19 @@ impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for Point2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Point2D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Point2D<T, U2> {
|
||||
type Output = Point2D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
point2(self.x / scale.0.clone(), self.y / scale.0)
|
||||
point2(self.x / scale.0, self.y / scale.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Point2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Point2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.x /= scale.0.clone();
|
||||
self.x /= scale.0;
|
||||
self.y /= scale.0;
|
||||
}
|
||||
}
|
||||
|
@ -642,7 +632,7 @@ impl<T: Round, U> Round for Point2D<T, U> {
|
|||
/// See [Point2D::round()](#method.round)
|
||||
#[inline]
|
||||
fn round(self) -> Self {
|
||||
(&self).round()
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -650,7 +640,7 @@ impl<T: Ceil, U> Ceil for Point2D<T, U> {
|
|||
/// See [Point2D::ceil()](#method.ceil)
|
||||
#[inline]
|
||||
fn ceil(self) -> Self {
|
||||
(&self).ceil()
|
||||
self.ceil()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -658,7 +648,7 @@ impl<T: Floor, U> Floor for Point2D<T, U> {
|
|||
/// See [Point2D::floor()](#method.floor)
|
||||
#[inline]
|
||||
fn floor(self) -> Self {
|
||||
(&self).floor()
|
||||
self.floor()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,18 +777,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Point3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Point3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
fmt::Display::fmt(&self.x, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.y, f)?;
|
||||
write!(f, ",")?;
|
||||
fmt::Display::fmt(&self.z, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Point3D<T, U> {
|
||||
fn default() -> Self {
|
||||
Point3D::new(Default::default(), Default::default(), Default::default())
|
||||
|
@ -853,7 +831,7 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
///
|
||||
/// Equivalent to subtracting the origin to this point.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> Vector3D<T, U> {
|
||||
pub fn to_vector(self) -> Vector3D<T, U> {
|
||||
Vector3D {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
|
@ -864,19 +842,19 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
|
||||
/// Returns a 2d point using this point's x and y coordinates
|
||||
#[inline]
|
||||
pub fn xy(&self) -> Point2D<T, U> {
|
||||
pub fn xy(self) -> Point2D<T, U> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and z coordinates
|
||||
#[inline]
|
||||
pub fn xz(&self) -> Point2D<T, U> {
|
||||
pub fn xz(self) -> Point2D<T, U> {
|
||||
point2(self.x, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and z coordinates
|
||||
#[inline]
|
||||
pub fn yz(&self) -> Point2D<T, U> {
|
||||
pub fn yz(self) -> Point2D<T, U> {
|
||||
point2(self.y, self.z)
|
||||
}
|
||||
|
||||
|
@ -893,12 +871,12 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// assert_eq!(point.to_array(), [1, -8, 0]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] {
|
||||
pub fn to_array(self) -> [T; 3] {
|
||||
[self.x, self.y, self.z]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array_4d(&self) -> [T; 4]
|
||||
pub fn to_array_4d(self) -> [T; 4]
|
||||
where
|
||||
T: One,
|
||||
{
|
||||
|
@ -918,12 +896,12 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// assert_eq!(point.to_tuple(), (1, -8, 0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_tuple(&self) -> (T, T, T) {
|
||||
pub fn to_tuple(self) -> (T, T, T) {
|
||||
(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_tuple_4d(&self) -> (T, T, T, T)
|
||||
pub fn to_tuple_4d(self) -> (T, T, T, T)
|
||||
where
|
||||
T: One,
|
||||
{
|
||||
|
@ -945,7 +923,7 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// assert_eq!(point.z, point.to_untyped().z);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point3D<T, UnknownUnit> {
|
||||
pub fn to_untyped(self) -> Point3D<T, UnknownUnit> {
|
||||
point3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
|
@ -965,13 +943,13 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// assert_eq!(point.z, point.cast_unit::<Cm>().z);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Point3D<T, V> {
|
||||
pub fn cast_unit<V>(self) -> Point3D<T, V> {
|
||||
point3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Convert into a 2d point.
|
||||
#[inline]
|
||||
pub fn to_2d(&self) -> Point2D<T, U> {
|
||||
pub fn to_2d(self) -> Point2D<T, U> {
|
||||
self.xy()
|
||||
}
|
||||
|
||||
|
@ -987,7 +965,7 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self
|
||||
pub fn round(self) -> Self
|
||||
where
|
||||
T: Round,
|
||||
{
|
||||
|
@ -1006,7 +984,7 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ceil(&self) -> Self
|
||||
pub fn ceil(self) -> Self
|
||||
where
|
||||
T: Ceil,
|
||||
{
|
||||
|
@ -1025,7 +1003,7 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn floor(&self) -> Self
|
||||
pub fn floor(self) -> Self
|
||||
where
|
||||
T: Floor,
|
||||
{
|
||||
|
@ -1050,7 +1028,7 @@ impl<T: Copy, U> Point3D<T, U> {
|
|||
/// assert_eq!(from.lerp(to, 2.0), point3(16.0, -18.0, 1.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self
|
||||
pub fn lerp(self, other: Self, t: T) -> Self
|
||||
where
|
||||
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
|
||||
{
|
||||
|
@ -1087,7 +1065,7 @@ impl<T: PartialOrd, U> Point3D<T, U> {
|
|||
///
|
||||
/// Shortcut for `self.max(start).min(end)`.
|
||||
#[inline]
|
||||
pub fn clamp(&self, start: Self, end: Self) -> Self
|
||||
pub fn clamp(self, start: Self, end: Self) -> Self
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
|
@ -1102,7 +1080,7 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
/// 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>(&self) -> Point3D<NewT, U> {
|
||||
pub fn cast<NewT: NumCast>(self) -> Point3D<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
|
@ -1111,7 +1089,7 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Point3D<NewT, U>> {
|
||||
pub fn try_cast<NewT: NumCast>(self) -> Option<Point3D<NewT, U>> {
|
||||
match (
|
||||
NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
|
@ -1126,13 +1104,13 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
|
||||
/// Cast into an `f32` point.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Point3D<f32, U> {
|
||||
pub fn to_f32(self) -> Point3D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` point.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Point3D<f64, U> {
|
||||
pub fn to_f64(self) -> Point3D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1120,7 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Point3D<usize, U> {
|
||||
pub fn to_usize(self) -> Point3D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1152,7 +1130,7 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Point3D<u32, U> {
|
||||
pub fn to_u32(self) -> Point3D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1162,7 +1140,7 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Point3D<i32, U> {
|
||||
pub fn to_i32(self) -> Point3D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1172,14 +1150,14 @@ impl<T: NumCast + Copy, U> Point3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Point3D<i64, U> {
|
||||
pub fn to_i64(self) -> Point3D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Point3D<T, U> {
|
||||
#[inline]
|
||||
pub fn add_size(&self, other: &Size3D<T, U>) -> Self {
|
||||
pub fn add_size(self, other: Size3D<T, U>) -> Self {
|
||||
point3(
|
||||
self.x + other.width,
|
||||
self.y + other.height,
|
||||
|
@ -1289,84 +1267,84 @@ impl<T: Copy + Sub<T, Output = T>, U> SubAssign<Vector3D<T, U>> for Point3D<T, U
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Point3D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Point3D<T, U> {
|
||||
type Output = Point3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
point3(
|
||||
self.x * scale.clone(),
|
||||
self.y * scale.clone(),
|
||||
self.x * scale,
|
||||
self.y * scale,
|
||||
self.z * scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Point3D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for Point3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
self.x *= scale.clone();
|
||||
self.y *= scale.clone();
|
||||
self.x *= scale;
|
||||
self.y *= scale;
|
||||
self.z *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Point3D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Point3D<T, U1> {
|
||||
type Output = Point3D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
point3(
|
||||
self.x * scale.0.clone(),
|
||||
self.y * scale.0.clone(),
|
||||
self.x * scale.0,
|
||||
self.y * scale.0,
|
||||
self.z * scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Point3D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Point3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
*self *= scale.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Point3D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Point3D<T, U> {
|
||||
type Output = Point3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
point3(
|
||||
self.x / scale.clone(),
|
||||
self.y / scale.clone(),
|
||||
self.x / scale,
|
||||
self.y / scale,
|
||||
self.z / scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Point3D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for Point3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
self.x /= scale.clone();
|
||||
self.y /= scale.clone();
|
||||
self.x /= scale;
|
||||
self.y /= scale;
|
||||
self.z /= scale;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Point3D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Point3D<T, U2> {
|
||||
type Output = Point3D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
point3(
|
||||
self.x / scale.0.clone(),
|
||||
self.y / scale.0.clone(),
|
||||
self.x / scale.0,
|
||||
self.y / scale.0,
|
||||
self.z / scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Point3D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Point3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
*self /= scale.0;
|
||||
|
@ -1384,7 +1362,7 @@ impl<T: Round, U> Round for Point3D<T, U> {
|
|||
/// See [Point3D::round()](#method.round)
|
||||
#[inline]
|
||||
fn round(self) -> Self {
|
||||
(&self).round()
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1392,7 +1370,7 @@ impl<T: Ceil, U> Ceil for Point3D<T, U> {
|
|||
/// See [Point3D::ceil()](#method.ceil)
|
||||
#[inline]
|
||||
fn ceil(self) -> Self {
|
||||
(&self).ceil()
|
||||
self.ceil()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1400,7 +1378,7 @@ impl<T: Floor, U> Floor for Point3D<T, U> {
|
|||
/// See [Point3D::floor()](#method.floor)
|
||||
#[inline]
|
||||
fn floor(self) -> Self {
|
||||
(&self).floor()
|
||||
self.floor()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
use super::UnknownUnit;
|
||||
use crate::box2d::Box2D;
|
||||
use crate::nonempty::NonEmpty;
|
||||
use crate::num::*;
|
||||
use crate::point::Point2D;
|
||||
use crate::scale::Scale;
|
||||
|
@ -28,6 +27,22 @@ use core::hash::{Hash, Hasher};
|
|||
use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
|
||||
|
||||
/// A 2d Rectangle optionally tagged with a unit.
|
||||
///
|
||||
/// # Representation
|
||||
///
|
||||
/// `Rect` is represented by an origin point and a size.
|
||||
///
|
||||
/// See [`Rect`] for a rectangle represented by two endpoints.
|
||||
///
|
||||
/// # Empty rectangle
|
||||
///
|
||||
/// A rectangle is considered empty (see [`is_empty`]) if any of the following is true:
|
||||
/// - it's area is empty,
|
||||
/// - it's area is negative (`size.x < 0` or `size.y < 0`),
|
||||
/// - it contains NaNs.
|
||||
///
|
||||
/// [`is_empty`]: #method.is_empty
|
||||
/// [`Box2D`]: struct.Box2D.html
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
|
@ -72,16 +87,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Rect<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Rect<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Rect(")?;
|
||||
fmt::Display::fmt(&self.size, f)?;
|
||||
write!(f, " at ")?;
|
||||
fmt::Display::fmt(&self.origin, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Rect<T, U> {
|
||||
fn default() -> Self {
|
||||
Rect::new(Default::default(), Default::default())
|
||||
|
@ -210,8 +215,9 @@ where
|
|||
{
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let box2d = self.to_box2d().intersection(&other.to_box2d());
|
||||
if box2d.is_empty_or_negative() {
|
||||
let box2d = self.to_box2d().intersection_unchecked(&other.to_box2d());
|
||||
|
||||
if box2d.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -245,7 +251,7 @@ where
|
|||
/// nonempty but this rectangle is empty.
|
||||
#[inline]
|
||||
pub fn contains_rect(&self, rect: &Self) -> bool {
|
||||
rect.is_empty_or_negative()
|
||||
rect.is_empty()
|
||||
|| (self.min_x() <= rect.min_x()
|
||||
&& rect.max_x() <= self.max_x()
|
||||
&& self.min_y() <= rect.min_y()
|
||||
|
@ -378,48 +384,41 @@ impl<T: Copy + Mul<T, Output = T>, U> Rect<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + PartialEq, U> Rect<T, U> {
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + PartialOrd, U> Rect<T, U> {
|
||||
impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
|
||||
#[inline]
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
self.size.is_empty_or_negative()
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.size.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
|
||||
#[inline]
|
||||
pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
|
||||
if self.is_empty_or_negative() {
|
||||
pub fn to_non_empty(&self) -> Option<Self> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NonEmpty(*self))
|
||||
Some(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Rect<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Rect<T, U> {
|
||||
type Output = Rect<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Rect::new(self.origin * scale.clone(), self.size * scale)
|
||||
Rect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Rect<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self *= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Rect<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Rect<T, U> {
|
||||
type Output = Rect<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
|
@ -428,14 +427,14 @@ impl<T: Clone + Div, U> Div<T> for Rect<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Rect<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self /= Scale::new(scale);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
|
||||
type Output = Rect<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
|
@ -444,7 +443,7 @@ impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Rect<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.origin *= scale.clone();
|
||||
|
@ -452,7 +451,7 @@ impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Rect<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
|
||||
type Output = Rect<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
|
@ -461,7 +460,7 @@ impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Rect<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Rect<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: Scale<T, U, U>) {
|
||||
self.origin /= scale.clone();
|
||||
|
|
|
@ -114,7 +114,7 @@ impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
|||
///
|
||||
/// i.e., this produces `self * other` in row-vector notation
|
||||
#[inline]
|
||||
pub fn post_transform<Dst2>(
|
||||
pub fn then<Dst2>(
|
||||
&self,
|
||||
other: &RigidTransform3D<T, Dst, Dst2>,
|
||||
) -> RigidTransform3D<T, Src, Dst2> {
|
||||
|
@ -131,7 +131,7 @@ impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
|||
// T' * T2 = T'' = vector addition of translations T2 and T'
|
||||
|
||||
let t_prime = other.rotation.transform_vector3d(self.translation);
|
||||
let r_prime = self.rotation.post_rotate(&other.rotation);
|
||||
let r_prime = self.rotation.then(&other.rotation);
|
||||
let t_prime2 = t_prime + other.translation;
|
||||
RigidTransform3D {
|
||||
rotation: r_prime,
|
||||
|
@ -139,18 +139,6 @@ impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two transforms such that
|
||||
/// self's transformation applies after other's transformation.
|
||||
///
|
||||
/// i.e., this produces `other * self` in row-vector notation
|
||||
#[inline]
|
||||
pub fn pre_transform<Src2>(
|
||||
&self,
|
||||
other: &RigidTransform3D<T, Src2, Src>,
|
||||
) -> RigidTransform3D<T, Src2, Dst> {
|
||||
other.post_transform(&self)
|
||||
}
|
||||
|
||||
/// Inverts the transformation
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
|
||||
|
@ -173,9 +161,7 @@ impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
|
|||
where
|
||||
T: Trig,
|
||||
{
|
||||
self.translation
|
||||
.to_transform()
|
||||
.pre_transform(&self.rotation.to_transform())
|
||||
self.rotation.to_transform().then(&self.translation.to_transform())
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
|
@ -223,16 +209,12 @@ mod test {
|
|||
|
||||
let rigid = RigidTransform3D::new(rotation, translation);
|
||||
assert!(rigid.to_transform().approx_eq(
|
||||
&translation
|
||||
.to_transform()
|
||||
.pre_transform(&rotation.to_transform())
|
||||
&rotation.to_transform().then(&translation.to_transform())
|
||||
));
|
||||
|
||||
let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
|
||||
assert!(rigid.to_transform().approx_eq(
|
||||
&translation
|
||||
.to_transform()
|
||||
.post_transform(&rotation.to_transform())
|
||||
&translation.to_transform().then(&rotation.to_transform())
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -245,7 +227,7 @@ mod test {
|
|||
let (t2, r2) = rigid.decompose_reversed();
|
||||
assert!(rigid
|
||||
.to_transform()
|
||||
.approx_eq(&t2.to_transform().post_transform(&r2.to_transform())));
|
||||
.approx_eq(&t2.to_transform().then(&r2.to_transform())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -256,7 +238,7 @@ mod test {
|
|||
let rigid = RigidTransform3D::new(rotation, translation);
|
||||
let inverse = rigid.inverse();
|
||||
assert!(rigid
|
||||
.post_transform(&inverse)
|
||||
.then(&inverse)
|
||||
.to_transform()
|
||||
.approx_eq(&Transform3D::identity()));
|
||||
assert!(inverse
|
||||
|
@ -274,12 +256,12 @@ mod test {
|
|||
let rigid2 = RigidTransform3D::new(rotation2, translation2);
|
||||
|
||||
assert!(rigid
|
||||
.post_transform(&rigid2)
|
||||
.then(&rigid2)
|
||||
.to_transform()
|
||||
.approx_eq(&rigid.to_transform().post_transform(&rigid2.to_transform())));
|
||||
assert!(rigid
|
||||
.pre_transform(&rigid2)
|
||||
.approx_eq(&rigid.to_transform().then(&rigid2.to_transform())));
|
||||
assert!(rigid2
|
||||
.then(&rigid)
|
||||
.to_transform()
|
||||
.approx_eq(&rigid.to_transform().pre_transform(&rigid2.to_transform())));
|
||||
.approx_eq(&rigid2.to_transform().then(&rigid.to_transform())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,11 +158,11 @@ impl<T: Copy, Src, Dst> Rotation2D<T, Src, Dst> {
|
|||
|
||||
impl<T, Src, Dst> Rotation2D<T, Src, Dst>
|
||||
where
|
||||
T: Clone,
|
||||
T: Copy,
|
||||
{
|
||||
/// Returns self.angle as a strongly typed `Angle<T>`.
|
||||
pub fn get_angle(&self) -> Angle<T> {
|
||||
Angle::radians(self.angle.clone())
|
||||
Angle::radians(self.angle)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,22 +181,13 @@ impl<T: Float, Src, Dst> Rotation2D<T, Src, Dst> {
|
|||
|
||||
/// Returns a rotation representing the other rotation followed by this rotation.
|
||||
#[inline]
|
||||
pub fn pre_rotate<NewSrc>(
|
||||
pub fn then<NewSrc>(
|
||||
&self,
|
||||
other: &Rotation2D<T, NewSrc, Src>,
|
||||
) -> Rotation2D<T, NewSrc, Dst> {
|
||||
Rotation2D::radians(self.angle + other.angle)
|
||||
}
|
||||
|
||||
/// Returns a rotation representing this rotation followed by the other rotation.
|
||||
#[inline]
|
||||
pub fn post_rotate<NewDst>(
|
||||
&self,
|
||||
other: &Rotation2D<T, Dst, NewDst>,
|
||||
) -> Rotation2D<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.
|
||||
|
@ -222,7 +213,7 @@ where
|
|||
/// Returns the matrix representation of this rotation.
|
||||
#[inline]
|
||||
pub fn to_transform(&self) -> Transform2D<T, Src, Dst> {
|
||||
Transform2D::create_rotation(self.get_angle())
|
||||
Transform2D::rotation(self.get_angle())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,38 +643,30 @@ where
|
|||
let m32 = jk - ri;
|
||||
let m33 = one - (ii + jj);
|
||||
|
||||
Transform3D::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: &Rotation3D<T, NewSrc, Src>,
|
||||
) -> Rotation3D<T, NewSrc, Dst>
|
||||
where
|
||||
T: ApproxEq<T>,
|
||||
{
|
||||
debug_assert!(self.is_normalized());
|
||||
Rotation3D::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,
|
||||
Transform3D::new(
|
||||
m11, m12, m13, zero,
|
||||
m21, m22, m23, zero,
|
||||
m31, m32, m33, zero,
|
||||
zero, zero, zero, one,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rotation representing this rotation followed by the other rotation.
|
||||
#[inline]
|
||||
pub fn post_rotate<NewDst>(
|
||||
pub fn then<NewDst>(
|
||||
&self,
|
||||
other: &Rotation3D<T, Dst, NewDst>,
|
||||
) -> Rotation3D<T, Src, NewDst>
|
||||
where
|
||||
T: ApproxEq<T>,
|
||||
{
|
||||
other.pre_rotate(self)
|
||||
debug_assert!(self.is_normalized());
|
||||
Rotation3D::quaternion(
|
||||
other.i * self.r + other.r * self.i + other.j * self.k - other.k * self.j,
|
||||
other.j * self.r + other.r * self.j + other.k * self.i - other.i * self.k,
|
||||
other.k * self.r + other.r * self.k + other.i * self.j - other.j * self.i,
|
||||
other.r * self.r - other.i * self.i - other.j * self.j - other.k * self.k,
|
||||
)
|
||||
}
|
||||
|
||||
// add, sub and mul are used internally for intermediate computation but aren't public
|
||||
|
@ -730,16 +713,6 @@ impl<T: fmt::Debug, Src, Dst> fmt::Debug for Rotation3D<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for Rotation3D<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 Rotation3D<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Neg<Output = T> + ApproxEq<T>,
|
||||
|
@ -837,18 +810,18 @@ fn pre_post() {
|
|||
|
||||
// Check that the order of transformations is correct (corresponds to what
|
||||
// we do in Transform3D).
|
||||
let p1 = r1.post_rotate(&r2).post_rotate(&r3).transform_point3d(p);
|
||||
let p1 = r1.then(&r2).then(&r3).transform_point3d(p);
|
||||
let p2 = t1
|
||||
.post_transform(&t2)
|
||||
.post_transform(&t3)
|
||||
.then(&t2)
|
||||
.then(&t3)
|
||||
.transform_point3d(p);
|
||||
|
||||
assert!(p1.approx_eq(&p2.unwrap()));
|
||||
|
||||
// Check that changing the order indeed matters.
|
||||
let p3 = t3
|
||||
.post_transform(&t1)
|
||||
.post_transform(&t2)
|
||||
.then(&t1)
|
||||
.then(&t2)
|
||||
.transform_point3d(p);
|
||||
assert!(!p1.approx_eq(&p3.unwrap()));
|
||||
}
|
||||
|
@ -1018,7 +991,7 @@ fn from_euler() {
|
|||
// Now check that the yaw pitch and roll transformations when combined 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_q = roll_rq.then(&pitch_rq).then(&yaw_rq);
|
||||
let ypr_pe = ypr_e.transform_point3d(p);
|
||||
let ypr_pq = ypr_q.transform_point3d(p);
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
use crate::num::One;
|
||||
|
||||
use crate::{Point2D, Rect, Size2D, Vector2D};
|
||||
use crate::{Point2D, Point3D, Rect, Size2D, Vector2D, Box2D, Box3D};
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::ops::{Add, Div, Mul, Sub};
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -56,6 +56,15 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
Scale(x, PhantomData)
|
||||
}
|
||||
|
||||
/// Creates an identity scale (1.0).
|
||||
#[inline]
|
||||
pub fn identity() -> Self
|
||||
where
|
||||
T: One
|
||||
{
|
||||
Scale::new(T::one())
|
||||
}
|
||||
|
||||
/// Returns the given point transformed by this scale.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -70,11 +79,20 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// assert_eq!(to_mm.transform_point(point2(42, -42)), point2(420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
|
||||
pub fn transform_point(self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Mul,
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Point2D::new(point.x * self.get(), point.y * self.get())
|
||||
Point2D::new(point.x * self.0, point.y * self.0)
|
||||
}
|
||||
|
||||
/// Returns the given point transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_point3d(self, point: Point3D<T, Src>) -> Point3D<T::Output, Dst>
|
||||
where
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Point3D::new(point.x * self.0, point.y * self.0, point.z * self.0)
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
|
@ -91,11 +109,11 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// assert_eq!(to_mm.transform_vector(vec2(42, -42)), vec2(420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
|
||||
pub fn transform_vector(self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Mul,
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Vector2D::new(vec.x * self.get(), vec.y * self.get())
|
||||
Vector2D::new(vec.x * self.0, vec.y * self.0)
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
|
@ -112,11 +130,11 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// assert_eq!(to_mm.transform_size(size2(42, -42)), size2(420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_size(&self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
|
||||
pub fn transform_size(self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Mul,
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Size2D::new(size.width * self.get(), size.height * self.get())
|
||||
Size2D::new(size.width * self.0, size.height * self.0)
|
||||
}
|
||||
|
||||
/// Returns the given rect transformed by this scale.
|
||||
|
@ -133,7 +151,7 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// assert_eq!(to_mm.transform_rect(&rect(1, 2, 42, -42)), rect(10, 20, 420, -420));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
|
||||
pub fn transform_rect(self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
|
||||
where
|
||||
T: Copy + Mul,
|
||||
{
|
||||
|
@ -143,13 +161,28 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Returns the inverse of this scale.
|
||||
/// Returns the given box transformed by this scale.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> Scale<T::Output, Dst, Src>
|
||||
pub fn transform_box2d(self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
|
||||
where
|
||||
T: Clone + Neg,
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Scale::new(-self.get())
|
||||
Box2D {
|
||||
min: self.transform_point(b.min),
|
||||
max: self.transform_point(b.max),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the given box transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_box3d(self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
|
||||
where
|
||||
T: Copy + Mul,
|
||||
{
|
||||
Box3D {
|
||||
min: self.transform_point3d(b.min),
|
||||
max: self.transform_point3d(b.max),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this scale has no effect.
|
||||
|
@ -170,18 +203,17 @@ impl<T, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// assert_eq!(mm_per_mm, Scale::one());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> bool
|
||||
pub fn is_identity(self) -> bool
|
||||
where
|
||||
T: PartialEq + One,
|
||||
{
|
||||
self.0 == T::one()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Scale<T, Src, Dst> {
|
||||
/// Returns the underlying scalar scale factor.
|
||||
#[inline]
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
pub fn get(self) -> T {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// The inverse Scale (1.0 / self).
|
||||
|
@ -195,18 +227,18 @@ impl<T: Clone, Src, Dst> Scale<T, Src, Dst> {
|
|||
///
|
||||
/// let cm_per_mm: Scale<f32, Cm, Mm> = Scale::new(0.1);
|
||||
///
|
||||
/// assert_eq!(cm_per_mm.inv(), Scale::new(10.0));
|
||||
/// assert_eq!(cm_per_mm.inverse(), Scale::new(10.0));
|
||||
/// ```
|
||||
pub fn inv(&self) -> Scale<T::Output, Dst, Src>
|
||||
pub fn inverse(self) -> Scale<T::Output, Dst, Src>
|
||||
where
|
||||
T: One + Div,
|
||||
{
|
||||
let one: T = One::one();
|
||||
Scale::new(one / self.0.clone())
|
||||
Scale::new(one / self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, Src, Dst> Scale<T, Src, Dst> {
|
||||
impl<T: NumCast, Src, Dst> Scale<T, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -235,7 +267,7 @@ impl<T: NumCast + Clone, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// let to_em: Scale<i32, Mm, Em> = Scale::new(10e20).cast();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast>(&self) -> Scale<NewT, Src, Dst> {
|
||||
pub fn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
|
@ -258,16 +290,11 @@ impl<T: NumCast + Clone, Src, Dst> Scale<T, Src, Dst> {
|
|||
/// // Integer to small to store that number
|
||||
/// assert_eq!(to_em.try_cast::<i32>(), None);
|
||||
/// ```
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Scale<NewT, Src, Dst>> {
|
||||
NumCast::from(self.get()).map(Scale::new)
|
||||
pub fn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> {
|
||||
NumCast::from(self.0).map(Scale::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Src, Dst> Scale<f32, Src, Dst> {
|
||||
/// Identity scaling, could be used to safely transit from one space to another.
|
||||
pub const ONE: Self = Scale(1.0, PhantomData);
|
||||
}
|
||||
|
||||
// scale0 * scale1
|
||||
// (A,B) * (B,C) = (A,C)
|
||||
impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
|
||||
|
@ -324,7 +351,7 @@ impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
|
|||
|
||||
impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
|
||||
fn clone(&self) -> Scale<T, Src, Dst> {
|
||||
Scale::new(self.get())
|
||||
Scale::new(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,12 +363,6 @@ impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for Scale<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
|
||||
fn default() -> Self {
|
||||
Self::new(T::default())
|
||||
|
@ -374,7 +395,7 @@ mod tests {
|
|||
let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
|
||||
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
|
||||
|
||||
let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inv();
|
||||
let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inverse();
|
||||
assert_eq!(mm_per_cm.get(), 10.0);
|
||||
|
||||
let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
|
||||
|
|
|
@ -165,6 +165,13 @@ impl<T, U> SideOffsets2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self
|
||||
where T: Zero,
|
||||
{
|
||||
SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
/// Returns `true` if all side offsets are zero.
|
||||
pub fn is_zero(&self) -> bool
|
||||
where
|
||||
|
@ -173,29 +180,30 @@ impl<T, U> SideOffsets2D<T, U> {
|
|||
let zero = T::zero();
|
||||
self.top == zero && self.right == zero && self.bottom == zero && self.left == zero
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> SideOffsets2D<T, U> {
|
||||
/// Constructor setting the same value to all sides, taking a scalar value directly.
|
||||
pub fn new_all_same(all: T) -> Self {
|
||||
pub fn new_all_same(all: T) -> Self
|
||||
where T : Copy
|
||||
{
|
||||
SideOffsets2D::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 {
|
||||
pub fn from_length_all_same(all: Length<T, U>) -> Self
|
||||
where T : Copy
|
||||
{
|
||||
SideOffsets2D::new_all_same(all.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> SideOffsets2D<T, U>
|
||||
where
|
||||
T: Add<T, Output = T> + Copy,
|
||||
{
|
||||
pub fn horizontal(&self) -> T {
|
||||
pub fn horizontal(&self) -> T
|
||||
where T: Copy + Add<T, Output = T>
|
||||
{
|
||||
self.left + self.right
|
||||
}
|
||||
|
||||
pub fn vertical(&self) -> T {
|
||||
pub fn vertical(&self) -> T
|
||||
where T: Copy + Add<T, Output = T>
|
||||
{
|
||||
self.top + self.bottom
|
||||
}
|
||||
}
|
||||
|
@ -215,97 +223,90 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> SideOffsets2D<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for SideOffsets2D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for SideOffsets2D<T, U> {
|
||||
type Output = SideOffsets2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top * scale.clone(),
|
||||
self.right * scale.clone(),
|
||||
self.bottom * scale.clone(),
|
||||
self.top * scale,
|
||||
self.right * scale,
|
||||
self.bottom * scale,
|
||||
self.left * scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: T) {
|
||||
self.top *= other.clone();
|
||||
self.right *= other.clone();
|
||||
self.bottom *= other.clone();
|
||||
self.top *= other;
|
||||
self.right *= other;
|
||||
self.bottom *= other;
|
||||
self.left *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
|
||||
type Output = SideOffsets2D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top * scale.0.clone(),
|
||||
self.right * scale.0.clone(),
|
||||
self.bottom * scale.0.clone(),
|
||||
self.top * scale.0,
|
||||
self.right * scale.0,
|
||||
self.bottom * scale.0,
|
||||
self.left * scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self *= other.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for SideOffsets2D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for SideOffsets2D<T, U> {
|
||||
type Output = SideOffsets2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top / scale.clone(),
|
||||
self.right / scale.clone(),
|
||||
self.bottom / scale.clone(),
|
||||
self.top / scale,
|
||||
self.right / scale,
|
||||
self.bottom / scale,
|
||||
self.left / scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: T) {
|
||||
self.top /= other.clone();
|
||||
self.right /= other.clone();
|
||||
self.bottom /= other.clone();
|
||||
self.top /= other;
|
||||
self.right /= other;
|
||||
self.bottom /= other;
|
||||
self.left /= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
|
||||
type Output = SideOffsets2D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
SideOffsets2D::new(
|
||||
self.top / scale.0.clone(),
|
||||
self.right / scale.0.clone(),
|
||||
self.bottom / scale.0.clone(),
|
||||
self.top / scale.0,
|
||||
self.right / scale.0,
|
||||
self.bottom / scale.0,
|
||||
self.left / scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
|
||||
fn div_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self /= other.0;
|
||||
}
|
||||
|
|
|
@ -111,16 +111,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Size2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Size2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
fmt::Display::fmt(&self.width, f)?;
|
||||
write!(f, "x")?;
|
||||
fmt::Display::fmt(&self.height, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Size2D<T, U> {
|
||||
fn default() -> Self {
|
||||
Size2D::new(Default::default(), Default::default())
|
||||
|
@ -164,31 +154,31 @@ impl<T, U> Size2D<T, U> {
|
|||
impl<T: Copy, U> Size2D<T, U> {
|
||||
/// Return this size as an array of two elements (width, then height).
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
pub fn to_array(self) -> [T; 2] {
|
||||
[self.width, self.height]
|
||||
}
|
||||
|
||||
/// Return this size as a tuple of two elements (width, then height).
|
||||
#[inline]
|
||||
pub fn to_tuple(&self) -> (T, T) {
|
||||
pub fn to_tuple(self) -> (T, T) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Return this size as a vector with width and height.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> Vector2D<T, U> {
|
||||
pub fn to_vector(self) -> Vector2D<T, U> {
|
||||
vec2(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Size2D<T, UnknownUnit> {
|
||||
pub fn to_untyped(self) -> Size2D<T, UnknownUnit> {
|
||||
self.cast_unit()
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Size2D<T, V> {
|
||||
pub fn cast_unit<V>(self) -> Size2D<T, V> {
|
||||
Size2D::new(self.width, self.height)
|
||||
}
|
||||
|
||||
|
@ -204,7 +194,7 @@ impl<T: Copy, U> Size2D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self
|
||||
pub fn round(self) -> Self
|
||||
where
|
||||
T: Round,
|
||||
{
|
||||
|
@ -223,7 +213,7 @@ impl<T: Copy, U> Size2D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ceil(&self) -> Self
|
||||
pub fn ceil(self) -> Self
|
||||
where
|
||||
T: Ceil,
|
||||
{
|
||||
|
@ -242,7 +232,7 @@ impl<T: Copy, U> Size2D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn floor(&self) -> Self
|
||||
pub fn floor(self) -> Self
|
||||
where
|
||||
T: Floor,
|
||||
{
|
||||
|
@ -250,7 +240,7 @@ impl<T: Copy, U> Size2D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns result of multiplication of both components
|
||||
pub fn area(&self) -> T::Output
|
||||
pub fn area(self) -> T::Output
|
||||
where
|
||||
T: Mul,
|
||||
{
|
||||
|
@ -275,12 +265,12 @@ impl<T: Copy, U> Size2D<T, U> {
|
|||
/// assert_eq!(from.lerp(to, 2.0), size2(16.0, -18.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self
|
||||
pub fn lerp(self, other: Self, t: T) -> Self
|
||||
where
|
||||
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
|
||||
{
|
||||
let one_t = T::one() - t;
|
||||
(*self) * one_t + other * t
|
||||
self * one_t + other * t
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,7 +281,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// 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>(&self) -> Size2D<NewT, U> {
|
||||
pub fn cast<NewT: NumCast>(self) -> Size2D<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
|
@ -300,7 +290,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Size2D<NewT, U>> {
|
||||
pub fn try_cast<NewT: NumCast>(self) -> Option<Size2D<NewT, U>> {
|
||||
match (NumCast::from(self.width), NumCast::from(self.height)) {
|
||||
(Some(w), Some(h)) => Some(Size2D::new(w, h)),
|
||||
_ => None,
|
||||
|
@ -311,13 +301,13 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
|
||||
/// Cast into an `f32` size.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Size2D<f32, U> {
|
||||
pub fn to_f32(self) -> Size2D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` size.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Size2D<f64, U> {
|
||||
pub fn to_f64(self) -> Size2D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -327,7 +317,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Size2D<usize, U> {
|
||||
pub fn to_usize(self) -> Size2D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -337,7 +327,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Size2D<u32, U> {
|
||||
pub fn to_u32(self) -> Size2D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -347,7 +337,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u64(&self) -> Size2D<u64, U> {
|
||||
pub fn to_u64(self) -> Size2D<u64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -357,7 +347,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Size2D<i32, U> {
|
||||
pub fn to_i32(self) -> Size2D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -367,7 +357,7 @@ impl<T: NumCast + Copy, U> Size2D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Size2D<i64, U> {
|
||||
pub fn to_i64(self) -> Size2D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
@ -378,12 +368,12 @@ impl<T: Signed, U> Size2D<T, U> {
|
|||
/// For `f32` and `f64`, `NaN` will be returned for component if the component is `NaN`.
|
||||
///
|
||||
/// For signed integers, `::MIN` will be returned for component if the component is `::MIN`.
|
||||
pub fn abs(&self) -> Self {
|
||||
pub fn abs(self) -> Self {
|
||||
size2(self.width.abs(), self.height.abs())
|
||||
}
|
||||
|
||||
/// Returns `true` if both components is positive and `false` any component is zero or negative.
|
||||
pub fn is_positive(&self) -> bool {
|
||||
pub fn is_positive(self) -> bool {
|
||||
self.width.is_positive() && self.height.is_positive()
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +396,7 @@ impl<T: PartialOrd, U> Size2D<T, U> {
|
|||
///
|
||||
/// Shortcut for `self.max(start).min(end)`.
|
||||
#[inline]
|
||||
pub fn clamp(&self, start: Self, end: Self) -> Self
|
||||
pub fn clamp(self, start: Self, end: Self) -> Self
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
|
@ -414,7 +404,7 @@ impl<T: PartialOrd, U> Size2D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns vector with results of "greater then" operation on each component.
|
||||
pub fn greater_than(&self, other: Self) -> BoolVector2D {
|
||||
pub fn greater_than(self, other: Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width > other.width,
|
||||
y: self.height > other.height,
|
||||
|
@ -422,15 +412,15 @@ impl<T: PartialOrd, U> Size2D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns vector with results of "lower then" operation on each component.
|
||||
pub fn lower_than(&self, other: Self) -> BoolVector2D {
|
||||
pub fn lower_than(self, other: Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width < other.width,
|
||||
y: self.height < other.height,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if any component of size is zero or negative.
|
||||
pub fn is_empty_or_negative(&self) -> bool
|
||||
/// Returns `true` if any component of size is zero, negative, or NaN.
|
||||
pub fn is_empty(self) -> bool
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
|
@ -443,7 +433,7 @@ impl<T: PartialOrd, U> Size2D<T, U> {
|
|||
|
||||
impl<T: PartialEq, U> Size2D<T, U> {
|
||||
/// Returns vector with results of "equal" operation on each component.
|
||||
pub fn equal(&self, other: Self) -> BoolVector2D {
|
||||
pub fn equal(self, other: Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width == other.width,
|
||||
y: self.height == other.height,
|
||||
|
@ -451,7 +441,7 @@ impl<T: PartialEq, U> Size2D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns vector with results of "not equal" operation on each component.
|
||||
pub fn not_equal(&self, other: Self) -> BoolVector2D {
|
||||
pub fn not_equal(self, other: Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width != other.width,
|
||||
y: self.height != other.height,
|
||||
|
@ -463,7 +453,7 @@ impl<T: Round, U> Round for Size2D<T, U> {
|
|||
/// See [`Size2D::round()`](#method.round).
|
||||
#[inline]
|
||||
fn round(self) -> Self {
|
||||
(&self).round()
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +461,7 @@ impl<T: Ceil, U> Ceil for Size2D<T, U> {
|
|||
/// See [`Size2D::ceil()`](#method.ceil).
|
||||
#[inline]
|
||||
fn ceil(self) -> Self {
|
||||
(&self).ceil()
|
||||
self.ceil()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,7 +469,7 @@ impl<T: Floor, U> Floor for Size2D<T, U> {
|
|||
/// See [`Size2D::floor()`](#method.floor).
|
||||
#[inline]
|
||||
fn floor(self) -> Self {
|
||||
(&self).floor()
|
||||
self.floor()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -533,66 +523,66 @@ impl<T: SubAssign, U> SubAssign for Size2D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Size2D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Size2D<T, U> {
|
||||
type Output = Size2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Size2D::new(self.width * scale.clone(), self.height * scale)
|
||||
Size2D::new(self.width * scale, self.height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Size2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for Size2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: T) {
|
||||
self.width *= other.clone();
|
||||
self.width *= other;
|
||||
self.height *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Size2D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Size2D<T, U1> {
|
||||
type Output = Size2D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Size2D::new(self.width * scale.0.clone(), self.height * scale.0)
|
||||
Size2D::new(self.width * scale.0, self.height * scale.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Size2D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Size2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self *= other.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Size2D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Size2D<T, U> {
|
||||
type Output = Size2D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Size2D::new(self.width / scale.clone(), self.height / scale)
|
||||
Size2D::new(self.width / scale, self.height / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Size2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for Size2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: T) {
|
||||
self.width /= other.clone();
|
||||
self.width /= other;
|
||||
self.height /= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Size2D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Size2D<T, U2> {
|
||||
type Output = Size2D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Size2D::new(self.width / scale.0.clone(), self.height / scale.0)
|
||||
Size2D::new(self.width / scale.0, self.height / scale.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Size2D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Size2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self /= other.0;
|
||||
|
@ -856,9 +846,9 @@ mod size2d {
|
|||
#[test]
|
||||
pub fn test_nan_empty() {
|
||||
use std::f32::NAN;
|
||||
assert!(Size2D::new(NAN, 2.0).is_empty_or_negative());
|
||||
assert!(Size2D::new(0.0, NAN).is_empty_or_negative());
|
||||
assert!(Size2D::new(NAN, -2.0).is_empty_or_negative());
|
||||
assert!(Size2D::new(NAN, 2.0).is_empty());
|
||||
assert!(Size2D::new(0.0, NAN).is_empty());
|
||||
assert!(Size2D::new(NAN, -2.0).is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -953,18 +943,6 @@ impl<T: fmt::Debug, U> fmt::Debug for Size3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for Size3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
fmt::Display::fmt(&self.width, f)?;
|
||||
write!(f, "x")?;
|
||||
fmt::Display::fmt(&self.height, f)?;
|
||||
write!(f, "x")?;
|
||||
fmt::Display::fmt(&self.depth, f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, U> Default for Size3D<T, U> {
|
||||
fn default() -> Self {
|
||||
Size3D::new(Default::default(), Default::default(), Default::default())
|
||||
|
@ -1009,31 +987,31 @@ impl<T, U> Size3D<T, U> {
|
|||
impl<T: Copy, U> Size3D<T, U> {
|
||||
/// Return this size as an array of three elements (width, then height, then depth).
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] {
|
||||
pub fn to_array(self) -> [T; 3] {
|
||||
[self.width, self.height, self.depth]
|
||||
}
|
||||
|
||||
/// Return this size as an array of three elements (width, then height, then depth).
|
||||
#[inline]
|
||||
pub fn to_tuple(&self) -> (T, T, T) {
|
||||
pub fn to_tuple(self) -> (T, T, T) {
|
||||
(self.width, self.height, self.depth)
|
||||
}
|
||||
|
||||
/// Return this size as a vector with width, height and depth.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> Vector3D<T, U> {
|
||||
pub fn to_vector(self) -> Vector3D<T, U> {
|
||||
vec3(self.width, self.height, self.depth)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Size3D<T, UnknownUnit> {
|
||||
pub fn to_untyped(self) -> Size3D<T, UnknownUnit> {
|
||||
self.cast_unit()
|
||||
}
|
||||
|
||||
/// Cast the unit
|
||||
#[inline]
|
||||
pub fn cast_unit<V>(&self) -> Size3D<T, V> {
|
||||
pub fn cast_unit<V>(self) -> Size3D<T, V> {
|
||||
Size3D::new(self.width, self.height, self.depth)
|
||||
}
|
||||
|
||||
|
@ -1049,7 +1027,7 @@ impl<T: Copy, U> Size3D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self
|
||||
pub fn round(self) -> Self
|
||||
where
|
||||
T: Round,
|
||||
{
|
||||
|
@ -1068,7 +1046,7 @@ impl<T: Copy, U> Size3D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ceil(&self) -> Self
|
||||
pub fn ceil(self) -> Self
|
||||
where
|
||||
T: Ceil,
|
||||
{
|
||||
|
@ -1087,7 +1065,7 @@ impl<T: Copy, U> Size3D<T, U> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn floor(&self) -> Self
|
||||
pub fn floor(self) -> Self
|
||||
where
|
||||
T: Floor,
|
||||
{
|
||||
|
@ -1095,7 +1073,7 @@ impl<T: Copy, U> Size3D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns result of multiplication of all components
|
||||
pub fn volume(&self) -> T
|
||||
pub fn volume(self) -> T
|
||||
where
|
||||
T: Mul<Output = T>,
|
||||
{
|
||||
|
@ -1120,12 +1098,12 @@ impl<T: Copy, U> Size3D<T, U> {
|
|||
/// assert_eq!(from.lerp(to, 2.0), size3(16.0, -18.0, 1.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self
|
||||
pub fn lerp(self, other: Self, t: T) -> Self
|
||||
where
|
||||
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
|
||||
{
|
||||
let one_t = T::one() - t;
|
||||
(*self) * one_t + other * t
|
||||
self * one_t + other * t
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1136,7 +1114,7 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
/// 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>(&self) -> Size3D<NewT, U> {
|
||||
pub fn cast<NewT: NumCast>(self) -> Size3D<NewT, U> {
|
||||
self.try_cast().unwrap()
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1123,7 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn try_cast<NewT: NumCast>(&self) -> Option<Size3D<NewT, U>> {
|
||||
pub fn try_cast<NewT: NumCast>(self) -> Option<Size3D<NewT, U>> {
|
||||
match (
|
||||
NumCast::from(self.width),
|
||||
NumCast::from(self.height),
|
||||
|
@ -1160,13 +1138,13 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
|
||||
/// Cast into an `f32` size.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> Size3D<f32, U> {
|
||||
pub fn to_f32(self) -> Size3D<f32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` size.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> Size3D<f64, U> {
|
||||
pub fn to_f64(self) -> Size3D<f64, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1176,7 +1154,7 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Size3D<usize, U> {
|
||||
pub fn to_usize(self) -> Size3D<usize, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1164,7 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Size3D<u32, U> {
|
||||
pub fn to_u32(self) -> Size3D<u32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1196,7 +1174,7 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Size3D<i32, U> {
|
||||
pub fn to_i32(self) -> Size3D<i32, U> {
|
||||
self.cast()
|
||||
}
|
||||
|
||||
|
@ -1206,7 +1184,7 @@ impl<T: NumCast + Copy, U> Size3D<T, U> {
|
|||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> Size3D<i64, U> {
|
||||
pub fn to_i64(self) -> Size3D<i64, U> {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
@ -1217,12 +1195,12 @@ impl<T: Signed, U> Size3D<T, U> {
|
|||
/// For `f32` and `f64`, `NaN` will be returned for component if the component is `NaN`.
|
||||
///
|
||||
/// For signed integers, `::MIN` will be returned for component if the component is `::MIN`.
|
||||
pub fn abs(&self) -> Self {
|
||||
pub fn abs(self) -> Self {
|
||||
size3(self.width.abs(), self.height.abs(), self.depth.abs())
|
||||
}
|
||||
|
||||
/// Returns `true` if all components is positive and `false` any component is zero or negative.
|
||||
pub fn is_positive(&self) -> bool {
|
||||
pub fn is_positive(self) -> bool {
|
||||
self.width.is_positive() && self.height.is_positive() && self.depth.is_positive()
|
||||
}
|
||||
}
|
||||
|
@ -1253,7 +1231,7 @@ impl<T: PartialOrd, U> Size3D<T, U> {
|
|||
///
|
||||
/// Shortcut for `self.max(start).min(end)`.
|
||||
#[inline]
|
||||
pub fn clamp(&self, start: Self, end: Self) -> Self
|
||||
pub fn clamp(self, start: Self, end: Self) -> Self
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
|
@ -1261,7 +1239,7 @@ impl<T: PartialOrd, U> Size3D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns vector with results of "greater than" operation on each component.
|
||||
pub fn greater_than(&self, other: Self) -> BoolVector3D {
|
||||
pub fn greater_than(self, other: Self) -> BoolVector3D {
|
||||
BoolVector3D {
|
||||
x: self.width > other.width,
|
||||
y: self.height > other.height,
|
||||
|
@ -1270,7 +1248,7 @@ impl<T: PartialOrd, U> Size3D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns vector with results of "lower than" operation on each component.
|
||||
pub fn lower_than(&self, other: Self) -> BoolVector3D {
|
||||
pub fn lower_than(self, other: Self) -> BoolVector3D {
|
||||
BoolVector3D {
|
||||
x: self.width < other.width,
|
||||
y: self.height < other.height,
|
||||
|
@ -1278,8 +1256,8 @@ impl<T: PartialOrd, U> Size3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if any component of size is zero or negative.
|
||||
pub fn is_empty_or_negative(&self) -> bool
|
||||
/// Returns `true` if any component of size is zero, negative or NaN.
|
||||
pub fn is_empty(self) -> bool
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
|
@ -1290,7 +1268,7 @@ impl<T: PartialOrd, U> Size3D<T, U> {
|
|||
|
||||
impl<T: PartialEq, U> Size3D<T, U> {
|
||||
/// Returns vector with results of "equal" operation on each component.
|
||||
pub fn equal(&self, other: Self) -> BoolVector3D {
|
||||
pub fn equal(self, other: Self) -> BoolVector3D {
|
||||
BoolVector3D {
|
||||
x: self.width == other.width,
|
||||
y: self.height == other.height,
|
||||
|
@ -1299,7 +1277,7 @@ impl<T: PartialEq, U> Size3D<T, U> {
|
|||
}
|
||||
|
||||
/// Returns vector with results of "not equal" operation on each component.
|
||||
pub fn not_equal(&self, other: Self) -> BoolVector3D {
|
||||
pub fn not_equal(self, other: Self) -> BoolVector3D {
|
||||
BoolVector3D {
|
||||
x: self.width != other.width,
|
||||
y: self.height != other.height,
|
||||
|
@ -1312,7 +1290,7 @@ impl<T: Round, U> Round for Size3D<T, U> {
|
|||
/// See [`Size3D::round()`](#method.round).
|
||||
#[inline]
|
||||
fn round(self) -> Self {
|
||||
(&self).round()
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1320,7 +1298,7 @@ impl<T: Ceil, U> Ceil for Size3D<T, U> {
|
|||
/// See [`Size3D::ceil()`](#method.ceil).
|
||||
#[inline]
|
||||
fn ceil(self) -> Self {
|
||||
(&self).ceil()
|
||||
self.ceil()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1328,7 +1306,7 @@ impl<T: Floor, U> Floor for Size3D<T, U> {
|
|||
/// See [`Size3D::floor()`](#method.floor).
|
||||
#[inline]
|
||||
fn floor(self) -> Self {
|
||||
(&self).floor()
|
||||
self.floor()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1392,84 +1370,84 @@ impl<T: SubAssign, U> SubAssign for Size3D<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U> Mul<T> for Size3D<T, U> {
|
||||
impl<T: Copy + Mul, U> Mul<T> for Size3D<T, U> {
|
||||
type Output = Size3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self::Output {
|
||||
Size3D::new(
|
||||
self.width * scale.clone(),
|
||||
self.height * scale.clone(),
|
||||
self.width * scale,
|
||||
self.height * scale,
|
||||
self.depth * scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<T> for Size3D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<T> for Size3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: T) {
|
||||
self.width *= other.clone();
|
||||
self.height *= other.clone();
|
||||
self.width *= other;
|
||||
self.height *= other;
|
||||
self.depth *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Size3D<T, U1> {
|
||||
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Size3D<T, U1> {
|
||||
type Output = Size3D<T::Output, U2>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Size3D::new(
|
||||
self.width * scale.0.clone(),
|
||||
self.height * scale.0.clone(),
|
||||
self.width * scale.0,
|
||||
self.height * scale.0,
|
||||
self.depth * scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + MulAssign, U> MulAssign<Scale<T, U, U>> for Size3D<T, U> {
|
||||
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Size3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self *= other.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U> Div<T> for Size3D<T, U> {
|
||||
impl<T: Copy + Div, U> Div<T> for Size3D<T, U> {
|
||||
type Output = Size3D<T::Output, U>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self::Output {
|
||||
Size3D::new(
|
||||
self.width / scale.clone(),
|
||||
self.height / scale.clone(),
|
||||
self.width / scale,
|
||||
self.height / scale,
|
||||
self.depth / scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<T> for Size3D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<T> for Size3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: T) {
|
||||
self.width /= other.clone();
|
||||
self.height /= other.clone();
|
||||
self.width /= other;
|
||||
self.height /= other;
|
||||
self.depth /= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Div, U1, U2> Div<Scale<T, U1, U2>> for Size3D<T, U2> {
|
||||
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Size3D<T, U2> {
|
||||
type Output = Size3D<T::Output, U1>;
|
||||
|
||||
#[inline]
|
||||
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
|
||||
Size3D::new(
|
||||
self.width / scale.0.clone(),
|
||||
self.height / scale.0.clone(),
|
||||
self.width / scale.0,
|
||||
self.height / scale.0,
|
||||
self.depth / scale.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + DivAssign, U> DivAssign<Scale<T, U, U>> for Size3D<T, U> {
|
||||
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Size3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: Scale<T, U, U>) {
|
||||
*self /= other.0;
|
||||
|
@ -1710,9 +1688,9 @@ mod size3d {
|
|||
#[test]
|
||||
pub fn test_nan_empty() {
|
||||
use std::f32::NAN;
|
||||
assert!(Size3D::new(NAN, 2.0, 3.0).is_empty_or_negative());
|
||||
assert!(Size3D::new(0.0, NAN, 0.0).is_empty_or_negative());
|
||||
assert!(Size3D::new(1.0, 2.0, NAN).is_empty_or_negative());
|
||||
assert!(Size3D::new(NAN, 2.0, 3.0).is_empty());
|
||||
assert!(Size3D::new(0.0, NAN, 0.0).is_empty());
|
||||
assert!(Size3D::new(1.0, 2.0, NAN).is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::num::{One, Zero};
|
|||
use crate::point::{Point2D, point2};
|
||||
use crate::vector::{Vector2D, vec2};
|
||||
use crate::rect::Rect;
|
||||
use crate::box2d::Box2D;
|
||||
use crate::transform3d::Transform3D;
|
||||
use core::ops::{Add, Mul, Div, Sub};
|
||||
use core::marker::PhantomData;
|
||||
|
@ -26,9 +27,9 @@ use crate::trig::Trig;
|
|||
use core::fmt;
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A 2d transform stored as a 3 by 2 matrix in row-major order in memory.
|
||||
/// A 2d transform represented by a column-major 3 by 3 matrix, compressed down to 3 by 2.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
|
@ -36,14 +37,30 @@ use serde;
|
|||
/// takes a `Point2D<f32, WorldSpace>` and returns a `Point2D<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.
|
||||
/// Pre-transformations (`pre_*` methods) correspond to adding an operation that is
|
||||
/// applied before the rest of the transformation, while post-transformations (`then_*`
|
||||
/// methods) add an operation that is applied after.
|
||||
///
|
||||
/// These transforms are for working with _row vectors_, so the matrix math for transforming
|
||||
/// a vector is `v * T`. If your library is using column vectors, use `row_major` functions when you
|
||||
/// are asked for `column_major` representations and vice versa.
|
||||
/// The matrix representation is conceptually equivalent to a 3 by 3 matrix transformation
|
||||
/// compressed to 3 by 2 with the components that aren't needed to describe the set of 2d
|
||||
/// transformations we are interested in implicitly defined:
|
||||
///
|
||||
/// ```text
|
||||
/// | m11 m12 0 | |x| |x'|
|
||||
/// | m21 m22 0 | x |y| = |y'|
|
||||
/// | m31 m32 1 | |1| |w |
|
||||
/// ```
|
||||
///
|
||||
/// When translating Transform2D into general matrix representations, consider that the
|
||||
/// representation follows the column-major notation with column vectors.
|
||||
///
|
||||
/// The translation terms are m31 and m32.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
|
||||
)]
|
||||
pub struct Transform2D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T,
|
||||
pub m21: T, pub m22: T,
|
||||
|
@ -68,42 +85,6 @@ impl<T: Clone, Src, Dst> Clone for Transform2D<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, Src, Dst> serde::Deserialize<'de> for Transform2D<T, Src, Dst>
|
||||
where T: serde::Deserialize<'de>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: serde::Deserializer<'de>
|
||||
{
|
||||
let (
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
) = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(Transform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Src, Dst> serde::Serialize for Transform2D<T, Src, Dst>
|
||||
where T: serde::Serialize
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer
|
||||
{
|
||||
(
|
||||
&self.m11, &self.m12,
|
||||
&self.m21, &self.m22,
|
||||
&self.m31, &self.m32,
|
||||
).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Eq for Transform2D<T, Src, Dst> where T: Eq {}
|
||||
|
||||
impl<T, Src, Dst> PartialEq for Transform2D<T, Src, Dst>
|
||||
|
@ -134,12 +115,22 @@ impl<T, Src, Dst> Hash for Transform2D<T, Src, Dst>
|
|||
|
||||
|
||||
impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
||||
/// Create a transform specifying its matrix elements in row-major order.
|
||||
/// Create a transform specifying its components in using the column-major-column-vector
|
||||
/// matrix notation.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `column_major`
|
||||
pub const fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
|
||||
/// For example, the translation terms m31 and m32 are the last two parameters parameters.
|
||||
///
|
||||
/// ```
|
||||
/// use euclid::default::Transform2D;
|
||||
/// let tx = 1.0;
|
||||
/// let ty = 2.0;
|
||||
/// let translation = Transform2D::new(
|
||||
/// 1.0, 0.0,
|
||||
/// 0.0, 1.0,
|
||||
/// tx, ty,
|
||||
/// );
|
||||
/// ```
|
||||
pub const fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
|
||||
Transform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
|
@ -148,21 +139,6 @@ impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its matrix elements in column-major order.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `row_major`
|
||||
pub const fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> Self {
|
||||
Transform2D {
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns true is this transform is approximately equal to the other one, using
|
||||
/// T's default epsilon value.
|
||||
///
|
||||
|
@ -189,14 +165,16 @@ impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Transform2D<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).
|
||||
/// Returns an array containing this transform's terms.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `to_column_major_array`
|
||||
/// The terms are laid out in the same order as they are
|
||||
/// specified in `Transform2D::new`, that is following the
|
||||
/// column-major-column-vector matrix notation.
|
||||
///
|
||||
/// For example the translation terms are found in the
|
||||
/// last two slots of the array.
|
||||
#[inline]
|
||||
pub fn to_row_major_array(&self) -> [T; 6] {
|
||||
pub fn to_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
|
@ -204,29 +182,26 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
/// Returns an array containing this transform's terms transposed.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), then please use `to_row_major_array`
|
||||
/// The terms are laid out in transposed order from the same order of
|
||||
/// `Transform3D::new` and `Transform3D::to_array`, that is following
|
||||
/// the row-major-column-vector matrix notation.
|
||||
///
|
||||
/// For example the translation terms are found at indices 2 and 5
|
||||
/// in the array.
|
||||
#[inline]
|
||||
pub fn to_column_major_array(&self) -> [T; 6] {
|
||||
pub fn to_array_transposed(&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.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), this will return column major arrays.
|
||||
/// Equivalent to `to_array` with elements packed two at a time
|
||||
/// in an array of arrays.
|
||||
#[inline]
|
||||
pub fn to_row_arrays(&self) -> [[T; 2]; 3] {
|
||||
pub fn to_arrays(&self) -> [[T; 2]; 3] {
|
||||
[
|
||||
[self.m11, self.m12],
|
||||
[self.m21, self.m22],
|
||||
|
@ -234,28 +209,30 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 6 elements in row-major order.
|
||||
/// Create a transform providing its components via an array
|
||||
/// of 6 elements instead of as individual parameters.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), please provide a column major array.
|
||||
/// The order of the components corresponds to the
|
||||
/// column-major-column-vector matrix notation (the same order
|
||||
/// as `Transform2D::new`).
|
||||
#[inline]
|
||||
pub fn from_row_major_array(array: [T; 6]) -> Self {
|
||||
Self::row_major(
|
||||
pub fn from_array(array: [T; 6]) -> Self {
|
||||
Self::new(
|
||||
array[0], array[1],
|
||||
array[2], array[3],
|
||||
array[4], array[5],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 3 rows of 2 elements (row-major order).
|
||||
/// Equivalent to `from_array` with elements packed two at a time
|
||||
/// in an array of arrays.
|
||||
///
|
||||
/// Beware: This library is written with the assumption that row vectors
|
||||
/// are being used. If your matrices use column vectors (i.e. transforming a vector
|
||||
/// is `T * v`), please provide a column major array.
|
||||
/// The order of the components corresponds to the
|
||||
/// column-major-column-vector matrix notation (the same order
|
||||
/// as `Transform3D::new`).
|
||||
#[inline]
|
||||
pub fn from_row_arrays(array: [[T; 2]; 3]) -> Self {
|
||||
Self::row_major(
|
||||
pub fn from_arrays(array: [[T; 2]; 3]) -> Self {
|
||||
Self::new(
|
||||
array[0][0], array[0][1],
|
||||
array[1][0], array[1][1],
|
||||
array[2][0], array[2][1],
|
||||
|
@ -265,7 +242,7 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Transform2D<T, UnknownUnit, UnknownUnit> {
|
||||
Transform2D::row_major(
|
||||
Transform2D::new(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
|
@ -275,7 +252,7 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Transform2D<T, UnknownUnit, UnknownUnit>) -> Self {
|
||||
Transform2D::row_major(
|
||||
Transform2D::new(
|
||||
p.m11, p.m12,
|
||||
p.m21, p.m22,
|
||||
p.m31, p.m32
|
||||
|
@ -285,7 +262,7 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> Transform2D<T, NewSrc, Dst> {
|
||||
Transform2D::row_major(
|
||||
Transform2D::new(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
|
@ -295,7 +272,7 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> Transform2D<T, Src, NewDst> {
|
||||
Transform2D::row_major(
|
||||
Transform2D::new(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
|
@ -307,7 +284,7 @@ impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
where
|
||||
T: Zero + One,
|
||||
{
|
||||
Transform3D::row_major_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
|
||||
Transform3D::new_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +303,7 @@ impl<T: NumCast + Copy, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
(Some(m11), Some(m12),
|
||||
Some(m21), Some(m22),
|
||||
Some(m31), Some(m32)) => {
|
||||
Some(Transform2D::row_major(
|
||||
Some(Transform2D::new(
|
||||
m11, m12,
|
||||
m21, m22,
|
||||
m31, m32
|
||||
|
@ -350,7 +327,7 @@ where
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
Self::create_translation(T::zero(), T::zero())
|
||||
Self::translation(T::zero(), T::zero())
|
||||
}
|
||||
|
||||
/// Intentional not public, because it checks for exact equivalence
|
||||
|
@ -372,11 +349,9 @@ where
|
|||
{
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to self * mat
|
||||
#[must_use]
|
||||
pub fn post_transform<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> {
|
||||
Transform2D::row_major(
|
||||
pub fn then<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> {
|
||||
Transform2D::new(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22,
|
||||
|
||||
|
@ -387,16 +362,6 @@ where
|
|||
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.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to mat * self
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pre_transform<NewSrc>(&self, mat: &Transform2D<T, NewSrc, Src>) -> Transform2D<T, NewSrc, Dst> {
|
||||
mat.post_transform(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for creating and combining translation transformations
|
||||
|
@ -412,11 +377,11 @@ where
|
|||
/// x y
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn create_translation(x: T, y: T) -> Self {
|
||||
pub fn translation(x: T, y: T) -> Self {
|
||||
let _0 = || T::zero();
|
||||
let _1 = || T::one();
|
||||
|
||||
Self::row_major(
|
||||
Self::new(
|
||||
_1(), _0(),
|
||||
_0(), _1(),
|
||||
x, y,
|
||||
|
@ -426,11 +391,11 @@ where
|
|||
/// Applies a translation after self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn post_translate(&self, v: Vector2D<T, Dst>) -> Self
|
||||
pub fn then_translate(&self, v: Vector2D<T, Dst>) -> Self
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
self.post_transform(&Transform2D::create_translation(v.x, v.y))
|
||||
self.then(&Transform2D::translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Applies a translation before self's transformation and returns the resulting transform.
|
||||
|
@ -440,7 +405,7 @@ where
|
|||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
self.pre_transform(&Transform2D::create_translation(v.x, v.y))
|
||||
Transform2D::translation(v.x, v.y).then(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,13 +416,13 @@ where
|
|||
{
|
||||
/// Returns a rotation transform.
|
||||
#[inline]
|
||||
pub fn create_rotation(theta: Angle<T>) -> Self {
|
||||
pub fn rotation(theta: Angle<T>) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
let cos = theta.get().cos();
|
||||
let sin = theta.get().sin();
|
||||
Transform2D::row_major(
|
||||
cos, _0 - sin,
|
||||
sin, cos,
|
||||
Transform2D::new(
|
||||
cos, sin,
|
||||
_0 - sin, cos,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
@ -465,15 +430,15 @@ where
|
|||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn post_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.post_transform(&Transform2D::create_rotation(theta))
|
||||
pub fn then_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.then(&Transform2D::rotation(theta))
|
||||
}
|
||||
|
||||
/// Applies a rotation before self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.pre_transform(&Transform2D::create_rotation(theta))
|
||||
Transform2D::rotation(theta).then(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,13 +452,13 @@ impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
/// 0 0
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn create_scale(x: T, y: T) -> Self
|
||||
pub fn scale(x: T, y: T) -> Self
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
let _0 = || Zero::zero();
|
||||
|
||||
Self::row_major(
|
||||
Self::new(
|
||||
x, _0(),
|
||||
_0(), y,
|
||||
_0(), _0(),
|
||||
|
@ -503,11 +468,11 @@ impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
/// Applies a scale after self's transformation and returns the resulting transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn post_scale(&self, x: T, y: T) -> Self
|
||||
pub fn then_scale(&self, x: T, y: T) -> Self
|
||||
where
|
||||
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
|
||||
{
|
||||
self.post_transform(&Transform2D::create_scale(x, y))
|
||||
self.then(&Transform2D::scale(x, y))
|
||||
}
|
||||
|
||||
/// Applies a scale before self's transformation and returns the resulting transform.
|
||||
|
@ -517,7 +482,7 @@ impl<T, Src, Dst> Transform2D<T, Src, Dst> {
|
|||
where
|
||||
T: Copy + Mul<Output = T>,
|
||||
{
|
||||
Transform2D::row_major(
|
||||
Transform2D::new(
|
||||
self.m11 * x, self.m12 * x,
|
||||
self.m21 * y, self.m22 * y,
|
||||
self.m31, self.m32
|
||||
|
@ -531,8 +496,6 @@ where
|
|||
T: Copy + Add<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Returns the given point transformed by this transform.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to `p * self`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> {
|
||||
|
@ -543,8 +506,6 @@ where
|
|||
}
|
||||
|
||||
/// Returns the given vector transformed by this matrix.
|
||||
///
|
||||
/// Assuming row vectors, this is equivalent to `v * self`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T, Dst> {
|
||||
|
@ -556,7 +517,7 @@ where
|
|||
/// transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst>
|
||||
pub fn outer_transformed_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst>
|
||||
where
|
||||
T: Sub<Output = T> + Zero + PartialOrd,
|
||||
{
|
||||
|
@ -569,6 +530,23 @@ where
|
|||
self.transform_point(point2(min.x, max.y)),
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
/// Returns a box that encompasses the result of transforming the given box by this
|
||||
/// transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn outer_transformed_box(&self, b: &Box2D<T, Src>) -> Box2D<T, Dst>
|
||||
where
|
||||
T: Sub<Output = T> + Zero + PartialOrd,
|
||||
{
|
||||
Box2D::from_points(&[
|
||||
self.transform_point(b.min),
|
||||
self.transform_point(b.max),
|
||||
self.transform_point(point2(b.max.x, b.min.y)),
|
||||
self.transform_point(point2(b.min.x, b.max.y)),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -600,7 +578,7 @@ where
|
|||
}
|
||||
|
||||
let inv_det = _1 / det;
|
||||
Some(Transform2D::row_major(
|
||||
Some(Transform2D::new(
|
||||
inv_det * self.m22,
|
||||
inv_det * (_0 - self.m12),
|
||||
inv_det * (_0 - self.m21),
|
||||
|
@ -611,7 +589,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl <T, Src, Dst> Default for Transform2D<T, Src, Dst>
|
||||
where T: Zero + One
|
||||
{
|
||||
|
@ -642,7 +619,7 @@ where T: Copy + fmt::Debug +
|
|||
if self.is_identity() {
|
||||
write!(f, "[I]")
|
||||
} else {
|
||||
self.to_row_major_array().fmt(f)
|
||||
self.to_array().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -686,35 +663,35 @@ mod test {
|
|||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mat::create_translation(1.0, 2.0);
|
||||
let t1 = Mat::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));
|
||||
let t3 = Mat::identity().then_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_transform(&t1), Mat::create_translation(2.0, 4.0));
|
||||
assert_eq!(t1.then(&t1), Mat::translation(2.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let r1 = Mat::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));
|
||||
let r3 = Mat::identity().then_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.transform_point(Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(-2.0, 1.0)));
|
||||
|
||||
assert!(r1.post_transform(&r1).approx_eq(&Mat::create_rotation(rad(FRAC_PI_2*2.0))));
|
||||
assert!(r1.then(&r1).approx_eq(&Mat::rotation(rad(FRAC_PI_2*2.0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mat::create_scale(2.0, 3.0);
|
||||
let s1 = Mat::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);
|
||||
let s3 = Mat::identity().then_scale(2.0, 3.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
|
@ -723,26 +700,10 @@ mod test {
|
|||
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post_scale() {
|
||||
let m = Mat::create_rotation(rad(FRAC_PI_2)).post_translate(vec2(6.0, 7.0));
|
||||
let s = Mat::create_scale(2.0, 3.0);
|
||||
assert_eq!(m.post_transform(&s), m.post_scale(2.0, 3.0));
|
||||
assert_eq!(m.pre_transform(&s), m.pre_scale(2.0, 3.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,
|
||||
)
|
||||
);
|
||||
pub fn test_pre_then_scale() {
|
||||
let m = Mat::rotation(rad(FRAC_PI_2)).then_translate(vec2(6.0, 7.0));
|
||||
let s = Mat::scale(2.0, 3.0);
|
||||
assert_eq!(m.then(&s), m.then_scale(2.0, 3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -754,42 +715,40 @@ mod test {
|
|||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mat::create_scale(1.5, 0.3);
|
||||
let m1 = Mat::scale(1.5, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_transform(&m2).approx_eq(&Mat::identity()));
|
||||
assert!(m1.then(&m2).approx_eq(&Mat::identity()));
|
||||
assert!(m2.then(&m1).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mat::create_translation(-132.0, 0.3);
|
||||
let m1 = Mat::translation(-132.0, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_transform(&m2).approx_eq(&Mat::identity()));
|
||||
assert!(m1.then(&m2).approx_eq(&Mat::identity()));
|
||||
assert!(m2.then(&m1).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());
|
||||
assert!(Mat::scale(2.0, 0.0).inverse().is_none());
|
||||
assert!(Mat::scale(2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = default::Transform2D::identity().post_scale(1.0, 2.0).post_translate(vec2(1.0, 2.0));
|
||||
let m1 = default::Transform2D::identity().then_scale(1.0, 2.0).then_translate(vec2(1.0, 2.0));
|
||||
let m2 = default::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 r = Mat::rotation(rad(FRAC_PI_2));
|
||||
let t = Mat::translation(2.0, 3.0);
|
||||
|
||||
let a = Point2D::new(1.0, 1.0);
|
||||
|
||||
assert!(r.post_transform(&t).transform_point(a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.post_transform(&r).transform_point(a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.post_transform(&r).transform_point(a).approx_eq(&r.transform_point(t.transform_point(a))));
|
||||
|
||||
assert!(r.pre_transform(&t).transform_point(a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.pre_transform(&r).transform_point(a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.pre_transform(&r).transform_point(a).approx_eq(&t.transform_point(r.transform_point(a))));
|
||||
assert!(r.then(&t).transform_point(a).approx_eq(&Point2D::new(1.0, 4.0)));
|
||||
assert!(t.then(&r).transform_point(a).approx_eq(&Point2D::new(-4.0, 3.0)));
|
||||
assert!(t.then(&r).transform_point(a).approx_eq(&r.transform_point(t.transform_point(a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -803,14 +762,14 @@ mod test {
|
|||
pub fn test_is_identity() {
|
||||
let m1 = default::Transform2D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec2(0.1, 0.0));
|
||||
let m2 = m1.then_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 m1 = Mat::translation(1.0, 1.0);
|
||||
let v1 = vec2(10.0, -10.0);
|
||||
assert_eq!(v1, m1.transform_vector(v1));
|
||||
}
|
||||
|
@ -818,7 +777,7 @@ mod test {
|
|||
#[cfg(feature = "mint")]
|
||||
#[test]
|
||||
pub fn test_mint() {
|
||||
let m1 = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let m1 = Mat::rotation(rad(FRAC_PI_2));
|
||||
let mm: mint::RowMatrix3x2<_> = m1.into();
|
||||
let m2 = Mat::from(mm);
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -271,7 +271,7 @@ where
|
|||
T: Zero + One,
|
||||
{
|
||||
fn into(self) -> Transform2D<T, Src, Dst> {
|
||||
Transform2D::create_translation(self.x, self.y)
|
||||
Transform2D::translation(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,12 +290,6 @@ impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation2D<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for Translation2D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
/// A 3d transformation from a space to another that can only express translations.
|
||||
///
|
||||
/// The main benefit of this type over a Vector3D is the ability to cast
|
||||
|
@ -586,7 +580,7 @@ where
|
|||
T: Zero + One,
|
||||
{
|
||||
fn into(self) -> Transform3D<T, Src, Dst> {
|
||||
Transform3D::create_translation(self.x, self.y, self.z)
|
||||
Transform3D::translation(self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -605,12 +599,6 @@ impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation3D<T, Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for Translation3D<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod _2d {
|
||||
#[test]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"868408c94825c0f95dbe21fa82748313c26c62f3cc2d3ed7a162dec4f115b9f3","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"558d046c8c9ad79f23adef1e4b26c28c4599dcba56dadd60e2e7dc3a03ec2806","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"34c92cff76488fea23bee209fed62eee2db8c9687991c6413605961782e1317e","src/clip.rs":"e9ac9102f85709336088f3887b53273958aa9b2505d7ab06ba9a3e1e720aa540","src/lib.rs":"62613b64dacbfcd4727c5ce57e09bd9289db5a678f86dfe8544f1efc0f3f62b0","src/polygon.rs":"b8458d3c01dc06918b8410f363b19f15542522f1037cdd731f08b65e92a822bd","tests/clip.rs":"87cd6b0ae58fbcec438eea667eabd3ed6aba89aedb97cbd9bd4c8b4c2046523c","tests/main.rs":"f6dbeb9fedf1e1dafe6d0112845fbafe361ea96607933e37f26db3b7885023bb","tests/split.rs":"6a47ae030c7c95d3149c3710d651958922061bbdd923ee56762e07519f719344"},"package":"ffe16a646a08f4b4dd74035b9ff8e378eb1a4012a74f14f5889e7001cdbece33"}
|
||||
{"files":{"Cargo.toml":"bbf7428797ff113d822633c126ccf5e0e7584fe15c825048952623fdd99ab677","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"558d046c8c9ad79f23adef1e4b26c28c4599dcba56dadd60e2e7dc3a03ec2806","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"34c92cff76488fea23bee209fed62eee2db8c9687991c6413605961782e1317e","src/clip.rs":"e9ac9102f85709336088f3887b53273958aa9b2505d7ab06ba9a3e1e720aa540","src/lib.rs":"62613b64dacbfcd4727c5ce57e09bd9289db5a678f86dfe8544f1efc0f3f62b0","src/polygon.rs":"b8458d3c01dc06918b8410f363b19f15542522f1037cdd731f08b65e92a822bd","tests/clip.rs":"1a17f850cd770dbfb08c61779bf11f36f39e4315a00d3101c459e882b7982269","tests/main.rs":"5ebe911ddbb0e5449aacf060b977083c2bc43e3d8e5d5a3279d7e4950529f4a7","tests/split.rs":"fac076a403b87de1bdd0d11a54d36d0b02753868fc9cde5e5c5c1e03297da4ff"},"package":"2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "plane-split"
|
||||
version = "0.15.0"
|
||||
version = "0.17.0"
|
||||
authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
|
||||
description = "Plane splitting"
|
||||
documentation = "https://docs.rs/plane-split"
|
||||
|
@ -23,7 +23,7 @@ repository = "https://github.com/servo/plane-split"
|
|||
version = "0.1.2"
|
||||
|
||||
[dependencies.euclid]
|
||||
version = "0.20"
|
||||
version = "0.22"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
|
|
@ -97,10 +97,10 @@ fn clip_repeat() {
|
|||
#[test]
|
||||
fn clip_transformed() {
|
||||
let t_rot: Transform3D<f32, (), ()> =
|
||||
Transform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
|
||||
Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
|
||||
let t_div: Transform3D<f32, (), ()> =
|
||||
Transform3D::create_perspective(5.0);
|
||||
let transform = t_rot.post_transform(&t_div);
|
||||
Transform3D::perspective(5.0);
|
||||
let transform = t_rot.then(&t_div);
|
||||
|
||||
let polygon = Polygon::from_rect(rect(-10.0, -10.0, 20.0, 20.0), 0);
|
||||
let bounds: Rect<f32, ()> = rect(-1.0, -1.0, 2.0, 2.0);
|
||||
|
@ -125,7 +125,7 @@ fn clip_badly_transformed() {
|
|||
|
||||
#[test]
|
||||
fn clip_near_coplanar() {
|
||||
let tx = Transform3D::<f32, (), ()>::row_major(
|
||||
let tx = Transform3D::<f32, (), ()>::new(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
-960.0, -625.0, 1.0, -1.0,
|
||||
|
|
|
@ -87,7 +87,7 @@ fn test_transformed(rect: Rect<f32, ()>, transform: Transform3D<f32, (), ()>) {
|
|||
fn from_transformed_rect() {
|
||||
let rect = Rect::new(point2(10.0, 10.0), Size2D::new(20.0, 30.0));
|
||||
let transform =
|
||||
Transform3D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
|
||||
Transform3D::rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
|
||||
.pre_translate(vec3(0.0, 0.0, 10.0));
|
||||
test_transformed(rect, transform);
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ fn from_transformed_rect() {
|
|||
fn from_transformed_rect_perspective() {
|
||||
let rect = Rect::new(point2(-10.0, -5.0), Size2D::new(20.0, 30.0));
|
||||
let mut transform =
|
||||
Transform3D::create_perspective(400.0)
|
||||
Transform3D::perspective(400.0)
|
||||
.pre_translate(vec3(0.0, 0.0, 100.0));
|
||||
transform.m44 = 0.7; //for fun
|
||||
test_transformed(rect, transform);
|
||||
|
|
|
@ -8,7 +8,7 @@ use euclid::{Angle, Transform3D, Rect, rect, vec3};
|
|||
use plane_split::{BspSplitter, Polygon, Splitter, make_grid};
|
||||
|
||||
|
||||
fn grid_impl(count: usize, splitter: &mut Splitter<f32, (), usize>) {
|
||||
fn grid_impl(count: usize, splitter: &mut dyn Splitter<f32, (), usize>) {
|
||||
let polys = make_grid(count);
|
||||
let result = splitter.solve(&polys, vec3(0.0, 0.0, 1.0));
|
||||
assert_eq!(result.len(), count + count*count + count*count*count);
|
||||
|
@ -20,13 +20,13 @@ fn grid_bsp() {
|
|||
}
|
||||
|
||||
|
||||
fn sort_rotation(splitter: &mut Splitter<f32, (), usize>) {
|
||||
fn sort_rotation(splitter: &mut dyn Splitter<f32, (), usize>) {
|
||||
let transform0: Transform3D<f32, (), ()> =
|
||||
Transform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
|
||||
Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
|
||||
let transform1: Transform3D<f32, (), ()> =
|
||||
Transform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(0.0));
|
||||
Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(0.0));
|
||||
let transform2: Transform3D<f32, (), ()> =
|
||||
Transform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(FRAC_PI_4));
|
||||
Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(FRAC_PI_4));
|
||||
|
||||
let rect: Rect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let p1 = Polygon::from_transformed_rect(rect, transform0, 0);
|
||||
|
@ -46,11 +46,11 @@ fn rotation_bsp() {
|
|||
}
|
||||
|
||||
|
||||
fn sort_trivial(splitter: &mut Splitter<f32, (), usize>) {
|
||||
fn sort_trivial(splitter: &mut dyn Splitter<f32, (), usize>) {
|
||||
let anchors: Vec<_> = (0usize .. 10).collect();
|
||||
let rect: Rect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let polys: Vec<_> = anchors.iter().map(|&anchor| {
|
||||
let transform: Transform3D<f32, (), ()> = Transform3D::create_translation(0.0, 0.0, anchor as f32);
|
||||
let transform: Transform3D<f32, (), ()> = Transform3D::translation(0.0, 0.0, anchor as f32);
|
||||
let poly = Polygon::from_transformed_rect(rect, transform, anchor);
|
||||
assert!(poly.is_some(), "Cannot construct transformed polygons");
|
||||
poly.unwrap()
|
||||
|
@ -64,13 +64,13 @@ fn sort_trivial(splitter: &mut Splitter<f32, (), usize>) {
|
|||
assert_eq!(anchors1, anchors2);
|
||||
}
|
||||
|
||||
fn sort_external(splitter: &mut Splitter<f32, (), usize>) {
|
||||
fn sort_external(splitter: &mut dyn Splitter<f32, (), usize>) {
|
||||
let rect0: Rect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let poly0 = Polygon::from_rect(rect0, 0);
|
||||
let poly1 = {
|
||||
let transform0: Transform3D<f32, (), ()> = Transform3D::create_rotation(1.0, 0.0, 0.0, Angle::radians(2.0 * FRAC_PI_4));
|
||||
let transform1: Transform3D<f32, (), ()> = Transform3D::create_translation(0.0, 100.0, 0.0);
|
||||
Polygon::from_transformed_rect(rect0, transform1.pre_transform(&transform0), 1).unwrap()
|
||||
let transform0: Transform3D<f32, (), ()> = Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(2.0 * FRAC_PI_4));
|
||||
let transform1: Transform3D<f32, (), ()> = Transform3D::translation(0.0, 100.0, 0.0);
|
||||
Polygon::from_transformed_rect(rect0, transform0.then(&transform1), 1).unwrap()
|
||||
};
|
||||
|
||||
let result = splitter.solve(&[poly0, poly1], vec3(1.0, 1.0, 0.0).normalize());
|
||||
|
|
Загрузка…
Ссылка в новой задаче