Bug 1459935 - Update Cargo lockfiles and re-vendor rust dependencies. r=jrmuizel
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 11 May 2018 09:15:09 -0400
changeset 417925 3ad8053cee91530997fb2eaec8a4913195076a7a
parent 417924 972eab977ff34fd59a6a6d829d426c2407d703e0
child 417926 84f298282f592fceb8f5f5bb9f3bbfafdf3b7d60
push id33983
push usercbrindusan@mozilla.com
push dateSat, 12 May 2018 09:45:12 +0000
treeherdermozilla-central@3adff617b271 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1459935
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1459935 - Update Cargo lockfiles and re-vendor rust dependencies. r=jrmuizel MozReview-Commit-ID: CoZ3PoUGFwU
Cargo.lock
third_party/rust/euclid/.cargo-checksum.json
third_party/rust/euclid/Cargo.toml
third_party/rust/euclid/src/homogen.rs
third_party/rust/euclid/src/length.rs
third_party/rust/euclid/src/lib.rs
third_party/rust/euclid/src/point.rs
third_party/rust/euclid/src/rect.rs
third_party/rust/euclid/src/scale.rs
third_party/rust/euclid/src/size.rs
third_party/rust/euclid/src/transform3d.rs
third_party/rust/euclid/src/vector.rs
third_party/rust/plane-split/.cargo-checksum.json
third_party/rust/plane-split/Cargo.toml
third_party/rust/plane-split/README.md
third_party/rust/plane-split/benches/split.rs
third_party/rust/plane-split/src/bsp.rs
third_party/rust/plane-split/src/lib.rs
third_party/rust/plane-split/src/naive.rs
third_party/rust/plane-split/src/polygon.rs
third_party/rust/plane-split/tests/clip.rs
third_party/rust/plane-split/tests/main.rs
third_party/rust/plane-split/tests/split.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -660,17 +660,17 @@ dependencies = [
 
 [[package]]
 name = "error-chain"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "euclid"
-version = "0.17.2"
+version = "0.17.3"
 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.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "fallible"
@@ -1130,17 +1130,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "malloc_size_of"
 version = "0.0.1"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "selectors 0.19.0",
  "servo_arc 0.1.1",
  "smallbitvec 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1532,21 +1532,21 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "plane-split"
-version = "0.8.0"
+version = "0.9.1"
 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.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "podio"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1887,17 +1887,17 @@ dependencies = [
  "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1941,32 +1941,32 @@ dependencies = [
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "selectors 0.19.0",
  "servo_arc 0.1.1",
 ]
 
 [[package]]
 name = "stylo_tests"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "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)",
@@ -2293,24 +2293,24 @@ dependencies = [
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 9.2.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.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (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)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (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.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "plane-split 0.9.1 (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.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.57.2",
 ]
@@ -2321,32 +2321,32 @@ version = "0.57.2"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.13.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.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.37 (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.37 (git+https://github.com/servo/serde?branch=deserialize_from_enums6)",
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_bindings"
 version = "0.1.0"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.13.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.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender 0.57.2",
 ]
 
@@ -2533,17 +2533,17 @@ dependencies = [
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
 "checksum dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b26e30aaa6bf31ec830db15fec14ed04f0f2ecfcc486ecfce88c55d3389b237f"
 "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
 "checksum ena 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe5a5078ac8c506d3e4430763b1ba9b609b1286913e7d08e581d1c2de9b7e5"
 "checksum encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "93ec52324ca72f423237a413ca0e1c60654c8b3d0934fcd5fd888508dfcc4ba7"
 "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d"
 "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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "adfe67a9343519c1449d208da5998c6de582de698f7a39c4ac82ffba23d131a5"
+"checksum euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c95fd0d455f114291a3109286bd387bd423770058474a2d3f38b712cd661df60"
 "checksum fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478"
 "checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
 "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
 "checksum foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ebc04f19019fff1f2d627b5581574ead502f80c48c88900575a46e0840fe5d0"
 "checksum freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b659e75b7a7338fe75afd7f909fc2b71937845cffb6ebe54ba2e50f13d8e903d"
 "checksum fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab76cfd2aaa59b7bf6688ad9ba15bbae64bff97f04ea02144cfd3443e5c2866"
 "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
 "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
@@ -2611,17 +2611,17 @@ dependencies = [
 "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
 "checksum petgraph 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7e5234c228fbfa874c86a77f685886127f82e0aef602ad1d48333fcac6ad61"
 "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
 "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
 "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.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69c557e11e3a1533bc969fa596e5011e1d9f76dd61cd102ef942c9f8654b17a2"
+"checksum plane-split 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7079b8485b4f9d9560dee7a69ca8f6ca781f9f284ff9d2bf27255d440b03e4af"
 "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.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1cb7aaaa4bf022ec2b14ff2f2ba1643a22f3cee88df014a85e14b392282c61d"
 "checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118"
 "checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
 "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
 "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
 "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
--- a/third_party/rust/euclid/.cargo-checksum.json
+++ b/third_party/rust/euclid/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"301590735ff27f124c03cef8598aa5397c88c59aba3d058edf0bde532965c346","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"af74e37daff01a2bf3c4b4994333dff11d472afbc6b79a7fd3910bfee4f0f276","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6594377e8f6c20f88f628520d8de9b9a59c5892a0ee9a6ccd13c8400c1499911","src/length.rs":"9b0fa8c38f8f5c88d8ba0c93b48d0cadb737b2f7f16536cc2f37b4db614d56f5","src/lib.rs":"a10d8f005826c20d4cb26ed019ffaebd474958a6023a2be2cbb7001e5a9fe7f3","src/macros.rs":"ccb9aeb942f571ec4207334b87c87f59a9a4d666039d143d7673372679c42347","src/num.rs":"4439479fad5729073e0bfe0b96b547672a237430d48e564519759b9550baa033","src/point.rs":"acebbd8caf264bdec48e7c26fac9b05795abd7d70d0be4cd336eb86958aeb971","src/rect.rs":"102cc77ccc8111ea49ee63210f91984bc51dad86603503259a6ff11414f3c953","src/rotation.rs":"2686d8624671f48e9c657a98c9ac3345f3c4028e65ee3ef588d407ffd020fb86","src/scale.rs":"1b31f1898b497489a8cfb67b210092cb05a07f9867068026513f30d8d91df824","src/side_offsets.rs":"604e104616777515e0e0e68262110c55fe9c0ce4deeb6d022e5b4984df11e29f","src/size.rs":"6de9bec02768015259df8d08a98a24eb79a47fd39d3a4842f1453d0539da160d","src/transform2d.rs":"edf9b82411a25d8f6b2a867a5b579c15316b3fd112eb463f6589012039670be3","src/transform3d.rs":"a4b0f709bc9787ca49f6c756efb04dc590b1847784fc112fc22fe84cd69024b3","src/trig.rs":"78b8fb26d2fded11c4b8aa47935e80c474696aee1999c688642534b667e005d9","src/vector.rs":"b6e3e9bfd9f55f96a734998878e8fcf57e6222bada999d65ede004f9f09c95a5"},"package":"adfe67a9343519c1449d208da5998c6de582de698f7a39c4ac82ffba23d131a5"}
\ No newline at end of file
+{"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"}
\ No newline at end of file
--- a/third_party/rust/euclid/Cargo.toml
+++ b/third_party/rust/euclid/Cargo.toml
@@ -7,32 +7,32 @@
 #
 # 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.2"
+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.3.7"
+version = "0.4"
 
 [dev-dependencies.serde_test]
 version = "1.0"
 
 [features]
 unstable = []
new file mode 100644
--- /dev/null
+++ b/third_party/rust/euclid/src/homogen.rs
@@ -0,0 +1,109 @@
+// 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());
+    }
+}
--- a/third_party/rust/euclid/src/length.rs
+++ b/third_party/rust/euclid/src/length.rs
@@ -26,17 +26,19 @@ use std::fmt;
 /// `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.
+/// 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)
     }
 }
--- a/third_party/rust/euclid/src/lib.rs
+++ b/third_party/rust/euclid/src/lib.rs
@@ -64,36 +64,40 @@ extern crate num_traits;
 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;
-#[macro_use]
-mod macros;
-mod transform2d;
-mod transform3d;
 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
@@ -114,8 +118,9 @@ pub type TypedMatrix4D<T, Src, Dst> = Ty
 
 /// 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;
+
--- a/third_party/rust/euclid/src/point.rs
+++ b/third_party/rust/euclid/src/point.rs
@@ -401,17 +401,17 @@ define_matrix! {
 }
 
 /// Default 3d point type with no unit.
 ///
 /// `Point3D` provides the same methods as `TypedPoint3D`.
 pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
 
 impl<T: Copy + Zero, U> TypedPoint3D<T, U> {
-    /// Constructor, setting all copmonents to zero.
+    /// 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]
@@ -465,17 +465,17 @@ impl<T: Copy, U> TypedPoint3D<T, U> {
     /// Constructor taking properly typed Lengths instead of scalar values.
     #[inline]
     pub fn from_lengths(x: Length<T, U>, y: Length<T, U>, z: Length<T, U>) -> Self {
         point3(x.0, y.0, z.0)
     }
 
     /// Cast this point into a vector.
     ///
-    /// Equivalent to substracting the origin to this point.
+    /// 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> {
--- a/third_party/rust/euclid/src/rect.rs
+++ b/third_party/rust/euclid/src/rect.rs
@@ -14,21 +14,24 @@ 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.
@@ -276,44 +279,42 @@ where
     /// 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<'a, I>(points: I) -> Self
+    pub fn from_points<I>(points: I) -> Self
     where
-        U: 'a,
-        T: 'a,
-        I: IntoIterator<Item = &'a TypedPoint2D<T, U>>,
+        I: IntoIterator,
+        I::Item: Borrow<TypedPoint2D<T, U>>,
     {
         let mut points = points.into_iter();
 
-        let first = if let Some(first) = points.next() {
-            first
-        } else {
-            return TypedRect::zero();
+        let (mut min_x, mut min_y) = match points.next() {
+            Some(first) => (first.borrow().x, first.borrow().y),
+            None => return TypedRect::zero(),
         };
 
-        let (mut min_x, mut min_y) = (first.x, first.y);
         let (mut max_x, mut max_y) = (min_x, min_y);
         for point in points {
-            if point.x < min_x {
-                min_x = point.x
-            }
-            if point.x > max_x {
-                max_x = point.x
+            let p = point.borrow();
+            if p.x < min_x {
+                min_x = p.x
             }
-            if point.y < min_y {
-                min_y = point.y
+            if p.x > max_x {
+                max_x = p.x
             }
-            if point.y > max_y {
-                max_y = point.y
+            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),
         )
     }
 }
--- a/third_party/rust/euclid/src/scale.rs
+++ b/third_party/rust/euclid/src/scale.rs
@@ -65,27 +65,32 @@ where
     where
         S: Serializer,
     {
         self.0.serialize(serializer)
     }
 }
 
 impl<T, Src, Dst> TypedScale<T, Src, Dst> {
-    pub fn new(x: T) -> 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())
     }
 }
 
--- a/third_party/rust/euclid/src/size.rs
+++ b/third_party/rust/euclid/src/size.rs
@@ -5,17 +5,17 @@
 // 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};
+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.
@@ -280,16 +280,49 @@ where
         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;
--- a/third_party/rust/euclid/src/transform3d.rs
+++ b/third_party/rust/euclid/src/transform3d.rs
@@ -4,18 +4,19 @@
 // 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, point2, point3};
+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;
@@ -36,17 +37,17 @@ define_matrix! {
     pub struct TypedTransform3D<T, Src, Dst> {
         pub m11: T, pub m12: T, pub m13: T, pub m14: T,
         pub m21: T, pub m22: T, pub m23: T, pub m24: T,
         pub m31: T, pub m32: T, pub m33: T, pub m34: T,
         pub m41: T, pub m42: T, pub m43: T, pub m44: T,
     }
 }
 
-/// The default 4d transform type with no units.
+/// 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]
@@ -147,17 +148,17 @@ where T: Copy + Clone +
         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.
+    /// 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 &&
@@ -388,56 +389,83 @@ where T: Copy + Clone +
         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.
+    /// 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;
 
-        point2(x/w, y/w)
+
+        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> {
-        let x = p.x * self.m11 + p.y * self.m21 + p.z * self.m31 + self.m41;
-        let y = p.x * self.m12 + p.y * self.m22 + p.z * self.m32 + self.m42;
-        let z = p.x * self.m13 + p.y * self.m23 + p.z * self.m33 + self.m43;
-        let w = p.x * self.m14 + p.y * self.m24 + p.z * self.m34 + self.m44;
-
-        point3(x/w, y/w, z/w)
+        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(
@@ -690,17 +718,17 @@ where T: Copy + fmt::Debug +
         }
     }
 }
 
 #[cfg(test)]
 mod tests {
     use approxeq::ApproxEq;
     use transform2d::Transform2D;
-    use point::{Point2D, Point3D};
+    use point::{point2, point3};
     use Angle;
     use super::*;
 
     use std::f32::consts::{FRAC_PI_2, PI};
 
     type Mf32 = Transform3D<f32>;
 
     // For convenience.
@@ -709,52 +737,52 @@ mod tests {
     #[test]
     pub fn test_translation() {
         let t1 = Mf32::create_translation(1.0, 2.0, 3.0);
         let t2 = Mf32::identity().pre_translate(vec3(1.0, 2.0, 3.0));
         let t3 = Mf32::identity().post_translate(vec3(1.0, 2.0, 3.0));
         assert_eq!(t1, t2);
         assert_eq!(t1, t3);
 
-        assert_eq!(t1.transform_point3d(&Point3D::new(1.0, 1.0, 1.0)), Point3D::new(2.0, 3.0, 4.0));
-        assert_eq!(t1.transform_point2d(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
+        assert_eq!(t1.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(&Point3D::new(1.0, 2.0, 3.0)).approx_eq(&Point3D::new(2.0, -1.0, 3.0)));
-        assert!(r1.transform_point2d(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
+        assert!(r1.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(&Point3D::new(2.0, 2.0, 2.0)).approx_eq(&Point3D::new(4.0, 6.0, 8.0)));
-        assert!(s1.transform_point2d(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
+        assert!(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]
@@ -837,19 +865,19 @@ mod tests {
     }
 
     #[test]
     pub fn test_inverse_transform_point_2d() {
         let m1 = Mf32::create_translation(100.0, 200.0, 0.0);
         let m2 = m1.inverse().unwrap();
         assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
 
-        let p1 = Point2D::new(1000.0, 2000.0);
+        let p1 = point2(1000.0, 2000.0);
         let p2 = m1.transform_point2d(&p1);
-        assert!(p2.eq(&Point2D::new(1100.0, 2200.0)));
+        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());
@@ -860,24 +888,24 @@ mod tests {
     pub fn test_pre_post() {
         let m1 = Transform3D::identity().post_scale(1.0, 2.0, 3.0).post_translate(vec3(1.0, 2.0, 3.0));
         let m2 = Transform3D::identity().pre_translate(vec3(1.0, 2.0, 3.0)).pre_scale(1.0, 2.0, 3.0);
         assert!(m1.approx_eq(&m2));
 
         let r = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
         let t = Mf32::create_translation(2.0, 3.0, 0.0);
 
-        let a = Point3D::new(1.0, 1.0, 1.0);
+        let a = point3(1.0, 1.0, 1.0);
 
-        assert!(r.post_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0)));
-        assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0)));
+        assert!(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(&Point3D::new(4.0, -3.0, 1.0)));
-        assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0)));
+        assert!(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>());
@@ -889,17 +917,17 @@ mod tests {
                                  0.0, 4.5, -1.0, -4.0,
                                  0.0, 3.5, 2.5, 40.0,
                                  0.0, 3.0, 0.0, 1.0);
         let m2 = Mf32::row_major(1.0, -1.0, 3.0, 0.0,
                                  -1.0, 0.5, 0.0, 2.0,
                                  1.5, -2.0, 6.0, 0.0,
                                  -2.5, 6.0, 1.0, 1.0);
 
-        let p = Point3D::new(1.0, 3.0, 5.0);
+        let 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();
@@ -936,9 +964,27 @@ mod tests {
         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),
+        );
+    }
 }
--- a/third_party/rust/euclid/src/vector.rs
+++ b/third_party/rust/euclid/src/vector.rs
@@ -118,17 +118,17 @@ impl<T: Copy, U> TypedVector2D<T, U> {
     }
 
     /// Drop the units, preserving only the numeric value.
     #[inline]
     pub fn to_untyped(&self) -> Vector2D<T> {
         vec2(self.x, self.y)
     }
 
-    /// Tag a unitless value with units.
+    /// Tag a unit-less value with units.
     #[inline]
     pub fn from_untyped(p: &Vector2D<T>) -> Self {
         vec2(p.x, p.y)
     }
 
     #[inline]
     pub fn to_array(&self) -> [T; 2] {
         [self.x, self.y]
@@ -274,25 +274,25 @@ impl<T: Copy + Div<T, Output = T>, U> Di
     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 TypedVector2D<T, U1> {
     type Output = TypedVector2D<T, U2>;
     #[inline]
-    fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedVector2D<T, U2> {
+    fn mul(self, scale: TypedScale<T, U1, U2>) -> Self::Output {
         vec2(self.x * scale.get(), self.y * scale.get())
     }
 }
 
 impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedVector2D<T, U2> {
     type Output = TypedVector2D<T, U1>;
     #[inline]
-    fn div(self, scale: TypedScale<T, U1, U2>) -> TypedVector2D<T, U1> {
+    fn div(self, scale: TypedScale<T, U1, U2>) -> Self::Output {
         vec2(self.x / scale.get(), self.y / scale.get())
     }
 }
 
 impl<T: Round, U> TypedVector2D<T, U> {
     /// Rounds each component to the nearest integer value.
     ///
     /// This behavior is preserved for negative values (unlike the basic cast).
@@ -699,16 +699,32 @@ impl<T: Float, U> TypedVector3D<T, U> {
         vec3(
             self.x.max(other.x),
             self.y.max(other.y),
             self.z.max(other.z),
         )
     }
 }
 
+impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedVector3D<T, U1> {
+    type Output = TypedVector3D<T, U2>;
+    #[inline]
+    fn mul(self, scale: TypedScale<T, U1, U2>) -> Self::Output {
+        vec3(self.x * scale.get(), self.y * scale.get(), self.z * scale.get())
+    }
+}
+
+impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedVector3D<T, U2> {
+    type Output = TypedVector3D<T, U1>;
+    #[inline]
+    fn div(self, scale: TypedScale<T, U1, U2>) -> Self::Output {
+        vec3(self.x / scale.get(), self.y / scale.get(), self.z / scale.get())
+    }
+}
+
 impl<T: Round, U> TypedVector3D<T, U> {
     /// Rounds each component to the nearest integer value.
     ///
     /// This behavior is preserved for negative values (unlike the basic cast).
     #[inline]
     #[cfg_attr(feature = "unstable", must_use)]
     pub fn round(&self) -> Self {
         vec3(self.x.round(), self.y.round(), self.z.round())
@@ -848,28 +864,274 @@ impl<T, U> TypedVector3D<T, U>
 where
     T: Signed,
 {
     pub fn abs(&self) -> Self {
         vec3(self.x.abs(), self.y.abs(), self.z.abs())
     }
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct BoolVector2D {
+    pub x: bool,
+    pub y: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct BoolVector3D {
+    pub x: bool,
+    pub y: bool,
+    pub z: bool,
+}
+
+impl BoolVector2D {
+    #[inline]
+    pub fn all(&self) -> bool {
+        self.x && self.y
+    }
+
+    #[inline]
+    pub fn any(&self) -> bool {
+        self.x || self.y
+    }
+
+    #[inline]
+    pub fn none(&self) -> bool {
+        !self.any()
+    }
+
+    #[inline]
+    pub fn and(&self, other: Self) -> Self {
+        BoolVector2D {
+            x: self.x && other.x,
+            y: self.y && other.y,
+        }
+    }
+
+    #[inline]
+    pub fn or(&self, other: Self) -> Self {
+        BoolVector2D {
+            x: self.x || other.x,
+            y: self.y || other.y,
+        }
+    }
+
+    #[inline]
+    pub fn not(&self) -> Self {
+        BoolVector2D {
+            x: !self.x,
+            y: !self.y,
+        }
+    }
+
+    #[inline]
+    pub fn select_point<T: Copy, U>(&self, a: &TypedPoint2D<T, U>, b: &TypedPoint2D<T, U>) -> TypedPoint2D<T, U> {
+        point2(
+            if self.x { a.x } else { b.x },
+            if self.y { a.y } else { b.y },
+        )
+    }
+
+    #[inline]
+    pub fn select_vector<T: Copy, U>(&self, a: &TypedVector2D<T, U>, b: &TypedVector2D<T, U>) -> TypedVector2D<T, U> {
+        vec2(
+            if self.x { a.x } else { b.x },
+            if self.y { a.y } else { b.y },
+        )
+    }
+
+    #[inline]
+    pub fn select_size<T: Copy, U>(&self, a: &TypedSize2D<T, U>, b: &TypedSize2D<T, U>) -> TypedSize2D<T, U> {
+        size2(
+            if self.x { a.width } else { b.width },
+            if self.y { a.height } else { b.height },
+        )
+    }
+}
+
+impl BoolVector3D {
+    #[inline]
+    pub fn all(&self) -> bool {
+        self.x && self.y && self.z
+    }
+
+    #[inline]
+    pub fn any(&self) -> bool {
+        self.x || self.y || self.z
+    }
+
+    #[inline]
+    pub fn none(&self) -> bool {
+        !self.any()
+    }
+
+    #[inline]
+    pub fn and(&self, other: Self) -> Self {
+        BoolVector3D {
+            x: self.x && other.x,
+            y: self.y && other.y,
+            z: self.z && other.z,
+        }
+    }
+
+    #[inline]
+    pub fn or(&self, other: Self) -> Self {
+        BoolVector3D {
+            x: self.x || other.x,
+            y: self.y || other.y,
+            z: self.z || other.z,
+        }
+    }
+
+    #[inline]
+    pub fn not(&self) -> Self {
+        BoolVector3D {
+            x: !self.x,
+            y: !self.y,
+            z: !self.z,
+        }
+    }
+
+
+    #[inline]
+    pub fn select_point<T: Copy, U>(&self, a: &TypedPoint3D<T, U>, b: &TypedPoint3D<T, U>) -> TypedPoint3D<T, U> {
+        point3(
+            if self.x { a.x } else { b.x },
+            if self.y { a.y } else { b.y },
+            if self.z { a.z } else { b.z },
+        )
+    }
+
+    #[inline]
+    pub fn select_vector<T: Copy, U>(&self, a: &TypedVector3D<T, U>, b: &TypedVector3D<T, U>) -> TypedVector3D<T, U> {
+        vec3(
+            if self.x { a.x } else { b.x },
+            if self.y { a.y } else { b.y },
+            if self.z { a.z } else { b.z },
+        )
+    }
+
+    #[inline]
+    pub fn xy(&self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.x,
+            y: self.y,
+        }
+    }
+
+    #[inline]
+    pub fn xz(&self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.x,
+            y: self.z,
+        }
+    }
+
+    #[inline]
+    pub fn yz(&self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.y,
+            y: self.z,
+        }
+    }
+}
+
+impl<T: PartialOrd, U> TypedVector2D<T, U> {
+    pub fn greater_than(&self, other: &Self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.x > other.x,
+            y: self.y > other.y,
+        }
+    }
+
+    pub fn lower_than(&self, other: &Self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.x < other.x,
+            y: self.y < other.y,
+        }
+    }
+}
+
+
+impl<T: PartialEq, U> TypedVector2D<T, U> {
+    pub fn equal(&self, other: &Self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.x == other.x,
+            y: self.y == other.y,
+        }
+    }
+
+    pub fn not_equal(&self, other: &Self) -> BoolVector2D {
+        BoolVector2D {
+            x: self.x != other.x,
+            y: self.y != other.y,
+        }
+    }
+}
+
+impl<T: PartialOrd, U> TypedVector3D<T, U> {
+    pub fn greater_than(&self, other: &Self) -> BoolVector3D {
+        BoolVector3D {
+            x: self.x > other.x,
+            y: self.y > other.y,
+            z: self.z > other.z,
+        }
+    }
+
+    pub fn lower_than(&self, other: &Self) -> BoolVector3D {
+        BoolVector3D {
+            x: self.x < other.x,
+            y: self.y < other.y,
+            z: self.z < other.z,
+        }
+    }
+}
+
+
+impl<T: PartialEq, U> TypedVector3D<T, U> {
+    pub fn equal(&self, other: &Self) -> BoolVector3D {
+        BoolVector3D {
+            x: self.x == other.x,
+            y: self.y == other.y,
+            z: self.z == other.z,
+        }
+    }
+
+    pub fn not_equal(&self, other: &Self) -> BoolVector3D {
+        BoolVector3D {
+            x: self.x != other.x,
+            y: self.y != other.y,
+            z: self.z != other.z,
+        }
+    }
+}
+
 /// Convenience constructor.
 #[inline]
 pub fn vec2<T, U>(x: T, y: T) -> TypedVector2D<T, U> {
     TypedVector2D::new(x, y)
 }
 
 /// Convenience constructor.
 #[inline]
 pub fn vec3<T, U>(x: T, y: T, z: T) -> TypedVector3D<T, U> {
     TypedVector3D::new(x, y, z)
 }
 
+#[inline]
+pub fn bvec2(x: bool, y: bool) -> BoolVector2D {
+    BoolVector2D { x, y }
+}
+
+#[inline]
+pub fn bvec3(x: bool, y: bool, z: bool) -> BoolVector3D {
+    BoolVector3D { x, y, z }
+}
+
+
 #[cfg(test)]
 mod vector2d {
     use super::{Vector2D, vec2};
     type Vec2 = Vector2D<f32>;
 
     #[test]
     pub fn test_scalar_mul() {
         let p1: Vec2 = vec2(3.0, 5.0);
@@ -966,33 +1228,35 @@ mod typedvector2d {
         p1 += vec2(3.0, 4.0);
 
         assert_eq!(p1, vec2(4.0, 6.0));
     }
 
     #[test]
     pub fn test_scalar_mul() {
         let p1 = Vector2DMm::new(1.0, 2.0);
-        let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
+        let cm_per_mm = TypedScale::<f32, Mm, Cm>::new(0.1);
 
         let result: Vector2DCm<f32> = p1 * cm_per_mm;
 
         assert_eq!(result, vec2(0.1, 0.2));
     }
 
     #[test]
     pub fn test_swizzling() {
         let p: Vector2D<i32> = vec2(1, 2);
         assert_eq!(p.yx(), vec2(2, 1));
     }
 }
 
 #[cfg(test)]
 mod vector3d {
-    use super::{Vector3D, vec2, vec3};
+    use super::{TypedVector3D, Vector3D, vec2, vec3};
+    use scale::TypedScale;
+
     type Vec3 = Vector3D<f32>;
 
     #[test]
     pub fn test_dot() {
         let p1: Vec3 = vec3(7.0, 21.0, 32.0);
         let p2: Vec3 = vec3(43.0, 5.0, 16.0);
         assert_eq!(p1.dot(p2), 918.0);
     }
@@ -1033,15 +1297,121 @@ mod vector3d {
         let p2: Vec3 = vec3(2.0, 2.0, -1.0);
 
         let result = p1.max(p2);
 
         assert_eq!(result, vec3(2.0, 3.0, 5.0));
     }
 
     #[test]
+    pub fn test_scalar_mul() {
+        enum Mm {}
+        enum Cm {}
+
+        let p1 = TypedVector3D::<f32, Mm>::new(1.0, 2.0, 3.0);
+        let cm_per_mm = TypedScale::<f32, Mm, Cm>::new(0.1);
+
+        let result: TypedVector3D<f32, Cm> = p1 * cm_per_mm;
+
+        assert_eq!(result, vec3(0.1, 0.2, 0.3));
+    }
+
+    #[test]
     pub fn test_swizzling() {
         let p: Vector3D<i32> = vec3(1, 2, 3);
         assert_eq!(p.xy(), vec2(1, 2));
         assert_eq!(p.xz(), vec2(1, 3));
         assert_eq!(p.yz(), vec2(2, 3));
     }
 }
+
+mod bool_vector {
+    use super::*;
+    type Vec2 = Vector2D<f32>;
+    type Vec3 = Vector3D<f32>;
+
+    #[test]
+    fn test_bvec2() {
+
+        assert_eq!(
+            Vec2::new(1.0, 2.0).greater_than(&Vec2::new(2.0, 1.0)),
+            bvec2(false, true),
+        );
+
+        assert_eq!(
+            Vec2::new(1.0, 2.0).lower_than(&Vec2::new(2.0, 1.0)),
+            bvec2(true, false),
+        );
+
+        assert_eq!(
+            Vec2::new(1.0, 2.0).equal(&Vec2::new(1.0, 3.0)),
+            bvec2(true, false),
+        );
+
+        assert_eq!(
+            Vec2::new(1.0, 2.0).not_equal(&Vec2::new(1.0, 3.0)),
+            bvec2(false, true),
+        );
+
+        assert!(bvec2(true, true).any());
+        assert!(bvec2(false, true).any());
+        assert!(bvec2(true, false).any());
+        assert!(!bvec2(false, false).any());
+        assert!(bvec2(false, false).none());
+        assert!(bvec2(true, true).all());
+        assert!(!bvec2(false, true).all());
+        assert!(!bvec2(true, false).all());
+        assert!(!bvec2(false, false).all());
+
+        assert_eq!(bvec2(true, false).not(), bvec2(false, true));
+        assert_eq!(bvec2(true, false).and(bvec2(true, true)), bvec2(true, false));
+        assert_eq!(bvec2(true, false).or(bvec2(true, true)), bvec2(true, true));
+
+        assert_eq!(
+            bvec2(true, false).select_vector(&Vec2::new(1.0, 2.0), &Vec2::new(3.0, 4.0)),
+            Vec2::new(1.0, 4.0),
+        );
+    }
+
+    #[test]
+    fn test_bvec3() {
+
+        assert_eq!(
+            Vec3::new(1.0, 2.0, 3.0).greater_than(&Vec3::new(3.0, 2.0, 1.0)),
+            bvec3(false, false, true),
+        );
+
+        assert_eq!(
+            Vec3::new(1.0, 2.0, 3.0).lower_than(&Vec3::new(3.0, 2.0, 1.0)),
+            bvec3(true, false, false),
+        );
+
+        assert_eq!(
+            Vec3::new(1.0, 2.0, 3.0).equal(&Vec3::new(3.0, 2.0, 1.0)),
+            bvec3(false, true, false),
+        );
+
+        assert_eq!(
+            Vec3::new(1.0, 2.0, 3.0).not_equal(&Vec3::new(3.0, 2.0, 1.0)),
+            bvec3(true, false, true),
+        );
+
+        assert!(bvec3(true, true, false).any());
+        assert!(bvec3(false, true, false).any());
+        assert!(bvec3(true, false, false).any());
+        assert!(!bvec3(false, false, false).any());
+        assert!(bvec3(false, false, false).none());
+        assert!(bvec3(true, true, true).all());
+        assert!(!bvec3(false, true, false).all());
+        assert!(!bvec3(true, false, false).all());
+        assert!(!bvec3(false, false, false).all());
+
+        assert_eq!(bvec3(true, false, true).not(), bvec3(false, true, false));
+        assert_eq!(bvec3(true, false, true).and(bvec3(true, true, false)), bvec3(true, false, false));
+        assert_eq!(bvec3(true, false, false).or(bvec3(true, true, false)), bvec3(true, true, false));
+
+        assert_eq!(
+            bvec3(true, false, true).select_vector(&Vec3::new(1.0, 2.0, 3.0), &Vec3::new(4.0, 5.0, 6.0)),
+            Vec3::new(1.0, 5.0, 3.0),
+        );
+    }
+
+}
\ No newline at end of file
--- a/third_party/rust/plane-split/.cargo-checksum.json
+++ b/third_party/rust/plane-split/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"a1d9fe24aa91d8a565216328d62ead47b5ab7d89815ee2c855a1bb890f42226b","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"70be33f99512c1b3a87d8b52fc37a0f2817280d654938ecc8856cb466e5b3bd2","benches/split.rs":"dfe01759652e2098f20547e0ddcc1b2937e88c6d6ddb025353c037a46b7ef85d","src/bsp.rs":"66e1690aa8540f744ee013ac0e550ecdee84633727cb3a2d8239db3597ad25d6","src/lib.rs":"21d6135c10dd820c2b9ac484cc018e1149f2bf44c315d27134edd3ecb8a7f3d2","src/naive.rs":"444d3298224009209ae329458fe8df953193b15a04da29cdd6f498572a6471bf","tests/main.rs":"54104b672128ae623e1ef6000c30110c2b713482b81bc5f18ac1f86088813cb1","tests/split.rs":"67259be206c2e9eb77b0ff285dc5c5d912f7e539c15fc9e278e5ec9959bc24af"},"package":"69c557e11e3a1533bc969fa596e5011e1d9f76dd61cd102ef942c9f8654b17a2"}
\ No newline at end of file
+{"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"}
\ No newline at end of file
--- a/third_party/rust/plane-split/Cargo.toml
+++ b/third_party/rust/plane-split/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "plane-split"
-version = "0.8.0"
+version = "0.9.1"
 authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
 description = "Plane splitting"
 documentation = "https://docs.rs/plane-split"
 keywords = ["geometry", "math"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/plane-split"
 [dependencies.binary-space-partition]
 version = "0.1.2"
--- a/third_party/rust/plane-split/README.md
+++ b/third_party/rust/plane-split/README.md
@@ -1,6 +1,6 @@
 # plane-split
 [![Build Status](https://travis-ci.org/servo/plane-split.svg)](https://travis-ci.org/servo/plane-split)
 [![](http://meritbadge.herokuapp.com/plane-split)](https://crates.io/crates/plane-split)
 [![Documentation](https://docs.rs/plane-split/badge.svg)](https://docs.rs/plane-split)
 
-Plane splitting with [euclid](https://crates.io/crates/euclid).
+Plane splitting with [euclid](https://crates.io/crates/euclid), made for [WebRender](https://github.com/servo/webrender/).
--- a/third_party/rust/plane-split/benches/split.rs
+++ b/third_party/rust/plane-split/benches/split.rs
@@ -1,31 +1,20 @@
 #![feature(test)]
 
 extern crate euclid;
 extern crate plane_split;
 extern crate test;
 
 use std::sync::Arc;
 use euclid::vec3;
-use plane_split::{BspSplitter, NaiveSplitter, Splitter, _make_grid};
-
-#[bench]
-fn bench_naive(b: &mut test::Bencher) {
-    let polys = Arc::new(_make_grid(5));
-    let mut splitter = NaiveSplitter::new();
-    let view = vec3(0.0, 0.0, 1.0);
-    b.iter(|| {
-        let p = polys.clone();
-        splitter.solve(&p, view);
-    });
-}
+use plane_split::{BspSplitter, Splitter, make_grid};
 
 #[bench]
 fn bench_bsp(b: &mut test::Bencher) {
-    let polys = Arc::new(_make_grid(5));
+    let polys = Arc::new(make_grid(5));
     let mut splitter = BspSplitter::new();
     let view = vec3(0.0, 0.0, 1.0);
     b.iter(|| {
         let p = polys.clone();
         splitter.solve(&p, view);
     });
 }
--- a/third_party/rust/plane-split/src/bsp.rs
+++ b/third_party/rust/plane-split/src/bsp.rs
@@ -1,26 +1,26 @@
-use binary_space_partition::{BspNode, Plane, PlaneCut};
+use binary_space_partition::{BspNode, Plane as BspPlane, PlaneCut};
 use euclid::{TypedPoint3D, TypedVector3D};
 use euclid::approxeq::ApproxEq;
 use num_traits::{Float, One, Zero};
 use std::{fmt, ops};
-use {Intersection, Polygon, Splitter};
+use {Intersection, Plane, Polygon, Splitter};
 
 
-impl<T, U> Plane for Polygon<T, U> where
+impl<T, U> BspPlane for Polygon<T, U> where
     T: Copy + fmt::Debug + ApproxEq<T> +
         ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
         ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
         Zero + One + Float,
     U: fmt::Debug,
 {
     fn cut(&self, mut plane: Self) -> PlaneCut<Self> {
         debug!("\tCutting anchor {}", plane.anchor);
-        let dist = self.signed_distance_sum_to(&plane);
+        let dist = self.plane.signed_distance_sum_to(&plane);
 
         match self.intersect(&plane) {
             Intersection::Coplanar if dist.approx_eq(&T::zero()) => {
                 debug!("\t\tCoplanar and matching");
                 PlaneCut::Sibling(plane)
             }
             Intersection::Coplanar | Intersection::Outside => {
                 debug!("\t\tCoplanar at {:?}", dist);
@@ -37,35 +37,35 @@ impl<T, U> Plane for Polygon<T, U> where
                 }
             }
             Intersection::Inside(line) => {
                 let (res_add1, res_add2) = plane.split(&line);
                 let mut front = Vec::new();
                 let mut back = Vec::new();
 
                 for sub in Some(plane).into_iter().chain(res_add1).chain(res_add2) {
-                    if self.signed_distance_sum_to(&sub) > T::zero() {
+                    if self.plane.signed_distance_sum_to(&sub) > T::zero() {
                         front.push(sub)
                     } else {
                         back.push(sub)
                     }
                 }
                 debug!("\t\tCut across {:?} by {} in front and {} in back",
                     line, front.len(), back.len());
 
                 PlaneCut::Cut {
                     front: front,
                     back: back,
                 }
             },
         }
     }
 
-    fn is_aligned(&self, plane: &Self) -> bool {
-        self.normal.dot(plane.normal) > T::zero()
+    fn is_aligned(&self, other: &Self) -> bool {
+        self.plane.normal.dot(other.plane.normal) > T::zero()
     }
 }
 
 
 /// Binary Space Partitioning splitter, uses a BSP tree.
 pub struct BspSplitter<T, U> {
     tree: BspNode<Polygon<T, U>>,
     result: Vec<Polygon<T, U>>,
@@ -95,17 +95,19 @@ impl<T, U> Splitter<T, U> for BspSplitte
     fn add(&mut self, poly: Polygon<T, U>) {
         self.tree.insert(poly);
     }
 
     fn sort(&mut self, view: TypedVector3D<T, U>) -> &[Polygon<T, U>] {
         //debug!("\t\ttree before sorting {:?}", self.tree);
         let poly = Polygon {
             points: [TypedPoint3D::origin(); 4],
-            normal: -view, //Note: BSP `order()` is back to front
-            offset: T::zero(),
+            plane: Plane {
+                normal: -view, //Note: BSP `order()` is back to front
+                offset: T::zero(),
+            },
             anchor: 0,
         };
         self.result.clear();
         self.tree.order(&poly, &mut self.result);
         &self.result
     }
 }
--- a/third_party/rust/plane-split/src/lib.rs
+++ b/third_party/rust/plane-split/src/lib.rs
@@ -12,40 +12,40 @@ the resulting sub-polygons by depth and 
 
 extern crate binary_space_partition;
 extern crate euclid;
 #[macro_use]
 extern crate log;
 extern crate num_traits;
 
 mod bsp;
-mod naive;
+mod polygon;
 
 use std::{fmt, mem, ops};
-use euclid::{Point2D, TypedTransform3D, TypedPoint3D, TypedVector3D, TypedRect};
+use euclid::{TypedPoint3D, TypedVector3D};
 use euclid::approxeq::ApproxEq;
-use euclid::Trig;
 use num_traits::{Float, One, Zero};
 
 pub use self::bsp::BspSplitter;
-pub use self::naive::NaiveSplitter;
+pub use self::polygon::{Intersection, LineProjection, Polygon};
 
 
 fn is_zero<T>(value: T) -> bool where
     T: Copy + Zero + ApproxEq<T> + ops::Mul<T, Output=T> {
     //HACK: this is rough, but the original Epsilon is too strict
     (value * value).approx_eq(&T::zero())
 }
 
 fn is_zero_vec<T, U>(vec: TypedVector3D<T, U>) -> bool where
    T: Copy + Zero + ApproxEq<T> +
       ops::Add<T, Output=T> + ops::Sub<T, Output=T> + ops::Mul<T, Output=T> {
     vec.dot(vec).approx_eq(&T::zero())
 }
 
+
 /// A generic line.
 #[derive(Debug)]
 pub struct Line<T, U> {
     /// Arbitrary point on the line.
     pub origin: TypedPoint3D<T, U>,
     /// Normalized direction of the line.
     pub dir: TypedVector3D<T, U>,
 }
@@ -61,441 +61,243 @@ impl<T, U> Line<T, U> where
     /// Check if two lines match each other.
     pub fn matches(&self, other: &Self) -> bool {
         let diff = self.origin - other.origin;
         is_zero_vec(self.dir.cross(other.dir)) &&
         is_zero_vec(self.dir.cross(diff))
     }
 }
 
-/// A convex flat polygon with 4 points, defined by equation:
+
+/// An infinite plane in 3D space, defined by equation:
 /// dot(v, normal) + offset = 0
+/// When used for plane splitting, it's defining a hemisphere
+/// with equation "dot(v, normal) + offset > 0".
 #[derive(Debug, PartialEq)]
-pub struct Polygon<T, U> {
-    /// Points making the polygon.
-    pub points: [TypedPoint3D<T, U>; 4],
-    /// Normalized vector perpendicular to the polygon plane.
+pub struct Plane<T, U> {
+    /// Normalized vector perpendicular to the plane.
     pub normal: TypedVector3D<T, U>,
     /// Constant offset from the normal plane, specified in the
     /// direction opposite to the normal.
     pub offset: T,
-    /// A simple anchoring index to allow association of the
-    /// produced split polygons with the original one.
-    pub anchor: usize,
 }
 
-impl<T: Clone, U> Clone for Polygon<T, U> {
+impl<T: Clone, U> Clone for Plane<T, U> {
     fn clone(&self) -> Self {
-        Polygon {
-            points: [self.points[0].clone(),
-                     self.points[1].clone(),
-                     self.points[2].clone(),
-                     self.points[3].clone()],
+        Plane {
             normal: self.normal.clone(),
             offset: self.offset.clone(),
-            anchor: self.anchor,
         }
     }
 }
 
-/// The projection of a `Polygon` on a line.
-pub struct LineProjection<T> {
-    /// Projected value of each point in the polygon.
-    pub markers: [T; 4],
-}
-
-impl<T> LineProjection<T> where
-    T : Copy + PartialOrd + ops::Sub<T, Output=T> + ops::Add<T, Output=T>
-{
-    /// Get the min/max of the line projection markers.
-    pub fn get_bounds(&self) -> (T, T) {
-        let (mut a, mut b, mut c, mut d) = (self.markers[0], self.markers[1], self.markers[2], self.markers[3]);
-        // bitonic sort of 4 elements
-        // we could not just use `min/max` since they require `Ord` bound
-        //TODO: make it nicer
-        if a > c {
-            mem::swap(&mut a, &mut c);
-        }
-        if b > d {
-            mem::swap(&mut b, &mut d);
-        }
-        if a > b {
-            mem::swap(&mut a, &mut b);
-        }
-        if c > d {
-            mem::swap(&mut c, &mut d);
-        }
-        if b > c {
-            mem::swap(&mut b, &mut c);
-        }
-        debug_assert!(a <= b && b <= c && c <= d);
-        (a, d)
+impl<
+    T: Copy + Zero + One + Float + ApproxEq<T> +
+        ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
+        ops::Mul<T, Output=T> + ops::Div<T, Output=T>,
+    U,
+> Plane<T, U> {
+    /// Check if this plane contains another one.
+    pub fn contains(&self, other: &Self) -> bool {
+        //TODO: actually check for inside/outside
+        self.normal == other.normal && self.offset == other.offset
     }
 
-    /// Check intersection with another line projection.
-    pub fn intersect(&self, other: &Self) -> bool {
-        // compute the bounds of both line projections
-        let span = self.get_bounds();
-        let other_span = other.get_bounds();
-        // compute the total footprint
-        let left = if span.0 < other_span.0 { span.0 } else { other_span.0 };
-        let right = if span.1 > other_span.1 { span.1 } else { other_span.1 };
-        // they intersect if the footprint is smaller than the sum
-        right - left < span.1 - span.0 + other_span.1 - other_span.0
-    }
-}
-
-/// Polygon intersection results.
-pub enum Intersection<T> {
-    /// Polygons are coplanar, including the case of being on the same plane.
-    Coplanar,
-    /// Polygon planes are intersecting, but polygons are not.
-    Outside,
-    /// Polygons are actually intersecting.
-    Inside(T),
-}
-
-impl<T> Intersection<T> {
-    /// Return true if the intersection is completely outside.
-    pub fn is_outside(&self) -> bool {
-        match *self {
-            Intersection::Outside => true,
-            _ => false,
-        }
-    }
-    /// Return true if the intersection cuts the source polygon.
-    pub fn is_inside(&self) -> bool {
-        match *self {
-            Intersection::Inside(_) => true,
-            _ => false,
-        }
-    }
-}
-
-impl<T, U> Polygon<T, U> where
-    T: Copy + fmt::Debug + ApproxEq<T> +
-        ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
-        ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
-        Zero + One + Float,
-    U: fmt::Debug,
-{
-    /// Construct a polygon from a transformed rectangle.
-    pub fn from_transformed_rect<V>(rect: TypedRect<T, V>,
-                                    transform: TypedTransform3D<T, V, U>,
-                                    anchor: usize)
-                                    -> Polygon<T, U>
-    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()),
-        ];
-
-        //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);
-
-        let normal = (points[1] - points[0]).cross(points[2] - points[0])
-                                            .normalize();
-        let offset = -TypedVector3D::new(transform.m41, transform.m42, transform.m43).dot(normal);
-
-        Polygon {
-            points: points,
-            normal: normal,
-            offset: offset,
-            anchor: anchor,
-        }
-    }
-
-    /// Bring a point into the local coordinate space, returning
-    /// the 2D normalized coordinates.
-    pub fn untransform_point(&self, point: TypedPoint3D<T, U>) -> Point2D<T> {
-        //debug_assert!(self.contains(point));
-        // get axises and target vector
-        let a = self.points[1] - self.points[0];
-        let b = self.points[3] - self.points[0];
-        let c = point - self.points[0];
-        // get pair-wise dot products
-        let a2 = a.dot(a);
-        let ab = a.dot(b);
-        let b2 = b.dot(b);
-        let ca = c.dot(a);
-        let cb = c.dot(b);
-        // compute the final coordinates
-        let denom = ab * ab - a2 * b2;
-        let x = ab * cb - b2 * ca;
-        let y = ab * ca - a2 * cb;
-        Point2D::new(x, y) / denom
-    }
-
-    /// Return the signed distance from this polygon to a point.
-    /// The distance is negative if the point is on the other side of the polygon
+    /// Return the signed distance from this plane to a point.
+    /// The distance is negative if the point is on the other side of the plane
     /// from the direction of the normal.
     pub fn signed_distance_to(&self, point: &TypedPoint3D<T, U>) -> T {
         point.to_vector().dot(self.normal) + self.offset
     }
 
-    /// Compute the distance across the line to the polygon plane,
+    /// Compute the distance across the line to the plane plane,
     /// starting from the line origin.
     pub fn distance_to_line(&self, line: &Line<T, U>) -> T
     where T: ops::Neg<Output=T> {
         self.signed_distance_to(&line.origin) / -self.normal.dot(line.dir)
     }
 
     /// Compute the sum of signed distances to each of the points
-    /// of another polygon. Useful to know the relation of a polygon that
+    /// of another plane. Useful to know the relation of a plane that
     /// is a product of a split, and we know it doesn't intersect `self`.
-    pub fn signed_distance_sum_to(&self, other: &Self) -> T {
-        other.points.iter().fold(T::zero(), |sum, p| {
-            sum + self.signed_distance_to(p)
-        })
-    }
-
-    /// Check if all the points are indeed placed on the plane defined by
-    /// the normal and offset, and the winding order is consistent.
-    pub fn is_valid(&self) -> bool {
-        let is_planar = self.points.iter()
-                                   .all(|p| is_zero(self.signed_distance_to(p)));
-        let edges = [self.points[1] - self.points[0],
-                     self.points[2] - self.points[1],
-                     self.points[3] - self.points[2],
-                     self.points[0] - self.points[3]];
-        let anchor = edges[3].cross(edges[0]);
-        let is_winding = edges.iter()
-                              .zip(edges[1..].iter())
-                              .all(|(a, &b)| a.cross(b).dot(anchor) >= T::zero());
-        is_planar && is_winding
+    pub fn signed_distance_sum_to(&self, poly: &Polygon<T, U>) -> T {
+        poly.points
+            .iter()
+            .fold(T::zero(), |u, p| u + self.signed_distance_to(p))
     }
 
     /// Check if a convex shape defined by a set of points is completely
-    /// outside of this polygon. Merely touching the surface is not
+    /// outside of this plane. Merely touching the surface is not
     /// considered an intersection.
     pub fn are_outside(&self, points: &[TypedPoint3D<T, U>]) -> bool {
         let d0 = self.signed_distance_to(&points[0]);
-        points[1..].iter()
-                   .all(|p| self.signed_distance_to(p) * d0 > T::zero())
-    }
-
-    /// Check if this polygon contains another one.
-    pub fn contains(&self, other: &Self) -> bool {
-        //TODO: actually check for inside/outside
-        self.normal == other.normal && self.offset == other.offset
+        points[1..]
+            .iter()
+            .all(|p| self.signed_distance_to(p) * d0 > T::zero())
     }
 
-    /// Project this polygon onto a 3D vector, returning a line projection.
-    /// Note: we can think of it as a projection to a ray placed at the origin.
-    pub fn project_on(&self, vector: &TypedVector3D<T, U>) -> LineProjection<T> {
-        LineProjection {
-            markers: [
-                vector.dot(self.points[0].to_vector()),
-                vector.dot(self.points[1].to_vector()),
-                vector.dot(self.points[2].to_vector()),
-                vector.dot(self.points[3].to_vector()),
-            ],
-        }
-    }
-
-    /// Compute the line of intersection with another polygon.
-    pub fn intersect(&self, other: &Self) -> Intersection<Line<T, U>> {
-        if self.are_outside(&other.points) || other.are_outside(&self.points) {
-            // one is completely outside the other
-            debug!("\t\tOutside");
-            return Intersection::Outside
-        }
+    /// Compute the line of intersection with another plane.
+    pub fn intersect(&self, other: &Self) -> Option<Line<T, U>> {
         let cross_dir = self.normal.cross(other.normal);
         if cross_dir.dot(cross_dir) < T::approx_epsilon() {
-            // polygons are co-planar
-            debug!("\t\tCoplanar");
-            return Intersection::Coplanar
+            return None
         }
-        let self_proj = self.project_on(&cross_dir);
-        let other_proj = other.project_on(&cross_dir);
-        if !self_proj.intersect(&other_proj) {
-            // projections on the line don't intersect
-            debug!("\t\tProjection outside");
-            return Intersection::Outside
-        }
+
         // compute any point on the intersection between planes
         // (n1, v) + d1 = 0
         // (n2, v) + d2 = 0
         // v = a*n1/w + b*n2/w; w = (n1, n2)
         // v = (d2*w - d1) / (1 - w*w) * n1 - (d2 - d1*w) / (1 - w*w) * n2
         let w = self.normal.dot(other.normal);
         let factor = T::one() / (T::one() - w * w);
-        let center = TypedPoint3D::origin() +
-                     self.normal * ((other.offset * w - self.offset) * factor) -
-                     other.normal* ((other.offset - self.offset * w) * factor);
-        Intersection::Inside(Line {
-            origin: center,
+        let origin = TypedPoint3D::origin() +
+            self.normal * ((other.offset * w - self.offset) * factor) -
+            other.normal* ((other.offset - self.offset * w) * factor);
+
+        Some(Line {
+            origin,
             dir: cross_dir.normalize(),
         })
     }
-
-    /// Split the polygon along the specified `Line`. Will do nothing if the line
-    /// doesn't belong to the polygon plane.
-    pub fn split(&mut self, line: &Line<T, U>)
-                 -> (Option<Polygon<T, U>>, Option<Polygon<T, U>>) {
-        debug!("\tSplitting");
-        // check if the cut is within the polygon plane first
-        if !is_zero(self.normal.dot(line.dir)) ||
-           !is_zero(self.signed_distance_to(&line.origin)) {
-            debug!("\t\tDoes not belong to the plane, normal dot={:?}, origin distance={:?}",
-                self.normal.dot(line.dir), self.signed_distance_to(&line.origin));
-            return (None, None)
-        }
-        // compute the intersection points for each edge
-        let mut cuts = [None; 4];
-        for ((&b, &a), cut) in self.points.iter()
-                                          .cycle()
-                                          .skip(1)
-                                          .zip(self.points.iter())
-                                          .zip(cuts.iter_mut()) {
-            // intersecting line segment [a, b] with `line`
-            //a + (b-a) * t = r + k * d
-            //(a, d) + t * (b-a, d) - (r, d) = k
-            // a + t * (b-a) = r + t * (b-a, d) * d + (a-r, d) * d
-            // t * ((b-a) - (b-a, d)*d) = (r-a) - (r-a, d) * d
-            let pr = line.origin - a - line.dir * line.dir.dot(line.origin - a);
-            let pb = b - a - line.dir * line.dir.dot(b - a);
-            let denom = pb.dot(pb);
-            if !denom.approx_eq(&T::zero()) {
-                let t = pr.dot(pb) / denom;
-                if t > T::zero() && t < T::one() {
-                    *cut = Some(a + (b - a) * t);
-                }
-            }
-        }
+}
 
-        let first = match cuts.iter().position(|c| c.is_some()) {
-            Some(pos) => pos,
-            None => return (None, None),
-        };
-        let second = match cuts[first+1 ..].iter().position(|c| c.is_some()) {
-            Some(pos) => first + 1 + pos,
-            None => return (None, None),
-        };
-        debug!("\t\tReached complex case [{}, {}]", first, second);
-        //TODO: can be optimized for when the polygon has a redundant 4th vertex
-        //TODO: can be simplified greatly if only working with triangles
-        let (a, b) = (cuts[first].unwrap(), cuts[second].unwrap());
-        match second-first {
-            2 => {
-                let mut other_points = self.points;
-                other_points[first] = a;
-                other_points[(first+3) % 4] = b;
-                self.points[first+1] = a;
-                self.points[first+2] = b;
-                let poly = Polygon {
-                    points: other_points,
-                    .. self.clone()
-                };
-                (Some(poly), None)
-            }
-            3 => {
-                let xpoints = [
-                    self.points[first+1],
-                    self.points[first+2],
-                    self.points[first+3],
-                    b];
-                let ypoints = [a, self.points[first+1], b, b];
-                self.points = [self.points[first], a, b, b];
-                let poly1 = Polygon {
-                    points: xpoints,
-                    .. self.clone()
-                };
-                let poly2 = Polygon {
-                    points: ypoints,
-                    .. self.clone()
-                };
-                (Some(poly1), Some(poly2))
-            }
-            1 => {
-                let xpoints = [
-                    b,
-                    self.points[(first+2) % 4],
-                    self.points[(first+3) % 4],
-                    self.points[first]
-                    ];
-                let ypoints = [self.points[first], a, b, b];
-                self.points = [a, self.points[first+1], b, b];
-                let poly1 = Polygon {
-                    points: xpoints,
-                    .. self.clone()
-                };
-                let poly2 = Polygon {
-                    points: ypoints,
-                    .. self.clone()
-                };
-                (Some(poly1), Some(poly2))
-            }
-            _ => panic!("Unexpected indices {} {}", first, second),
-        }
-    }
-}
 
 
 /// Generic plane splitter interface
 pub trait Splitter<T, U> {
     /// Reset the splitter results.
     fn reset(&mut self);
 
     /// Add a new polygon and return a slice of the subdivisions
     /// that avoid collision with any of the previously added polygons.
-    fn add(&mut self, Polygon<T, U>);
+    fn add(&mut self, polygon: Polygon<T, U>);
 
     /// Sort the produced polygon set by the ascending distance across
     /// the specified view vector. Return the sorted slice.
-    fn sort(&mut self, TypedVector3D<T, U>) -> &[Polygon<T, U>];
+    fn sort(&mut self, view: TypedVector3D<T, U>) -> &[Polygon<T, U>];
 
     /// Process a set of polygons at once.
-    fn solve(&mut self, input: &[Polygon<T, U>], view: TypedVector3D<T, U>)
-             -> &[Polygon<T, U>]
-    where T: Clone, U: Clone {
+    fn solve(
+        &mut self,
+        input: &[Polygon<T, U>],
+        view: TypedVector3D<T, U>,
+    ) -> &[Polygon<T, U>]
+    where
+        T: Clone,
+        U: Clone,
+    {
         self.reset();
-        for p in input.iter() {
+        for p in input {
             self.add(p.clone());
         }
         self.sort(view)
     }
 }
 
 
+/// A helper object to clip polygons by a number of planes.
+#[derive(Debug)]
+pub struct Clipper<T, U> {
+    clips: Vec<Plane<T, U>>,
+    results: Vec<Polygon<T, U>>,
+    temp: Vec<Polygon<T, U>>,
+}
+
+impl<
+    T: Copy + fmt::Debug + ApproxEq<T> +
+        ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
+        ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
+        Zero + One + Float,
+    U: fmt::Debug,
+> Clipper<T, U> {
+    /// Create a new clipper object.
+    pub fn new() -> Self {
+        Clipper {
+            clips: Vec::new(),
+            results: Vec::new(),
+            temp: Vec::new(),
+        }
+    }
+
+    /// Add a clipping plane to the list. The plane will clip everything behind it,
+    /// where the direction is set by the plane normal.
+    pub fn add(&mut self, plane: Plane<T, U>) {
+        self.clips.push(plane);
+    }
+
+    /// Clip specified polygon by the contained planes, return the fragmented polygons.
+    pub fn clip(&mut self, polygon: Polygon<T, U>) -> &[Polygon<T, U>] {
+        self.results.clear();
+        self.results.push(polygon);
+        for clip in &self.clips {
+            self.temp.clear();
+            mem::swap(&mut self.results, &mut self.temp);
+
+            for mut poly in self.temp.drain(..) {
+                if let Intersection::Inside(line) = poly.intersect_plane(clip) {
+                    let (res1, res2) = poly.split(&line);
+                    self.results.extend(
+                        res1
+                            .into_iter()
+                            .chain(res2.into_iter())
+                            .filter(|p| clip.signed_distance_sum_to(p) > T::zero())
+                    );
+                }
+                // Note: if the intersection has happened, the `poly` will now
+                // contain the remainder of the original polygon.
+                if clip.signed_distance_sum_to(&poly) > T::zero() {
+                    self.results.push(poly);
+                }
+            }
+        }
+
+        &self.results
+    }
+}
+
+
 /// Helper method used for benchmarks and tests.
 /// Constructs a 3D grid of polygons.
-pub fn _make_grid(count: usize) -> Vec<Polygon<f32, ()>> {
+#[doc(hidden)]
+pub fn make_grid(count: usize) -> Vec<Polygon<f32, ()>> {
     let mut polys: Vec<Polygon<f32, ()>> = Vec::with_capacity(count*3);
     let len = count as f32;
     polys.extend((0 .. count).map(|i| Polygon {
         points: [
             TypedPoint3D::new(0.0, i as f32, 0.0),
             TypedPoint3D::new(len, i as f32, 0.0),
             TypedPoint3D::new(len, i as f32, len),
             TypedPoint3D::new(0.0, i as f32, len),
         ],
-        normal: TypedVector3D::new(0.0, 1.0, 0.0),
-        offset: -(i as f32),
+        plane: Plane {
+            normal: TypedVector3D::new(0.0, 1.0, 0.0),
+            offset: -(i as f32),
+        },
         anchor: 0,
     }));
     polys.extend((0 .. count).map(|i| Polygon {
         points: [
             TypedPoint3D::new(i as f32, 0.0, 0.0),
             TypedPoint3D::new(i as f32, len, 0.0),
             TypedPoint3D::new(i as f32, len, len),
             TypedPoint3D::new(i as f32, 0.0, len),
         ],
-        normal: TypedVector3D::new(1.0, 0.0, 0.0),
-        offset: -(i as f32),
+        plane: Plane {
+            normal: TypedVector3D::new(1.0, 0.0, 0.0),
+            offset: -(i as f32),
+        },
         anchor: 0,
     }));
     polys.extend((0 .. count).map(|i| Polygon {
         points: [
             TypedPoint3D::new(0.0, 0.0, i as f32),
             TypedPoint3D::new(len, 0.0, i as f32),
             TypedPoint3D::new(len, len, i as f32),
             TypedPoint3D::new(0.0, len, i as f32),
         ],
-        normal: TypedVector3D::new(0.0, 0.0, 1.0),
-        offset: -(i as f32),
+        plane: Plane {
+            normal: TypedVector3D::new(0.0, 0.0, 1.0),
+            offset: -(i as f32),
+        },
         anchor: 0,
     }));
     polys
 }
deleted file mode 100644
--- a/third_party/rust/plane-split/src/naive.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-use std::{fmt, ops};
-use std::cmp::Ordering;
-use {Intersection, Line, Polygon, Splitter};
-use euclid::TypedVector3D;
-use euclid::approxeq::ApproxEq;
-use num_traits::{Float, One, Zero};
-
-
-/// Naive plane splitter, has at least O(n^2) complexity.
-pub struct NaiveSplitter<T, U> {
-    result: Vec<Polygon<T, U>>,
-    current: Vec<Polygon<T, U>>,
-    temp: Vec<Polygon<T, U>>,
-}
-
-impl<T, U> NaiveSplitter<T, U> {
-    /// Create a new `NaiveSplitter`.
-    pub fn new() -> Self {
-        NaiveSplitter {
-            result: Vec::new(),
-            current: Vec::new(),
-            temp: Vec::new(),
-        }
-    }
-}
-
-/// Find a closest intersection point between two polygons,
-/// across the specified direction.
-fn intersect_across<T, U>(a: &Polygon<T, U>, b: &Polygon<T, U>,
-                          dir: TypedVector3D<T, U>)
-                          -> TypedVector3D<T, U>
-where
-    T: Copy + fmt::Debug + PartialOrd + ApproxEq<T> +
-        ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
-        ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
-        Zero + One + Float,
-    U: fmt::Debug,
-{
-    let pa = a.project_on(&dir).get_bounds();
-    let pb = b.project_on(&dir).get_bounds();
-    let pmin = pa.0.max(pb.0);
-    let pmax = pa.1.min(pb.1);
-    let k = (pmin + pmax) / (T::one() + T::one());
-    debug!("\t\tIntersection pa {:?} pb {:?} k {:?}", pa, pb, k);
-    dir * k
-}
-
-fn partial_sort_by<T, F>(array: &mut [T], fun: F) where
-    F: Fn(&T, &T) -> Ordering,
-    T: fmt::Debug,
-{
-    debug!("Sorting");
-    if array.is_empty() {
-        return
-    }
-    for i in 0 .. array.len() - 1 {
-        let mut up_start = array.len();
-        // placement is: [i, ... equals ..., up_start, ... greater ..., j]
-        // if this condition fails, everything to the right is greater
-        'find_smallest: while i + 1 != up_start {
-            let mut j = i + 1;
-            'partition: loop {
-                debug!("\tComparing {} to {}, up_start = {}", i, j, up_start);
-                let order = fun(&array[i], &array[j]);
-                debug!("\t\t{:?}", order);
-                match order {
-                    Ordering::Less => {
-                        // push back to "greater" area
-                        up_start -= 1;
-                        if j == up_start {
-                            break 'partition
-                        }
-                        array.swap(j, up_start);
-                    },
-                    Ordering::Equal => {
-                        // continue
-                        j += 1;
-                        if j == up_start {
-                            // we reached the end of the "equal" area
-                            // so our "i" can be placed anywhere
-                            break 'find_smallest;
-                        }
-                    }
-                    Ordering::Greater => {
-                        array.swap(i, j);
-                        up_start -= 1;
-                        array.swap(j, up_start);
-                        // found a smaller one, push "i" to the "greater" area
-                        // and restart the search from the current element
-                        break 'partition;
-                    },
-                }
-            }
-        }
-        debug!("\tEnding {} with up_start={}, poly {:?}", i, up_start, array[i]);
-    }
-}
-
-
-impl<
-    T: Copy + fmt::Debug + PartialOrd + ApproxEq<T> +
-       ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
-       ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
-       Zero + One + Float,
-    U: fmt::Debug,
-> Splitter<T, U> for NaiveSplitter<T, U> {
-    fn reset(&mut self) {
-        self.result.clear();
-        self.current.clear();
-        self.temp.clear();
-    }
-
-    fn add(&mut self, poly: Polygon<T, U>) {
-        // "current" accumulates all the subdivisions of the originally
-        // added polygon
-        self.current.push(poly);
-        for old in self.result.iter() {
-            for new in self.current.iter_mut() {
-                // temp accumulates all the new subdivisions to be added
-                // to the current, since we can't modify it in place
-                if let Intersection::Inside(line) = old.intersect(new) {
-                    let (res_add1, res_add2) = new.split(&line);
-                    if let Some(res) = res_add1 {
-                        self.temp.push(res);
-                    }
-                    if let Some(res) = res_add2 {
-                        self.temp.push(res);
-                    }
-                }
-            }
-            self.current.extend(self.temp.drain(..));
-        }
-        let index = self.result.len();
-        self.result.extend(self.current.drain(..));
-        debug!("Split result: {:?}", &self.result[index..]);
-    }
-
-    //TODO: verify/prove that the sorting approach is consistent
-    fn sort(&mut self, view: TypedVector3D<T, U>) -> &[Polygon<T, U>] {
-        // choose the most perpendicular axis among these two
-        let axis_pre = {
-            let axis_pre0 = TypedVector3D::new(T::one(), T::zero(), T::zero());
-            let axis_pre1 = TypedVector3D::new(T::zero(), T::one(), T::zero());
-            if view.dot(axis_pre0).abs() < view.dot(axis_pre1).abs() {
-                axis_pre0
-            } else {
-                axis_pre1
-            }
-        };
-        // do the orthogonalization
-        let axis_x = view.cross(axis_pre);
-        let axis_y = view.cross(axis_x);
-        debug!("Chosen axis {:?} {:?}", axis_x, axis_y);
-        // sort everything
-        partial_sort_by(&mut self.result, |a, b| {
-            debug!("\t\t{:?}\n\t\t{:?}", a, b);
-            //TODO: proper intersection
-            // compute the origin
-            let comp_x = intersect_across(a, b, axis_x);
-            let comp_y = intersect_across(a, b, axis_y);
-            // line that tries to intersect both
-            let line = Line {
-                origin: (comp_x + comp_y).to_point(),
-                dir: view,
-            };
-            debug!("\t\tGot {:?}", line);
-            // distances across the line
-            let da = a.distance_to_line(&line);
-            let db = b.distance_to_line(&line);
-            debug!("\t\tDistances {:?} {:?}", da, db);
-            // final compare
-            da.partial_cmp(&db).unwrap_or(Ordering::Equal)
-        });
-        // done
-        &self.result
-    }
-}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/plane-split/src/polygon.rs
@@ -0,0 +1,365 @@
+use {Line, Plane, is_zero};
+
+use std::{fmt, mem, ops};
+use euclid::{Point2D, TypedTransform3D, TypedPoint3D, TypedVector3D, TypedRect};
+use euclid::approxeq::ApproxEq;
+use euclid::Trig;
+use num_traits::{Float, One, Zero};
+
+
+/// The projection of a `Polygon` on a line.
+pub struct LineProjection<T> {
+    /// Projected value of each point in the polygon.
+    pub markers: [T; 4],
+}
+
+impl<T> LineProjection<T> where
+    T : Copy + PartialOrd + ops::Sub<T, Output=T> + ops::Add<T, Output=T>
+{
+    /// Get the min/max of the line projection markers.
+    pub fn get_bounds(&self) -> (T, T) {
+        let (mut a, mut b, mut c, mut d) = (self.markers[0], self.markers[1], self.markers[2], self.markers[3]);
+        // bitonic sort of 4 elements
+        // we could not just use `min/max` since they require `Ord` bound
+        //TODO: make it nicer
+        if a > c {
+            mem::swap(&mut a, &mut c);
+        }
+        if b > d {
+            mem::swap(&mut b, &mut d);
+        }
+        if a > b {
+            mem::swap(&mut a, &mut b);
+        }
+        if c > d {
+            mem::swap(&mut c, &mut d);
+        }
+        if b > c {
+            mem::swap(&mut b, &mut c);
+        }
+        debug_assert!(a <= b && b <= c && c <= d);
+        (a, d)
+    }
+
+    /// Check intersection with another line projection.
+    pub fn intersect(&self, other: &Self) -> bool {
+        // compute the bounds of both line projections
+        let span = self.get_bounds();
+        let other_span = other.get_bounds();
+        // compute the total footprint
+        let left = if span.0 < other_span.0 { span.0 } else { other_span.0 };
+        let right = if span.1 > other_span.1 { span.1 } else { other_span.1 };
+        // they intersect if the footprint is smaller than the sum
+        right - left < span.1 - span.0 + other_span.1 - other_span.0
+    }
+}
+
+
+/// Polygon intersection results.
+pub enum Intersection<T> {
+    /// Polygons are coplanar, including the case of being on the same plane.
+    Coplanar,
+    /// Polygon planes are intersecting, but polygons are not.
+    Outside,
+    /// Polygons are actually intersecting.
+    Inside(T),
+}
+
+impl<T> Intersection<T> {
+    /// Return true if the intersection is completely outside.
+    pub fn is_outside(&self) -> bool {
+        match *self {
+            Intersection::Outside => true,
+            _ => false,
+        }
+    }
+    /// Return true if the intersection cuts the source polygon.
+    pub fn is_inside(&self) -> bool {
+        match *self {
+            Intersection::Inside(_) => true,
+            _ => false,
+        }
+    }
+}
+
+
+/// A convex polygon with 4 points lying on a plane.
+#[derive(Debug, PartialEq)]
+pub struct Polygon<T, U> {
+    /// Points making the polygon.
+    pub points: [TypedPoint3D<T, U>; 4],
+    /// A plane describing polygon orientation.
+    pub plane: Plane<T, U>,
+    /// A simple anchoring index to allow association of the
+    /// produced split polygons with the original one.
+    pub anchor: usize,
+}
+
+impl<T: Clone, U> Clone for Polygon<T, U> {
+    fn clone(&self) -> Self {
+        Polygon {
+            points: [
+                self.points[0].clone(),
+                 self.points[1].clone(),
+                 self.points[2].clone(),
+                 self.points[3].clone(),
+            ],
+            plane: self.plane.clone(),
+            anchor: self.anchor,
+        }
+    }
+}
+
+impl<T, U> Polygon<T, U> where
+    T: Copy + fmt::Debug + ApproxEq<T> +
+        ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
+        ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
+        Zero + One + Float,
+    U: fmt::Debug,
+{
+    /// Construct a polygon from points that are already transformed.
+    pub fn from_points(
+        points: [TypedPoint3D<T, U>; 4],
+        anchor: usize,
+    ) -> Self {
+        let normal = (points[1] - points[0])
+            .cross(points[2] - points[0])
+            .normalize();
+        let offset = -points[0].to_vector()
+            .dot(normal);
+
+        Polygon {
+            points,
+            plane: Plane {
+                normal,
+                offset,
+            },
+            anchor,
+        }
+    }
+
+    /// Construct a polygon from a rectangle with 3D transform.
+    pub fn from_transformed_rect<V>(
+        rect: TypedRect<T, V>,
+        transform: TypedTransform3D<T, V, U>,
+        anchor: usize,
+    ) -> 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()),
+        ];
+
+        //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)
+    }
+
+    /// Bring a point into the local coordinate space, returning
+    /// the 2D normalized coordinates.
+    pub fn untransform_point(&self, point: TypedPoint3D<T, U>) -> Point2D<T> {
+        //debug_assert!(self.contains(point));
+        // get axises and target vector
+        let a = self.points[1] - self.points[0];
+        let b = self.points[3] - self.points[0];
+        let c = point - self.points[0];
+        // get pair-wise dot products
+        let a2 = a.dot(a);
+        let ab = a.dot(b);
+        let b2 = b.dot(b);
+        let ca = c.dot(a);
+        let cb = c.dot(b);
+        // compute the final coordinates
+        let denom = ab * ab - a2 * b2;
+        let x = ab * cb - b2 * ca;
+        let y = ab * ca - a2 * cb;
+        Point2D::new(x, y) / denom
+    }
+
+    /// Check if all the points are indeed placed on the plane defined by
+    /// the normal and offset, and the winding order is consistent.
+    pub fn is_valid(&self) -> bool {
+        let is_planar = self.points
+            .iter()
+            .all(|p| is_zero(self.plane.signed_distance_to(p)));
+        let edges = [
+            self.points[1] - self.points[0],
+            self.points[2] - self.points[1],
+            self.points[3] - self.points[2],
+            self.points[0] - self.points[3],
+        ];
+        let anchor = edges[3].cross(edges[0]);
+        let is_winding = edges
+            .iter()
+            .zip(edges[1..].iter())
+            .all(|(a, &b)| a.cross(b).dot(anchor) >= T::zero());
+        is_planar && is_winding
+    }
+
+    /// Check if this polygon contains another one.
+    pub fn contains(&self, other: &Self) -> bool {
+        //TODO: actually check for inside/outside
+        self.plane.contains(&other.plane)
+    }
+
+
+    /// Project this polygon onto a 3D vector, returning a line projection.
+    /// Note: we can think of it as a projection to a ray placed at the origin.
+    pub fn project_on(&self, vector: &TypedVector3D<T, U>) -> LineProjection<T> {
+        LineProjection {
+            markers: [
+                vector.dot(self.points[0].to_vector()),
+                vector.dot(self.points[1].to_vector()),
+                vector.dot(self.points[2].to_vector()),
+                vector.dot(self.points[3].to_vector()),
+            ],
+        }
+    }
+
+    /// Compute the line of intersection with an infinite plane.
+    pub fn intersect_plane(&self, other: &Plane<T, U>) -> Intersection<Line<T, U>> {
+        if other.are_outside(&self.points) {
+            debug!("\t\tOutside of the plane");
+            return Intersection::Outside
+        }
+        match self.plane.intersect(&other) {
+            Some(line) => Intersection::Inside(line),
+            None => {
+                debug!("\t\tCoplanar");
+                Intersection::Coplanar
+            }
+        }
+    }
+
+    /// Compute the line of intersection with another polygon.
+    pub fn intersect(&self, other: &Self) -> Intersection<Line<T, U>> {
+        if self.plane.are_outside(&other.points) || other.plane.are_outside(&self.points) {
+            debug!("\t\tOne is completely outside of the other");
+            return Intersection::Outside
+        }
+        match self.plane.intersect(&other.plane) {
+            Some(line) => {
+                let self_proj = self.project_on(&line.dir);
+                let other_proj = other.project_on(&line.dir);
+                if self_proj.intersect(&other_proj) {
+                    Intersection::Inside(line)
+                } else {
+                    // projections on the line don't intersect
+                    debug!("\t\tProjection is outside");
+                    Intersection::Outside
+                }
+            }
+            None => {
+                debug!("\t\tCoplanar");
+                Intersection::Coplanar
+            }
+        }
+    }
+
+    /// Split the polygon along the specified `Line`. Will do nothing if the line
+    /// doesn't belong to the polygon plane.
+    pub fn split(&mut self, line: &Line<T, U>) -> (Option<Self>, Option<Self>) {
+        debug!("\tSplitting");
+        // check if the cut is within the polygon plane first
+        if !is_zero(self.plane.normal.dot(line.dir)) ||
+           !is_zero(self.plane.signed_distance_to(&line.origin)) {
+            debug!("\t\tDoes not belong to the plane, normal dot={:?}, origin distance={:?}",
+                self.plane.normal.dot(line.dir), self.plane.signed_distance_to(&line.origin));
+            return (None, None)
+        }
+        // compute the intersection points for each edge
+        let mut cuts = [None; 4];
+        for ((&b, &a), cut) in self.points
+            .iter()
+            .cycle()
+            .skip(1)
+            .zip(self.points.iter())
+            .zip(cuts.iter_mut())
+        {
+            // intersecting line segment [a, b] with `line`
+            //a + (b-a) * t = r + k * d
+            //(a, d) + t * (b-a, d) - (r, d) = k
+            // a + t * (b-a) = r + t * (b-a, d) * d + (a-r, d) * d
+            // t * ((b-a) - (b-a, d)*d) = (r-a) - (r-a, d) * d
+            let pr = line.origin - a - line.dir * line.dir.dot(line.origin - a);
+            let pb = b - a - line.dir * line.dir.dot(b - a);
+            let denom = pb.dot(pb);
+            if !denom.approx_eq(&T::zero()) {
+                let t = pr.dot(pb) / denom;
+                if t > T::zero() && t < T::one() {
+                    *cut = Some(a + (b - a) * t);
+                }
+            }
+        }
+
+        let first = match cuts.iter().position(|c| c.is_some()) {
+            Some(pos) => pos,
+            None => return (None, None),
+        };
+        let second = match cuts[first+1 ..].iter().position(|c| c.is_some()) {
+            Some(pos) => first + 1 + pos,
+            None => return (None, None),
+        };
+        debug!("\t\tReached complex case [{}, {}]", first, second);
+        //TODO: can be optimized for when the polygon has a redundant 4th vertex
+        //TODO: can be simplified greatly if only working with triangles
+        let (a, b) = (cuts[first].unwrap(), cuts[second].unwrap());
+        match second-first {
+            2 => {
+                let mut other_points = self.points;
+                other_points[first] = a;
+                other_points[(first+3) % 4] = b;
+                self.points[first+1] = a;
+                self.points[first+2] = b;
+                let poly = Polygon {
+                    points: other_points,
+                    .. self.clone()
+                };
+                (Some(poly), None)
+            }
+            3 => {
+                let xpoints = [
+                    self.points[first+1],
+                    self.points[first+2],
+                    self.points[first+3],
+                    b];
+                let ypoints = [a, self.points[first+1], b, b];
+                self.points = [self.points[first], a, b, b];
+                let poly1 = Polygon {
+                    points: xpoints,
+                    .. self.clone()
+                };
+                let poly2 = Polygon {
+                    points: ypoints,
+                    .. self.clone()
+                };
+                (Some(poly1), Some(poly2))
+            }
+            1 => {
+                let xpoints = [
+                    b,
+                    self.points[(first+2) % 4],
+                    self.points[(first+3) % 4],
+                    self.points[first]
+                    ];
+                let ypoints = [self.points[first], a, b, b];
+                self.points = [a, self.points[first+1], b, b];
+                let poly1 = Polygon {
+                    points: xpoints,
+                    .. self.clone()
+                };
+                let poly2 = Polygon {
+                    points: ypoints,
+                    .. self.clone()
+                };
+                (Some(poly1), Some(poly2))
+            }
+            _ => panic!("Unexpected indices {} {}", first, second),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/plane-split/tests/clip.rs
@@ -0,0 +1,89 @@
+extern crate euclid;
+extern crate plane_split;
+
+use euclid::{point3, vec3};
+use plane_split::{Clipper, Plane, Polygon};
+
+
+#[test]
+fn clip_in() {
+    let plane: Plane<f32, ()> = Plane {
+        normal: vec3(1.0, 0.0, 1.0).normalize(),
+        offset: 20.0,
+    };
+    let mut clipper = Clipper::new();
+    clipper.add(plane);
+
+    let poly = Polygon::from_points([
+        point3(-10.0, -10.0, 0.0),
+        point3(10.0, -10.0, 0.0),
+        point3(10.0, 10.0, 0.0),
+        point3(-10.0, 10.0, 0.0),
+    ], 0);
+
+    let results = clipper.clip(poly.clone());
+    assert_eq!(results[0], poly);
+    assert_eq!(results.len(), 1);
+}
+
+#[test]
+fn clip_out() {
+    let plane: Plane<f32, ()> = Plane {
+        normal: vec3(1.0, 0.0, 1.0).normalize(),
+        offset: -20.0,
+    };
+    let mut clipper = Clipper::new();
+    clipper.add(plane);
+
+    let poly = Polygon::from_points([
+        point3(-10.0, -10.0, 0.0),
+        point3(10.0, -10.0, 0.0),
+        point3(10.0, 10.0, 0.0),
+        point3(-10.0, 10.0, 0.0),
+    ], 0);
+
+    let results = clipper.clip(poly);
+    assert!(results.is_empty());
+}
+
+#[test]
+fn clip_parallel() {
+    let plane: Plane<f32, ()> = Plane {
+        normal: vec3(0.0, 0.0, 1.0),
+        offset: 0.0,
+    };
+    let mut clipper = Clipper::new();
+    clipper.add(plane);
+
+    let poly = Polygon::from_points([
+        point3(-10.0, -10.0, 0.0),
+        point3(10.0, -10.0, 0.0),
+        point3(10.0, 10.0, 0.0),
+        point3(-10.0, 10.0, 0.0),
+    ], 0);
+
+    let results = clipper.clip(poly);
+    assert!(results.is_empty());
+}
+
+#[test]
+fn clip_repeat() {
+    let plane: Plane<f32, ()> = Plane {
+        normal: vec3(1.0, 0.0, 1.0).normalize(),
+        offset: 0.0,
+    };
+    let mut clipper = Clipper::new();
+    clipper.add(plane.clone());
+    clipper.add(plane.clone());
+
+    let poly = Polygon::from_points([
+        point3(-10.0, -10.0, 0.0),
+        point3(10.0, -10.0, 0.0),
+        point3(10.0, 10.0, 0.0),
+        point3(-10.0, 10.0, 0.0),
+    ], 0);
+
+    let results = clipper.clip(poly);
+    assert_eq!(results.len(), 1);
+    assert!(plane.signed_distance_sum_to(&results[0]) > 0.0);
+}
--- a/third_party/rust/plane-split/tests/main.rs
+++ b/third_party/rust/plane-split/tests/main.rs
@@ -1,14 +1,14 @@
 extern crate euclid;
 extern crate plane_split;
 
 use euclid::{Angle, TypedRect, TypedSize2D, TypedTransform3D, point2, point3, vec3};
 use euclid::approxeq::ApproxEq;
-use plane_split::{Intersection, Line, LineProjection, Polygon};
+use plane_split::{Intersection, Line, LineProjection, Plane, Polygon};
 
 
 #[test]
 fn line_proj_bounds() {
     assert_eq!((-5i8, 4), LineProjection { markers: [-5i8, 1, 4, 2] }.get_bounds());
     assert_eq!((1f32, 4.0), LineProjection { markers: [4f32, 3.0, 2.0, 1.0] }.get_bounds());
 }
 
@@ -16,42 +16,48 @@ fn line_proj_bounds() {
 fn valid() {
     let poly_a: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 0.0, 0.0),
             point3(1.0, 1.0, 1.0),
             point3(1.0, 1.0, 0.0),
             point3(0.0, 1.0, 1.0),
         ],
-        normal: vec3(0.0, 1.0, 0.0),
-        offset: -1.0,
+        plane: Plane {
+            normal: vec3(0.0, 1.0, 0.0),
+            offset: -1.0,
+        },
         anchor: 0,
     };
     assert!(!poly_a.is_valid()); // points[0] is outside
     let poly_b: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 1.0, 0.0),
             point3(1.0, 1.0, 1.0),
             point3(1.0, 1.0, 0.0),
             point3(0.0, 1.0, 1.0),
         ],
-        normal: vec3(0.0, 1.0, 0.0),
-        offset: -1.0,
+        plane: Plane {
+            normal: vec3(0.0, 1.0, 0.0),
+            offset: -1.0,
+        },
         anchor: 0,
     };
     assert!(!poly_b.is_valid()); // winding is incorrect
     let poly_c: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 0.0, 1.0),
             point3(1.0, 0.0, 1.0),
             point3(1.0, 1.0, 1.0),
             point3(0.0, 1.0, 1.0),
         ],
-        normal: vec3(0.0, 0.0, 1.0),
-        offset: -1.0,
+        plane: Plane {
+            normal: vec3(0.0, 0.0, 1.0),
+            offset: -1.0,
+        },
         anchor: 0,
     };
     assert!(poly_c.is_valid());
 }
 
 #[test]
 fn from_transformed_rect() {
     let rect: TypedRect<f32, ()> = TypedRect::new(point2(10.0, 10.0), TypedSize2D::new(20.0, 30.0));
@@ -66,113 +72,115 @@ fn from_transformed_rect() {
 fn untransform_point() {
     let poly: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 0.0, 0.0),
             point3(0.5, 1.0, 0.0),
             point3(1.5, 1.0, 0.0),
             point3(1.0, 0.0, 0.0),
         ],
-        normal: vec3(0.0, 1.0, 0.0),
-        offset: 0.0,
+        plane: Plane {
+            normal: vec3(0.0, 1.0, 0.0),
+            offset: 0.0,
+        },
         anchor: 0,
     };
     assert_eq!(poly.untransform_point(poly.points[0]), point2(0.0, 0.0));
     assert_eq!(poly.untransform_point(poly.points[1]), point2(1.0, 0.0));
     assert_eq!(poly.untransform_point(poly.points[2]), point2(1.0, 1.0));
     assert_eq!(poly.untransform_point(poly.points[3]), point2(0.0, 1.0));
 }
 
 #[test]
 fn are_outside() {
-    let poly: Polygon<f32, ()> = Polygon {
-        points: [
-            point3(0.0, 0.0, 1.0),
-            point3(1.0, 0.0, 1.0),
-            point3(1.0, 1.0, 1.0),
-            point3(0.0, 1.0, 1.0),
-        ],
+    let plane: Plane<f32, ()> = Plane {
         normal: vec3(0.0, 0.0, 1.0),
         offset: -1.0,
-        anchor: 0,
     };
-    assert!(poly.is_valid());
-    assert!(poly.are_outside(&[
+    assert!(plane.are_outside(&[
         point3(0.0, 0.0, 1.1),
         point3(1.0, 1.0, 2.0),
     ]));
-    assert!(poly.are_outside(&[
+    assert!(plane.are_outside(&[
         point3(0.5, 0.5, 1.0),
     ]));
-    assert!(!poly.are_outside(&[
+    assert!(!plane.are_outside(&[
         point3(0.0, 0.0, 1.0),
         point3(0.0, 0.0, -1.0),
     ]));
 }
 
 #[test]
 fn intersect() {
     let poly_a: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 0.0, 1.0),
             point3(1.0, 0.0, 1.0),
             point3(1.0, 1.0, 1.0),
             point3(0.0, 1.0, 1.0),
         ],
-        normal: vec3(0.0, 0.0, 1.0),
-        offset: -1.0,
+        plane: Plane {
+            normal: vec3(0.0, 0.0, 1.0),
+            offset: -1.0,
+        },
         anchor: 0,
     };
     assert!(poly_a.is_valid());
     let poly_b: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.5, 0.0, 2.0),
             point3(0.5, 1.0, 2.0),
             point3(0.5, 1.0, 0.0),
             point3(0.5, 0.0, 0.0),
         ],
-        normal: vec3(1.0, 0.0, 0.0),
-        offset: -0.5,
+        plane: Plane {
+            normal: vec3(1.0, 0.0, 0.0),
+            offset: -0.5,
+        },
         anchor: 0,
     };
     assert!(poly_b.is_valid());
 
     let intersection = match poly_a.intersect(&poly_b) {
         Intersection::Inside(result) => result,
         _ => panic!("Bad intersection"),
     };
     assert!(intersection.is_valid());
     // confirm the origin is on both planes
-    assert!(poly_a.signed_distance_to(&intersection.origin).approx_eq(&0.0));
-    assert!(poly_b.signed_distance_to(&intersection.origin).approx_eq(&0.0));
+    assert!(poly_a.plane.signed_distance_to(&intersection.origin).approx_eq(&0.0));
+    assert!(poly_b.plane.signed_distance_to(&intersection.origin).approx_eq(&0.0));
     // confirm the direction is coplanar to both planes
-    assert!(poly_a.normal.dot(intersection.dir).approx_eq(&0.0));
-    assert!(poly_b.normal.dot(intersection.dir).approx_eq(&0.0));
+    assert!(poly_a.plane.normal.dot(intersection.dir).approx_eq(&0.0));
+    assert!(poly_b.plane.normal.dot(intersection.dir).approx_eq(&0.0));
 
     let poly_c: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, -1.0, 2.0),
             point3(0.0, -1.0, 0.0),
             point3(0.0, 0.0, 0.0),
             point3(0.0, 0.0, 2.0),
         ],
-        normal: vec3(1.0, 0.0, 0.0),
-        offset: 0.0,
+        plane: Plane {
+            normal: vec3(1.0, 0.0, 0.0),
+            offset: 0.0,
+        },
         anchor: 0,
     };
     assert!(poly_c.is_valid());
     let poly_d: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 0.0, 0.5),
             point3(1.0, 0.0, 0.5),
             point3(1.0, 1.0, 0.5),
             point3(0.0, 1.0, 0.5),
         ],
-        normal: vec3(0.0, 0.0, 1.0),
-        offset: -0.5,
+        plane: Plane {
+            normal: vec3(0.0, 0.0, 1.0),
+            offset: -0.5,
+        },
         anchor: 0,
     };
     assert!(poly_d.is_valid());
 
     assert!(poly_a.intersect(&poly_c).is_outside());
     assert!(poly_a.intersect(&poly_d).is_outside());
 }
 
@@ -195,18 +203,20 @@ fn test_cut(poly_base: &Polygon<f32, ()>
 fn split() {
     let poly: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 1.0, 0.0),
             point3(1.0, 1.0, 0.0),
             point3(1.0, 1.0, 1.0),
             point3(0.0, 1.0, 1.0),
         ],
-        normal: vec3(0.0, 1.0, 0.0),
-        offset: -1.0,
+        plane: Plane {
+            normal: vec3(0.0, 1.0, 0.0),
+            offset: -1.0,
+        },
         anchor: 0,
     };
 
     // non-intersecting line
     test_cut(&poly, 0, Line {
         origin: point3(0.0, 1.0, 0.5),
         dir: vec3(0.0, 1.0, 0.0),
     });
--- a/third_party/rust/plane-split/tests/split.rs
+++ b/third_party/rust/plane-split/tests/split.rs
@@ -1,28 +1,23 @@
 extern crate euclid;
 extern crate plane_split;
 
 use std::f32::consts::FRAC_PI_4;
 use euclid::{Angle, TypedTransform3D, TypedRect, vec3};
-use plane_split::{BspSplitter, NaiveSplitter, Polygon, Splitter, _make_grid};
+use plane_split::{BspSplitter, Polygon, Splitter, make_grid};
 
 
 fn grid_impl(count: usize, splitter: &mut Splitter<f32, ()>) {
-    let polys = _make_grid(count);
+    let polys = make_grid(count);
     let result = splitter.solve(&polys, vec3(0.0, 0.0, 1.0));
     assert_eq!(result.len(), count + count*count + count*count*count);
 }
 
 #[test]
-fn grid_naive() {
-    grid_impl(2, &mut NaiveSplitter::new());
-}
-
-#[test]
 fn grid_bsp() {
     grid_impl(2, &mut BspSplitter::new());
 }
 
 
 fn sort_rotation(splitter: &mut Splitter<f32, ()>) {
     let transform0: TypedTransform3D<f32, (), ()> =
         TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
@@ -39,21 +34,16 @@ fn sort_rotation(splitter: &mut Splitter
     ];
 
     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]);
 }
 
 #[test]
-fn rotation_naive() {
-    sort_rotation(&mut NaiveSplitter::new());
-}
-
-#[test]
 fn rotation_bsp() {
     sort_rotation(&mut BspSplitter::new());
 }
 
 
 fn sort_trivial(splitter: &mut Splitter<f32, ()>) {
     let anchors: Vec<_> = (0usize .. 10).collect();
     let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
@@ -65,16 +55,11 @@ fn sort_trivial(splitter: &mut Splitter<
     let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
     let anchors1: Vec<_> = result.iter().map(|p| p.anchor).collect();
     let mut anchors2 = anchors1.clone();
     anchors2.sort_by_key(|&a| -(a as i32));
     assert_eq!(anchors1, anchors2); //make sure Z is sorted backwards
 }
 
 #[test]
-fn trivial_naive() {
-    sort_trivial(&mut NaiveSplitter::new());
-}
-
-#[test]
 fn trivial_bsp() {
     sort_trivial(&mut BspSplitter::new());
 }