зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1476636. Update Cargo lockfiles, re-vendor rust dependencies
--HG-- rename : third_party/rust/euclid-0.17.3/LICENSE-APACHE => third_party/rust/smallvec/LICENSE-APACHE
This commit is contained in:
Родитель
8ebc4e4580
Коммит
4e530d2f5f
|
@ -437,7 +437,7 @@ dependencies = [
|
|||
"proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -661,27 +661,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.17.3"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"hashglobe 0.1.0",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -811,7 +803,7 @@ dependencies = [
|
|||
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"selectors 0.19.0",
|
||||
"servo_arc 0.1.1",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"style 0.0.1",
|
||||
"style_traits 0.0.1",
|
||||
]
|
||||
|
@ -873,7 +865,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gleam"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1145,7 +1137,7 @@ dependencies = [
|
|||
"selectors 0.19.0",
|
||||
"servo_arc 0.1.1",
|
||||
"smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1502,7 +1494,7 @@ dependencies = [
|
|||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1566,11 +1558,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.9.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1814,7 +1806,7 @@ dependencies = [
|
|||
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo_arc 0.1.1",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1890,8 +1882,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.0"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
|
@ -1976,7 +1971,7 @@ dependencies = [
|
|||
"selectors 0.19.0",
|
||||
"servo_arc 0.1.1",
|
||||
"smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"style_derive 0.0.1",
|
||||
"style_traits 0.0.1",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2028,7 +2023,7 @@ dependencies = [
|
|||
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"selectors 0.19.0",
|
||||
"size_of_test 0.0.1",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"style 0.0.1",
|
||||
"style_traits 0.0.1",
|
||||
]
|
||||
|
@ -2354,18 +2349,18 @@ dependencies = [
|
|||
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_api 0.57.2",
|
||||
|
@ -2382,7 +2377,7 @@ dependencies = [
|
|||
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
|
||||
|
@ -2398,10 +2393,10 @@ dependencies = [
|
|||
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nsstring 0.1.0",
|
||||
"rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2596,7 +2591,6 @@ dependencies = [
|
|||
"checksum encoding_rs 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88a1b66a0d28af4b03a8c8278c6dcb90e6e600d89c14500a9e7a02e64b9ee3ac"
|
||||
"checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c95fd0d455f114291a3109286bd387bd423770058474a2d3f38b712cd661df60"
|
||||
"checksum euclid 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47d5eb6310c8dd3e79f973952ddcb180bf6a98c01d341add49126a094b5598cc"
|
||||
"checksum fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478"
|
||||
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
|
||||
|
@ -2612,7 +2606,7 @@ dependencies = [
|
|||
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
||||
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
|
||||
"checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a"
|
||||
"checksum gleam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e47b39459e47b76be4d2c82702932cdc66df09dcb8f813424167991adb8b3380"
|
||||
"checksum gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d41e7ac812597988fdae31c9baec3c6d35cadb8ad9ab88a9bf9c0f119ed66c2"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
|
||||
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
|
||||
|
@ -2678,7 +2672,7 @@ dependencies = [
|
|||
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
||||
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
|
||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||
"checksum plane-split 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7079b8485b4f9d9560dee7a69ca8f6ca781f9f284ff9d2bf27255d440b03e4af"
|
||||
"checksum plane-split 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e14382aabad89085fbf714f75d527492bb672725facb9b2ced2fada47cf418c"
|
||||
"checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
|
||||
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
"checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4"
|
||||
|
@ -2715,7 +2709,7 @@ dependencies = [
|
|||
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c63726029f0069f88467873e47f392575f28f9f16b72ac65465263db4b3a13c"
|
||||
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
|
||||
"checksum smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8"
|
||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||
"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
|
||||
"checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"301590735ff27f124c03cef8598aa5397c88c59aba3d058edf0bde532965c346","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"2d7ade90b1883e9ec1e718b52a7c1785adf7d8f482a3d2a2813079ad15de906b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6594377e8f6c20f88f628520d8de9b9a59c5892a0ee9a6ccd13c8400c1499911","src/homogen.rs":"df6bdb87aee7422c19cf8ce633c70656ebea789b96f6fcc396baecc4c3ef6ab3","src/length.rs":"5c0784bccb1840f1bc86f45c80094584ca2f60b6a644797a5e760341c15c6e50","src/lib.rs":"ef31060a582a8a133750aeaa7244cc6bbb709a0aec7d964a76b54643bb9f7134","src/macros.rs":"ccb9aeb942f571ec4207334b87c87f59a9a4d666039d143d7673372679c42347","src/num.rs":"4439479fad5729073e0bfe0b96b547672a237430d48e564519759b9550baa033","src/point.rs":"50ccf38962b2aee2b0b2e7c516f24e54908286953cb7cf97b5a6b9fb7bdfc91b","src/rect.rs":"b96f267123972d7d924d08d8b93bea9333d71654febe20063c532a11f7c7ae30","src/rotation.rs":"2686d8624671f48e9c657a98c9ac3345f3c4028e65ee3ef588d407ffd020fb86","src/scale.rs":"80c96c99cc916cac155fc898cd34a771a64ab46a60340cb7de876d224f0c7cb1","src/side_offsets.rs":"604e104616777515e0e0e68262110c55fe9c0ce4deeb6d022e5b4984df11e29f","src/size.rs":"ee722964a6e6654eacd8f321f5c3f62452237316d9d2dac8a98753f6227c4fce","src/transform2d.rs":"edf9b82411a25d8f6b2a867a5b579c15316b3fd112eb463f6589012039670be3","src/transform3d.rs":"797c445c99edace0a6e51e166cdbeb667620c6fd98cbc0249742bbd09588dc7f","src/trig.rs":"78b8fb26d2fded11c4b8aa47935e80c474696aee1999c688642534b667e005d9","src/vector.rs":"37215522068612107acca427c83159f4bca79ae41a2f54d9c7d0feb4b28b2348"},"package":"c95fd0d455f114291a3109286bd387bd423770058474a2d3f38b712cd661df60"}
|
|
@ -1,25 +0,0 @@
|
|||
language: rust
|
||||
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
||||
rust:
|
||||
- 1.23.0
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
env:
|
||||
- FEATURES=""
|
||||
- FEATURES="--features serde"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: nightly
|
||||
env: FEATURES="--features unstable"
|
||||
- rust: nightly
|
||||
env: FEATURES="--features unstable,serde"
|
||||
|
||||
script:
|
||||
- cargo build $FEATURES
|
||||
- cargo test --verbose $FEATURES
|
|
@ -1,5 +0,0 @@
|
|||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
option. All files in the project carrying such notice may not be
|
||||
copied, modified, or distributed except according to those terms.
|
|
@ -1,38 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "euclid"
|
||||
version = "0.17.3"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Geometry primitives"
|
||||
documentation = "https://docs.rs/euclid/"
|
||||
keywords = ["matrix", "vector", "linear-algebra", "geometry"]
|
||||
categories = ["science"]
|
||||
license = "MIT / Apache-2.0"
|
||||
repository = "https://github.com/servo/euclid"
|
||||
[dependencies.num-traits]
|
||||
version = "0.1.32"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["serde_derive"]
|
||||
optional = true
|
||||
[dev-dependencies.rand]
|
||||
version = "0.4"
|
||||
|
||||
[dev-dependencies.serde_test]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
unstable = []
|
|
@ -1,8 +0,0 @@
|
|||
# euclid
|
||||
|
||||
This is a small library for geometric types with a focus on 2d graphics and
|
||||
layout.
|
||||
|
||||
* [Documentation](https://docs.rs/euclid/)
|
||||
* [Release notes](https://github.com/servo/euclid/releases)
|
||||
* [crates.io](https://crates.io/crates/euclid)
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Trait for testing approximate equality
|
||||
pub trait ApproxEq<Eps> {
|
||||
fn approx_epsilon() -> Eps;
|
||||
fn approx_eq(&self, other: &Self) -> bool;
|
||||
fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! approx_eq {
|
||||
($ty:ty, $eps:expr) => (
|
||||
impl ApproxEq<$ty> for $ty {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> $ty { $eps }
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &$ty) -> bool {
|
||||
self.approx_eq_eps(other, &$eps)
|
||||
}
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &$ty, approx_epsilon: &$ty) -> bool {
|
||||
(*self - *other).abs() < *approx_epsilon
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
approx_eq!(f32, 1.0e-6);
|
||||
approx_eq!(f64, 1.0e-6);
|
|
@ -1,109 +0,0 @@
|
|||
// Copyright 2018 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use point::{TypedPoint2D, TypedPoint3D};
|
||||
use vector::{TypedVector2D, TypedVector3D};
|
||||
|
||||
use num::{One, Zero};
|
||||
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Div;
|
||||
|
||||
|
||||
define_matrix! {
|
||||
/// Homogeneous vector in 3D space.
|
||||
pub struct HomogeneousVector<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
pub w: T,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T, U> HomogeneousVector<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T, w: T) -> Self {
|
||||
HomogeneousVector { x, y, z, w, _unit: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> HomogeneousVector<T, U> {
|
||||
/// Convert into Cartesian 2D point.
|
||||
///
|
||||
/// Note: possible division by zero.
|
||||
#[inline]
|
||||
pub fn to_point2d(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x / self.w, self.y / self.w)
|
||||
}
|
||||
|
||||
/// Convert into Cartesian 3D point.
|
||||
///
|
||||
/// Note: possible division by zero.
|
||||
#[inline]
|
||||
pub fn to_point3d(&self) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x / self.w, self.y / self.w, self.z / self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> From<TypedVector2D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(v: TypedVector2D<T, U>) -> Self {
|
||||
HomogeneousVector::new(v.x, v.y, T::zero(), T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> From<TypedVector3D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(v: TypedVector3D<T, U>) -> Self {
|
||||
HomogeneousVector::new(v.x, v.y, v.z, T::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + One, U> From<TypedPoint2D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(p: TypedPoint2D<T, U>) -> Self {
|
||||
HomogeneousVector::new(p.x, p.y, T::zero(), T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: One, U> From<TypedPoint3D<T, U>> for HomogeneousVector<T, U> {
|
||||
#[inline]
|
||||
fn from(p: TypedPoint3D<T, U>) -> Self {
|
||||
HomogeneousVector::new(p.x, p.y, p.z, T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?},{:?})", self.x, self.y, self.z, self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for HomogeneousVector<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{},{},{})", self.x, self.y, self.z, self.w)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod homogeneous {
|
||||
use super::HomogeneousVector;
|
||||
use point::{Point2D, Point3D};
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
assert_eq!(Point2D::new(1.0, 2.0), HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d());
|
||||
assert_eq!(Point3D::new(1.0, -2.0, 0.1), HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d());
|
||||
}
|
||||
}
|
|
@ -1,515 +0,0 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A one-dimensional length, tagged with its units.
|
||||
|
||||
use scale::TypedScale;
|
||||
use num::Zero;
|
||||
|
||||
use num_traits::{NumCast, Saturating};
|
||||
use num::One;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use std::ops::{AddAssign, DivAssign, MulAssign, SubAssign};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
|
||||
/// A one-dimensional distance, with value represented by `T` and unit of measurement `Unit`.
|
||||
///
|
||||
/// `T` can be any numeric type, for example a primitive type like `u64` or `f32`.
|
||||
///
|
||||
/// `Unit` is not used in the representation of a `Length` value. It is used only at compile time
|
||||
/// to ensure that a `Length` stored with one unit is converted explicitly before being used in an
|
||||
/// expression that requires a different unit. It may be a type without values, such as an empty
|
||||
/// enum.
|
||||
///
|
||||
/// You can multiply a `Length` by a `scale::TypedScale` to convert it from one unit to
|
||||
/// another. See the [`TypedScale`] docs for an example.
|
||||
///
|
||||
/// [`TypedScale`]: struct.TypedScale.html
|
||||
#[repr(C)]
|
||||
pub struct Length<T, Unit>(pub T, PhantomData<Unit>);
|
||||
|
||||
impl<T: Clone, Unit> Clone for Length<T, Unit> {
|
||||
fn clone(&self) -> Self {
|
||||
Length(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Unit> Copy for Length<T, Unit> {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, Unit, T> Deserialize<'de> for Length<T, Unit>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Length(
|
||||
try!(Deserialize::deserialize(deserializer)),
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Unit> Serialize for Length<T, Unit>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Unit> Length<T, Unit> {
|
||||
pub fn new(x: T) -> Self {
|
||||
Length(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone> Length<T, Unit> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Clone, U> fmt::Debug for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + Clone, U> fmt::Display for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// length + length
|
||||
impl<U, T: Clone + Add<T, Output = T>> Add for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
fn add(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length += length
|
||||
impl<U, T: Clone + AddAssign<T>> AddAssign for Length<T, U> {
|
||||
fn add_assign(&mut self, other: Length<T, U>) {
|
||||
self.0 += other.get();
|
||||
}
|
||||
}
|
||||
|
||||
// length - length
|
||||
impl<U, T: Clone + Sub<T, Output = T>> Sub<Length<T, U>> for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
fn sub(self, other: Length<T, U>) -> <Self as Sub>::Output {
|
||||
Length::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length -= length
|
||||
impl<U, T: Clone + SubAssign<T>> SubAssign for Length<T, U> {
|
||||
fn sub_assign(&mut self, other: Length<T, U>) {
|
||||
self.0 -= other.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Saturating length + length and length - length.
|
||||
impl<U, T: Clone + Saturating> Saturating for Length<T, U> {
|
||||
fn saturating_add(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get().saturating_add(other.get()))
|
||||
}
|
||||
|
||||
fn saturating_sub(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get().saturating_sub(other.get()))
|
||||
}
|
||||
}
|
||||
|
||||
// length / length
|
||||
impl<Src, Dst, T: Clone + Div<T, Output = T>> Div<Length<T, Src>> for Length<T, Dst> {
|
||||
type Output = TypedScale<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn div(self, other: Length<T, Src>) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get() / other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length * scalar
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for Length<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
Length::new(self.get() * scale)
|
||||
}
|
||||
}
|
||||
|
||||
// length *= scalar
|
||||
impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
// length / scalar
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for Length<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
Length::new(self.get() / scale)
|
||||
}
|
||||
}
|
||||
|
||||
// length /= scalar
|
||||
impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for Length<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
// length * scaleFactor
|
||||
impl<Src, Dst, T: Clone + Mul<T, Output = T>> Mul<TypedScale<T, Src, Dst>> for Length<T, Src> {
|
||||
type Output = Length<T, Dst>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, Src, Dst>) -> Length<T, Dst> {
|
||||
Length::new(self.get() * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length / scaleFactor
|
||||
impl<Src, Dst, T: Clone + Div<T, Output = T>> Div<TypedScale<T, Src, Dst>> for Length<T, Dst> {
|
||||
type Output = Length<T, Src>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, Src, Dst>) -> Length<T, Src> {
|
||||
Length::new(self.get() / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// -length
|
||||
impl<U, T: Clone + Neg<Output = T>> Neg for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
#[inline]
|
||||
fn neg(self) -> Length<T, U> {
|
||||
Length::new(-self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T0: NumCast + Clone> Length<T0, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> Option<Length<T1, Unit>> {
|
||||
NumCast::from(self.get()).map(Length::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialEq> PartialEq for Length<T, Unit> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.get().eq(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialOrd> PartialOrd for Length<T, Unit> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.get().partial_cmp(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + Eq> Eq for Length<T, Unit> {}
|
||||
|
||||
impl<Unit, T: Clone + Ord> Ord for Length<T, Unit> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.get().cmp(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Zero> Zero for Length<T, Unit> {
|
||||
fn zero() -> Self {
|
||||
Length::new(Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Length<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this length and another length.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
Length::new(one_t * self.get() + t * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Length;
|
||||
use num::Zero;
|
||||
|
||||
use num_traits::Saturating;
|
||||
use scale::TypedScale;
|
||||
use std::f32::INFINITY;
|
||||
|
||||
enum Inch {}
|
||||
enum Mm {}
|
||||
enum Cm {}
|
||||
enum Second {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde {
|
||||
use super::*;
|
||||
|
||||
extern crate serde_test;
|
||||
use self::serde_test::Token;
|
||||
use self::serde_test::assert_tokens;
|
||||
|
||||
#[test]
|
||||
fn test_length_serde() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
assert_tokens(&one_cm, &[Token::F32(10.0)]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
// A cloned Length is a separate length with the state matching the
|
||||
// original Length at the point it was cloned.
|
||||
let mut variable_length: Length<f32, Inch> = Length::new(12.0);
|
||||
|
||||
let one_foot = variable_length.clone();
|
||||
variable_length.0 = 24.0;
|
||||
|
||||
assert_eq!(one_foot.get(), 12.0);
|
||||
assert_eq!(variable_length.get(), 24.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_clones_length_value() {
|
||||
// Calling get returns a clone of the Length's value.
|
||||
// To test this, we need something clone-able - hence a vector.
|
||||
let mut length: Length<Vec<i32>, Inch> = Length::new(vec![1, 2, 3]);
|
||||
|
||||
let value = length.get();
|
||||
length.0.push(4);
|
||||
|
||||
assert_eq!(value, vec![1, 2, 3]);
|
||||
assert_eq!(length.get(), vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 + length2;
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement += one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), 15.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 - length2;
|
||||
|
||||
assert_eq!(result.get(), 245);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement -= one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), -5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(6);
|
||||
|
||||
let result = length1.saturating_add(length2);
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(5);
|
||||
let length2: Length<u8, Mm> = Length::new(10);
|
||||
|
||||
let result = length1.saturating_sub(length2);
|
||||
|
||||
assert_eq!(result.get(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_length() {
|
||||
// Division results in a TypedScale from denominator units
|
||||
// to numerator units.
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let duration: Length<f32, Second> = Length::new(10.0);
|
||||
|
||||
let result = length / duration;
|
||||
|
||||
let expected: TypedScale<f32, Second, Cm> = TypedScale::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let length_mm: Length<f32, Mm> = Length::new(10.0);
|
||||
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
|
||||
|
||||
let result = length_mm * cm_per_mm;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(1.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication_with_scalar() {
|
||||
let length_mm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = length_mm * 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(20.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication_assignment() {
|
||||
let mut length: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
length *= 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(20.0);
|
||||
assert_eq!(length, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalefactor() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let cm_per_second: TypedScale<f32, Second, Cm> = TypedScale::new(10.0);
|
||||
|
||||
let result = length / cm_per_second;
|
||||
|
||||
let expected: Length<f32, Second> = Length::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalar() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = length / 2.0;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(2.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_assignment() {
|
||||
let mut length: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
length /= 2.0;
|
||||
|
||||
let expected: Length<f32, Mm> = Length::new(5.0);
|
||||
assert_eq!(length, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = -length;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(-5.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
let length_as_i32: Length<i32, Cm> = Length::new(5);
|
||||
|
||||
let result: Length<f32, Cm> = length_as_i32.cast().unwrap();
|
||||
|
||||
let length_as_f32: Length<f32, Cm> = Length::new(5.0);
|
||||
assert_eq!(result, length_as_f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 == length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_0 != length_5_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 < length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_1 > length_5_point_0);
|
||||
assert!(length_5_point_1 >= length_5_point_0);
|
||||
assert!(length_5_point_0 >= length_5_point_1 - length_0_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_add() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
|
||||
let result = length - LengthCm::zero();
|
||||
|
||||
assert_eq!(result, length);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_division() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
let length_zero: LengthCm = Length::zero();
|
||||
|
||||
let result = length / length_zero;
|
||||
|
||||
let expected: TypedScale<f32, Cm, Cm> = TypedScale::new(INFINITY);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "unstable", feature(fn_must_use))]
|
||||
|
||||
//! A collection of strongly typed math tools for computer graphics with an inclination
|
||||
//! towards 2d graphics and layout.
|
||||
//!
|
||||
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
|
||||
//! and tagged with a generic Unit parameter which is useful to prevent mixing
|
||||
//! values from different spaces. For example it should not be legal to translate
|
||||
//! a screen-space position by a world-space vector and this can be expressed using
|
||||
//! the generic Unit parameter.
|
||||
//!
|
||||
//! This unit system is not mandatory and all Typed* structures have an alias
|
||||
//! with the default unit: `UnknownUnit`.
|
||||
//! for example ```Point2D<T>``` is equivalent to ```TypedPoint2D<T, UnknownUnit>```.
|
||||
//! Client code typically creates a set of aliases for each type and doesn't need
|
||||
//! to deal with the specifics of typed units further. For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use euclid::*;
|
||||
//! pub struct ScreenSpace;
|
||||
//! pub type ScreenPoint = TypedPoint2D<f32, ScreenSpace>;
|
||||
//! pub type ScreenSize = TypedSize2D<f32, ScreenSpace>;
|
||||
//! pub struct WorldSpace;
|
||||
//! pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
|
||||
//! pub type ProjectionMatrix = TypedTransform3D<f32, WorldSpace, ScreenSpace>;
|
||||
//! // etc...
|
||||
//! ```
|
||||
//!
|
||||
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
|
||||
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
|
||||
//!
|
||||
//! Components are accessed in their scalar form by default for convenience, and most
|
||||
//! types additionally implement strongly typed accessors which return typed ```Length``` wrappers.
|
||||
//! For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use euclid::*;
|
||||
//! # pub struct WorldSpace;
|
||||
//! # pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
|
||||
//! let p = WorldPoint::new(0.0, 1.0, 1.0);
|
||||
//! // p.x is an f32.
|
||||
//! println!("p.x = {:?} ", p.x);
|
||||
//! // p.x is a Length<f32, WorldSpace>.
|
||||
//! println!("p.x_typed() = {:?} ", p.x_typed());
|
||||
//! // Length::get returns the scalar value (f32).
|
||||
//! assert_eq!(p.x, p.x_typed().get());
|
||||
//! ```
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
extern crate num_traits;
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
|
||||
pub use length::Length;
|
||||
pub use scale::TypedScale;
|
||||
pub use transform2d::{Transform2D, TypedTransform2D};
|
||||
pub use transform3d::{Transform3D, TypedTransform3D};
|
||||
pub use point::{Point2D, Point3D, TypedPoint2D, TypedPoint3D, point2, point3};
|
||||
pub use vector::{TypedVector2D, TypedVector3D, Vector2D, Vector3D, vec2, vec3};
|
||||
pub use vector::{BoolVector2D, BoolVector3D, bvec2, bvec3};
|
||||
pub use homogen::HomogeneousVector;
|
||||
|
||||
pub use rect::{rect, Rect, TypedRect};
|
||||
pub use rotation::{Angle, Rotation2D, Rotation3D, TypedRotation2D, TypedRotation3D};
|
||||
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
|
||||
pub use size::{Size2D, TypedSize2D, size2};
|
||||
pub use trig::Trig;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod approxeq;
|
||||
mod homogen;
|
||||
pub mod num;
|
||||
mod length;
|
||||
mod point;
|
||||
mod rect;
|
||||
mod rotation;
|
||||
mod scale;
|
||||
mod side_offsets;
|
||||
mod size;
|
||||
mod transform2d;
|
||||
mod transform3d;
|
||||
mod trig;
|
||||
mod vector;
|
||||
|
||||
/// The default unit.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UnknownUnit;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type Matrix2D<T> = Transform2D<T>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type TypedMatrix2D<T, Src, Dst> = TypedTransform2D<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type Matrix4D<T> = Transform3D<T>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type TypedMatrix4D<T, Src, Dst> = TypedTransform3D<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type ScaleFactor<T, Src, Dst> = TypedScale<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub use Angle as Radians;
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
macro_rules! define_matrix {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $name:ident<T, $($phantom:ident),+> {
|
||||
$(pub $field:ident: T,)+
|
||||
}
|
||||
) => (
|
||||
#[repr(C)]
|
||||
$(#[$attr])*
|
||||
pub struct $name<T, $($phantom),+> {
|
||||
$(pub $field: T,)+
|
||||
_unit: PhantomData<($($phantom),+)>
|
||||
}
|
||||
|
||||
impl<T: Clone, $($phantom),+> Clone for $name<T, $($phantom),+> {
|
||||
fn clone(&self) -> Self {
|
||||
$name {
|
||||
$($field: self.$field.clone(),)+
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, $($phantom),+> Copy for $name<T, $($phantom),+> {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, $($phantom),+> ::serde::Deserialize<'de> for $name<T, $($phantom),+>
|
||||
where T: ::serde::Deserialize<'de>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: ::serde::Deserializer<'de>
|
||||
{
|
||||
let ($($field,)+) =
|
||||
try!(::serde::Deserialize::deserialize(deserializer));
|
||||
Ok($name {
|
||||
$($field: $field,)+
|
||||
_unit: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, $($phantom),+> ::serde::Serialize for $name<T, $($phantom),+>
|
||||
where T: ::serde::Serialize
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ::serde::Serializer
|
||||
{
|
||||
($(&self.$field,)+).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::std::cmp::Eq for $name<T, $($phantom),+>
|
||||
where T: ::std::cmp::Eq {}
|
||||
|
||||
impl<T, $($phantom),+> ::std::cmp::PartialEq for $name<T, $($phantom),+>
|
||||
where T: ::std::cmp::PartialEq
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
true $(&& self.$field == other.$field)+
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::std::hash::Hash for $name<T, $($phantom),+>
|
||||
where T: ::std::hash::Hash
|
||||
{
|
||||
fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) {
|
||||
$(self.$field.hash(h);)+
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A one-dimensional length, tagged with its units.
|
||||
|
||||
use num_traits;
|
||||
|
||||
pub trait Zero {
|
||||
fn zero() -> Self;
|
||||
}
|
||||
|
||||
impl<T: num_traits::Zero> Zero for T {
|
||||
fn zero() -> T {
|
||||
num_traits::Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait One {
|
||||
fn one() -> Self;
|
||||
}
|
||||
|
||||
impl<T: num_traits::One> One for T {
|
||||
fn one() -> T {
|
||||
num_traits::One::one()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Round: Copy {
|
||||
fn round(self) -> Self;
|
||||
}
|
||||
pub trait Floor: Copy {
|
||||
fn floor(self) -> Self;
|
||||
}
|
||||
pub trait Ceil: Copy {
|
||||
fn ceil(self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! num_int {
|
||||
($ty:ty) => (
|
||||
impl Round for $ty {
|
||||
#[inline]
|
||||
fn round(self) -> $ty { self }
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty { self }
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty { self }
|
||||
}
|
||||
)
|
||||
}
|
||||
macro_rules! num_float {
|
||||
($ty:ty) => (
|
||||
impl Round for $ty {
|
||||
#[inline]
|
||||
fn round(self) -> $ty { self.round() }
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty { self.floor() }
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty { self.ceil() }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
num_int!(i16);
|
||||
num_int!(u16);
|
||||
num_int!(i32);
|
||||
num_int!(u32);
|
||||
num_int!(i64);
|
||||
num_int!(u64);
|
||||
num_int!(isize);
|
||||
num_int!(usize);
|
||||
num_float!(f32);
|
||||
num_float!(f64);
|
|
@ -1,899 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use approxeq::ApproxEq;
|
||||
use length::Length;
|
||||
use scale::TypedScale;
|
||||
use size::TypedSize2D;
|
||||
use num::*;
|
||||
use num_traits::{Float, NumCast};
|
||||
use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d Point tagged with a unit.
|
||||
pub struct TypedPoint2D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d point type with no unit.
|
||||
///
|
||||
/// `Point2D` provides the same methods as `TypedPoint2D`.
|
||||
pub type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint2D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn origin() -> Self {
|
||||
point2(Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Self::origin()
|
||||
}
|
||||
|
||||
/// Convert into a 3d point.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> TypedPoint3D<T, U> {
|
||||
point3(self.x, self.y, Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint2D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
TypedPoint2D {
|
||||
x: x,
|
||||
y: y,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint2D<T, U> {
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>) -> Self {
|
||||
point2(x.0, y.0)
|
||||
}
|
||||
|
||||
/// Create a 3d point from this one, using the specified z value.
|
||||
#[inline]
|
||||
pub fn extend(&self, z: T) -> TypedPoint3D<T, U> {
|
||||
point3(self.x, self.y, z)
|
||||
}
|
||||
|
||||
/// Cast this point into a vector.
|
||||
///
|
||||
/// Equivalent to subtracting the origin from this point.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Swap x and y.
|
||||
#[inline]
|
||||
pub fn yx(&self) -> Self {
|
||||
point2(self.y, self.x)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.x)
|
||||
}
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.y)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point2D<T> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point2D<T>) -> Self {
|
||||
point2(p.x, p.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
pub fn add_size(&self, other: &TypedSize2D<T, U>) -> Self {
|
||||
point2(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add<TypedSize2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: TypedSize2D<T, U>) -> Self {
|
||||
point2(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> AddAssign<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: TypedVector2D<T, U>) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> SubAssign<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: TypedVector2D<T, U>) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: TypedVector2D<T, U>) -> Self {
|
||||
point2(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedPoint2D<T, U> {
|
||||
type Output = TypedVector2D<T, U>;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: TypedVector2D<T, U>) -> Self {
|
||||
point2(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
point2(self.x.min(other.x), self.y.min(other.y))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
point2(self.x.max(other.x), self.y.max(other.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
point2(self.x * scale, self.y * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedPoint2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
point2(self.x / scale, self.y / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedPoint2D<T, U1> {
|
||||
type Output = TypedPoint2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedPoint2D<T, U2> {
|
||||
point2(self.x * scale.get(), self.y * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedPoint2D<T, U2> {
|
||||
type Output = TypedPoint2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, U1, U2>) -> TypedPoint2D<T, U1> {
|
||||
point2(self.x / scale.get(), self.y / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.round() == { 0.0, -1.0 }`.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round(&self) -> Self {
|
||||
point2(self.x.round(), self.y.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.ceil() == { 0.0, 0.0 }`.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn ceil(&self) -> Self {
|
||||
point2(self.x.ceil(), self.y.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.floor() == { -1.0, -1.0 }`.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn floor(&self) -> Self {
|
||||
point2(self.x.floor(), self.y.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint2D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint2D<NewT, U>> {
|
||||
match (NumCast::from(self.x), NumCast::from(self.y)) {
|
||||
(Some(x), Some(y)) => Some(point2(x, y)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedPoint2D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` point.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> TypedPoint2D<f64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> TypedPoint2D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> TypedPoint2D<u32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i32 point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> TypedPoint2D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i64 point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> TypedPoint2D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint2D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this point and another point.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
point2(one_t * self.x + t * other.x, one_t * self.y + t * other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + ApproxEq<T>, U> ApproxEq<TypedPoint2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
point2(T::approx_epsilon(), T::approx_epsilon())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Into<[T; 2]> for TypedPoint2D<T, U> {
|
||||
fn into(self) -> [T; 2] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 2]> for TypedPoint2D<T, U> {
|
||||
fn from(array: [T; 2]) -> Self {
|
||||
point2(array[0], array[1])
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d Point tagged with a unit.
|
||||
pub struct TypedPoint3D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 3d point type with no unit.
|
||||
///
|
||||
/// `Point3D` provides the same methods as `TypedPoint3D`.
|
||||
pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint3D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn origin() -> Self {
|
||||
point3(Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + One, U> TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
pub fn to_array_4d(&self) -> [T; 4] {
|
||||
[self.x, self.y, self.z, One::one()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint3D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this point and another point.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
point3(
|
||||
one_t * self.x + t * other.x,
|
||||
one_t * self.y + t * other.y,
|
||||
one_t * self.z + t * other.z,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint3D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T) -> Self {
|
||||
TypedPoint3D {
|
||||
x: x,
|
||||
y: y,
|
||||
z: z,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>, z: Length<T, U>) -> Self {
|
||||
point3(x.0, y.0, z.0)
|
||||
}
|
||||
|
||||
/// Cast this point into a vector.
|
||||
///
|
||||
/// Equivalent to subtracting the origin to this point.
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector3D<T, U> {
|
||||
vec3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and y coordinates
|
||||
#[inline]
|
||||
pub fn xy(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and z coordinates
|
||||
#[inline]
|
||||
pub fn xz(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.x, self.z)
|
||||
}
|
||||
|
||||
/// Returns a 2d point using this point's x and z coordinates
|
||||
#[inline]
|
||||
pub fn yz(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.y, self.z)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.x)
|
||||
}
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.y)
|
||||
}
|
||||
|
||||
/// Returns self.z as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn z_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] {
|
||||
[self.x, self.y, self.z]
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point3D<T> {
|
||||
point3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point3D<T>) -> Self {
|
||||
point3(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
/// Convert into a 2d point.
|
||||
#[inline]
|
||||
pub fn to_2d(&self) -> TypedPoint2D<T, U> {
|
||||
self.xy()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> AddAssign<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: TypedVector3D<T, U>) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> SubAssign<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: TypedVector3D<T, U>) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: TypedVector3D<T, U>) -> Self {
|
||||
point3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedPoint3D<T, U> {
|
||||
type Output = TypedVector3D<T, U>;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> TypedVector3D<T, U> {
|
||||
vec3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: TypedVector3D<T, U>) -> Self {
|
||||
point3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
point3(self.x * scale, self.y * scale, self.z * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
point3(self.x / scale, self.y / scale, self.z / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
point3(
|
||||
self.x.min(other.x),
|
||||
self.y.min(other.y),
|
||||
self.z.min(other.z),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
point3(
|
||||
self.x.max(other.x),
|
||||
self.y.max(other.y),
|
||||
self.z.max(other.z),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round(&self) -> Self {
|
||||
point3(self.x.round(), self.y.round(), self.z.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn ceil(&self) -> Self {
|
||||
point3(self.x.ceil(), self.y.ceil(), self.z.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn floor(&self) -> Self {
|
||||
point3(self.x.floor(), self.y.floor(), self.z.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint3D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint3D<NewT, U>> {
|
||||
match (
|
||||
NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
NumCast::from(self.z),
|
||||
) {
|
||||
(Some(x), Some(y), Some(z)) => Some(point3(x, y, z)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedPoint3D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` point.
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> TypedPoint3D<f64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> TypedPoint3D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> TypedPoint3D<u32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> TypedPoint3D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> TypedPoint3D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + ApproxEq<T>, U> ApproxEq<TypedPoint3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
point3(
|
||||
T::approx_epsilon(),
|
||||
T::approx_epsilon(),
|
||||
T::approx_epsilon(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y) && self.z.approx_eq(&other.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
&& self.z.approx_eq_eps(&other.z, &eps.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Into<[T; 3]> for TypedPoint3D<T, U> {
|
||||
fn into(self) -> [T; 3] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 3]> for TypedPoint3D<T, U> {
|
||||
fn from(array: [T; 3]) -> Self {
|
||||
point3(array[0], array[1], array[2])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point2<T: Copy, U>(x: T, y: T) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(x, y)
|
||||
}
|
||||
|
||||
pub fn point3<T: Copy, U>(x: T, y: T, z: T) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(x, y, z)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point2d {
|
||||
use super::Point2D;
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1: Point2D<f32> = Point2D::new(3.0, 5.0);
|
||||
|
||||
let result = p1 * 5.0;
|
||||
|
||||
assert_eq!(result, Point2D::new(15.0, 25.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point2D::new(1.0, 3.0);
|
||||
let p2 = Point2D::new(2.0, 2.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point2D::new(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point2D::new(1.0, 3.0);
|
||||
let p2 = Point2D::new(2.0, 2.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point2D::new(2.0, 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod typedpoint2d {
|
||||
use super::{Point2D, TypedPoint2D, point2};
|
||||
use scale::TypedScale;
|
||||
use vector::vec2;
|
||||
|
||||
pub enum Mm {}
|
||||
pub enum Cm {}
|
||||
|
||||
pub type Point2DMm<T> = TypedPoint2D<T, Mm>;
|
||||
pub type Point2DCm<T> = TypedPoint2D<T, Cm>;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Point2DMm::new(1.0, 2.0);
|
||||
let p2 = vec2(3.0, 4.0);
|
||||
|
||||
let result = p1 + p2;
|
||||
|
||||
assert_eq!(result, Point2DMm::new(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_assign() {
|
||||
let mut p1 = Point2DMm::new(1.0, 2.0);
|
||||
p1 += vec2(3.0, 4.0);
|
||||
|
||||
assert_eq!(p1, Point2DMm::new(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1 = Point2DMm::new(1.0, 2.0);
|
||||
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
|
||||
|
||||
let result = p1 * cm_per_mm;
|
||||
|
||||
assert_eq!(result, Point2DCm::new(0.1, 0.2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_conv_vector() {
|
||||
use {Point2D, point2};
|
||||
|
||||
for i in 0..100 {
|
||||
// We don't care about these values as long as they are not the same.
|
||||
let x = i as f32 * 0.012345;
|
||||
let y = i as f32 * 0.987654;
|
||||
let p: Point2D<f32> = point2(x, y);
|
||||
assert_eq!(p.to_vector().to_point(), p);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_swizzling() {
|
||||
let p: Point2D<i32> = point2(1, 2);
|
||||
assert_eq!(p.yx(), point2(2, 1));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point3d {
|
||||
use super::{Point3D, point2, point3};
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p2 = Point3D::new(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point3D::new(1.0, 2.0, -1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p2 = Point3D::new(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point3D::new(2.0, 3.0, 5.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_conv_vector() {
|
||||
use point3;
|
||||
for i in 0..100 {
|
||||
// We don't care about these values as long as they are not the same.
|
||||
let x = i as f32 * 0.012345;
|
||||
let y = i as f32 * 0.987654;
|
||||
let z = x * y;
|
||||
let p: Point3D<f32> = point3(x, y, z);
|
||||
assert_eq!(p.to_vector().to_point(), p);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_swizzling() {
|
||||
let p: Point3D<i32> = point3(1, 2, 3);
|
||||
assert_eq!(p.xy(), point2(1, 2));
|
||||
assert_eq!(p.xz(), point2(1, 3));
|
||||
assert_eq!(p.yz(), point2(2, 3));
|
||||
}
|
||||
}
|
|
@ -1,805 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale::TypedScale;
|
||||
use num::*;
|
||||
use point::TypedPoint2D;
|
||||
use vector::TypedVector2D;
|
||||
use side_offsets::TypedSideOffsets2D;
|
||||
use size::TypedSize2D;
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::PartialOrd;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
|
||||
|
||||
/// A 2d Rectangle optionally tagged with a unit.
|
||||
#[repr(C)]
|
||||
pub struct TypedRect<T, U = UnknownUnit> {
|
||||
pub origin: TypedPoint2D<T, U>,
|
||||
pub size: TypedSize2D<T, U>,
|
||||
}
|
||||
|
||||
/// The default rectangle type with no unit.
|
||||
pub type Rect<T> = TypedRect<T, UnknownUnit>;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T: Copy + Deserialize<'de>, U> Deserialize<'de> for TypedRect<T, U> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let (origin, size) = try!(Deserialize::deserialize(deserializer));
|
||||
Ok(TypedRect::new(origin, size))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: Serialize, U> Serialize for TypedRect<T, U> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
(&self.origin, &self.size).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for TypedRect<T, U> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.origin.hash(h);
|
||||
self.size.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for TypedRect<T, U> {}
|
||||
|
||||
impl<T: Copy, U> Clone for TypedRect<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq<TypedRect<T, U>> for TypedRect<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.origin.eq(&other.origin) && self.size.eq(&other.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for TypedRect<T, U> {}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedRect<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TypedRect({:?} at {:?})", self.size, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedRect<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "Rect({} at {})", self.size, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
/// Constructor.
|
||||
pub fn new(origin: TypedPoint2D<T, U>, size: TypedSize2D<T, U>) -> Self {
|
||||
TypedRect {
|
||||
origin: origin,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
self.origin.x < other.origin.x + other.size.width
|
||||
&& other.origin.x < self.origin.x + self.size.width
|
||||
&& self.origin.y < other.origin.y + other.size.height
|
||||
&& other.origin.y < self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_x(&self) -> T {
|
||||
self.origin.x + self.size.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_x(&self) -> T {
|
||||
self.origin.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_y(&self) -> T {
|
||||
self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_y(&self) -> T {
|
||||
self.origin.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.max_x())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.min_x())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.min_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
if !self.intersects(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let upper_left = TypedPoint2D::new(
|
||||
max(self.min_x(), other.min_x()),
|
||||
max(self.min_y(), other.min_y()),
|
||||
);
|
||||
let lower_right_x = min(self.max_x(), other.max_x());
|
||||
let lower_right_y = min(self.max_y(), other.max_y());
|
||||
|
||||
Some(TypedRect::new(
|
||||
upper_left,
|
||||
TypedSize2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y),
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the same rectangle, translated by a vector.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn translate(&self, by: &TypedVector2D<T, U>) -> Self {
|
||||
Self::new(self.origin + *by, self.size)
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the point. Points are considered
|
||||
/// in the rectangle if they are on the left or top edge, but outside if they
|
||||
/// are on the right or bottom edge.
|
||||
#[inline]
|
||||
pub fn contains(&self, other: &TypedPoint2D<T, U>) -> bool {
|
||||
self.origin.x <= other.x && other.x < self.origin.x + self.size.width
|
||||
&& self.origin.y <= other.y && other.y < self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the interior of rect. Always
|
||||
/// returns true if rect is empty, and always returns false if rect is
|
||||
/// nonempty but this rectangle is empty.
|
||||
#[inline]
|
||||
pub fn contains_rect(&self, rect: &Self) -> bool {
|
||||
rect.is_empty()
|
||||
|| (self.min_x() <= rect.min_x() && rect.max_x() <= self.max_x()
|
||||
&& self.min_y() <= rect.min_y() && rect.max_y() <= self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn inflate(&self, width: T, height: T) -> Self {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x - width, self.origin.y - height),
|
||||
TypedSize2D::new(
|
||||
self.size.width + width + width,
|
||||
self.size.height + height + height,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn inflate_typed(&self, width: Length<T, U>, height: Length<T, U>) -> Self {
|
||||
self.inflate(width.get(), height.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn top_right(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.max_x(), self.origin.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bottom_left(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.origin.x, self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bottom_right(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.max_x(), self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn translate_by_size(&self, size: &TypedSize2D<T, U>) -> Self {
|
||||
self.translate(&size.to_vector())
|
||||
}
|
||||
|
||||
/// Calculate the size and position of an inner rectangle.
|
||||
///
|
||||
/// Subtracts the side offsets from all sides. The horizontal and vertical
|
||||
/// offsets must not be larger than the original side length.
|
||||
pub fn inner_rect(&self, offsets: TypedSideOffsets2D<T, U>) -> Self {
|
||||
let rect = TypedRect::new(
|
||||
TypedPoint2D::new(
|
||||
self.origin.x + offsets.left,
|
||||
self.origin.y + offsets.top
|
||||
),
|
||||
TypedSize2D::new(
|
||||
self.size.width - offsets.horizontal(),
|
||||
self.size.height - offsets.vertical()
|
||||
)
|
||||
);
|
||||
debug_assert!(rect.size.width >= Zero::zero());
|
||||
debug_assert!(rect.size.height >= Zero::zero());
|
||||
rect
|
||||
}
|
||||
|
||||
/// Calculate the size and position of an outer rectangle.
|
||||
///
|
||||
/// Add the offsets to all sides. The expanded rectangle is returned.
|
||||
pub fn outer_rect(&self, offsets: TypedSideOffsets2D<T, U>) -> Self {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(
|
||||
self.origin.x - offsets.left,
|
||||
self.origin.y - offsets.top
|
||||
),
|
||||
TypedSize2D::new(
|
||||
self.size.width + offsets.horizontal(),
|
||||
self.size.height + offsets.vertical()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the smallest rectangle defined by the top/bottom/left/right-most
|
||||
/// points provided as parameter.
|
||||
///
|
||||
/// Note: This function has a behavior that can be surprising because
|
||||
/// the right-most and bottom-most points are exactly on the edge
|
||||
/// of the rectangle while the `contains` function is has exclusive
|
||||
/// semantic on these edges. This means that the right-most and bottom-most
|
||||
/// points provided to `from_points` will count as not contained by the rect.
|
||||
/// This behavior may change in the future.
|
||||
pub fn from_points<I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<TypedPoint2D<T, U>>,
|
||||
{
|
||||
let mut points = points.into_iter();
|
||||
|
||||
let (mut min_x, mut min_y) = match points.next() {
|
||||
Some(first) => (first.borrow().x, first.borrow().y),
|
||||
None => return TypedRect::zero(),
|
||||
};
|
||||
|
||||
let (mut max_x, mut max_y) = (min_x, min_y);
|
||||
for point in points {
|
||||
let p = point.borrow();
|
||||
if p.x < min_x {
|
||||
min_x = p.x
|
||||
}
|
||||
if p.x > max_x {
|
||||
max_x = p.x
|
||||
}
|
||||
if p.y < min_y {
|
||||
min_y = p.y
|
||||
}
|
||||
if p.y > max_y {
|
||||
max_y = p.y
|
||||
}
|
||||
}
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(min_x, min_y),
|
||||
TypedSize2D::new(max_x - min_x, max_y - min_y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this rectangle and another rectangle.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
Self::new(
|
||||
self.origin.lerp(other.origin, t),
|
||||
self.size.lerp(other.size, t),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where
|
||||
T: Copy + Clone + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
|
||||
{
|
||||
#[inline]
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
if self.size == Zero::zero() {
|
||||
return *other;
|
||||
}
|
||||
if other.size == Zero::zero() {
|
||||
return *self;
|
||||
}
|
||||
|
||||
let upper_left = TypedPoint2D::new(
|
||||
min(self.min_x(), other.min_x()),
|
||||
min(self.min_y(), other.min_y()),
|
||||
);
|
||||
|
||||
let lower_right_x = max(self.max_x(), other.max_x());
|
||||
let lower_right_y = max(self.max_y(), other.max_y());
|
||||
|
||||
TypedRect::new(
|
||||
upper_left,
|
||||
TypedSize2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
#[inline]
|
||||
pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
|
||||
where
|
||||
T: Copy + Clone + Mul<S, Output = T>,
|
||||
{
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x * x, self.origin.y * y),
|
||||
TypedSize2D::new(self.size.width * x, self.size.height * y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq + Zero, U> TypedRect<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
TypedRect::new(TypedPoint2D::origin(), TypedSize2D::zero())
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero, regardless of the origin's value.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.size.width == Zero::zero() || self.size.height == Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
||||
if x <= y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
||||
if x >= y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedRect<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedRect<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedRect<T, U1> {
|
||||
type Output = TypedRect<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedRect<T, U2> {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedRect<T, U2> {
|
||||
type Output = TypedRect<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, U1, U2>) -> TypedRect<T, U1> {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Unit> TypedRect<T, Unit> {
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Rect<T> {
|
||||
TypedRect::new(self.origin.to_untyped(), self.size.to_untyped())
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(r: &Rect<T>) -> TypedRect<T, Unit> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::from_untyped(&r.origin),
|
||||
TypedSize2D::from_untyped(&r.size),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Unit> TypedRect<T0, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> Option<TypedRect<T1, Unit>> {
|
||||
match (self.origin.cast(), self.size.cast()) {
|
||||
(Some(origin), Some(size)) => Some(TypedRect::new(origin, size)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor + Ceil + Round + Add<T, Output = T> + Sub<T, Output = T>, U> TypedRect<T, U> {
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the returned rectangle has the same set of pixel centers as the original
|
||||
/// one.
|
||||
/// Edges at offset 0.5 round up.
|
||||
/// Suitable for most places where integral device coordinates
|
||||
/// are needed, but note that any translation should be applied first to
|
||||
/// avoid pixel rounding errors.
|
||||
/// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
/// They are always rounding as floor(n + 0.5).
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round(&self) -> Self {
|
||||
let origin = self.origin.round();
|
||||
let size = self.origin.add_size(&self.size).round() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle contains the resulting rectangle.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round_in(&self) -> Self {
|
||||
let origin = self.origin.ceil();
|
||||
let size = self.origin.add_size(&self.size).floor() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle is contained in the resulting rectangle.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn round_out(&self) -> Self {
|
||||
let origin = self.origin.floor();
|
||||
let size = self.origin.add_size(&self.size).ceil() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
impl<T: NumCast + Copy, Unit> TypedRect<T, Unit> {
|
||||
/// Cast into an `f32` rectangle.
|
||||
pub fn to_f32(&self) -> TypedRect<f32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` rectangle.
|
||||
pub fn to_f64(&self) -> TypedRect<f64, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedRect<usize, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_u32(&self) -> TypedRect<u32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedRect<i32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedRect<i64, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))`.
|
||||
pub fn rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use point::Point2D;
|
||||
use vector::vec2;
|
||||
use side_offsets::SideOffsets2D;
|
||||
use size::Size2D;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_min_max() {
|
||||
assert!(min(0u32, 1u32) == 0u32);
|
||||
assert!(min(-1.0f32, 0.0f32) == -1.0f32);
|
||||
|
||||
assert!(max(0u32, 1u32) == 1u32);
|
||||
assert!(max(-1.0f32, 0.0f32) == 0.0f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate(&vec2(10, 15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
assert!(pp.origin.x == 10);
|
||||
assert!(pp.origin.y == 15);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate(&vec2(0, -10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate_by_size() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate_by_size(&Size2D::new(10, 15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
assert!(pp.origin.x == 10);
|
||||
assert!(pp.origin.y == 15);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate_by_size(&Size2D::new(0, -10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40));
|
||||
let q = Rect::new(Point2D::new(20, 20), Size2D::new(5, 5));
|
||||
let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15));
|
||||
let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200));
|
||||
|
||||
let pq = p.union(&q);
|
||||
assert!(pq.origin == Point2D::new(0, 0));
|
||||
assert!(pq.size == Size2D::new(50, 40));
|
||||
|
||||
let pr = p.union(&r);
|
||||
assert!(pr.origin == Point2D::new(-15, -30));
|
||||
assert!(pr.size == Size2D::new(200, 70));
|
||||
|
||||
let ps = p.union(&s);
|
||||
assert!(ps.origin == Point2D::new(0, -15));
|
||||
assert!(ps.size == Size2D::new(270, 200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10));
|
||||
let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8));
|
||||
|
||||
let pq = p.intersection(&q);
|
||||
assert!(pq.is_some());
|
||||
let pq = pq.unwrap();
|
||||
assert!(pq.origin == Point2D::new(5, 15));
|
||||
assert!(pq.size == Size2D::new(5, 5));
|
||||
|
||||
let pr = p.intersection(&r);
|
||||
assert!(pr.is_some());
|
||||
let pr = pr.unwrap();
|
||||
assert!(pr.origin == Point2D::new(0, 0));
|
||||
assert!(pr.size == Size2D::new(3, 3));
|
||||
|
||||
let qr = q.intersection(&r);
|
||||
assert!(qr.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
|
||||
|
||||
assert!(r.contains(&Point2D::new(0, 50)));
|
||||
assert!(r.contains(&Point2D::new(-10, 200)));
|
||||
|
||||
// The `contains` method is inclusive of the top/left edges, but not the
|
||||
// bottom/right edges.
|
||||
assert!(r.contains(&Point2D::new(-20, 15)));
|
||||
assert!(!r.contains(&Point2D::new(80, 15)));
|
||||
assert!(!r.contains(&Point2D::new(80, 215)));
|
||||
assert!(!r.contains(&Point2D::new(-20, 215)));
|
||||
|
||||
// Points beyond the top-left corner.
|
||||
assert!(!r.contains(&Point2D::new(-25, 15)));
|
||||
assert!(!r.contains(&Point2D::new(-15, 10)));
|
||||
|
||||
// Points beyond the top-right corner.
|
||||
assert!(!r.contains(&Point2D::new(85, 20)));
|
||||
assert!(!r.contains(&Point2D::new(75, 10)));
|
||||
|
||||
// Points beyond the bottom-right corner.
|
||||
assert!(!r.contains(&Point2D::new(85, 210)));
|
||||
assert!(!r.contains(&Point2D::new(75, 220)));
|
||||
|
||||
// Points beyond the bottom-left corner.
|
||||
assert!(!r.contains(&Point2D::new(-25, 210)));
|
||||
assert!(!r.contains(&Point2D::new(-15, 220)));
|
||||
|
||||
let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
|
||||
assert!(r.contains_rect(&r));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(-0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(0.0, 0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(0.0, -0.1))));
|
||||
// Empty rectangles are always considered as contained in other rectangles,
|
||||
// even if their origin is not.
|
||||
let p = Point2D::new(1.0, 1.0);
|
||||
assert!(!r.contains(&p));
|
||||
assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.scale(10, 15);
|
||||
|
||||
assert!(pp.size.width == 500);
|
||||
assert!(pp.size.height == 600);
|
||||
assert!(pp.origin.x == 0);
|
||||
assert!(pp.origin.y == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.scale(1, 20);
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 800);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inflate() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
|
||||
let pp = p.inflate(10, 20);
|
||||
|
||||
assert!(pp.size.width == 30);
|
||||
assert!(pp.size.height == 50);
|
||||
assert!(pp.origin.x == -10);
|
||||
assert!(pp.origin.y == -20);
|
||||
|
||||
let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let rr = r.inflate(-2, -5);
|
||||
|
||||
assert!(rr.size.width == 6);
|
||||
assert!(rr.size.height == 10);
|
||||
assert!(rr.origin.x == 2);
|
||||
assert!(rr.origin.y == 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inner_outer_rect() {
|
||||
let inner_rect: Rect<i32> = Rect::new(Point2D::new(20, 40), Size2D::new(80, 100));
|
||||
let offsets = SideOffsets2D::new(20, 10, 10, 10);
|
||||
let outer_rect = inner_rect.outer_rect(offsets);
|
||||
assert_eq!(outer_rect.origin.x, 10);
|
||||
assert_eq!(outer_rect.origin.y, 20);
|
||||
assert_eq!(outer_rect.size.width, 100);
|
||||
assert_eq!(outer_rect.size.height, 130);
|
||||
assert_eq!(outer_rect.inner_rect(offsets), inner_rect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max_x_y() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
assert!(p.max_y() == 40);
|
||||
assert!(p.min_y() == 0);
|
||||
assert!(p.max_x() == 50);
|
||||
assert!(p.min_x() == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
assert!(r.max_y() == 35);
|
||||
assert!(r.min_y() == -5);
|
||||
assert!(r.max_x() == 40);
|
||||
assert!(r.min_x() == -10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty());
|
||||
assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty());
|
||||
assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round() {
|
||||
let mut x = -2.0;
|
||||
let mut y = -2.0;
|
||||
let mut w = -2.0;
|
||||
let mut h = -2.0;
|
||||
while x < 2.0 {
|
||||
while y < 2.0 {
|
||||
while w < 2.0 {
|
||||
while h < 2.0 {
|
||||
let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h));
|
||||
|
||||
assert!(rect.contains_rect(&rect.round_in()));
|
||||
assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect));
|
||||
|
||||
assert!(rect.round_out().contains_rect(&rect));
|
||||
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out()));
|
||||
|
||||
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round()));
|
||||
assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect));
|
||||
|
||||
h += 0.1;
|
||||
}
|
||||
w += 0.1;
|
||||
}
|
||||
y += 0.1;
|
||||
}
|
||||
x += 0.1
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,230 +0,0 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A type-checked scaling factor between units.
|
||||
|
||||
use num::One;
|
||||
|
||||
use num_traits::NumCast;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use std::marker::PhantomData;
|
||||
use {TypedPoint2D, TypedRect, TypedSize2D, TypedVector2D};
|
||||
|
||||
/// A scaling factor between two different units of measurement.
|
||||
///
|
||||
/// This is effectively a type-safe float, intended to be used in combination with other types like
|
||||
/// `length::Length` to enforce conversion between systems of measurement at compile time.
|
||||
///
|
||||
/// `Src` and `Dst` represent the units before and after multiplying a value by a `TypedScale`. They
|
||||
/// may be types without values, such as empty enums. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::TypedScale;
|
||||
/// use euclid::Length;
|
||||
/// enum Mm {};
|
||||
/// enum Inch {};
|
||||
///
|
||||
/// let mm_per_inch: TypedScale<f32, Inch, Mm> = TypedScale::new(25.4);
|
||||
///
|
||||
/// let one_foot: Length<f32, Inch> = Length::new(12.0);
|
||||
/// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch;
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
pub struct TypedScale<T, Src, Dst>(pub T, PhantomData<(Src, Dst)>);
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T, Src, Dst> Deserialize<'de> for TypedScale<T, Src, Dst>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<TypedScale<T, Src, Dst>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(TypedScale(
|
||||
try!(Deserialize::deserialize(deserializer)),
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T, Src, Dst> Serialize for TypedScale<T, Src, Dst>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedScale<T, Src, Dst> {
|
||||
pub fn new(x: T) -> Self {
|
||||
TypedScale(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> TypedScale<T, Src, Dst> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Src, Dst> TypedScale<f32, Src, Dst> {
|
||||
/// Identity scaling, could be used to safely transit from one space to another.
|
||||
pub const ONE: Self = TypedScale(1.0, PhantomData);
|
||||
}
|
||||
|
||||
impl<T: Clone + One + Div<T, Output = T>, Src, Dst> TypedScale<T, Src, Dst> {
|
||||
/// The inverse TypedScale (1.0 / self).
|
||||
pub fn inv(&self) -> TypedScale<T, Dst, Src> {
|
||||
let one: T = One::one();
|
||||
TypedScale::new(one / self.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 * scale1
|
||||
impl<T: Clone + Mul<T, Output = T>, A, B, C> Mul<TypedScale<T, B, C>> for TypedScale<T, A, B> {
|
||||
type Output = TypedScale<T, A, C>;
|
||||
#[inline]
|
||||
fn mul(self, other: TypedScale<T, B, C>) -> TypedScale<T, A, C> {
|
||||
TypedScale::new(self.get() * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 + scale1
|
||||
impl<T: Clone + Add<T, Output = T>, Src, Dst> Add for TypedScale<T, Src, Dst> {
|
||||
type Output = TypedScale<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn add(self, other: TypedScale<T, Src, Dst>) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 - scale1
|
||||
impl<T: Clone + Sub<T, Output = T>, Src, Dst> Sub for TypedScale<T, Src, Dst> {
|
||||
type Output = TypedScale<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn sub(self, other: TypedScale<T, Src, Dst>) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, Src, Dst0> TypedScale<T, Src, Dst0> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> Option<TypedScale<T1, Src, Dst0>> {
|
||||
NumCast::from(self.get()).map(TypedScale::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedScale<T, Src, Dst>
|
||||
where
|
||||
T: Copy + Clone + Mul<T, Output = T> + Neg<Output = T> + PartialEq + One,
|
||||
{
|
||||
/// Returns the given point transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
TypedPoint2D::new(point.x * self.get(), point.y * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
TypedVector2D::new(vec.x * self.get(), vec.y * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_size(&self, size: &TypedSize2D<T, Src>) -> TypedSize2D<T, Dst> {
|
||||
TypedSize2D::new(size.width * self.get(), size.height * self.get())
|
||||
}
|
||||
|
||||
/// Returns the given rect transformed by this scale.
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::new(
|
||||
self.transform_point(&rect.origin),
|
||||
self.transform_size(&rect.size),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the inverse of this scale.
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> TypedScale<T, Dst, Src> {
|
||||
TypedScale::new(-self.get())
|
||||
}
|
||||
|
||||
/// Returns true if this scale has no effect.
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> bool {
|
||||
self.get() == T::one()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
|
||||
// https://github.com/mozilla/rust/issues/7671
|
||||
|
||||
impl<T: PartialEq, Src, Dst> PartialEq for TypedScale<T, Src, Dst> {
|
||||
fn eq(&self, other: &TypedScale<T, Src, Dst>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for TypedScale<T, Src, Dst> {
|
||||
fn clone(&self) -> TypedScale<T, Src, Dst> {
|
||||
TypedScale::new(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for TypedScale<T, Src, Dst> {}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for TypedScale<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for TypedScale<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::TypedScale;
|
||||
|
||||
enum Inch {}
|
||||
enum Cm {}
|
||||
enum Mm {}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let mm_per_inch: TypedScale<f32, Inch, Mm> = TypedScale::new(25.4);
|
||||
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
|
||||
|
||||
let mm_per_cm: TypedScale<f32, Cm, Mm> = cm_per_mm.inv();
|
||||
assert_eq!(mm_per_cm.get(), 10.0);
|
||||
|
||||
let cm_per_inch: TypedScale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
|
||||
assert_eq!(cm_per_inch, TypedScale::new(2.54));
|
||||
|
||||
let a: TypedScale<isize, Inch, Inch> = TypedScale::new(2);
|
||||
let b: TypedScale<isize, Inch, Inch> = TypedScale::new(3);
|
||||
assert!(a != b);
|
||||
assert_eq!(a, a.clone());
|
||||
assert_eq!(a.clone() + b.clone(), TypedScale::new(5));
|
||||
assert_eq!(a - b, TypedScale::new(-1));
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
|
||||
//! and margins in CSS.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use num::Zero;
|
||||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
|
||||
/// and margins in CSS, optionally tagged with a unit.
|
||||
define_matrix! {
|
||||
pub struct TypedSideOffsets2D<T, U> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedSideOffsets2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"({:?},{:?},{:?},{:?})",
|
||||
self.top, self.right, self.bottom, self.left
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The default side offset type with no unit.
|
||||
pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor taking a scalar for each side.
|
||||
pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
|
||||
TypedSideOffsets2D {
|
||||
top: top,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
left: left,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor taking a typed Length for each side.
|
||||
pub fn from_lengths(
|
||||
top: Length<T, U>,
|
||||
right: Length<T, U>,
|
||||
bottom: Length<T, U>,
|
||||
left: Length<T, U>,
|
||||
) -> Self {
|
||||
TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0)
|
||||
}
|
||||
|
||||
/// Access self.top as a typed Length instead of a scalar value.
|
||||
pub fn top_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.top)
|
||||
}
|
||||
|
||||
/// Access self.right as a typed Length instead of a scalar value.
|
||||
pub fn right_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.right)
|
||||
}
|
||||
|
||||
/// Access self.bottom as a typed Length instead of a scalar value.
|
||||
pub fn bottom_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.bottom)
|
||||
}
|
||||
|
||||
/// Access self.left as a typed Length instead of a scalar value.
|
||||
pub fn left_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.left)
|
||||
}
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a scalar value directly.
|
||||
pub fn new_all_same(all: T) -> Self {
|
||||
TypedSideOffsets2D::new(all, all, all, all)
|
||||
}
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a typed Length.
|
||||
pub fn from_length_all_same(all: Length<T, U>) -> Self {
|
||||
TypedSideOffsets2D::new_all_same(all.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSideOffsets2D<T, U>
|
||||
where
|
||||
T: Add<T, Output = T> + Copy,
|
||||
{
|
||||
pub fn horizontal(&self) -> T {
|
||||
self.left + self.right
|
||||
}
|
||||
|
||||
pub fn vertical(&self) -> T {
|
||||
self.top + self.bottom
|
||||
}
|
||||
|
||||
pub fn horizontal_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.horizontal())
|
||||
}
|
||||
|
||||
pub fn vertical_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.vertical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Add for TypedSideOffsets2D<T, U>
|
||||
where
|
||||
T: Copy + Add<T, Output = T>,
|
||||
{
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
TypedSideOffsets2D::new(
|
||||
self.top + other.top,
|
||||
self.right + other.right,
|
||||
self.bottom + other.bottom,
|
||||
self.left + other.left,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Zero, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
TypedSideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
|
@ -1,373 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale::TypedScale;
|
||||
use vector::{TypedVector2D, vec2, BoolVector2D};
|
||||
use num::*;
|
||||
|
||||
use num_traits::{NumCast, Signed};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A 2d size tagged with a unit.
|
||||
define_matrix! {
|
||||
pub struct TypedSize2D<T, U> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d size type with no unit.
|
||||
///
|
||||
/// `Size2D` provides the same methods as `TypedSize2D`.
|
||||
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}×{:?}", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({}x{})", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar values.
|
||||
pub fn new(width: T, height: T) -> Self {
|
||||
TypedSize2D {
|
||||
width: width,
|
||||
height: height,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar strongly typed lengths.
|
||||
pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> Self {
|
||||
TypedSize2D::new(width.get(), height.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn round(&self) -> Self {
|
||||
TypedSize2D::new(self.width.round(), self.height.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn ceil(&self) -> Self {
|
||||
TypedSize2D::new(self.width.ceil(), self.height.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn floor(&self) -> Self {
|
||||
TypedSize2D::new(self.width.floor(), self.height.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output = T>, U> Add for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
TypedSize2D::new(self.width + other.width, self.height + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
TypedSize2D::new(self.width - other.width, self.height - other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Clone + Mul<T>, U> TypedSize2D<T, U> {
|
||||
pub fn area(&self) -> T::Output {
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U>
|
||||
where
|
||||
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
|
||||
{
|
||||
/// Linearly interpolate between this size and another size.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
size2(
|
||||
one_t * self.width + t * other.width,
|
||||
one_t * self.height + t * other.height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + PartialOrd, U> TypedSize2D<T, U> {
|
||||
pub fn is_empty_or_negative(&self) -> bool {
|
||||
let zero = T::zero();
|
||||
self.width <= zero || self.height <= zero
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> TypedSize2D<T, U> {
|
||||
pub fn zero() -> Self {
|
||||
TypedSize2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> Zero for TypedSize2D<T, U> {
|
||||
fn zero() -> Self {
|
||||
TypedSize2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
TypedSize2D::new(self.width * scale, self.height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
TypedSize2D::new(self.width / scale, self.height / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedSize2D<T, U1> {
|
||||
type Output = TypedSize2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedSize2D<T, U2> {
|
||||
TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedSize2D<T, U2> {
|
||||
type Output = TypedSize2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: TypedScale<T, U1, U2>) -> TypedSize2D<T, U1> {
|
||||
TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedSize2D<T, U> {
|
||||
/// Returns self.width as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn width_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.width)
|
||||
}
|
||||
|
||||
/// Returns self.height as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn height_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.height)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.width, self.height]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Size2D<T> {
|
||||
TypedSize2D::new(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Size2D<T>) -> Self {
|
||||
TypedSize2D::new(p.width, p.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
|
||||
match (NumCast::from(self.width), NumCast::from(self.height)) {
|
||||
(Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` size.
|
||||
pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `f64` size.
|
||||
pub fn to_f64(&self) -> TypedSize2D<f64, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `uint` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedSize2D<usize, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `u32` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_u32(&self) -> TypedSize2D<u32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U>
|
||||
where
|
||||
T: Signed,
|
||||
{
|
||||
pub fn abs(&self) -> Self {
|
||||
size2(self.width.abs(), self.height.abs())
|
||||
}
|
||||
|
||||
pub fn is_positive(&self) -> bool {
|
||||
self.width.is_positive() && self.height.is_positive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, U> TypedSize2D<T, U> {
|
||||
pub fn greater_than(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width > other.width,
|
||||
y: self.height > other.height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_than(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width < other.width,
|
||||
y: self.height < other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: PartialEq, U> TypedSize2D<T, U> {
|
||||
pub fn equal(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width == other.width,
|
||||
y: self.height == other.height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_equal(&self, other: &Self) -> BoolVector2D {
|
||||
BoolVector2D {
|
||||
x: self.width != other.width,
|
||||
y: self.height != other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `TypedSize2D::new(w, h)`.
|
||||
pub fn size2<T, U>(w: T, h: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(w, h)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod size2d {
|
||||
use super::Size2D;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(3.0, 4.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(4.0, 6.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(1.0, 2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(-3.0, -4.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(-2.0, -2.0));
|
||||
|
||||
let p1 = Size2D::new(0.0, 0.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(3.0, 4.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(-2.0, -2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(1.0, 2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(-3.0, -4.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(4.0, 6.0));
|
||||
|
||||
let p1 = Size2D::new(0.0, 0.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_area() {
|
||||
let p = Size2D::new(1.5, 2.0);
|
||||
assert_eq!(p.area(), 3.0);
|
||||
}
|
||||
}
|
|
@ -1,525 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{UnknownUnit, Angle};
|
||||
use num::{One, Zero};
|
||||
use point::TypedPoint2D;
|
||||
use vector::{TypedVector2D, vec2};
|
||||
use rect::TypedRect;
|
||||
use transform3d::TypedTransform3D;
|
||||
use std::ops::{Add, Mul, Div, Sub, Neg};
|
||||
use std::marker::PhantomData;
|
||||
use approxeq::ApproxEq;
|
||||
use trig::Trig;
|
||||
use std::fmt;
|
||||
use num_traits::NumCast;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d transform stored as a 3 by 2 matrix in row-major order in memory.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
/// For example, `TypedTransform2D<f32, WorldSpace, ScreenSpace>::transform_point4d`
|
||||
/// takes a `TypedPoint2D<f32, WorldSpace>` and returns a `TypedPoint2D<f32, ScreenSpace>`.
|
||||
///
|
||||
/// Transforms expose a set of convenience methods for pre- and post-transformations.
|
||||
/// A pre-transformation corresponds to adding an operation that is applied before
|
||||
/// the rest of the transformation, while a post-transformation adds an operation
|
||||
/// that is applied after.
|
||||
pub struct TypedTransform2D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T,
|
||||
pub m21: T, pub m22: T,
|
||||
pub m31: T, pub m32: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 2d transform type with no units.
|
||||
pub type Transform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T: Copy, Src, Dst> TypedTransform2D<T, Src, Dst> {
|
||||
/// Create a transform specifying its matrix elements in row-major order.
|
||||
pub fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
|
||||
TypedTransform2D {
|
||||
m11: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its matrix elements in column-major order.
|
||||
pub fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> Self {
|
||||
TypedTransform2D {
|
||||
m11: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in row-major order (the order
|
||||
/// in which the transform is actually laid out in memory).
|
||||
pub fn to_row_major_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
pub fn to_column_major_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m21, self.m31,
|
||||
self.m12, self.m22, self.m32
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 3 rows in (in row-major order)
|
||||
/// as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_row_arrays(&self) -> [[T; 2]; 3] {
|
||||
[
|
||||
[self.m11, self.m12],
|
||||
[self.m21, self.m22],
|
||||
[self.m31, self.m32],
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 6 elements in row-major order.
|
||||
pub fn from_row_major_array(array: [T; 6]) -> Self {
|
||||
Self::row_major(
|
||||
array[0], array[1],
|
||||
array[2], array[3],
|
||||
array[4], array[5],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 3 rows of 2 elements (row-major order).
|
||||
pub fn from_row_arrays(array: [[T; 2]; 3]) -> Self {
|
||||
Self::row_major(
|
||||
array[0][0], array[0][1],
|
||||
array[1][0], array[1][1],
|
||||
array[2][0], array[2][1],
|
||||
)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Transform2D<T> {
|
||||
Transform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Transform2D<T>) -> Self {
|
||||
TypedTransform2D::row_major(
|
||||
p.m11, p.m12,
|
||||
p.m21, p.m22,
|
||||
p.m31, p.m32
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Src, Dst> TypedTransform2D<T0, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> Option<TypedTransform2D<T1, Src, Dst>> {
|
||||
match (NumCast::from(self.m11), NumCast::from(self.m12),
|
||||
NumCast::from(self.m21), NumCast::from(self.m22),
|
||||
NumCast::from(self.m31), NumCast::from(self.m32)) {
|
||||
(Some(m11), Some(m12),
|
||||
Some(m21), Some(m22),
|
||||
Some(m31), Some(m32)) => {
|
||||
Some(TypedTransform2D::row_major(m11, m12,
|
||||
m21, m22,
|
||||
m31, m32))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
pub fn identity() -> Self {
|
||||
let (_0, _1) = (Zero::zero(), One::one());
|
||||
TypedTransform2D::row_major(
|
||||
_1, _0,
|
||||
_0, _1,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
// Intentional not public, because it checks for exact equivalence
|
||||
// while most consumers will probably want some sort of approximate
|
||||
// equivalence to deal with floating-point errors.
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform2D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Trig +
|
||||
PartialOrd +
|
||||
One + Zero {
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_mul<NewDst>(&self, mat: &TypedTransform2D<T, Dst, NewDst>) -> TypedTransform2D<T, Src, NewDst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22,
|
||||
self.m21 * mat.m11 + self.m22 * mat.m21,
|
||||
self.m21 * mat.m12 + self.m22 * mat.m22,
|
||||
self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
|
||||
self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform2D<T, NewSrc, Src>) -> TypedTransform2D<T, NewSrc, Dst> {
|
||||
mat.post_mul(self)
|
||||
}
|
||||
|
||||
/// Returns a translation transform.
|
||||
pub fn create_translation(x: T, y: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform2D::row_major(
|
||||
_1, _0,
|
||||
_0, _1,
|
||||
x, y
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a translation after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_translate(&self, v: TypedVector2D<T, Dst>) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Applies a translation before self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_translate(&self, v: TypedVector2D<T, Src>) -> Self {
|
||||
self.pre_mul(&TypedTransform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Returns a scale transform.
|
||||
pub fn create_scale(x: T, y: T) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
TypedTransform2D::row_major(
|
||||
x, _0,
|
||||
_0, y,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a scale after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_scale(&self, x: T, y: T) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_scale(x, y))
|
||||
}
|
||||
|
||||
/// Applies a scale before self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_scale(&self, x: T, y: T) -> Self {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11 * x, self.m12,
|
||||
self.m21, self.m22 * y,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rotation transform.
|
||||
pub fn create_rotation(theta: Angle<T>) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
let cos = theta.get().cos();
|
||||
let sin = theta.get().sin();
|
||||
TypedTransform2D::row_major(
|
||||
cos, _0 - sin,
|
||||
sin, cos,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
|
||||
self.pre_mul(&TypedTransform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Returns the given point transformed by this transform.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
TypedPoint2D::new(point.x * self.m11 + point.y * self.m21 + self.m31,
|
||||
point.x * self.m12 + point.y * self.m22 + self.m32)
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this matrix.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
vec2(vec.x * self.m11 + vec.y * self.m21,
|
||||
vec.x * self.m12 + vec.y * self.m22)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
|
||||
/// transform.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::from_points(&[
|
||||
self.transform_point(&rect.origin),
|
||||
self.transform_point(&rect.top_right()),
|
||||
self.transform_point(&rect.bottom_left()),
|
||||
self.transform_point(&rect.bottom_right()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Computes and returns the determinant of this transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m11 * self.m22 - self.m12 * self.m21
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn inverse(&self) -> Option<TypedTransform2D<T, Dst, Src>> {
|
||||
let det = self.determinant();
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
|
||||
if det == _0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inv_det = _1 / det;
|
||||
Some(TypedTransform2D::row_major(
|
||||
inv_det * self.m22,
|
||||
inv_det * (_0 - self.m12),
|
||||
inv_det * (_0 - self.m21),
|
||||
inv_det * self.m11,
|
||||
inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
|
||||
inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> TypedTransform2D<T, Src, NewDst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> TypedTransform2D<T, NewSrc, Dst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Neg<Output=T> +
|
||||
PartialOrd +
|
||||
Trig +
|
||||
One + Zero {
|
||||
/// Create a 3D transform from the current transform
|
||||
pub fn to_3d(&self) -> TypedTransform3D<T, Src, Dst> {
|
||||
TypedTransform3D::row_major_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<T: ApproxEq<T>, Src, Dst> TypedTransform2D<T, Src, Dst> {
|
||||
pub fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
|
||||
self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
|
||||
self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + fmt::Debug, Src, Dst> fmt::Debug for TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + fmt::Debug +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_identity() {
|
||||
write!(f, "[I]")
|
||||
} else {
|
||||
self.to_row_major_array().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use approxeq::ApproxEq;
|
||||
use point::Point2D;
|
||||
use Angle;
|
||||
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
type Mat = Transform2D<f32>;
|
||||
|
||||
fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mat::create_translation(1.0, 2.0);
|
||||
let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
|
||||
let t3 = Mat::identity().post_translate(vec2(1.0, 2.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_mul(&t1), Mat::create_translation(2.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
|
||||
let r3 = Mat::identity().post_rotate(rad(FRAC_PI_2));
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r1, r3);
|
||||
|
||||
assert!(r1.transform_point(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
|
||||
|
||||
assert!(r1.post_mul(&r1).approx_eq(&Mat::create_rotation(rad(FRAC_PI_2*2.0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mat::create_scale(2.0, 3.0);
|
||||
let s2 = Mat::identity().pre_scale(2.0, 3.0);
|
||||
let s3 = Mat::identity().post_scale(2.0, 3.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
assert!(s1.transform_point(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_major() {
|
||||
assert_eq!(
|
||||
Mat::row_major(
|
||||
1.0, 2.0,
|
||||
3.0, 4.0,
|
||||
5.0, 6.0
|
||||
),
|
||||
Mat::column_major(
|
||||
1.0, 3.0, 5.0,
|
||||
2.0, 4.0, 6.0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_simple() {
|
||||
let m1 = Mat::identity();
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.approx_eq(&m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mat::create_scale(1.5, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mat::create_translation(-132.0, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inverse_none() {
|
||||
assert!(Mat::create_scale(2.0, 0.0).inverse().is_none());
|
||||
assert!(Mat::create_scale(2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = Transform2D::identity().post_scale(1.0, 2.0).post_translate(vec2(1.0, 2.0));
|
||||
let m2 = Transform2D::identity().pre_translate(vec2(1.0, 2.0)).pre_scale(1.0, 2.0);
|
||||
assert!(m1.approx_eq(&m2));
|
||||
|
||||
let r = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let t = Mat::create_translation(2.0, 3.0);
|
||||
|
||||
let a = Point2D::new(1.0, 1.0);
|
||||
|
||||
assert!(r.post_mul(&t).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.post_mul(&r).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.post_mul(&r).transform_point(&a).approx_eq(&r.transform_point(&t.transform_point(&a))));
|
||||
|
||||
assert!(r.pre_mul(&t).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&t.transform_point(&r.transform_point(&a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use std::mem::size_of;
|
||||
assert_eq!(size_of::<Transform2D<f32>>(), 6*size_of::<f32>());
|
||||
assert_eq!(size_of::<Transform2D<f64>>(), 6*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = Transform2D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec2(0.1, 0.0));
|
||||
assert!(!m2.is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_vector() {
|
||||
// Translation does not apply to vectors.
|
||||
let m1 = Mat::create_translation(1.0, 1.0);
|
||||
let v1 = vec2(10.0, -10.0);
|
||||
assert_eq!(v1, m1.transform_vector(&v1));
|
||||
}
|
||||
}
|
|
@ -1,990 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{UnknownUnit, Angle};
|
||||
use approxeq::ApproxEq;
|
||||
use homogen::HomogeneousVector;
|
||||
use trig::Trig;
|
||||
use point::{TypedPoint2D, TypedPoint3D};
|
||||
use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
|
||||
use rect::TypedRect;
|
||||
use transform2d::TypedTransform2D;
|
||||
use scale::TypedScale;
|
||||
use num::{One, Zero};
|
||||
use std::ops::{Add, Mul, Sub, Div, Neg};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
use num_traits::NumCast;
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
/// For example, `TypedTransform3D<f32, WorldSpace, ScreenSpace>::transform_point3d`
|
||||
/// takes a `TypedPoint3D<f32, WorldSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
|
||||
///
|
||||
/// Transforms expose a set of convenience methods for pre- and post-transformations.
|
||||
/// A pre-transformation corresponds to adding an operation that is applied before
|
||||
/// the rest of the transformation, while a post-transformation adds an operation
|
||||
/// that is applied after.
|
||||
pub struct TypedTransform3D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T, pub m13: T, pub m14: T,
|
||||
pub m21: T, pub m22: T, pub m23: T, pub m24: T,
|
||||
pub m31: T, pub m32: T, pub m33: T, pub m34: T,
|
||||
pub m41: T, pub m42: T, pub m43: T, pub m44: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 3d transform type with no units.
|
||||
pub type Transform3D<T> = TypedTransform3D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T, Src, Dst> TypedTransform3D<T, Src, Dst> {
|
||||
/// Create a transform specifying its components in row-major order.
|
||||
///
|
||||
/// For example, the translation terms m41, m42, m43 on the last row with the
|
||||
/// row-major convention) are the 13rd, 14th and 15th parameters.
|
||||
#[inline]
|
||||
pub fn row_major(
|
||||
m11: T, m12: T, m13: T, m14: T,
|
||||
m21: T, m22: T, m23: T, m24: T,
|
||||
m31: T, m32: T, m33: T, m34: T,
|
||||
m41: T, m42: T, m43: T, m44: T)
|
||||
-> Self {
|
||||
TypedTransform3D {
|
||||
m11: m11, m12: m12, m13: m13, m14: m14,
|
||||
m21: m21, m22: m22, m23: m23, m24: m24,
|
||||
m31: m31, m32: m32, m33: m33, m34: m34,
|
||||
m41: m41, m42: m42, m43: m43, m44: m44,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its components in column-major order.
|
||||
///
|
||||
/// For example, the translation terms m41, m42, m43 on the last column with the
|
||||
/// column-major convention) are the 4th, 8th and 12nd parameters.
|
||||
#[inline]
|
||||
pub fn column_major(
|
||||
m11: T, m21: T, m31: T, m41: T,
|
||||
m12: T, m22: T, m32: T, m42: T,
|
||||
m13: T, m23: T, m33: T, m43: T,
|
||||
m14: T, m24: T, m34: T, m44: T)
|
||||
-> Self {
|
||||
TypedTransform3D {
|
||||
m11: m11, m12: m12, m13: m13, m14: m14,
|
||||
m21: m21, m22: m22, m23: m23, m24: m24,
|
||||
m31: m31, m32: m32, m33: m33, m34: m34,
|
||||
m41: m41, m42: m42, m43: m43, m44: m44,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
// Intentional not public, because it checks for exact equivalence
|
||||
// while most consumers will probably want some sort of approximate
|
||||
// equivalence to deal with floating-point errors.
|
||||
#[inline]
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform3D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Neg<Output=T> +
|
||||
PartialOrd +
|
||||
Trig +
|
||||
One + Zero {
|
||||
|
||||
/// Create a 4 by 4 transform representing a 2d transformation, specifying its components
|
||||
/// in row-major order.
|
||||
#[inline]
|
||||
pub fn row_major_2d(m11: T, m12: T, m21: T, m22: T, m41: T, m42: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
m11, m12, _0, _0,
|
||||
m21, m22, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
m41, m42, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Create an orthogonal projection transform.
|
||||
pub fn ortho(left: T, right: T,
|
||||
bottom: T, top: T,
|
||||
near: T, far: T) -> Self {
|
||||
let tx = -((right + left) / (right - left));
|
||||
let ty = -((top + bottom) / (top - bottom));
|
||||
let tz = -((far + near) / (far - near));
|
||||
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let _2 = _1 + _1;
|
||||
TypedTransform3D::row_major(
|
||||
_2 / (right - left), _0 , _0 , _0,
|
||||
_0 , _2 / (top - bottom), _0 , _0,
|
||||
_0 , _0 , -_2 / (far - near), _0,
|
||||
tx , ty , tz , _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this transform can be represented with a `TypedTransform2D`.
|
||||
///
|
||||
/// See <https://drafts.csswg.org/css-transforms/#2d-transform>
|
||||
#[inline]
|
||||
pub fn is_2d(&self) -> bool {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
self.m31 == _0 && self.m32 == _0 &&
|
||||
self.m13 == _0 && self.m23 == _0 &&
|
||||
self.m43 == _0 && self.m14 == _0 &&
|
||||
self.m24 == _0 && self.m34 == _0 &&
|
||||
self.m33 == _1 && self.m44 == _1
|
||||
}
|
||||
|
||||
/// Create a 2D transform picking the relevant terms from this transform.
|
||||
///
|
||||
/// This method assumes that self represents a 2d transformation, callers
|
||||
/// should check that self.is_2d() returns true beforehand.
|
||||
pub fn to_2d(&self) -> TypedTransform2D<T, Src, Dst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m41, self.m42
|
||||
)
|
||||
}
|
||||
|
||||
/// Check whether shapes on the XY plane with Z pointing towards the
|
||||
/// screen transformed by this matrix would be facing back.
|
||||
pub fn is_backface_visible(&self) -> bool {
|
||||
// inverse().m33 < 0;
|
||||
let det = self.determinant();
|
||||
let m33 = self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 +
|
||||
self.m14 * self.m21 * self.m42 - self.m11 * self.m24 * self.m42 -
|
||||
self.m12 * self.m21 * self.m44 + self.m11 * self.m22 * self.m44;
|
||||
let _0: T = Zero::zero();
|
||||
(m33 * det) < _0
|
||||
}
|
||||
|
||||
pub fn approx_eq(&self, other: &Self) -> bool
|
||||
where T : ApproxEq<T> {
|
||||
self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
|
||||
self.m13.approx_eq(&other.m13) && self.m14.approx_eq(&other.m14) &&
|
||||
self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
|
||||
self.m23.approx_eq(&other.m23) && self.m24.approx_eq(&other.m24) &&
|
||||
self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32) &&
|
||||
self.m33.approx_eq(&other.m33) && self.m34.approx_eq(&other.m34) &&
|
||||
self.m41.approx_eq(&other.m41) && self.m42.approx_eq(&other.m42) &&
|
||||
self.m43.approx_eq(&other.m43) && self.m44.approx_eq(&other.m44)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> TypedTransform3D<T, Src, NewDst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> TypedTransform3D<T, NewSrc, Dst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Transform3D<T> {
|
||||
Transform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(m: &Transform3D<T>) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
m.m11, m.m12, m.m13, m.m14,
|
||||
m.m21, m.m22, m.m23, m.m24,
|
||||
m.m31, m.m32, m.m33, m.m34,
|
||||
m.m41, m.m42, m.m43, m.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
pub fn post_mul<NewDst>(&self, mat: &TypedTransform3D<T, Dst, NewDst>) -> TypedTransform3D<T, Src, NewDst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21 + self.m13 * mat.m31 + self.m14 * mat.m41,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22 + self.m13 * mat.m32 + self.m14 * mat.m42,
|
||||
self.m11 * mat.m13 + self.m12 * mat.m23 + self.m13 * mat.m33 + self.m14 * mat.m43,
|
||||
self.m11 * mat.m14 + self.m12 * mat.m24 + self.m13 * mat.m34 + self.m14 * mat.m44,
|
||||
self.m21 * mat.m11 + self.m22 * mat.m21 + self.m23 * mat.m31 + self.m24 * mat.m41,
|
||||
self.m21 * mat.m12 + self.m22 * mat.m22 + self.m23 * mat.m32 + self.m24 * mat.m42,
|
||||
self.m21 * mat.m13 + self.m22 * mat.m23 + self.m23 * mat.m33 + self.m24 * mat.m43,
|
||||
self.m21 * mat.m14 + self.m22 * mat.m24 + self.m23 * mat.m34 + self.m24 * mat.m44,
|
||||
self.m31 * mat.m11 + self.m32 * mat.m21 + self.m33 * mat.m31 + self.m34 * mat.m41,
|
||||
self.m31 * mat.m12 + self.m32 * mat.m22 + self.m33 * mat.m32 + self.m34 * mat.m42,
|
||||
self.m31 * mat.m13 + self.m32 * mat.m23 + self.m33 * mat.m33 + self.m34 * mat.m43,
|
||||
self.m31 * mat.m14 + self.m32 * mat.m24 + self.m33 * mat.m34 + self.m34 * mat.m44,
|
||||
self.m41 * mat.m11 + self.m42 * mat.m21 + self.m43 * mat.m31 + self.m44 * mat.m41,
|
||||
self.m41 * mat.m12 + self.m42 * mat.m22 + self.m43 * mat.m32 + self.m44 * mat.m42,
|
||||
self.m41 * mat.m13 + self.m42 * mat.m23 + self.m43 * mat.m33 + self.m44 * mat.m43,
|
||||
self.m41 * mat.m14 + self.m42 * mat.m24 + self.m43 * mat.m34 + self.m44 * mat.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies before self's transformation.
|
||||
pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform3D<T, NewSrc, Src>) -> TypedTransform3D<T, NewSrc, Dst> {
|
||||
mat.post_mul(self)
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
pub fn inverse(&self) -> Option<TypedTransform3D<T, Dst, Src>> {
|
||||
let det = self.determinant();
|
||||
|
||||
if det == Zero::zero() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// todo(gw): this could be made faster by special casing
|
||||
// for simpler transform types.
|
||||
let m = TypedTransform3D::row_major(
|
||||
self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 +
|
||||
self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 -
|
||||
self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44,
|
||||
|
||||
self.m14*self.m33*self.m42 - self.m13*self.m34*self.m42 -
|
||||
self.m14*self.m32*self.m43 + self.m12*self.m34*self.m43 +
|
||||
self.m13*self.m32*self.m44 - self.m12*self.m33*self.m44,
|
||||
|
||||
self.m13*self.m24*self.m42 - self.m14*self.m23*self.m42 +
|
||||
self.m14*self.m22*self.m43 - self.m12*self.m24*self.m43 -
|
||||
self.m13*self.m22*self.m44 + self.m12*self.m23*self.m44,
|
||||
|
||||
self.m14*self.m23*self.m32 - self.m13*self.m24*self.m32 -
|
||||
self.m14*self.m22*self.m33 + self.m12*self.m24*self.m33 +
|
||||
self.m13*self.m22*self.m34 - self.m12*self.m23*self.m34,
|
||||
|
||||
self.m24*self.m33*self.m41 - self.m23*self.m34*self.m41 -
|
||||
self.m24*self.m31*self.m43 + self.m21*self.m34*self.m43 +
|
||||
self.m23*self.m31*self.m44 - self.m21*self.m33*self.m44,
|
||||
|
||||
self.m13*self.m34*self.m41 - self.m14*self.m33*self.m41 +
|
||||
self.m14*self.m31*self.m43 - self.m11*self.m34*self.m43 -
|
||||
self.m13*self.m31*self.m44 + self.m11*self.m33*self.m44,
|
||||
|
||||
self.m14*self.m23*self.m41 - self.m13*self.m24*self.m41 -
|
||||
self.m14*self.m21*self.m43 + self.m11*self.m24*self.m43 +
|
||||
self.m13*self.m21*self.m44 - self.m11*self.m23*self.m44,
|
||||
|
||||
self.m13*self.m24*self.m31 - self.m14*self.m23*self.m31 +
|
||||
self.m14*self.m21*self.m33 - self.m11*self.m24*self.m33 -
|
||||
self.m13*self.m21*self.m34 + self.m11*self.m23*self.m34,
|
||||
|
||||
self.m22*self.m34*self.m41 - self.m24*self.m32*self.m41 +
|
||||
self.m24*self.m31*self.m42 - self.m21*self.m34*self.m42 -
|
||||
self.m22*self.m31*self.m44 + self.m21*self.m32*self.m44,
|
||||
|
||||
self.m14*self.m32*self.m41 - self.m12*self.m34*self.m41 -
|
||||
self.m14*self.m31*self.m42 + self.m11*self.m34*self.m42 +
|
||||
self.m12*self.m31*self.m44 - self.m11*self.m32*self.m44,
|
||||
|
||||
self.m12*self.m24*self.m41 - self.m14*self.m22*self.m41 +
|
||||
self.m14*self.m21*self.m42 - self.m11*self.m24*self.m42 -
|
||||
self.m12*self.m21*self.m44 + self.m11*self.m22*self.m44,
|
||||
|
||||
self.m14*self.m22*self.m31 - self.m12*self.m24*self.m31 -
|
||||
self.m14*self.m21*self.m32 + self.m11*self.m24*self.m32 +
|
||||
self.m12*self.m21*self.m34 - self.m11*self.m22*self.m34,
|
||||
|
||||
self.m23*self.m32*self.m41 - self.m22*self.m33*self.m41 -
|
||||
self.m23*self.m31*self.m42 + self.m21*self.m33*self.m42 +
|
||||
self.m22*self.m31*self.m43 - self.m21*self.m32*self.m43,
|
||||
|
||||
self.m12*self.m33*self.m41 - self.m13*self.m32*self.m41 +
|
||||
self.m13*self.m31*self.m42 - self.m11*self.m33*self.m42 -
|
||||
self.m12*self.m31*self.m43 + self.m11*self.m32*self.m43,
|
||||
|
||||
self.m13*self.m22*self.m41 - self.m12*self.m23*self.m41 -
|
||||
self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 +
|
||||
self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43,
|
||||
|
||||
self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 +
|
||||
self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 -
|
||||
self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33
|
||||
);
|
||||
|
||||
let _1: T = One::one();
|
||||
Some(m.mul_s(_1 / det))
|
||||
}
|
||||
|
||||
/// Compute the determinant of the transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m14 * self.m23 * self.m32 * self.m41 -
|
||||
self.m13 * self.m24 * self.m32 * self.m41 -
|
||||
self.m14 * self.m22 * self.m33 * self.m41 +
|
||||
self.m12 * self.m24 * self.m33 * self.m41 +
|
||||
self.m13 * self.m22 * self.m34 * self.m41 -
|
||||
self.m12 * self.m23 * self.m34 * self.m41 -
|
||||
self.m14 * self.m23 * self.m31 * self.m42 +
|
||||
self.m13 * self.m24 * self.m31 * self.m42 +
|
||||
self.m14 * self.m21 * self.m33 * self.m42 -
|
||||
self.m11 * self.m24 * self.m33 * self.m42 -
|
||||
self.m13 * self.m21 * self.m34 * self.m42 +
|
||||
self.m11 * self.m23 * self.m34 * self.m42 +
|
||||
self.m14 * self.m22 * self.m31 * self.m43 -
|
||||
self.m12 * self.m24 * self.m31 * self.m43 -
|
||||
self.m14 * self.m21 * self.m32 * self.m43 +
|
||||
self.m11 * self.m24 * self.m32 * self.m43 +
|
||||
self.m12 * self.m21 * self.m34 * self.m43 -
|
||||
self.m11 * self.m22 * self.m34 * self.m43 -
|
||||
self.m13 * self.m22 * self.m31 * self.m44 +
|
||||
self.m12 * self.m23 * self.m31 * self.m44 +
|
||||
self.m13 * self.m21 * self.m32 * self.m44 -
|
||||
self.m11 * self.m23 * self.m32 * self.m44 -
|
||||
self.m12 * self.m21 * self.m33 * self.m44 +
|
||||
self.m11 * self.m22 * self.m33 * self.m44
|
||||
}
|
||||
|
||||
/// Multiplies all of the transform's component by a scalar and returns the result.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn mul_s(&self, x: T) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * x, self.m12 * x, self.m13 * x, self.m14 * x,
|
||||
self.m21 * x, self.m22 * x, self.m23 * x, self.m24 * x,
|
||||
self.m31 * x, self.m32 * x, self.m33 * x, self.m34 * x,
|
||||
self.m41 * x, self.m42 * x, self.m43 * x, self.m44 * x
|
||||
)
|
||||
}
|
||||
|
||||
/// Convenience function to create a scale transform from a `TypedScale`.
|
||||
pub fn from_scale(scale: TypedScale<T, Src, Dst>) -> Self {
|
||||
TypedTransform3D::create_scale(scale.get(), scale.get(), scale.get())
|
||||
}
|
||||
|
||||
/// Returns the homogeneous vector corresponding to the transformed 2d point.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point2d_homogeneous(
|
||||
&self, p: &TypedPoint2D<T, Src>
|
||||
) -> HomogeneousVector<T, Dst> {
|
||||
let x = p.x * self.m11 + p.y * self.m21 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + self.m42;
|
||||
let z = p.x * self.m13 + p.y * self.m23 + self.m43;
|
||||
let w = p.x * self.m14 + p.y * self.m24 + self.m44;
|
||||
|
||||
HomogeneousVector::new(x, y, z, w)
|
||||
}
|
||||
|
||||
/// Returns the given 2d point transformed by this transform.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point2d(&self, p: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
//Note: could use `transform_point2d_homogeneous()` but it would waste the calculus of `z`
|
||||
|
||||
let x = p.x * self.m11 + p.y * self.m21 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + self.m42;
|
||||
let w = p.x * self.m14 + p.y * self.m24 + self.m44;
|
||||
|
||||
|
||||
TypedPoint2D::new(x / w, y / w)
|
||||
}
|
||||
|
||||
/// Returns the given 2d vector transformed by this matrix.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector2d(&self, v: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
vec2(
|
||||
v.x * self.m11 + v.y * self.m21,
|
||||
v.x * self.m12 + v.y * self.m22,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the homogeneous vector corresponding to the transformed 3d point.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point3d_homogeneous(
|
||||
&self, p: &TypedPoint3D<T, Src>
|
||||
) -> HomogeneousVector<T, Dst> {
|
||||
let x = p.x * self.m11 + p.y * self.m21 + p.z * self.m31 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + p.z * self.m32 + self.m42;
|
||||
let z = p.x * self.m13 + p.y * self.m23 + p.z * self.m33 + self.m43;
|
||||
let w = p.x * self.m14 + p.y * self.m24 + p.z * self.m34 + self.m44;
|
||||
|
||||
HomogeneousVector::new(x, y, z, w)
|
||||
}
|
||||
|
||||
/// Returns the given 3d point transformed by this transform.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point3d(&self, p: &TypedPoint3D<T, Src>) -> TypedPoint3D<T, Dst> {
|
||||
self.transform_point3d_homogeneous(p).to_point3d()
|
||||
}
|
||||
|
||||
/// Returns the given 3d vector transformed by this matrix.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector3d(&self, v: &TypedVector3D<T, Src>) -> TypedVector3D<T, Dst> {
|
||||
vec3(
|
||||
v.x * self.m11 + v.y * self.m21 + v.z * self.m31,
|
||||
v.x * self.m12 + v.y * self.m22 + v.z * self.m32,
|
||||
v.x * self.m13 + v.y * self.m23 + v.z * self.m33,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
|
||||
/// transform.
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::from_points(&[
|
||||
self.transform_point2d(&rect.origin),
|
||||
self.transform_point2d(&rect.top_right()),
|
||||
self.transform_point2d(&rect.bottom_left()),
|
||||
self.transform_point2d(&rect.bottom_right()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Create a 3d translation transform
|
||||
pub fn create_translation(x: T, y: T, z: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
x, y, z, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a translation applied before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_translate(&self, v: TypedVector3D<T, Src>) -> Self {
|
||||
self.pre_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
|
||||
}
|
||||
|
||||
/// Returns a transform with a translation applied after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_translate(&self, v: TypedVector3D<T, Dst>) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
|
||||
}
|
||||
|
||||
/// Create a 3d scale transform
|
||||
pub fn create_scale(x: T, y: T, z: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
x, _0, _0, _0,
|
||||
_0, y, _0, _0,
|
||||
_0, _0, z, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a scale applied before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_scale(&self, x: T, y: T, z: T) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * x, self.m12, self.m13, self.m14,
|
||||
self.m21 , self.m22 * y, self.m23, self.m24,
|
||||
self.m31 , self.m32, self.m33 * z, self.m34,
|
||||
self.m41 , self.m42, self.m43, self.m44
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a scale applied after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_scale(&self, x: T, y: T, z: T) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_scale(x, y, z))
|
||||
}
|
||||
|
||||
/// Create a 3d rotation transform from an angle / axis.
|
||||
/// The supplied axis must be normalized.
|
||||
pub fn create_rotation(x: T, y: T, z: T, theta: Angle<T>) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let _2 = _1 + _1;
|
||||
|
||||
let xx = x * x;
|
||||
let yy = y * y;
|
||||
let zz = z * z;
|
||||
|
||||
let half_theta = theta.get() / _2;
|
||||
let sc = half_theta.sin() * half_theta.cos();
|
||||
let sq = half_theta.sin() * half_theta.sin();
|
||||
|
||||
TypedTransform3D::row_major(
|
||||
_1 - _2 * (yy + zz) * sq,
|
||||
_2 * (x * y * sq - z * sc),
|
||||
_2 * (x * z * sq + y * sc),
|
||||
_0,
|
||||
|
||||
_2 * (x * y * sq + z * sc),
|
||||
_1 - _2 * (xx + zz) * sq,
|
||||
_2 * (y * z * sq - x * sc),
|
||||
_0,
|
||||
|
||||
_2 * (x * z * sq - y * sc),
|
||||
_2 * (y * z * sq + x * sc),
|
||||
_1 - _2 * (xx + yy) * sq,
|
||||
_0,
|
||||
|
||||
_0,
|
||||
_0,
|
||||
_0,
|
||||
_1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a rotation applied after self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn post_rotate(&self, x: T, y: T, z: T, theta: Angle<T>) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
|
||||
}
|
||||
|
||||
/// Returns a transform with a rotation applied before self's transformation.
|
||||
#[cfg_attr(feature = "unstable", must_use)]
|
||||
pub fn pre_rotate(&self, x: T, y: T, z: T, theta: Angle<T>) -> Self {
|
||||
self.pre_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
|
||||
}
|
||||
|
||||
/// Create a 2d skew transform.
|
||||
///
|
||||
/// See <https://drafts.csswg.org/css-transforms/#funcdef-skew>
|
||||
pub fn create_skew(alpha: Angle<T>, beta: Angle<T>) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let (sx, sy) = (beta.get().tan(), alpha.get().tan());
|
||||
TypedTransform3D::row_major(
|
||||
_1, sx, _0, _0,
|
||||
sy, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a simple perspective projection transform
|
||||
pub fn create_perspective(d: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, -_1 / d,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> TypedTransform3D<T, Src, Dst> {
|
||||
/// Returns an array containing this transform's terms in row-major order (the order
|
||||
/// in which the transform is actually laid out in memory).
|
||||
pub fn to_row_major_array(&self) -> [T; 16] {
|
||||
[
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
pub fn to_column_major_array(&self) -> [T; 16] {
|
||||
[
|
||||
self.m11, self.m21, self.m31, self.m41,
|
||||
self.m12, self.m22, self.m32, self.m42,
|
||||
self.m13, self.m23, self.m33, self.m43,
|
||||
self.m14, self.m24, self.m34, self.m44
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 4 rows in (in row-major order)
|
||||
/// as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_row_arrays(&self) -> [[T; 4]; 4] {
|
||||
[
|
||||
[self.m11, self.m12, self.m13, self.m14],
|
||||
[self.m21, self.m22, self.m23, self.m24],
|
||||
[self.m31, self.m32, self.m33, self.m34],
|
||||
[self.m41, self.m42, self.m43, self.m44]
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 4 columns in (in row-major order,
|
||||
/// or 4 rows in column-major order) as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_column_arrays(&self) -> [[T; 4]; 4] {
|
||||
[
|
||||
[self.m11, self.m21, self.m31, self.m41],
|
||||
[self.m12, self.m22, self.m32, self.m42],
|
||||
[self.m13, self.m23, self.m33, self.m43],
|
||||
[self.m14, self.m24, self.m34, self.m44]
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 16 elements in row-major order.
|
||||
pub fn from_array(array: [T; 16]) -> Self {
|
||||
Self::row_major(
|
||||
array[0], array[1], array[2], array[3],
|
||||
array[4], array[5], array[6], array[7],
|
||||
array[8], array[9], array[10], array[11],
|
||||
array[12], array[13], array[14], array[15],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 4 rows of 4 elements (row-major order).
|
||||
pub fn from_row_arrays(array: [[T; 4]; 4]) -> Self {
|
||||
Self::row_major(
|
||||
array[0][0], array[0][1], array[0][2], array[0][3],
|
||||
array[1][0], array[1][1], array[1][2], array[1][3],
|
||||
array[2][0], array[2][1], array[2][2], array[2][3],
|
||||
array[3][0], array[3][1], array[3][2], array[3][3],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Src, Dst> TypedTransform3D<T0, Src, Dst> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> Option<TypedTransform3D<T1, Src, Dst>> {
|
||||
match (NumCast::from(self.m11), NumCast::from(self.m12),
|
||||
NumCast::from(self.m13), NumCast::from(self.m14),
|
||||
NumCast::from(self.m21), NumCast::from(self.m22),
|
||||
NumCast::from(self.m23), NumCast::from(self.m24),
|
||||
NumCast::from(self.m31), NumCast::from(self.m32),
|
||||
NumCast::from(self.m33), NumCast::from(self.m34),
|
||||
NumCast::from(self.m41), NumCast::from(self.m42),
|
||||
NumCast::from(self.m43), NumCast::from(self.m44)) {
|
||||
(Some(m11), Some(m12), Some(m13), Some(m14),
|
||||
Some(m21), Some(m22), Some(m23), Some(m24),
|
||||
Some(m31), Some(m32), Some(m33), Some(m34),
|
||||
Some(m41), Some(m42), Some(m43), Some(m44)) => {
|
||||
Some(TypedTransform3D::row_major(m11, m12, m13, m14,
|
||||
m21, m22, m23, m24,
|
||||
m31, m32, m33, m34,
|
||||
m41, m42, m43, m44))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> fmt::Debug for TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + fmt::Debug +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_identity() {
|
||||
write!(f, "[I]")
|
||||
} else {
|
||||
self.to_row_major_array().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use approxeq::ApproxEq;
|
||||
use transform2d::Transform2D;
|
||||
use point::{point2, point3};
|
||||
use Angle;
|
||||
use super::*;
|
||||
|
||||
use std::f32::consts::{FRAC_PI_2, PI};
|
||||
|
||||
type Mf32 = Transform3D<f32>;
|
||||
|
||||
// For convenience.
|
||||
fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mf32::create_translation(1.0, 2.0, 3.0);
|
||||
let t2 = Mf32::identity().pre_translate(vec3(1.0, 2.0, 3.0));
|
||||
let t3 = Mf32::identity().post_translate(vec3(1.0, 2.0, 3.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point3d(&point3(1.0, 1.0, 1.0)), point3(2.0, 3.0, 4.0));
|
||||
assert_eq!(t1.transform_point2d(&point2(1.0, 1.0)), point2(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_mul(&t1), Mf32::create_translation(2.0, 4.0, 6.0));
|
||||
|
||||
assert!(!t1.is_2d());
|
||||
assert_eq!(Mf32::create_translation(1.0, 2.0, 3.0).to_2d(), Transform2D::create_translation(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let r2 = Mf32::identity().pre_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let r3 = Mf32::identity().post_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r1, r3);
|
||||
|
||||
assert!(r1.transform_point3d(&point3(1.0, 2.0, 3.0)).approx_eq(&point3(2.0, -1.0, 3.0)));
|
||||
assert!(r1.transform_point2d(&point2(1.0, 2.0)).approx_eq(&point2(2.0, -1.0)));
|
||||
|
||||
assert!(r1.post_mul(&r1).approx_eq(&Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2*2.0))));
|
||||
|
||||
assert!(r1.is_2d());
|
||||
assert!(r1.to_2d().approx_eq(&Transform2D::create_rotation(rad(FRAC_PI_2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mf32::create_scale(2.0, 3.0, 4.0);
|
||||
let s2 = Mf32::identity().pre_scale(2.0, 3.0, 4.0);
|
||||
let s3 = Mf32::identity().post_scale(2.0, 3.0, 4.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
assert!(s1.transform_point3d(&point3(2.0, 2.0, 2.0)).approx_eq(&point3(4.0, 6.0, 8.0)));
|
||||
assert!(s1.transform_point2d(&point2(2.0, 2.0)).approx_eq(&point2(4.0, 6.0)));
|
||||
|
||||
assert_eq!(s1.post_mul(&s1), Mf32::create_scale(4.0, 9.0, 16.0));
|
||||
|
||||
assert!(!s1.is_2d());
|
||||
assert_eq!(Mf32::create_scale(2.0, 3.0, 0.0).to_2d(), Transform2D::create_scale(2.0, 3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_ortho() {
|
||||
let (left, right, bottom, top) = (0.0f32, 1.0f32, 0.1f32, 1.0f32);
|
||||
let (near, far) = (-1.0f32, 1.0f32);
|
||||
let result = Mf32::ortho(left, right, bottom, top, near, far);
|
||||
let expected = Mf32::row_major(
|
||||
2.0, 0.0, 0.0, 0.0,
|
||||
0.0, 2.22222222, 0.0, 0.0,
|
||||
0.0, 0.0, -1.0, 0.0,
|
||||
-1.0, -1.22222222, -0.0, 1.0
|
||||
);
|
||||
assert!(result.approx_eq(&expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_2d() {
|
||||
assert!(Mf32::identity().is_2d());
|
||||
assert!(Mf32::create_rotation(0.0, 0.0, 1.0, rad(0.7854)).is_2d());
|
||||
assert!(!Mf32::create_rotation(0.0, 1.0, 0.0, rad(0.7854)).is_2d());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_row_major_2d() {
|
||||
let m1 = Mf32::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
|
||||
let m2 = Mf32::row_major(
|
||||
1.0, 2.0, 0.0, 0.0,
|
||||
3.0, 4.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
5.0, 6.0, 0.0, 1.0
|
||||
);
|
||||
assert_eq!(m1, m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_major() {
|
||||
assert_eq!(
|
||||
Mf32::row_major(
|
||||
1.0, 2.0, 3.0, 4.0,
|
||||
5.0, 6.0, 7.0, 8.0,
|
||||
9.0, 10.0, 11.0, 12.0,
|
||||
13.0, 14.0, 15.0, 16.0,
|
||||
),
|
||||
Mf32::column_major(
|
||||
1.0, 5.0, 9.0, 13.0,
|
||||
2.0, 6.0, 10.0, 14.0,
|
||||
3.0, 7.0, 11.0, 15.0,
|
||||
4.0, 8.0, 12.0, 16.0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_simple() {
|
||||
let m1 = Mf32::identity();
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.approx_eq(&m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mf32::create_scale(1.5, 0.3, 2.1);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mf32::create_translation(-132.0, 0.3, 493.0);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_rotate() {
|
||||
let m1 = Mf32::create_rotation(0.0, 1.0, 0.0, rad(1.57));
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_transform_point_2d() {
|
||||
let m1 = Mf32::create_translation(100.0, 200.0, 0.0);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
|
||||
let p1 = point2(1000.0, 2000.0);
|
||||
let p2 = m1.transform_point2d(&p1);
|
||||
assert!(p2.eq(&point2(1100.0, 2200.0)));
|
||||
|
||||
let p3 = m2.transform_point2d(&p2);
|
||||
assert!(p3.eq(&p1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inverse_none() {
|
||||
assert!(Mf32::create_scale(2.0, 0.0, 2.0).inverse().is_none());
|
||||
assert!(Mf32::create_scale(2.0, 2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = Transform3D::identity().post_scale(1.0, 2.0, 3.0).post_translate(vec3(1.0, 2.0, 3.0));
|
||||
let m2 = Transform3D::identity().pre_translate(vec3(1.0, 2.0, 3.0)).pre_scale(1.0, 2.0, 3.0);
|
||||
assert!(m1.approx_eq(&m2));
|
||||
|
||||
let r = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let t = Mf32::create_translation(2.0, 3.0, 0.0);
|
||||
|
||||
let a = point3(1.0, 1.0, 1.0);
|
||||
|
||||
assert!(r.post_mul(&t).transform_point3d(&a).approx_eq(&point3(3.0, 2.0, 1.0)));
|
||||
assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&point3(4.0, -3.0, 1.0)));
|
||||
assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&r.transform_point3d(&t.transform_point3d(&a))));
|
||||
|
||||
assert!(r.pre_mul(&t).transform_point3d(&a).approx_eq(&point3(4.0, -3.0, 1.0)));
|
||||
assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&point3(3.0, 2.0, 1.0)));
|
||||
assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&t.transform_point3d(&r.transform_point3d(&a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use std::mem::size_of;
|
||||
assert_eq!(size_of::<Transform3D<f32>>(), 16*size_of::<f32>());
|
||||
assert_eq!(size_of::<Transform3D<f64>>(), 16*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_associativity() {
|
||||
let m1 = Mf32::row_major(3.0, 2.0, 1.5, 1.0,
|
||||
0.0, 4.5, -1.0, -4.0,
|
||||
0.0, 3.5, 2.5, 40.0,
|
||||
0.0, 3.0, 0.0, 1.0);
|
||||
let m2 = Mf32::row_major(1.0, -1.0, 3.0, 0.0,
|
||||
-1.0, 0.5, 0.0, 2.0,
|
||||
1.5, -2.0, 6.0, 0.0,
|
||||
-2.5, 6.0, 1.0, 1.0);
|
||||
|
||||
let p = point3(1.0, 3.0, 5.0);
|
||||
let p1 = m2.pre_mul(&m1).transform_point3d(&p);
|
||||
let p2 = m2.transform_point3d(&m1.transform_point3d(&p));
|
||||
assert!(p1.approx_eq(&p2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = Transform3D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec3(0.1, 0.0, 0.0));
|
||||
assert!(!m2.is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_vector() {
|
||||
// Translation does not apply to vectors.
|
||||
let m = Mf32::create_translation(1.0, 2.0, 3.0);
|
||||
let v1 = vec3(10.0, -10.0, 3.0);
|
||||
assert_eq!(v1, m.transform_vector3d(&v1));
|
||||
// While it does apply to points.
|
||||
assert!(v1.to_point() != m.transform_point3d(&v1.to_point()));
|
||||
|
||||
// same thing with 2d vectors/points
|
||||
let v2 = vec2(10.0, -5.0);
|
||||
assert_eq!(v2, m.transform_vector2d(&v2));
|
||||
assert!(v2.to_point() != m.transform_point2d(&v2.to_point()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_backface_visible() {
|
||||
// backface is not visible for rotate-x 0 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(0.0));
|
||||
assert!(!r1.is_backface_visible());
|
||||
// backface is not visible for rotate-x 45 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI * 0.25));
|
||||
assert!(!r1.is_backface_visible());
|
||||
// backface is visible for rotate-x 180 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI));
|
||||
assert!(r1.is_backface_visible());
|
||||
// backface is visible for rotate-x 225 degree.
|
||||
let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI * 1.25));
|
||||
assert!(r1.is_backface_visible());
|
||||
// backface is not visible for non-inverseable matrix
|
||||
let r1 = Mf32::create_scale(2.0, 0.0, 2.0);
|
||||
assert!(!r1.is_backface_visible());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_homogeneous() {
|
||||
let m = Mf32::row_major(
|
||||
1.0, 2.0, 0.5, 5.0,
|
||||
3.0, 4.0, 0.25, 6.0,
|
||||
0.5, -1.0, 1.0, -1.0,
|
||||
-1.0, 1.0, -1.0, 2.0,
|
||||
);
|
||||
assert_eq!(
|
||||
m.transform_point2d_homogeneous(&point2(1.0, 2.0)),
|
||||
HomogeneousVector::new(6.0, 11.0, 0.0, 19.0),
|
||||
);
|
||||
assert_eq!(
|
||||
m.transform_point3d_homogeneous(&point3(1.0, 2.0, 4.0)),
|
||||
HomogeneousVector::new(8.0, 7.0, 4.0, 15.0),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Trait for basic trigonometry functions, so they can be used on generic numeric types
|
||||
pub trait Trig {
|
||||
fn sin(self) -> Self;
|
||||
fn cos(self) -> Self;
|
||||
fn tan(self) -> Self;
|
||||
fn fast_atan2(y: Self, x: Self) -> Self;
|
||||
fn degrees_to_radians(deg: Self) -> Self;
|
||||
fn radians_to_degrees(rad: Self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! trig {
|
||||
($ty:ident) => (
|
||||
impl Trig for $ty {
|
||||
#[inline]
|
||||
fn sin(self) -> $ty { self.sin() }
|
||||
#[inline]
|
||||
fn cos(self) -> $ty { self.cos() }
|
||||
#[inline]
|
||||
fn tan(self) -> $ty { self.tan() }
|
||||
|
||||
/// A slightly faster approximation of `atan2`.
|
||||
///
|
||||
/// Note that it does not deal with the case where both x and y are 0.
|
||||
#[inline]
|
||||
fn fast_atan2(y: $ty, x: $ty) -> $ty {
|
||||
// See https://math.stackexchange.com/questions/1098487/atan2-faster-approximation#1105038
|
||||
use std::$ty::consts;
|
||||
let x_abs = x.abs();
|
||||
let y_abs = y.abs();
|
||||
let a = x_abs.min(y_abs) / x_abs.max(y_abs);
|
||||
let s = a * a;
|
||||
let mut result = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a;
|
||||
if y_abs > x_abs {
|
||||
result = consts::FRAC_PI_2 - result;
|
||||
}
|
||||
if x < 0.0 {
|
||||
result = consts::PI - result
|
||||
}
|
||||
if y < 0.0 {
|
||||
result = -result
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn degrees_to_radians(deg: Self) -> Self {
|
||||
deg.to_radians()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn radians_to_degrees(rad: Self) -> Self {
|
||||
rad.to_degrees()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
trig!(f32);
|
||||
trig!(f64);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"29b74b95210896ce634c11a9037638668473b5a1b3b1716c505cb04dbb6341fa","COPYING":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"96141412a2322a5dfe693d87d5792e7c9859340f751109856024b9870d2289d3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"2de24b7458d6b88f20324303a48acf64a4f2bbfb83d2ec4d6ff2b4f4a1fd2275","build.rs":"a1616d76f35b3a6015c2ee22ecf13eb225095b22e4190ef5ef2fcd9197b43ad1","src/gl.rs":"0a2f631a09ddfa267d6dcf2274342529b6f487e1cb982b92e1111a64c1d7437e","src/gl_fns.rs":"ead6bbdc6a937264275e849ed84ab9b1be83624fcea31aa7bde2d933390ee151","src/gles_fns.rs":"ba64f9a124af370121ba526c262ccb0f87ebf6f99115fc25e2a9ae5a8d39af34","src/lib.rs":"16610c19b45a3f26d56b379a3591aa2e4fc9477e7bd88f86b31c6ea32e834861"},"package":"e47b39459e47b76be4d2c82702932cdc66df09dcb8f813424167991adb8b3380"}
|
||||
{"files":{".travis.yml":"29b74b95210896ce634c11a9037638668473b5a1b3b1716c505cb04dbb6341fa","COPYING":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"07f8164277c2a586bae90fdc42d01d060cc54dbf8c62a2f3d7ba7871bbee20cb","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"2de24b7458d6b88f20324303a48acf64a4f2bbfb83d2ec4d6ff2b4f4a1fd2275","build.rs":"e155f5d7f71fe10d18e108fe6557f31ae5fec425a3acbdf564b02b0bc695fde4","src/gl.rs":"4b0fcc262883f226699769ef76611380bbfba1b56b762257b99be328195be4c5","src/gl_fns.rs":"58a8ab3e82d2d582874e95465ce44e0827d48f846668bc9620978d6748ea0255","src/gles_fns.rs":"4b1eb8eaacdd5514c3a93ab77403e4232d8a680452311c120b9340ab94a4a0b9","src/lib.rs":"16610c19b45a3f26d56b379a3591aa2e4fc9477e7bd88f86b31c6ea32e834861"},"package":"0d41e7ac812597988fdae31c9baec3c6d35cadb8ad9ab88a9bf9c0f119ed66c2"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "gleam"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
build = "build.rs"
|
||||
description = "Generated OpenGL bindings and wrapper for Servo."
|
||||
|
|
|
@ -13,13 +13,14 @@ fn main() {
|
|||
|
||||
// OpenGL 3.3 bindings
|
||||
let gl_extensions = [
|
||||
"GL_APPLE_client_storage",
|
||||
"GL_APPLE_fence",
|
||||
"GL_APPLE_texture_range",
|
||||
"GL_ARB_blend_func_extended",
|
||||
"GL_ARB_get_program_binary",
|
||||
"GL_ARB_texture_rectangle",
|
||||
"GL_EXT_debug_marker",
|
||||
"GL_APPLE_client_storage",
|
||||
"GL_APPLE_texture_range",
|
||||
"GL_APPLE_fence",
|
||||
"GL_ARB_get_program_binary",
|
||||
"GL_ARB_blend_func_extended",
|
||||
"GL_EXT_texture_filter_anisotropic",
|
||||
"GL_KHR_debug",
|
||||
];
|
||||
let gl_reg = Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, gl_extensions);
|
||||
|
@ -28,12 +29,14 @@ fn main() {
|
|||
|
||||
// GLES 3.0 bindings
|
||||
let gles_extensions = [
|
||||
"GL_EXT_texture_format_BGRA8888",
|
||||
"GL_OES_EGL_image",
|
||||
"GL_OES_EGL_image_external",
|
||||
"GL_EXT_disjoint_timer_query",
|
||||
"GL_EXT_debug_marker",
|
||||
"GL_EXT_disjoint_timer_query",
|
||||
"GL_EXT_shader_texture_lod",
|
||||
"GL_EXT_texture_filter_anisotropic",
|
||||
"GL_EXT_texture_format_BGRA8888",
|
||||
"GL_KHR_debug",
|
||||
"GL_OES_EGL_image_external",
|
||||
"GL_OES_EGL_image",
|
||||
];
|
||||
let gles_reg = Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, gles_extensions);
|
||||
gles_reg.write_bindings(gl_generator::StructGenerator, &mut file_gles)
|
||||
|
|
|
@ -62,37 +62,6 @@ fn calculate_length(width: GLsizei, height: GLsizei, format: GLenum, pixel_type:
|
|||
return (width * height * colors * depth) as usize;
|
||||
}
|
||||
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
|
||||
fn get_uniform_iv_vector_length(uniform_type: &GLuint) -> usize {
|
||||
match *uniform_type {
|
||||
ffi::BOOL |
|
||||
ffi::INT |
|
||||
ffi::SAMPLER_2D |
|
||||
ffi::SAMPLER_CUBE => 1,
|
||||
ffi::INT_VEC2 |
|
||||
ffi::BOOL_VEC2 => 2,
|
||||
ffi::INT_VEC3 |
|
||||
ffi::BOOL_VEC3 => 3,
|
||||
ffi::INT_VEC4 |
|
||||
ffi::BOOL_VEC4 => 4,
|
||||
_ => panic!("Invalid location argument"),
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
|
||||
fn get_uniform_fv_vector_length(uniform_type: &GLuint) -> usize {
|
||||
match *uniform_type {
|
||||
ffi::FLOAT => 1,
|
||||
ffi::FLOAT_VEC2 => 2,
|
||||
ffi::FLOAT_VEC3 => 3,
|
||||
ffi::FLOAT_VEC4 |
|
||||
ffi::FLOAT_MAT2 => 4,
|
||||
ffi::FLOAT_MAT3 => 9,
|
||||
ffi::FLOAT_MAT4 => 16,
|
||||
_ => panic!("Invalid location argument"),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugMessage {
|
||||
pub message: String,
|
||||
pub source: GLenum,
|
||||
|
@ -185,8 +154,8 @@ declare_gl_apis! {
|
|||
fn active_texture(&self, texture: GLenum);
|
||||
fn attach_shader(&self, program: GLuint, shader: GLuint);
|
||||
fn bind_attrib_location(&self, program: GLuint, index: GLuint, name: &str);
|
||||
fn get_uniform_iv(&self, program: GLuint, location: GLint) -> Vec<GLint>;
|
||||
fn get_uniform_fv(&self, program: GLuint, location: GLint) -> Vec<GLfloat>;
|
||||
unsafe fn get_uniform_iv(&self, program: GLuint, location: GLint, result: &mut [GLint]);
|
||||
unsafe fn get_uniform_fv(&self, program: GLuint, location: GLint, result: &mut [GLfloat]);
|
||||
fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint;
|
||||
fn get_uniform_indices(&self, program: GLuint, names: &[&str]) -> Vec<GLuint>;
|
||||
fn bind_buffer_base(&self, target: GLenum, index: GLuint, buffer: GLuint);
|
||||
|
|
|
@ -19,21 +19,6 @@ impl GlFns
|
|||
ffi_gl_: ffi_gl_,
|
||||
}) as Rc<Gl>
|
||||
}
|
||||
|
||||
fn get_active_uniform_type(&self, program: GLuint) -> GLuint {
|
||||
let mut size: GLint = 0;
|
||||
let mut uniform_type: GLuint = 0;
|
||||
unsafe {
|
||||
self.ffi_gl_.GetActiveUniform(program,
|
||||
0 as GLuint,
|
||||
0 as GLsizei,
|
||||
ptr::null_mut(),
|
||||
&mut size,
|
||||
&mut uniform_type,
|
||||
ptr::null_mut());
|
||||
}
|
||||
uniform_type
|
||||
}
|
||||
}
|
||||
|
||||
impl Gl for GlFns {
|
||||
|
@ -309,25 +294,15 @@ impl Gl for GlFns {
|
|||
}
|
||||
|
||||
// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml
|
||||
fn get_uniform_iv(&self, program: GLuint, location: GLint) -> Vec<GLint> {
|
||||
let uniform_type = self.get_active_uniform_type(program);
|
||||
let len = get_uniform_iv_vector_length(&uniform_type);
|
||||
let mut result: [GLint; 4] = [0; 4];
|
||||
unsafe {
|
||||
self.ffi_gl_.GetUniformiv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
Vec::from(&result[0..len])
|
||||
unsafe fn get_uniform_iv(&self, program: GLuint, location: GLint, result: &mut [GLint]) {
|
||||
assert!(!result.is_empty());
|
||||
self.ffi_gl_.GetUniformiv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
|
||||
// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml
|
||||
fn get_uniform_fv(&self, program: GLuint, location: GLint) -> Vec<GLfloat> {
|
||||
let uniform_type = self.get_active_uniform_type(program);
|
||||
let len = get_uniform_fv_vector_length(&uniform_type);
|
||||
let mut result: [GLfloat; 16] = [0.0; 16];
|
||||
unsafe {
|
||||
self.ffi_gl_.GetUniformfv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
Vec::from(&result[0..len])
|
||||
unsafe fn get_uniform_fv(&self, program: GLuint, location: GLint, result: &mut [GLfloat]) {
|
||||
assert!(!result.is_empty());
|
||||
self.ffi_gl_.GetUniformfv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
|
||||
fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint {
|
||||
|
|
|
@ -19,21 +19,6 @@ impl GlesFns
|
|||
ffi_gl_: ffi_gl_,
|
||||
}) as Rc<Gl>
|
||||
}
|
||||
|
||||
fn get_active_uniform_type(&self, program: GLuint) -> GLuint {
|
||||
let mut size: GLint = 0;
|
||||
let mut uniform_type: GLuint = 0;
|
||||
unsafe {
|
||||
self.ffi_gl_.GetActiveUniform(program,
|
||||
0 as GLuint,
|
||||
0 as GLsizei,
|
||||
ptr::null_mut(),
|
||||
&mut size,
|
||||
&mut uniform_type,
|
||||
ptr::null_mut());
|
||||
}
|
||||
uniform_type
|
||||
}
|
||||
}
|
||||
|
||||
impl Gl for GlesFns {
|
||||
|
@ -333,25 +318,15 @@ impl Gl for GlesFns {
|
|||
}
|
||||
|
||||
// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml
|
||||
fn get_uniform_iv(&self, program: GLuint, location: GLint) -> Vec<GLint> {
|
||||
let uniform_type = self.get_active_uniform_type(program);
|
||||
let len = get_uniform_iv_vector_length(&uniform_type);
|
||||
let mut result: [GLint; 4] = [0; 4];
|
||||
unsafe {
|
||||
self.ffi_gl_.GetUniformiv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
Vec::from(&result[0..len])
|
||||
unsafe fn get_uniform_iv(&self, program: GLuint, location: GLint, result: &mut [GLint]) {
|
||||
assert!(!result.is_empty());
|
||||
self.ffi_gl_.GetUniformiv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
|
||||
// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml
|
||||
fn get_uniform_fv(&self, program: GLuint, location: GLint) -> Vec<GLfloat> {
|
||||
let uniform_type = self.get_active_uniform_type(program);
|
||||
let len = get_uniform_fv_vector_length(&uniform_type);
|
||||
let mut result: [GLfloat; 16] = [0.0; 16];
|
||||
unsafe {
|
||||
self.ffi_gl_.GetUniformfv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
Vec::from(&result[0..len])
|
||||
unsafe fn get_uniform_fv(&self, program: GLuint, location: GLint, result: &mut [GLfloat]) {
|
||||
assert!(!result.is_empty());
|
||||
self.ffi_gl_.GetUniformfv(program, location, result.as_mut_ptr());
|
||||
}
|
||||
|
||||
fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint {
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"8f2b69b8ed35600a4fc9543a17f07f4eb1332602a8c31f07f75d8c415465e6b3","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"a65ed5c817c867fe23bc2029f34baea4a645a07dd5d101a0027e796d2923be58","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"98cd4982b1641146ac0a2bd4878a634768b94cbe2db7719bad30501e4ae8e82e","src/lib.rs":"ac7a25ea2f1543dc2b45decd3ec27e0c27dcc19c78bee8a3e0bbb3324e158d53","src/polygon.rs":"1c86fe39d10414c3cc7238ce2b1b22e7ce2aaa15cc931adecb5ef217ce861d50","tests/clip.rs":"606aff2a26a2054ed0dbd5377a356791440b5f88e6bf0c8447702cc5851fac4c","tests/main.rs":"182669f26ce9f88ff82a36669aaae22458fd20f997a54375d3748a150af3f51a","tests/split.rs":"ec57ecb682bcd1e1c059905f6b05b72f2ae86be3ce2a0e657058bb83c6cff68e"},"package":"7079b8485b4f9d9560dee7a69ca8f6ca781f9f284ff9d2bf27255d440b03e4af"}
|
||||
{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"ee68f6bd7972f6918091b4557302bbb655327d822413ba09102c0e63b49502fb","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"a65ed5c817c867fe23bc2029f34baea4a645a07dd5d101a0027e796d2923be58","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"98cd4982b1641146ac0a2bd4878a634768b94cbe2db7719bad30501e4ae8e82e","src/lib.rs":"ac7a25ea2f1543dc2b45decd3ec27e0c27dcc19c78bee8a3e0bbb3324e158d53","src/polygon.rs":"ee10d7ddc8369b964d548e53ad9cfa09d84a65b22e02ec2866175dc44469bb6e","tests/clip.rs":"606aff2a26a2054ed0dbd5377a356791440b5f88e6bf0c8447702cc5851fac4c","tests/main.rs":"c64d2d597a1f3c702921639a2af49c4a61ea6952b983a79749458c18c64eb69f","tests/split.rs":"0eb1afb1f26cdecd5fffbf32d57e889f8f69254c0a57eecb8ccbbdf38efcdf27"},"package":"6e14382aabad89085fbf714f75d527492bb672725facb9b2ced2fada47cf418c"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "plane-split"
|
||||
version = "0.9.1"
|
||||
version = "0.10.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.17"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
|
|
@ -143,21 +143,21 @@ impl<T, U> Polygon<T, U> where
|
|||
rect: TypedRect<T, V>,
|
||||
transform: TypedTransform3D<T, V, U>,
|
||||
anchor: usize,
|
||||
) -> Self
|
||||
) -> Option<Self>
|
||||
where
|
||||
T: Trig + ops::Neg<Output=T>,
|
||||
{
|
||||
let points = [
|
||||
transform.transform_point3d(&rect.origin.to_3d()),
|
||||
transform.transform_point3d(&rect.top_right().to_3d()),
|
||||
transform.transform_point3d(&rect.bottom_right().to_3d()),
|
||||
transform.transform_point3d(&rect.bottom_left().to_3d()),
|
||||
transform.transform_point3d(&rect.origin.to_3d())?,
|
||||
transform.transform_point3d(&rect.top_right().to_3d())?,
|
||||
transform.transform_point3d(&rect.bottom_right().to_3d())?,
|
||||
transform.transform_point3d(&rect.bottom_left().to_3d())?,
|
||||
];
|
||||
|
||||
//Note: this code path could be more efficient if we had inverse-transpose
|
||||
//let n4 = transform.transform_point4d(&TypedPoint4D::new(T::zero(), T::zero(), T::one(), T::zero()));
|
||||
//let normal = TypedPoint3D::new(n4.x, n4.y, n4.z);
|
||||
Self::from_points(points, anchor)
|
||||
Some(Self::from_points(points, anchor))
|
||||
}
|
||||
|
||||
/// Bring a point into the local coordinate space, returning
|
||||
|
|
|
@ -65,7 +65,7 @@ fn from_transformed_rect() {
|
|||
TypedTransform3D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
|
||||
.pre_translate(vec3(0.0, 0.0, 10.0));
|
||||
let poly = Polygon::from_transformed_rect(rect, transform, 0);
|
||||
assert!(poly.is_valid());
|
||||
assert!(poly.is_some() && poly.unwrap().is_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -27,12 +27,12 @@ fn sort_rotation(splitter: &mut Splitter<f32, ()>) {
|
|||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(FRAC_PI_4));
|
||||
|
||||
let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let polys = [
|
||||
Polygon::from_transformed_rect(rect, transform0, 0),
|
||||
Polygon::from_transformed_rect(rect, transform1, 1),
|
||||
Polygon::from_transformed_rect(rect, transform2, 2),
|
||||
];
|
||||
let p1 = Polygon::from_transformed_rect(rect, transform0, 0);
|
||||
let p2 = Polygon::from_transformed_rect(rect, transform1, 1);
|
||||
let p3 = Polygon::from_transformed_rect(rect, transform2, 2);
|
||||
assert!(p1.is_some() && p2.is_some() && p3.is_some(), "Cannot construct transformed polygons");
|
||||
|
||||
let polys = [ p1.unwrap(), p2.unwrap(), p3.unwrap() ];
|
||||
let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
|
||||
let ids: Vec<_> = result.iter().map(|poly| poly.anchor).collect();
|
||||
assert_eq!(&ids, &[2, 1, 0, 1, 2]);
|
||||
|
@ -49,7 +49,9 @@ fn sort_trivial(splitter: &mut Splitter<f32, ()>) {
|
|||
let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let polys: Vec<_> = anchors.iter().map(|&anchor| {
|
||||
let transform: TypedTransform3D<f32, (), ()> = TypedTransform3D::create_translation(0.0, 0.0, anchor as f32);
|
||||
Polygon::from_transformed_rect(rect, transform, anchor)
|
||||
let poly = Polygon::from_transformed_rect(rect, transform, anchor);
|
||||
assert!(poly.is_some(), "Cannot construct transformed polygons");
|
||||
poly.unwrap()
|
||||
}).collect();
|
||||
|
||||
let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"91edce5ea2a1956399db4b17f580c8b7995af3aa9801c4314865f560c55d6d09","Cargo.toml":"1bbfc40ffd7370696242dd27dd4f0e211d3309aebca8b564029a6ac167e81726","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"1bc64a621160a291c86b8770f3eeaa45a31c31d91c2a071f39981c14fdacb035","benches/bench.rs":"54cf4879d36ba2a9f3423af91bb93227b70849200e5bf74e384a166d6aa09893","lib.rs":"bd237262110649b266c6599d4f8b3d1f7d7c758d6852b65243c8221811d273e8"},"package":"44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"}
|
||||
{"files":{".travis.yml":"1fb562c82e3ba8668667016eb5be043130a943a3e22c2c692dfcefd23bb07028","Cargo.toml":"2f8fa5e2e7894727dab3b256f93c739ee2fdd715cad0ea18b466330325dc6c90","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9","README.md":"1bc64a621160a291c86b8770f3eeaa45a31c31d91c2a071f39981c14fdacb035","benches/bench.rs":"bf8c9a06dad072e14e844daf43895c41d632db183f33fa6de53a43d3677a7375","lib.rs":"dd8993f008a5512d33a74d5e141a4d9d6294baa11174bfeeec2251d731d51957"},"package":"26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8"}
|
|
@ -5,10 +5,9 @@ rust:
|
|||
- stable
|
||||
script: |
|
||||
cargo build --verbose &&
|
||||
cargo build --all-features --verbose &&
|
||||
cargo test --verbose &&
|
||||
cargo test --all-features --verbose &&
|
||||
([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --no-default-features) &&
|
||||
([ $TRAVIS_RUST_VERSION != nightly ] || cargo check --verbose --no-default-features) &&
|
||||
([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --features union) &&
|
||||
([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench)
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
[package]
|
||||
name = "smallvec"
|
||||
version = "0.6.0"
|
||||
version = "0.6.3"
|
||||
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
|
||||
description = "'Small vector' optimization: store up to a small number of items on the stack"
|
||||
documentation = "http://doc.servo.org/smallvec/"
|
||||
readme = "README.md"
|
||||
keywords = ["small", "vec", "vector", "stack", "no_std"]
|
||||
categories = ["data-structures"]
|
||||
license = "MPL-2.0"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/servo/rust-smallvec"
|
||||
|
||||
[lib]
|
||||
|
@ -28,9 +28,13 @@ path = "lib.rs"
|
|||
[dependencies.serde]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.unreachable]
|
||||
version = "1.0.0"
|
||||
[dev-dependencies.bincode]
|
||||
version = "0.8"
|
||||
version = "1.0.1"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
union = []
|
||||
|
|
|
@ -1,374 +0,0 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2012-2013 Mozilla Foundation
|
||||
Copyright (c) 2018 The Servo Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
|
@ -1,92 +1,248 @@
|
|||
#![feature(test)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate smallvec;
|
||||
extern crate test;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use self::test::Bencher;
|
||||
use smallvec::{ExtendFromSlice, SmallVec};
|
||||
|
||||
#[bench]
|
||||
fn bench_push(b: &mut Bencher) {
|
||||
const VEC_SIZE: usize = 16;
|
||||
const SPILLED_SIZE: usize = 100;
|
||||
|
||||
trait Vector<T>: for<'a> From<&'a [T]> + Extend<T> + ExtendFromSlice<T> {
|
||||
fn new() -> Self;
|
||||
fn push(&mut self, val: T);
|
||||
fn pop(&mut self) -> Option<T>;
|
||||
fn remove(&mut self, p: usize) -> T;
|
||||
fn insert(&mut self, n: usize, val: T);
|
||||
fn from_elem(val: T, n: usize) -> Self;
|
||||
}
|
||||
|
||||
impl<T: Copy> Vector<T> for Vec<T> {
|
||||
fn new() -> Self {
|
||||
Self::with_capacity(VEC_SIZE)
|
||||
}
|
||||
|
||||
fn push(&mut self, val: T) {
|
||||
self.push(val)
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<T> {
|
||||
self.pop()
|
||||
}
|
||||
|
||||
fn remove(&mut self, p: usize) -> T {
|
||||
self.remove(p)
|
||||
}
|
||||
|
||||
fn insert(&mut self, n: usize, val: T) {
|
||||
self.insert(n, val)
|
||||
}
|
||||
|
||||
fn from_elem(val: T, n: usize) -> Self {
|
||||
vec![val; n]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Vector<T> for SmallVec<[T; VEC_SIZE]> {
|
||||
fn new() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
fn push(&mut self, val: T) {
|
||||
self.push(val)
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<T> {
|
||||
self.pop()
|
||||
}
|
||||
|
||||
fn remove(&mut self, p: usize) -> T {
|
||||
self.remove(p)
|
||||
}
|
||||
|
||||
fn insert(&mut self, n: usize, val: T) {
|
||||
self.insert(n, val)
|
||||
}
|
||||
|
||||
fn from_elem(val: T, n: usize) -> Self {
|
||||
smallvec![val; n]
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! make_benches {
|
||||
($typ:ty { $($b_name:ident => $g_name:ident($($args:expr),*),)* }) => {
|
||||
$(
|
||||
#[bench]
|
||||
fn $b_name(b: &mut Bencher) {
|
||||
$g_name::<$typ>($($args,)* b)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_benches! {
|
||||
SmallVec<[u64; VEC_SIZE]> {
|
||||
bench_push => gen_push(SPILLED_SIZE as _),
|
||||
bench_push_small => gen_push(VEC_SIZE as _),
|
||||
bench_insert => gen_insert(SPILLED_SIZE as _),
|
||||
bench_insert_small => gen_insert(VEC_SIZE as _),
|
||||
bench_remove => gen_remove(SPILLED_SIZE as _),
|
||||
bench_remove_small => gen_remove(VEC_SIZE as _),
|
||||
bench_extend => gen_extend(SPILLED_SIZE as _),
|
||||
bench_extend_small => gen_extend(VEC_SIZE as _),
|
||||
bench_from_slice => gen_from_slice(SPILLED_SIZE as _),
|
||||
bench_from_slice_small => gen_from_slice(VEC_SIZE as _),
|
||||
bench_extend_from_slice => gen_extend_from_slice(SPILLED_SIZE as _),
|
||||
bench_extend_from_slice_small => gen_extend_from_slice(VEC_SIZE as _),
|
||||
bench_macro_from_elem => gen_from_elem(SPILLED_SIZE as _),
|
||||
bench_macro_from_elem_small => gen_from_elem(VEC_SIZE as _),
|
||||
bench_pushpop => gen_pushpop(),
|
||||
}
|
||||
}
|
||||
|
||||
make_benches! {
|
||||
Vec<u64> {
|
||||
bench_push_vec => gen_push(SPILLED_SIZE as _),
|
||||
bench_push_vec_small => gen_push(VEC_SIZE as _),
|
||||
bench_insert_vec => gen_insert(SPILLED_SIZE as _),
|
||||
bench_insert_vec_small => gen_insert(VEC_SIZE as _),
|
||||
bench_remove_vec => gen_remove(SPILLED_SIZE as _),
|
||||
bench_remove_vec_small => gen_remove(VEC_SIZE as _),
|
||||
bench_extend_vec => gen_extend(SPILLED_SIZE as _),
|
||||
bench_extend_vec_small => gen_extend(VEC_SIZE as _),
|
||||
bench_from_slice_vec => gen_from_slice(SPILLED_SIZE as _),
|
||||
bench_from_slice_vec_small => gen_from_slice(VEC_SIZE as _),
|
||||
bench_extend_from_slice_vec => gen_extend_from_slice(SPILLED_SIZE as _),
|
||||
bench_extend_from_slice_vec_small => gen_extend_from_slice(VEC_SIZE as _),
|
||||
bench_macro_from_elem_vec => gen_from_elem(SPILLED_SIZE as _),
|
||||
bench_macro_from_elem_vec_small => gen_from_elem(VEC_SIZE as _),
|
||||
bench_pushpop_vec => gen_pushpop(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_push<V: Vector<u64>>(n: u64, b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn push_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
|
||||
vec.push(x)
|
||||
fn push_noinline<V: Vector<u64>>(vec: &mut V, x: u64) {
|
||||
vec.push(x);
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
for x in 0..100 {
|
||||
let mut vec = V::new();
|
||||
for x in 0..n {
|
||||
push_noinline(&mut vec, x);
|
||||
}
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert(b: &mut Bencher) {
|
||||
fn gen_insert<V: Vector<u64>>(n: u64, b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn insert_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
|
||||
vec.insert(0, x)
|
||||
fn insert_noinline<V: Vector<u64>>(vec: &mut V, p: usize, x: u64) {
|
||||
vec.insert(p, x)
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
for x in 0..100 {
|
||||
insert_noinline(&mut vec, x);
|
||||
let mut vec = V::new();
|
||||
// Add one element, with each iteration we insert one before the end.
|
||||
// This means that we benchmark the insertion operation and not the
|
||||
// time it takes to `ptr::copy` the data.
|
||||
vec.push(0);
|
||||
for x in 0..n {
|
||||
insert_noinline(&mut vec, x as _, x);
|
||||
}
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_remove<V: Vector<u64>>(n: usize, b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn remove_noinline<V: Vector<u64>>(vec: &mut V, p: usize) -> u64 {
|
||||
vec.remove(p)
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec = V::from_elem(0, n as _);
|
||||
|
||||
for x in (0..n - 1).rev() {
|
||||
remove_noinline(&mut vec, x);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_extend<V: Vector<u64>>(n: u64, b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut vec = V::new();
|
||||
vec.extend(0..n);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_from_slice<V: Vector<u64>>(n: u64, b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..n).collect();
|
||||
b.iter(|| {
|
||||
let vec = V::from(&v);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_extend_from_slice<V: Vector<u64>>(n: u64, b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..n).collect();
|
||||
b.iter(|| {
|
||||
let mut vec = V::new();
|
||||
vec.extend_from_slice(&v);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_pushpop<V: Vector<u64>>(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn pushpop_noinline<V: Vector<u64>>(vec: &mut V, x: u64) -> Option<u64> {
|
||||
vec.push(x);
|
||||
vec.pop()
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec = V::new();
|
||||
for x in 0..SPILLED_SIZE as _ {
|
||||
pushpop_noinline(&mut vec, x);
|
||||
}
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_from_elem<V: Vector<u64>>(n: usize, b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let vec = V::from_elem(42, n);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert_many(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn insert_many_noinline<I: IntoIterator<Item=u64>>(
|
||||
vec: &mut SmallVec<[u64; 16]>, index: usize, iterable: I) {
|
||||
fn insert_many_noinline<I: IntoIterator<Item = u64>>(
|
||||
vec: &mut SmallVec<[u64; VEC_SIZE]>,
|
||||
index: usize,
|
||||
iterable: I,
|
||||
) {
|
||||
vec.insert_many(index, iterable)
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
insert_many_noinline(&mut vec, 0, 0..100);
|
||||
insert_many_noinline(&mut vec, 0, 0..100);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_extend(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
vec.extend(0..100);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_from_slice(b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..100).collect();
|
||||
b.iter(|| {
|
||||
let vec: SmallVec<[u64; 16]> = SmallVec::from_slice(&v);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_extend_from_slice(b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..100).collect();
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
vec.extend_from_slice(&v);
|
||||
let mut vec = SmallVec::<[u64; VEC_SIZE]>::new();
|
||||
insert_many_noinline(&mut vec, 0, 0..SPILLED_SIZE as _);
|
||||
insert_many_noinline(&mut vec, 0, 0..SPILLED_SIZE as _);
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert_from_slice(b: &mut Bencher) {
|
||||
let v: Vec<u64> = (0..100).collect();
|
||||
let v: Vec<u64> = (0..SPILLED_SIZE as _).collect();
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
let mut vec = SmallVec::<[u64; VEC_SIZE]>::new();
|
||||
vec.insert_from_slice(0, &v);
|
||||
vec.insert_from_slice(0, &v);
|
||||
vec
|
||||
|
@ -94,18 +250,25 @@ fn bench_insert_from_slice(b: &mut Bencher) {
|
|||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pushpop(b: &mut Bencher) {
|
||||
#[inline(never)]
|
||||
fn pushpop_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
|
||||
vec.push(x);
|
||||
vec.pop();
|
||||
}
|
||||
|
||||
fn bench_macro_from_list(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
|
||||
for x in 0..100 {
|
||||
pushpop_noinline(&mut vec, x);
|
||||
}
|
||||
let vec: SmallVec<[u64; 16]> = smallvec![
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80,
|
||||
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000,
|
||||
0x80000, 0x100000,
|
||||
];
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_macro_from_list_vec(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let vec: Vec<u64> = vec![
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80,
|
||||
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000,
|
||||
0x80000, 0x100000,
|
||||
];
|
||||
vec
|
||||
});
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче