├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── bin └── test.rs ├── consts.rs ├── lib.rs ├── r1 ├── interval.rs └── mod.rs ├── r2 ├── mod.rs ├── point.rs └── rect.rs ├── r3 ├── mod.rs ├── precisevector.rs └── vector.rs ├── s1 ├── angle.rs ├── chordangle.rs ├── interval.rs └── mod.rs └── s2 ├── cap.rs ├── cell.rs ├── cellid.rs ├── cellunion.rs ├── edge_clipping.rs ├── edgeutil.rs ├── latlng.rs ├── metric.rs ├── mod.rs ├── point.rs ├── predicates.rs ├── random.rs ├── rect.rs ├── rect_bounder.rs ├── region.rs ├── shape.rs └── stuv.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | toolchain: stable 14 | target: wasm32-unknown-unknown 15 | - uses: actions-rs/cargo@v1 16 | with: 17 | command: check 18 | - uses: actions-rs/cargo@v1 19 | with: 20 | command: check 21 | args: --features serde 22 | - uses: actions-rs/cargo@v1 23 | with: 24 | command: fmt 25 | args: --all --check 26 | - uses: actions-rs/cargo@v1 27 | with: 28 | command: check 29 | args: --target wasm32-unknown-unknown --no-default-features 30 | 31 | test: 32 | name: Run Test Suite 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v3 36 | - uses: actions-rs/toolchain@v1 37 | with: 38 | toolchain: stable 39 | - uses: actions-rs/cargo@v1 40 | with: 41 | command: test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.sw[a-z] 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "s2" 3 | edition = "2018" 4 | 5 | version = "0.0.12" 6 | authors = ["Jihyun Yu "] 7 | 8 | license = "Apache-2.0" 9 | 10 | description = "S2 geometric library" 11 | homepage = "https://github.com/yjh0502/rust-s2" 12 | repository = "https://github.com/yjh0502/rust-s2" 13 | documentation = "https://docs.rs/rust-s2" 14 | 15 | keywords = ["geo", "s2"] 16 | 17 | [features] 18 | default = ["serde", "float_extras"] 19 | serde = ["dep:serde", "bigdecimal/serde"] 20 | 21 | [dependencies] 22 | float_extras = { version = "0.1.6", optional = true } 23 | lazy_static = "1.4.0" 24 | rand = { version = "0.8.5", optional = true } 25 | cgmath = "0.18.0" 26 | libm = "0.2.6" 27 | bigdecimal = { version = "0.3.0", default_features = false } 28 | serde = { version = "1.0.151", features = ["serde_derive"], optional = true } 29 | 30 | [dev-dependencies] 31 | rand = { version = "0.8.5" } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-s2 2 | 3 | Rust port of Google S2 geometry library. 4 | 5 | [![Build Status](https://travis-ci.org/yjh0502/rust-s2.svg?branch=master)](https://travis-ci.org/yjh0502/rust-s2) 6 | [![docs](https://docs.rs/s2/badge.svg)](https://docs.rs/s2/0.0.10/s2/) 7 | 8 | # Status of the Rust Library 9 | 10 | This library is principally a port of [the Golang S2 11 | library](https://github.com/golang/geo), adapting to Rust idioms where it makes sense. 12 | We detail the progress of this port below relative to that Go library. 13 | 14 | ## [ℝ¹](https://docs.rs/s2/~0/s2/r1/) - One-dimensional Cartesian coordinates 15 | 16 | Full parity with Go. 17 | 18 | ## [ℝ²](https://docs.rs/s2/~0/s2/r2/) - Two-dimensional Cartesian coordinates 19 | 20 | Full parity with Go. 21 | 22 | ## [ℝ³](https://docs.rs/s2/~0/s2/r3/) - Three-dimensional Cartesian coordinates 23 | 24 | Full parity with Go. 25 | 26 | ## [S¹](https://docs.rs/s2/~0/s2/s1/) - Circular Geometry 27 | 28 | Full parity with Go. 29 | 30 | ## [S²](https://docs.rs/s2/~0/s2/s2/) - Spherical Geometry 31 | 32 | **complete** 33 | 34 | - Cell, CellID, LatLng, Metric, Point, Region, stuv 35 | 36 | **in progress** 37 | 38 | - CellUnion, edgeutil, predicates, Rect 39 | 40 | **pending** 41 | 42 | - loop, paddedcell, polygon, polyline, shapeindex 43 | -------------------------------------------------------------------------------- /src/bin/test.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /src/consts.rs: -------------------------------------------------------------------------------- 1 | /// Define the maximum rounding error for arithmetic operations. Depending on the 2 | /// platform the mantissa precision may be different than others, so we choose to 3 | /// use specific values to be consistent across all. 4 | /// The values come from the C++ implementation. 5 | 6 | /// EPSILON is a small number that represents a reasonable level of noise between two 7 | /// values that can be considered to be equal. 8 | pub const EPSILON: f64 = 1e-14; 9 | 10 | /// DBL_EPSILON is a smaller number for values that require more precision. 11 | pub const DBL_EPSILON: f64 = 2.220446049250313e-16; 12 | 13 | #[macro_export] 14 | macro_rules! f64_eq { 15 | ($x:expr, $y:expr) => { 16 | ($x - $y).abs() < EPSILON 17 | }; 18 | } 19 | 20 | #[macro_export] 21 | macro_rules! assert_f64_eq { 22 | ($x:expr, $y:expr) => { 23 | assert!(($x - $y).abs() < EPSILON) 24 | }; 25 | } 26 | 27 | #[allow(unused)] 28 | /// f64_eq reports whether the two values are within the default epsilon. 29 | pub fn f64_eq(x: f64, y: f64) -> bool { 30 | f64_near(x, y, EPSILON) 31 | } 32 | 33 | #[allow(unused)] 34 | /// f64_near reports whether the two values are within the specified epsilon. 35 | pub fn f64_near(x: f64, y: f64, eps: f64) -> bool { 36 | (x - y).abs() <= eps 37 | } 38 | 39 | ///TODO: to util module? 40 | pub fn remainder(x: f64, y: f64) -> f64 { 41 | ::libm::remquo(x, y).0 42 | } 43 | 44 | pub fn clamp(val: T, min: T, max: T) -> T 45 | where 46 | T: PartialOrd, 47 | { 48 | if val < min { 49 | min 50 | } else if val > max { 51 | max 52 | } else { 53 | val 54 | } 55 | } 56 | 57 | pub fn search_lower_by(len: usize, f: F) -> usize 58 | where 59 | F: Fn(usize) -> bool, 60 | { 61 | let mut i = 0; 62 | let mut j = len; 63 | 64 | while i < j { 65 | let h = i + (j - i) / 2; 66 | if !f(h) { 67 | i = h + 1; 68 | } else { 69 | j = h; 70 | } 71 | } 72 | i 73 | } 74 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | 4 | #[cfg(feature = "serde")] 5 | #[macro_use] 6 | extern crate serde; 7 | 8 | #[macro_use] 9 | mod consts; 10 | 11 | pub mod r1; 12 | pub mod r2; 13 | pub mod r3; 14 | 15 | pub mod s1; 16 | 17 | // export s2 modules directly 18 | mod s2; 19 | pub use crate::s2::*; 20 | -------------------------------------------------------------------------------- /src/r1/interval.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use std; 19 | 20 | use crate::consts::EPSILON; 21 | 22 | /// Interval represents a closed interval on ℝ. 23 | /// Zero-length intervals (where Lo == Hi) represent single points. 24 | /// If Lo > Hi then the interval is empty. 25 | #[derive(Clone, Copy, Default)] 26 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 27 | pub struct Interval { 28 | /// lower bound of the interval 29 | pub lo: f64, 30 | /// upper bound of the interval 31 | pub hi: f64, 32 | } 33 | impl std::fmt::Debug for Interval { 34 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 35 | write!(f, "[{:.7}, {:.7}]", self.lo, self.hi) 36 | } 37 | } 38 | 39 | /// An empty interval. 40 | pub const EMPTY: Interval = Interval { lo: 1., hi: 0. }; 41 | 42 | impl Interval { 43 | pub fn new(lo: f64, hi: f64) -> Self { 44 | Self { lo, hi } 45 | } 46 | 47 | /// from_point returns an interval representing a single point. 48 | pub fn from_point(p: f64) -> Self { 49 | Self { lo: p, hi: p } 50 | } 51 | 52 | // Convenience method to construct the minimal interval containing 53 | // the two given points. This is equivalent to starting with an empty 54 | // interval and calling AddPoint() twice, but it is more efficient. 55 | pub fn from_point_pair(p1: f64, p2: f64) -> Self { 56 | if p1 <= p2 { 57 | Self { lo: p1, hi: p2 } 58 | } else { 59 | Self { lo: p2, hi: p1 } 60 | } 61 | } 62 | 63 | /// is_empty reports whether the interval is empty. 64 | pub fn is_empty(&self) -> bool { 65 | self.lo > self.hi 66 | } 67 | 68 | /// center returns the midpoint of the interval. 69 | /// It is undefined for empty intervals. 70 | pub fn center(&self) -> f64 { 71 | 0.5 * (self.lo + self.hi) 72 | } 73 | 74 | /// len returns the length of the interval. 75 | /// The length of an empty interval is negative. 76 | pub fn len(&self) -> f64 { 77 | self.hi - self.lo 78 | } 79 | 80 | /// contains returns true iff the interval contains p. 81 | pub fn contains(&self, p: f64) -> bool { 82 | self.lo <= p && p <= self.hi 83 | } 84 | 85 | /// contains_interval returns true iff the interval contains oi. 86 | pub fn contains_interval(&self, oi: &Self) -> bool { 87 | if oi.is_empty() { 88 | true 89 | } else { 90 | self.lo <= oi.lo && oi.hi <= self.hi 91 | } 92 | } 93 | 94 | /// interior_contains returns true iff the the interval strictly contains p. 95 | pub fn interior_contains(&self, p: f64) -> bool { 96 | self.lo < p && p < self.hi 97 | } 98 | 99 | /// interior_contains_interval returns true iff the interval strictly contains oi. 100 | pub fn interior_contains_interval(&self, oi: &Self) -> bool { 101 | if oi.is_empty() { 102 | true 103 | } else { 104 | self.lo < oi.lo && oi.hi < self.hi 105 | } 106 | } 107 | 108 | /// intersects returns true iff the interval contains any points in common with oi. 109 | pub fn intersects(&self, oi: &Self) -> bool { 110 | if self.lo <= oi.lo { 111 | // oi.Lo ∈ i and oi is not empty 112 | oi.lo <= self.hi && oi.lo <= oi.hi 113 | } else { 114 | // i.Lo ∈ oi and i is not empty 115 | self.lo <= oi.hi && self.lo <= self.hi 116 | } 117 | } 118 | 119 | /// interior_intersects returns true iff the interior of the interval contains any points in 120 | /// common with oi, including the latter's boundary. 121 | pub fn interior_intersects(&self, oi: &Self) -> bool { 122 | oi.lo < self.hi && self.lo < oi.hi && self.lo < self.hi && oi.lo <= oi.hi 123 | } 124 | 125 | /// intersection returns the interval containing all points common to i and j. 126 | pub fn intersection(&self, other: &Self) -> Self { 127 | // Empty intervals do not need to be special-cased. 128 | Interval { 129 | lo: self.lo.max(other.lo), 130 | hi: self.hi.min(other.hi), 131 | } 132 | } 133 | 134 | /// clamp_point returns the closest point in the interval to the given point "p". 135 | /// The interval must be non-empty. 136 | pub fn clamp_point(&self, p: f64) -> f64 { 137 | self.lo.max(self.hi.min(p)) 138 | } 139 | 140 | /// expanded returns an interval that has been expanded on each side by margin. 141 | /// If margin is negative, then the function shrinks the interval on 142 | /// each side by margin instead. The resulting interval may be empty. Any 143 | /// expansion of an empty interval remains empty. 144 | pub fn expanded(&self, margin: f64) -> Self { 145 | if self.is_empty() { 146 | *self 147 | } else { 148 | Interval { 149 | lo: self.lo - margin, 150 | hi: self.hi + margin, 151 | } 152 | } 153 | } 154 | 155 | /// union returns the smallest interval that contains this interval and the given interval. 156 | pub fn union(&self, other: &Self) -> Self { 157 | if self.is_empty() { 158 | *other 159 | } else if other.is_empty() { 160 | *self 161 | } else { 162 | Interval { 163 | lo: self.lo.min(other.lo), 164 | hi: self.hi.max(other.hi), 165 | } 166 | } 167 | } 168 | 169 | /// approx_eq reports whether the interval can be transformed into the 170 | /// given interval by moving each endpoint a small distance. 171 | /// The empty interval is considered to be positioned arbitrarily on the 172 | /// real line, so any interval with a small enough length will match 173 | /// the empty interval. 174 | pub fn approx_eq(&self, other: &Self) -> bool { 175 | self.approx_eq_by(other, EPSILON) 176 | } 177 | 178 | pub fn approx_eq_by(&self, other: &Self, max_error: f64) -> bool { 179 | if self.is_empty() { 180 | other.len() < 2. * max_error 181 | } else if other.is_empty() { 182 | self.len() < 2. * max_error 183 | } else { 184 | (self.lo - other.lo).abs() <= max_error && (self.hi - other.hi).abs() <= max_error 185 | } 186 | } 187 | 188 | /// Returns the Hausdorff distance to the given interval. 189 | /// For two intervals x and y, this distance is defined as 190 | /// h(x, y) = max_{p in x} min_{q in y} d(p, q). 191 | pub fn directed_hausdorff_distance(&self, other: &Self) -> f64 { 192 | if self.is_empty() { 193 | return 0.0; 194 | } 195 | if other.is_empty() { 196 | return f64::INFINITY; 197 | } 198 | 0_f64.max((self.hi - other.hi).max(other.lo - self.lo)) 199 | } 200 | } 201 | 202 | impl std::ops::Add for Interval { 203 | type Output = Interval; 204 | fn add(self, p: f64) -> Self::Output { 205 | &self + p 206 | } 207 | } 208 | impl<'a> std::ops::Add for &'a Interval { 209 | type Output = Interval; 210 | /// returns the interval expanded so that it contains the given point. 211 | fn add(self, p: f64) -> Self::Output { 212 | if self.is_empty() { 213 | Interval { lo: p, hi: p } 214 | } else if p < self.lo { 215 | Interval { lo: p, hi: self.hi } 216 | } else if p > self.hi { 217 | Interval { lo: self.lo, hi: p } 218 | } else { 219 | *self 220 | } 221 | } 222 | } 223 | 224 | impl std::cmp::PartialEq for Interval { 225 | /// returns true iff the interval contains the same points as other. 226 | fn eq(&self, other: &Interval) -> bool { 227 | (self.lo == other.lo && self.hi == other.hi) || (self.is_empty() && other.is_empty()) 228 | } 229 | } 230 | 231 | #[cfg(test)] 232 | mod tests { 233 | use super::*; 234 | 235 | macro_rules! I { 236 | ($lo:expr, $hi:expr) => { 237 | Interval { lo: $lo, hi: $hi } 238 | }; 239 | } 240 | 241 | const UNIT: Interval = I! {0., 1.}; 242 | const NEG_UNIT: Interval = I! {-1., 0.}; 243 | const HALF: Interval = I! {0.5, 0.5}; 244 | const ZERO: Interval = I! {0., 0.}; 245 | 246 | #[test] 247 | fn is_empty() { 248 | assert_eq!(false, UNIT.is_empty()); 249 | assert_eq!(false, HALF.is_empty()); 250 | assert_eq!(true, EMPTY.is_empty()); 251 | assert_eq!(false, ZERO.is_empty()); 252 | } 253 | 254 | #[test] 255 | fn center() { 256 | assert_eq!(UNIT.center(), 0.5); 257 | assert_eq!(NEG_UNIT.center(), -0.5); 258 | assert_eq!(HALF.center(), 0.5); 259 | } 260 | 261 | #[test] 262 | fn length() { 263 | assert_eq!(UNIT.len(), 1.); 264 | assert_eq!(NEG_UNIT.len(), 1.); 265 | assert_eq!(HALF.len(), 0.); 266 | } 267 | 268 | #[test] 269 | fn test_from_point_pair() { 270 | assert_eq!(Interval::from_point_pair(4., 4.), I!(4., 4.)); 271 | assert_eq!(Interval::from_point_pair(-1., -2.), I!(-2., -1.)); 272 | assert_eq!(Interval::from_point_pair(-5., 3.), I!(-5., 3.)); 273 | } 274 | 275 | #[test] 276 | fn interval_contains() { 277 | assert_eq!(true, UNIT.contains(0.5)); 278 | assert_eq!(true, UNIT.interior_contains(0.5)); 279 | 280 | assert_eq!(true, UNIT.contains(0.)); 281 | assert_eq!(false, UNIT.interior_contains(0.)); 282 | 283 | assert_eq!(true, UNIT.contains(1.)); 284 | assert_eq!(false, UNIT.interior_contains(1.)); 285 | } 286 | 287 | fn test_interval_ops( 288 | have: &Interval, 289 | other: &Interval, 290 | contains: bool, 291 | interior_contains: bool, 292 | intersects: bool, 293 | interior_intersects: bool, 294 | ) { 295 | assert_eq!(contains, have.contains_interval(other)); 296 | assert_eq!(interior_contains, have.interior_contains_interval(other)); 297 | assert_eq!(intersects, have.intersects(other)); 298 | assert_eq!(interior_intersects, have.interior_intersects(other)); 299 | } 300 | 301 | #[test] 302 | fn interval_operations() { 303 | test_interval_ops(&EMPTY, &EMPTY, true, true, false, false); 304 | test_interval_ops(&EMPTY, &UNIT, false, false, false, false); 305 | test_interval_ops(&UNIT, &HALF, true, true, true, true); 306 | test_interval_ops(&UNIT, &UNIT, true, false, true, true); 307 | test_interval_ops(&UNIT, &EMPTY, true, true, false, false); 308 | test_interval_ops(&UNIT, &NEG_UNIT, false, false, true, false); 309 | } 310 | 311 | #[test] 312 | fn intersection() { 313 | assert_eq!(HALF, UNIT.intersection(&HALF)); 314 | assert_eq!(ZERO, UNIT.intersection(&NEG_UNIT)); 315 | assert_eq!(EMPTY, NEG_UNIT.intersection(&HALF)); 316 | assert_eq!(EMPTY, UNIT.intersection(&EMPTY)); 317 | assert_eq!(EMPTY, EMPTY.intersection(&UNIT)); 318 | } 319 | 320 | fn test_union(x: Interval, y: Interval, want: Interval) { 321 | assert_eq!(want, x.union(&y)); 322 | assert_eq!(want, y.union(&x)); 323 | } 324 | 325 | #[test] 326 | fn union() { 327 | let i_99_100 = Interval { lo: 99., hi: 100. }; 328 | test_union(i_99_100.clone(), EMPTY, i_99_100.clone()); 329 | test_union(I!(5., 3.), I!(0., -2.), EMPTY); 330 | test_union(UNIT, UNIT, UNIT); 331 | test_union(UNIT, NEG_UNIT, I!(-1., 1.)); 332 | test_union(HALF, UNIT, UNIT); 333 | } 334 | 335 | #[test] 336 | fn add() { 337 | assert_eq!(I!(5., 5.), EMPTY + 5.); 338 | assert_eq!(I!(-1., 5.), I!(5., 5.) + -1.); 339 | assert_eq!(I!(-1., 5.), I!(-1., 5.) + -1.); 340 | assert_eq!(I!(-1., 6.), I!(-1., 5.) + 6.); 341 | } 342 | 343 | #[test] 344 | fn clamp_point() { 345 | let i = I!(0.1, 0.4); 346 | assert_eq!(0.3, i.clamp_point(0.3)); 347 | assert_eq!(0.1, i.clamp_point(-7.0)); 348 | assert_eq!(0.4, i.clamp_point(0.6)); 349 | } 350 | 351 | #[test] 352 | fn expanded() { 353 | assert_eq!(EMPTY, EMPTY.expanded(0.45)); 354 | assert_eq!(I!(-0.5, 1.5), UNIT.expanded(0.5)); 355 | assert_eq!(I!(0.5, 0.5), UNIT.expanded(-0.5)); 356 | assert_eq!(EMPTY, UNIT.expanded(-0.51)); 357 | } 358 | 359 | fn test_approx_eq(i1: &Interval, i2: &Interval, expected: bool) { 360 | assert_eq!(expected, i1.approx_eq(&i2)); 361 | } 362 | 363 | #[test] 364 | fn approx_equal() { 365 | test_approx_eq(&EMPTY, &EMPTY, true); 366 | test_approx_eq(&EMPTY, &ZERO, true); 367 | test_approx_eq(&EMPTY, &I!(1., 1.), true); 368 | test_approx_eq(&EMPTY, &I!(0., 1.), false); 369 | test_approx_eq(&EMPTY, &I!(1., 1. + 2. * EPSILON), true); 370 | 371 | test_approx_eq(&I!(1., 1.), &I!(1., 1.), true); 372 | test_approx_eq(&I!(1., 1.), &I!(1. - EPSILON, 1. - EPSILON), true); 373 | test_approx_eq(&I!(1., 1.), &I!(1. + EPSILON, 1. + EPSILON), true); 374 | test_approx_eq(&I!(1., 1.), &I!(1. - 3. * EPSILON, 1.), false); 375 | test_approx_eq(&I!(1., 1.), &I!(1. - EPSILON, 1. + EPSILON), true); 376 | test_approx_eq(&ZERO, &I!(1., 1.), false); 377 | 378 | test_approx_eq(&I!(1. - EPSILON, 2. + EPSILON), &I!(1., 2.), false); 379 | test_approx_eq(&I!(1. + EPSILON, 2. - EPSILON), &I!(1., 2.), true); 380 | test_approx_eq(&I!(1. - 3. * EPSILON, 2. + EPSILON), &I!(1., 2.), false); 381 | test_approx_eq(&I!(1. + 3. * EPSILON, 2. - EPSILON), &I!(1., 2.), false); 382 | test_approx_eq(&I!(1. - EPSILON, 2. + 3. * EPSILON), &I!(1., 2.), false); 383 | test_approx_eq(&I!(1. + EPSILON, 2. - 3. * EPSILON), &I!(1., 2.), false); 384 | } 385 | 386 | #[test] 387 | fn test_directed_hausdorff_distance() { 388 | assert_eq!(EMPTY.directed_hausdorff_distance(&UNIT), 0.0); 389 | assert_eq!(UNIT.directed_hausdorff_distance(&EMPTY), f64::INFINITY); 390 | assert_f64_eq!(HALF.directed_hausdorff_distance(&HALF), 0.0); 391 | assert_f64_eq!(HALF.directed_hausdorff_distance(&I!(1., 2.)), 0.5); 392 | assert_f64_eq!(I!(1., 2.).directed_hausdorff_distance(&HALF), 1.5); 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /src/r1/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod interval; 2 | -------------------------------------------------------------------------------- /src/r2/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod point; 2 | pub mod rect; 3 | -------------------------------------------------------------------------------- /src/r2/point.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use crate::consts::*; 19 | use std; 20 | use std::cmp::Ordering; 21 | 22 | /// Point represents a point in ℝ². 23 | #[derive(Clone, Copy, Debug)] 24 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 25 | pub struct Point { 26 | /// x coordinate of the point 27 | pub x: f64, 28 | /// y coordinate of the point 29 | pub y: f64, 30 | } 31 | 32 | impl Eq for Point {} 33 | 34 | impl Ord for Point { 35 | fn cmp(&self, ov: &Point) -> Ordering { 36 | if self.x < ov.x { 37 | return Ordering::Less; 38 | } 39 | if self.x > ov.x { 40 | return Ordering::Greater; 41 | } 42 | 43 | // First elements were the same, try the next. 44 | if self.y < ov.y { 45 | return Ordering::Less; 46 | } 47 | if self.y > ov.y { 48 | return Ordering::Greater; 49 | } 50 | Ordering::Equal 51 | } 52 | } 53 | 54 | impl PartialOrd for Point { 55 | fn partial_cmp(&self, other: &Point) -> Option { 56 | Some(self.cmp(other)) 57 | } 58 | } 59 | 60 | impl PartialEq for Point { 61 | fn eq(&self, other: &Point) -> bool { 62 | self.cmp(other) == Ordering::Equal 63 | } 64 | } 65 | 66 | impl std::ops::Add for Point { 67 | type Output = Point; 68 | /// returns the sum of p and other. 69 | fn add(self, other: Point) -> Self::Output { 70 | Point { 71 | x: self.x + other.x, 72 | y: self.y + other.y, 73 | } 74 | } 75 | } 76 | 77 | impl std::ops::Sub for Point { 78 | type Output = Point; 79 | /// returns the difference of p and other. 80 | fn sub(self, other: Point) -> Self::Output { 81 | Self::Output { 82 | x: self.x - other.x, 83 | y: self.y - other.y, 84 | } 85 | } 86 | } 87 | 88 | impl<'a, 'b> std::ops::Mul<&'b Point> for &'a Point { 89 | type Output = Point; 90 | /// returns the product between p and other. 91 | fn mul(self, other: &'b Point) -> Self::Output { 92 | Point { 93 | x: self.x * self.x, 94 | y: other.y * other.y, 95 | } 96 | } 97 | } 98 | impl<'b> std::ops::Mul<&'b Point> for Point { 99 | type Output = Point; 100 | /// returns the product between p and other. 101 | fn mul(self, other: &'b Point) -> Self::Output { 102 | &self * other 103 | } 104 | } 105 | impl std::ops::Mul for Point { 106 | type Output = Point; 107 | /// returns the product between p and other. 108 | fn mul(self, other: Point) -> Self::Output { 109 | &self * &other 110 | } 111 | } 112 | 113 | impl<'a> std::ops::Mul for &'a Point { 114 | type Output = Point; 115 | /// returns the scalar product of p and other. 116 | fn mul(self, other: f64) -> Self::Output { 117 | Self::Output { 118 | x: self.x * other, 119 | y: self.y * other, 120 | } 121 | } 122 | } 123 | 124 | impl Point { 125 | pub fn new(x: f64, y: f64) -> Self { 126 | Self { x, y } 127 | } 128 | 129 | /// returns a counterclockwise orthogonal point with the same norm. 130 | pub fn ortho(&self) -> Self { 131 | Self { 132 | x: -self.y, 133 | y: self.x, 134 | } 135 | } 136 | 137 | /// returns the dot product between p and op. 138 | pub fn dot(&self, other: &Self) -> f64 { 139 | self.x * other.x + self.y * other.y 140 | } 141 | 142 | /// returns the cross product of p and op. 143 | pub fn cross(&self, other: &Self) -> f64 { 144 | self.x * other.y - self.y * other.x 145 | } 146 | 147 | /// returns the vector's norm. 148 | pub fn norm(&self) -> f64 { 149 | self.x.hypot(self.y) 150 | } 151 | 152 | /// returns a unit point in the same direction as p. 153 | pub fn normalize(&self) -> Self { 154 | if self.x == 0. && self.y == 0. { 155 | *self 156 | } else { 157 | self * (1.0 / self.norm()) 158 | } 159 | } 160 | 161 | pub fn approx_eq(&self, other: &Self) -> bool { 162 | f64_eq!(self.x, other.x) && f64_eq!(self.y, other.y) 163 | } 164 | } 165 | 166 | #[cfg(test)] 167 | mod tests { 168 | use super::*; 169 | 170 | macro_rules! P { 171 | ($x:expr, $y:expr) => { 172 | Point { x: $x, y: $y } 173 | }; 174 | } 175 | 176 | #[test] 177 | fn ortho() { 178 | assert_eq!(P!(0., 0.), P!(0., 0.).ortho()); 179 | assert_eq!(P!(-1., 0.), P!(0., 1.).ortho()); 180 | assert_eq!(P!(-1., 1.), P!(1., 1.).ortho()); 181 | assert_eq!(P!(-7., -4.), P!(-4., 7.).ortho()); 182 | assert_eq!(P!(-(3f64).sqrt(), 1.), P!(1., (3f64).sqrt()).ortho()); 183 | } 184 | 185 | #[test] 186 | fn dot() { 187 | assert_eq!(0., P!(0., 0.).dot(&P!(0., 0.))); 188 | assert_eq!(0., P!(0., 1.).dot(&P!(0., 0.))); 189 | assert_eq!(7., P!(1., 1.).dot(&P!(4., 3.))); 190 | assert_eq!(31., P!(-4., 7.).dot(&P!(1., 5.))); 191 | } 192 | 193 | #[test] 194 | fn cross() { 195 | assert_eq!(0., P!(0., 0.).cross(&P!(0., 0.))); 196 | assert_eq!(0., P!(0., 1.).cross(&P!(0., 0.))); 197 | assert_eq!(0., P!(1., 1.).cross(&P!(-1., -1.))); 198 | assert_eq!(-1., P!(1., 1.).cross(&P!(4., 3.))); 199 | assert_eq!(13., P!(1., 5.).cross(&P!(-2., 3.))); 200 | } 201 | 202 | #[test] 203 | fn norm() { 204 | assert_eq!(0., P!(0., 0.).norm()); 205 | assert_eq!(1., P!(0., 1.).norm()); 206 | assert_eq!(1., P!(-1., 0.).norm()); 207 | assert_eq!(5., P!(3., 4.).norm()); 208 | assert_eq!(5., P!(3., -4.).norm()); 209 | assert_eq!(2. * 2f64.sqrt(), P!(2., 2.).norm()); 210 | assert_f64_eq!(2., P!(1., 3f64.sqrt()).norm()); 211 | assert_f64_eq!(29. * 2., P!(29., 29. * 3f64.sqrt()).norm()); 212 | assert_f64_eq!(1e15, P!(1., 1e15).norm()); 213 | assert_f64_eq!(std::f64::MAX, P!(1e14, std::f64::MAX - 1.).norm()); 214 | } 215 | 216 | fn test_normalize(p1: Point, p2: Point) { 217 | assert!(p1.normalize().approx_eq(&p2)); 218 | } 219 | 220 | #[test] 221 | fn normalize() { 222 | test_normalize(P!(0., 0.), P!(0., 0.)); 223 | test_normalize(P!(0., 1.), P!(0., 1.)); 224 | test_normalize(P!(-1., 0.), P!(-1., 0.)); 225 | test_normalize(P!(3., 4.), P!(0.6, 0.8)); 226 | test_normalize(P!(3., -4.), P!(0.6, -0.8)); 227 | test_normalize(P!(2., 2.), P!(2f64.sqrt() / 2., 2f64.sqrt() / 2.)); 228 | test_normalize(P!(7., 7. * 3f64.sqrt()), P!(0.5, 3f64.sqrt() / 2.)); 229 | test_normalize(P!(1e21, 1e21 * 3f64.sqrt()), P!(0.5, 3f64.sqrt() / 2.)); 230 | test_normalize(P!(1., 1e16), P!(0., 1.)); 231 | test_normalize(P!(1e4, std::f64::MAX - 1.), P!(0., 1.)); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/r2/rect.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use std; 19 | 20 | use crate::r1::interval::{self, Interval}; 21 | use crate::r2::point::Point; 22 | 23 | /// Rect represents a closed axis-aligned rectangle in the (x,y) plane. 24 | #[derive(Clone, Debug, Default, PartialEq)] 25 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 26 | pub struct Rect { 27 | /// x interval of the rect 28 | pub x: Interval, 29 | /// y interval of the rect 30 | pub y: Interval, 31 | } 32 | 33 | /// Canonical empty rectangle. Use IsEmpty() to test 34 | /// for empty rectangles, since they have more than one representation. A Rect::default() 35 | /// is not the same as the EmptyRect. 36 | pub const EMPTY: Rect = Rect { 37 | x: interval::EMPTY, 38 | y: interval::EMPTY, 39 | }; 40 | 41 | impl Rect { 42 | /// from_points constructs a rect that contains the given points. 43 | pub fn from_points(points: &[Point]) -> Self { 44 | // Because the default value on interval is 0,0, we need to manually 45 | // define the interval from the first point passed in as our starting 46 | // interval, otherwise we end up with the case of passing in 47 | // Point{0.2, 0.3} and getting the starting Rect of {0, 0.2}, {0, 0.3} 48 | // instead of the Rect {0.2, 0.2}, {0.3, 0.3} which is not correct. 49 | if points.is_empty() { 50 | return Self::default(); 51 | } 52 | 53 | let mut r = Rect { 54 | x: Interval::from_point(points[0].x), 55 | y: Interval::from_point(points[0].y), 56 | }; 57 | for p in &points[1..] { 58 | r = r + p; 59 | } 60 | r 61 | } 62 | 63 | /// from_center_size constructs a rectangle with the given center and size. 64 | /// Both dimensions of size must be non-negative. 65 | pub fn from_center_size(center: &Point, size: &Point) -> Self { 66 | Rect { 67 | x: Interval::from_point(center.x).expanded(size.x / 2.), 68 | y: Interval::from_point(center.y).expanded(size.y / 2.), 69 | } 70 | } 71 | 72 | /// is_valid reports whether the rectangle is valid. 73 | /// This requires the width to be empty iff the height is empty. 74 | pub fn is_valid(&self) -> bool { 75 | self.x.is_empty() == self.y.is_empty() 76 | } 77 | 78 | /// is_empty reports whether the rectangle is empty. 79 | pub fn is_empty(&self) -> bool { 80 | self.x.is_empty() 81 | } 82 | 83 | /// vertices returns all four vertices of the rectangle. Vertices are returned in 84 | /// CCW direction starting with the lower left corner. 85 | pub fn vertices(&self) -> [Point; 4] { 86 | [ 87 | Point::new(self.x.lo, self.y.lo), 88 | Point::new(self.x.hi, self.y.lo), 89 | Point::new(self.x.hi, self.y.hi), 90 | Point::new(self.x.lo, self.y.hi), 91 | ] 92 | } 93 | 94 | /// vertex_ij returns the vertex in direction i along the X-axis (0=left, 1=right) and 95 | /// direction j along the Y-axis (0=down, 1=up). 96 | pub fn vertex_ij(&self, i: isize, j: isize) -> Point { 97 | let x = if i == 0 { self.x.lo } else { self.x.hi }; 98 | let y = if j == 0 { self.y.lo } else { self.y.hi }; 99 | Point::new(x, y) 100 | } 101 | 102 | /// lo returns the low corner of the rect. 103 | pub fn lo(&self) -> Point { 104 | Point::new(self.x.lo, self.y.lo) 105 | } 106 | 107 | /// hi returns the high corner of the rect. 108 | pub fn hi(&self) -> Point { 109 | Point::new(self.x.hi, self.y.hi) 110 | } 111 | 112 | /// center returns the center of the rectangle in (x,y)-space 113 | pub fn center(&self) -> Point { 114 | Point::new(self.x.center(), self.y.center()) 115 | } 116 | 117 | /// size returns the width and height of this rectangle in (x,y)-space. Empty 118 | /// rectangles have a negative width and height. 119 | pub fn size(&self) -> Point { 120 | Point::new(self.x.len(), self.y.len()) 121 | } 122 | 123 | /// contains_point reports whether the rectangle contains the given point. 124 | /// Rectangles are closed regions, i.e. they contain their boundary. 125 | pub fn contains_point(&self, p: &Point) -> bool { 126 | self.x.contains(p.x) && self.y.contains(p.y) 127 | } 128 | 129 | /// interior_contains_point returns true iff the given point is contained in the interior 130 | /// of the region (i.e. the region excluding its boundary). 131 | pub fn interior_contains_point(&self, p: &Point) -> bool { 132 | self.x.interior_contains(p.x) && self.y.interior_contains(p.y) 133 | } 134 | 135 | /// contains reports whether the rectangle contains the given rectangle. 136 | pub fn contains(&self, r: &Self) -> bool { 137 | self.x.contains_interval(&r.x) && self.y.contains_interval(&r.y) 138 | } 139 | 140 | /// interior_contains reports whether the interior of this rectangle contains all of the 141 | /// points of the given other rectangle (including its boundary). 142 | pub fn interior_contains(&self, r: &Self) -> bool { 143 | self.x.interior_contains_interval(&r.x) && self.y.interior_contains_interval(&r.y) 144 | } 145 | 146 | /// intersects reports whether this rectangle and the other rectangle have any points in common. 147 | pub fn intersects(&self, r: &Self) -> bool { 148 | self.x.intersects(&r.x) && self.y.intersects(&r.y) 149 | } 150 | 151 | /// interior_intersects reports whether the interior of this rectangle intersects 152 | /// any point (including the boundary) of the given other rectangle. 153 | pub fn interior_intersects(&self, r: &Self) -> bool { 154 | self.x.interior_intersects(&r.x) && self.y.interior_intersects(&r.y) 155 | } 156 | 157 | /// clamp_point returns the closest point in the rectangle to the given point. 158 | /// The rectangle must be non-empty. 159 | pub fn clamp_point(&self, p: &Point) -> Point { 160 | Point::new(self.x.clamp_point(p.x), self.y.clamp_point(p.y)) 161 | } 162 | 163 | /// expanded returns a rectangle that has been expanded in the x-direction 164 | /// by margin.X, and in y-direction by margin.Y. If either margin is empty, 165 | /// then shrink the interval on the corresponding sides instead. The resulting 166 | /// rectangle may be empty. Any expansion of an empty rectangle remains empty. 167 | pub fn expanded(&self, margin: &Point) -> Self { 168 | let x = self.x.expanded(margin.x); 169 | let y = self.y.expanded(margin.y); 170 | if x.is_empty() || y.is_empty() { 171 | EMPTY 172 | } else { 173 | Rect { x, y } 174 | } 175 | } 176 | 177 | /// expanded_by_margin returns a Rect that has been expanded by the amount on all sides. 178 | pub fn expanded_by_margin(&self, margin: f64) -> Self { 179 | self.expanded(&Point::new(margin, margin)) 180 | } 181 | 182 | /// union returns the smallest rectangle containing the union of this rectangle and 183 | /// the given rectangle. 184 | pub fn union(&self, other: &Self) -> Self { 185 | Rect { 186 | x: self.x.union(&other.x), 187 | y: self.y.union(&other.y), 188 | } 189 | } 190 | 191 | /// intersection returns the smallest rectangle containing the intersection of this 192 | /// rectangle and the given rectangle. 193 | pub fn intersection(&self, other: &Self) -> Self { 194 | let x = self.x.intersection(&other.x); 195 | let y = self.y.intersection(&other.y); 196 | if x.is_empty() || y.is_empty() { 197 | EMPTY 198 | } else { 199 | Rect { x, y } 200 | } 201 | } 202 | 203 | /// Returns true if the x- and y-intervals of the two rectangles are 204 | /// the same up to some tolerance. 205 | pub fn approx_eq(&self, other: &Self) -> bool { 206 | self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y) 207 | } 208 | 209 | // Return true if the x- and y-intervals of the two rectangles are the same 210 | // up to the given tolerance. 211 | pub fn approx_eq_by(&self, other: &Self, max_error: f64) -> bool { 212 | self.x.approx_eq_by(&other.x, max_error) && self.y.approx_eq_by(&other.y, max_error) 213 | } 214 | } 215 | 216 | impl<'b> std::ops::Add<&'b Point> for Rect { 217 | type Output = Self; 218 | /// expands the rectangle to include the given point. The rectangle is 219 | /// expanded by the minimum amount possible. 220 | fn add(self, p: &'b Point) -> Self::Output { 221 | Self::Output { 222 | x: self.x + p.x, 223 | y: self.y + p.y, 224 | } 225 | } 226 | } 227 | 228 | impl<'b> std::ops::Add<&'b Rect> for Rect { 229 | type Output = Self; 230 | /// expands the rectangle to include the given rectangle. This is the 231 | /// same as replacing the rectangle by the union of the two rectangles, but 232 | /// is more efficient. 233 | fn add(self, p: &'b Rect) -> Self::Output { 234 | Self::Output { 235 | x: self.x.union(&p.x), 236 | y: self.y.union(&p.y), 237 | } 238 | } 239 | } 240 | 241 | #[cfg(test)] 242 | mod tests { 243 | use super::*; 244 | 245 | const SW: Point = Point { x: 0., y: 0.25 }; 246 | const SE: Point = Point { x: 0.5, y: 0.25 }; 247 | const NE: Point = Point { x: 0.5, y: 0.75 }; 248 | const NW: Point = Point { x: 0., y: 0.75 }; 249 | 250 | const RECT: Rect = Rect { 251 | x: Interval { lo: 0., hi: 0.5 }, 252 | y: Interval { lo: 0.25, hi: 0.75 }, 253 | }; 254 | 255 | const RECT_MID: Rect = Rect { 256 | x: Interval { lo: 0.25, hi: 0.25 }, 257 | y: Interval { lo: 0.5, hi: 0.5 }, 258 | }; 259 | 260 | const RECT_SW: Rect = Rect { 261 | x: Interval { lo: SW.x, hi: SW.x }, 262 | y: Interval { lo: SW.y, hi: SW.y }, 263 | }; 264 | 265 | const RECT_NE: Rect = Rect { 266 | x: Interval { lo: NE.x, hi: NE.x }, 267 | y: Interval { lo: NE.y, hi: NE.y }, 268 | }; 269 | 270 | use crate::r2::point::Point; 271 | 272 | #[test] 273 | fn empty_rect() { 274 | assert!(EMPTY.is_valid()); 275 | assert!(EMPTY.is_empty()); 276 | } 277 | 278 | #[test] 279 | fn test_from_various_types() { 280 | let d1 = Rect::from_points(&[Point::new(0.1, 0.), Point::new(0.25, 0.1)]); 281 | 282 | assert!( 283 | Rect::from_center_size(&Point::new(0.3, 0.5), &Point::new(0.2, 0.4)).approx_eq( 284 | &Rect::from_points(&[Point::new(0.2, 0.3), Point::new(0.4, 0.7)]) 285 | ) 286 | ); 287 | 288 | assert!( 289 | Rect::from_center_size(&Point::new(1., 0.1), &Point::new(0., 2.)).approx_eq( 290 | &Rect::from_points(&[Point::new(1., -0.9), Point::new(1., 1.1)]) 291 | ) 292 | ); 293 | 294 | assert!(d1.approx_eq(&Rect { x: d1.x, y: d1.y })); 295 | 296 | assert!( 297 | Rect::from_points(&[Point::new(0.15, 0.3), Point::new(0.35, 0.9)]).approx_eq( 298 | &Rect::from_points(&[Point::new(0.15, 0.9), Point::new(0.35, 0.3)]) 299 | ) 300 | ); 301 | 302 | assert!( 303 | Rect::from_points(&[Point::new(0.12, 0.), Point::new(0.83, 0.5)]).approx_eq( 304 | &Rect::from_points(&[Point::new(0.83, 0.), Point::new(0.12, 0.5)]) 305 | ) 306 | ); 307 | } 308 | 309 | #[test] 310 | fn test_center() { 311 | assert!(EMPTY.center().approx_eq(&Point::new(0.5, 0.5))); 312 | assert!(RECT.center().approx_eq(&Point::new(0.25, 0.5))); 313 | } 314 | 315 | #[test] 316 | fn test_vertices() { 317 | let want = &[SW, SE, NE, NW]; 318 | assert_eq!(&RECT.vertices(), want); 319 | } 320 | 321 | #[test] 322 | fn test_contains_point() { 323 | assert_eq!(true, RECT.contains_point(&Point::new(0.2, 0.4))); 324 | assert_eq!(false, RECT.contains_point(&Point::new(0.2, 0.8))); 325 | assert_eq!(false, RECT.contains_point(&Point::new(-0.1, 0.4))); 326 | assert_eq!(false, RECT.contains_point(&Point::new(0.6, 0.1))); 327 | assert_eq!(true, RECT.contains_point(&Point::new(RECT.x.lo, RECT.y.lo))); 328 | assert_eq!(true, RECT.contains_point(&Point::new(RECT.x.hi, RECT.y.hi))); 329 | } 330 | 331 | #[test] 332 | fn test_interior_contains_point() { 333 | // Check corners are not contained. 334 | assert_eq!(false, RECT.interior_contains_point(&SW)); 335 | assert_eq!(false, RECT.interior_contains_point(&NE)); 336 | // Check a point on the border is not contained. 337 | assert_eq!(false, RECT.interior_contains_point(&Point::new(0., 0.5))); 338 | assert_eq!(false, RECT.interior_contains_point(&Point::new(0.25, 0.25))); 339 | assert_eq!(false, RECT.interior_contains_point(&Point::new(0.5, 0.5))); 340 | // Check points inside are contained. 341 | assert_eq!(true, RECT.interior_contains_point(&Point::new(0.125, 0.6))); 342 | } 343 | 344 | fn test_interval_cases( 345 | r1: &Rect, 346 | r2: &Rect, 347 | contains: bool, 348 | int_contains: bool, 349 | intersects: bool, 350 | int_intersects: bool, 351 | want_union: &Rect, 352 | want_intersection: &Rect, 353 | ) { 354 | assert_eq!(contains, r1.contains(r2)); 355 | assert_eq!(int_contains, r1.interior_contains(r2)); 356 | assert_eq!(intersects, r1.intersects(r2)); 357 | assert_eq!(int_intersects, r1.interior_intersects(r2)); 358 | 359 | assert!(r1.union(r2).approx_eq(&want_union)); 360 | assert!(r1.intersection(r2).approx_eq(&want_intersection)); 361 | 362 | assert!((r1.clone() + r2).approx_eq(&want_union)); 363 | } 364 | 365 | #[test] 366 | fn test_interval_ops() { 367 | test_interval_cases(&RECT, &RECT_MID, true, true, true, true, &RECT, &RECT_MID); 368 | test_interval_cases(&RECT, &RECT_SW, true, false, true, false, &RECT, &RECT_SW); 369 | test_interval_cases(&RECT, &RECT_NE, true, false, true, false, &RECT, &RECT_NE); 370 | 371 | test_interval_cases( 372 | &RECT, 373 | &Rect::from_points(&[Point::new(0.45, 0.1), Point::new(0.75, 0.3)]), 374 | false, 375 | false, 376 | true, 377 | true, 378 | &Rect::from_points(&[Point::new(0., 0.1), Point::new(0.75, 0.75)]), 379 | &Rect::from_points(&[Point::new(0.45, 0.25), Point::new(0.5, 0.3)]), 380 | ); 381 | 382 | test_interval_cases( 383 | &RECT, 384 | &Rect::from_points(&[Point::new(0.5, 0.1), Point::new(0.7, 0.3)]), 385 | false, 386 | false, 387 | true, 388 | false, 389 | &Rect::from_points(&[Point::new(0., 0.1), Point::new(0.7, 0.75)]), 390 | &Rect::from_points(&[Point::new(0.5, 0.25), Point::new(0.5, 0.3)]), 391 | ); 392 | 393 | test_interval_cases( 394 | &RECT, 395 | &Rect::from_points(&[Point::new(0.45, 0.1), Point::new(0.7, 0.25)]), 396 | false, 397 | false, 398 | true, 399 | false, 400 | &Rect::from_points(&[Point::new(0., 0.1), Point::new(0.7, 0.75)]), 401 | &Rect::from_points(&[Point::new(0.45, 0.25), Point::new(0.5, 0.25)]), 402 | ); 403 | 404 | test_interval_cases( 405 | &Rect::from_points(&[Point::new(0.1, 0.2), Point::new(0.1, 0.3)]), 406 | &Rect::from_points(&[Point::new(0.15, 0.7), Point::new(0.2, 0.8)]), 407 | false, 408 | false, 409 | false, 410 | false, 411 | &Rect::from_points(&[Point::new(0.1, 0.2), Point::new(0.2, 0.8)]), 412 | &EMPTY, 413 | ); 414 | 415 | // Check that the intersection of two rectangles that overlap in x but not y 416 | // is valid, and vice versa. 417 | test_interval_cases( 418 | &Rect::from_points(&[Point::new(0.1, 0.2), Point::new(0.4, 0.5)]), 419 | &Rect::from_points(&[Point::new(0., 0.), Point::new(0.2, 0.1)]), 420 | false, 421 | false, 422 | false, 423 | false, 424 | &Rect::from_points(&[Point::new(0., 0.), Point::new(0.4, 0.5)]), 425 | &EMPTY, 426 | ); 427 | 428 | test_interval_cases( 429 | &Rect::from_points(&[Point::new(0., 0.), Point::new(0.1, 0.3)]), 430 | &Rect::from_points(&[Point::new(0.2, 0.1), Point::new(0.3, 0.4)]), 431 | false, 432 | false, 433 | false, 434 | false, 435 | &Rect::from_points(&[Point::new(0., 0.), Point::new(0.3, 0.4)]), 436 | &EMPTY, 437 | ); 438 | } 439 | 440 | #[test] 441 | fn test_add_point() { 442 | let r1 = RECT.clone(); 443 | let mut r2 = EMPTY.clone(); 444 | 445 | r2 = r2 + &SW; 446 | r2 = r2 + &SE; 447 | r2 = r2 + &NW; 448 | r2 = r2 + &Point::new(0.1, 0.4); 449 | 450 | assert!(r1.approx_eq(&r2)); 451 | } 452 | 453 | fn test_clamp_cases(r: &Rect, p: &Point, want: &Point) { 454 | assert_eq!(want, &r.clamp_point(p)); 455 | } 456 | 457 | #[test] 458 | fn test_clamp_point() { 459 | let r = Rect { 460 | x: Interval::new(0., 0.5), 461 | y: Interval::new(0.25, 0.75), 462 | }; 463 | 464 | test_clamp_cases(&r, &Point::new(-0.01, 0.24), &Point::new(0., 0.25)); 465 | test_clamp_cases(&r, &Point::new(-5.0, 0.48), &Point::new(0., 0.48)); 466 | test_clamp_cases(&r, &Point::new(-5.0, 2.48), &Point::new(0., 0.75)); 467 | test_clamp_cases(&r, &Point::new(0.17, 2.48), &Point::new(0.17, 0.75)); 468 | 469 | test_clamp_cases(&r, &Point::new(6.19, 2.48), &Point::new(0.5, 0.75)); 470 | test_clamp_cases(&r, &Point::new(6.19, 0.53), &Point::new(0.5, 0.53)); 471 | test_clamp_cases(&r, &Point::new(6.19, -2.53), &Point::new(0.5, 0.25)); 472 | test_clamp_cases(&r, &Point::new(0.33, 2.48), &Point::new(0.33, 0.75)); 473 | test_clamp_cases(&r, &Point::new(0.33, 0.37), &Point::new(0.33, 0.37)); 474 | } 475 | 476 | #[test] 477 | fn test_expanded_empty() { 478 | assert!(EMPTY.expanded(&Point::new(0.1, 0.3)).is_empty()); 479 | assert!(EMPTY.expanded(&Point::new(-0.1, -0.3)).is_empty()); 480 | assert!(EMPTY.expanded(&Point::new(-0.1, 0.3)).is_empty()); 481 | assert!(EMPTY.expanded(&Point::new(0.1, -0.2)).is_empty()); 482 | } 483 | 484 | #[test] 485 | fn test_expanded_equals() { 486 | assert!( 487 | Rect::from_points(&[Point::new(0.2, 0.4), Point::new(0.3, 0.7)]) 488 | .expanded(&Point::new(0.1, 0.3)) 489 | .approx_eq(&Rect::from_points(&[ 490 | Point::new(0.1, 0.1), 491 | Point::new(0.4, 1.0) 492 | ])) 493 | ); 494 | 495 | assert!( 496 | Rect::from_points(&[Point::new(0.2, 0.4), Point::new(0.3, 0.7)]) 497 | .expanded(&Point::new(0.1, -0.1)) 498 | .approx_eq(&Rect::from_points(&[ 499 | Point::new(0.1, 0.5), 500 | Point::new(0.4, 0.6) 501 | ])) 502 | ); 503 | 504 | assert!( 505 | Rect::from_points(&[Point::new(0.2, 0.4), Point::new(0.3, 0.7)]) 506 | .expanded(&Point::new(0.1, 0.1)) 507 | .approx_eq(&Rect::from_points(&[ 508 | Point::new(0.1, 0.3), 509 | Point::new(0.4, 0.8) 510 | ])) 511 | ); 512 | } 513 | } 514 | -------------------------------------------------------------------------------- /src/r3/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod precisevector; 2 | pub mod vector; 3 | -------------------------------------------------------------------------------- /src/r3/precisevector.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Alexander Haynes. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use crate::r3; 18 | use bigdecimal; 19 | use bigdecimal::{FromPrimitive, ToPrimitive}; 20 | use std::str::FromStr; 21 | 22 | pub fn prec_str(s: String) -> bigdecimal::BigDecimal { 23 | bigdecimal::BigDecimal::from_str(&s).unwrap() 24 | } 25 | 26 | pub fn prec_int(i: i64) -> bigdecimal::BigDecimal { 27 | bigdecimal::BigDecimal::from(i) 28 | } 29 | 30 | pub fn prec_float(f: f64) -> bigdecimal::BigDecimal { 31 | bigdecimal::BigDecimal::from_f64(f).unwrap() 32 | } 33 | 34 | /// PreciseVector represents a point in ℝ³ using high-precision values. 35 | /// Note that this is NOT a complete implementation because there are some 36 | /// operations that Vector supports that are not feasible with arbitrary precision 37 | /// math. (e.g., methods that need divison like Normalize, or methods needing a 38 | /// square root operation such as Norm) 39 | #[derive(Clone, PartialEq, Eq, Debug)] 40 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 41 | pub struct PreciseVector { 42 | x: bigdecimal::BigDecimal, 43 | y: bigdecimal::BigDecimal, 44 | z: bigdecimal::BigDecimal, 45 | } 46 | 47 | impl From for PreciseVector { 48 | fn from(v: r3::vector::Vector) -> Self { 49 | PreciseVector::new(v.x, v.y, v.z) 50 | } 51 | } 52 | 53 | impl Into for PreciseVector { 54 | fn into(self) -> r3::vector::Vector { 55 | // The accuracy flag is ignored on these conversions back to float64. 56 | let x = self.x.to_f64().unwrap(); 57 | let y = self.y.to_f64().unwrap(); 58 | let z = self.z.to_f64().unwrap(); 59 | r3::vector::Vector { x, y, z }.normalize() 60 | } 61 | } 62 | 63 | impl PreciseVector { 64 | /// Creates a high precision vector from the given floating point values. 65 | fn new(x: f64, y: f64, z: f64) -> PreciseVector { 66 | PreciseVector { 67 | x: prec_float(x), 68 | y: prec_float(y), 69 | z: prec_float(z), 70 | } 71 | } 72 | 73 | /// norm2 returns the square of the norm. 74 | pub fn norm2(&self) -> bigdecimal::BigDecimal { 75 | self.dot(self.clone()) 76 | } 77 | 78 | /// is_unit reports whether this vector is of unit length. 79 | pub fn is_unit(&self) -> bool { 80 | self.norm2() == prec_int(1_i64) 81 | } 82 | 83 | /// abs returns the vector with nonnegative components. 84 | pub fn abs(&self) -> PreciseVector { 85 | PreciseVector { 86 | x: bigdecimal::BigDecimal::abs(&self.x), 87 | y: bigdecimal::BigDecimal::abs(&self.y), 88 | z: bigdecimal::BigDecimal::abs(&self.z), 89 | } 90 | } 91 | } 92 | 93 | impl std::ops::Add for PreciseVector { 94 | type Output = PreciseVector; 95 | /// add returns the standard vector sum of v and ov. 96 | fn add(self, other: Self) -> Self { 97 | PreciseVector { 98 | x: self.x + other.x, 99 | y: self.y + other.y, 100 | z: self.z + other.z, 101 | } 102 | } 103 | } 104 | 105 | impl std::ops::Sub for PreciseVector { 106 | type Output = PreciseVector; 107 | /// sub returns the standard vector difference of v and ov. 108 | fn sub(self, other: Self) -> Self { 109 | PreciseVector { 110 | x: self.x - other.x, 111 | y: self.y - other.y, 112 | z: self.z - other.z, 113 | } 114 | } 115 | } 116 | 117 | impl std::ops::Mul for PreciseVector { 118 | type Output = PreciseVector; 119 | /// mul returns the standard scalar product of v and f. 120 | fn mul(self, other: bigdecimal::BigDecimal) -> Self { 121 | PreciseVector { 122 | x: self.x * &other, 123 | y: self.y * &other, 124 | z: self.z * &other, 125 | } 126 | } 127 | } 128 | 129 | impl std::ops::Mul for PreciseVector { 130 | type Output = PreciseVector; 131 | /// mul returns the standard scalar product of v and f. 132 | fn mul(self, other: f64) -> Self { 133 | self * prec_float(other) 134 | } 135 | } 136 | 137 | impl PreciseVector { 138 | /// dot returns the standard dot product of v and ov. 139 | pub fn dot(&self, ov: PreciseVector) -> bigdecimal::BigDecimal { 140 | let a = self.clone(); 141 | let b = ov.clone(); 142 | return (a.x * b.x) + (a.y * b.y) + (a.z * b.z); 143 | } 144 | 145 | /// cross returns the standard cross product of v and ov. 146 | pub fn cross(&self, ov: PreciseVector) -> PreciseVector { 147 | return PreciseVector { 148 | x: (&self.y * &ov.z) - (&self.z * &ov.y), 149 | y: (&self.z * &ov.x) - (&self.x * &ov.z), 150 | z: (&self.x * &ov.y) - (&self.y * &ov.x), 151 | }; 152 | } 153 | 154 | /// largest_component returns the axis that represents the largest component in this vector. 155 | pub fn largest_component(&self) -> r3::vector::Axis { 156 | let a = self.abs(); 157 | if a.x > a.y { 158 | if a.x > a.z { 159 | r3::vector::Axis::X 160 | } else { 161 | r3::vector::Axis::Z 162 | } 163 | } else { 164 | if a.y > a.z { 165 | r3::vector::Axis::Y 166 | } else { 167 | r3::vector::Axis::Z 168 | } 169 | } 170 | } 171 | 172 | /// smallest_component returns the axis that represents the smallest component in this vector. 173 | pub fn smallest_component(&self) -> r3::vector::Axis { 174 | let t = self.abs(); 175 | if t.x < t.y { 176 | if t.x < t.z { 177 | r3::vector::Axis::X 178 | } else { 179 | r3::vector::Axis::Z 180 | } 181 | } else { 182 | if t.y < t.z { 183 | r3::vector::Axis::Y 184 | } else { 185 | r3::vector::Axis::Z 186 | } 187 | } 188 | } 189 | } 190 | 191 | #[cfg(test)] 192 | mod tests { 193 | use super::*; 194 | use crate::consts::EPSILON; 195 | use r3::precisevector; 196 | use r3::vector::Axis; 197 | use r3::vector::Vector; 198 | 199 | fn pv(x: f64, y: f64, z: f64) -> PreciseVector { 200 | PreciseVector::new(x, y, z) 201 | } 202 | 203 | pub fn test_precise_round_trip_case(x: f64, y: f64, z: f64) { 204 | const EPSILON: f64 = 0.0000000000001; 205 | let pvn: Vector = PreciseVector::from(Vector { x, y, z }).into(); 206 | let nvn = Vector { x, y, z }.normalize(); 207 | assert!((pvn.x - nvn.x).abs() <= EPSILON); 208 | assert!((pvn.y - nvn.y).abs() <= EPSILON); 209 | assert!((pvn.z - nvn.z).abs() <= EPSILON); 210 | } 211 | 212 | #[test] 213 | pub fn test_precise_round_trip() { 214 | test_precise_round_trip_case(0.0, 0.0, 0.0); 215 | test_precise_round_trip_case(1.0, 2.0, 3.0); 216 | test_precise_round_trip_case(3.0, -4.0, 12.0); 217 | test_precise_round_trip_case(1.0, 1e-16, 1e-32); 218 | } 219 | 220 | #[test] 221 | pub fn test_precise_is_unit() { 222 | assert!(pv(0.0, 0.0, 0.0).is_unit() == false); 223 | assert!(pv(1.0, 0.0, 0.0).is_unit() == true); 224 | assert!(pv(0.0, 1.0, 0.0).is_unit() == true); 225 | assert!(pv(0.0, 0.0, 1.0).is_unit() == true); 226 | assert!(pv(1.0 + 2.0 * EPSILON, 0.0, 0.0).is_unit() == false); 227 | assert!(pv(0.0 * (1.0 + EPSILON), 0.0, 0.0).is_unit() == false); 228 | assert!(pv(1.0, 1.0, 1.0).is_unit() == false); 229 | } 230 | 231 | #[test] 232 | pub fn test_precise_norm2() { 233 | assert!(pv(0.0, 0.0, 0.0).norm2() == precisevector::prec_float(0.0)); 234 | assert!(pv(0.0, 1.0, 0.0).norm2() == precisevector::prec_float(1.0)); 235 | assert!(pv(1.0, 1.0, 1.0).norm2() == precisevector::prec_float(3.0)); 236 | assert!(pv(1.0, 2.0, 3.0).norm2() == precisevector::prec_float(14.0)); 237 | assert!(pv(3.0, -4.0, 12.0).norm2() == precisevector::prec_float(169.0)); 238 | } 239 | 240 | #[test] 241 | pub fn test_precise_add() { 242 | assert_eq!(pv(0.0, 0.0, 0.0) + pv(0.0, 0.0, 0.0), pv(0.0, 0.0, 0.0)); 243 | assert_eq!(pv(1.0, 0.0, 0.0) + pv(0.0, 0.0, 0.0), pv(1.0, 0.0, 0.0)); 244 | assert_eq!(pv(1.0, 2.0, 3.0) + pv(4.0, 5.0, 7.0), pv(5.0, 7.0, 10.0)); 245 | assert_eq!( 246 | pv(1.0, -3.0, 5.0) + pv(1.0, -6.0, -6.0), 247 | pv(2.0, -9.0, -1.0) 248 | ); 249 | } 250 | 251 | #[test] 252 | pub fn test_precise_sub() { 253 | assert_eq!(pv(0.0, 0.0, 0.0) - pv(0.0, 0.0, 0.0), pv(0.0, 0.0, 0.0)); 254 | assert_eq!(pv(1.0, 0.0, 0.0) - pv(0.0, 0.0, 0.0), pv(1.0, 0.0, 0.0)); 255 | assert_eq!(pv(1.0, 2.0, 3.0) - pv(4.0, 5.0, 7.0), pv(-3.0, -3.0, -4.0)); 256 | assert_eq!(pv(1.0, -3.0, 5.0) - pv(1.0, -6.0, -6.0), pv(0.0, 3.0, 11.0)); 257 | } 258 | 259 | #[test] 260 | pub fn test_precise_mul() { 261 | assert_eq!( 262 | pv(0.0, 0.0, 0.0) * precisevector::prec_float(3.0), 263 | pv(0.0, 0.0, 0.0) 264 | ); 265 | assert_eq!( 266 | pv(1.0, 0.0, 0.0) * precisevector::prec_float(1.0), 267 | pv(1.0, 0.0, 0.0) 268 | ); 269 | assert_eq!( 270 | pv(1.0, 0.0, 0.0) * precisevector::prec_float(0.0), 271 | pv(0.0, 0.0, 0.0) 272 | ); 273 | assert_eq!( 274 | pv(1.0, 0.0, 0.0) * precisevector::prec_float(3.0), 275 | pv(3.0, 0.0, 0.0) 276 | ); 277 | assert_eq!( 278 | pv(1.0, -3.0, 5.0) * precisevector::prec_float(-1.0), 279 | pv(-1.0, 3.0, -5.0) 280 | ); 281 | assert_eq!( 282 | pv(1.0, -3.0, 5.0) * precisevector::prec_float(2.0), 283 | pv(2.0, -6.0, 10.0) 284 | ); 285 | } 286 | 287 | #[test] 288 | pub fn test_precise_mul_by_f64() { 289 | assert_eq!(pv(0.0, 0.0, 0.0) * 3.0, pv(0.0, 0.0, 0.0)); 290 | assert_eq!(pv(1.0, 0.0, 0.0) * 1.0, pv(1.0, 0.0, 0.0)); 291 | assert_eq!(pv(1.0, 0.0, 0.0) * 0.0, pv(0.0, 0.0, 0.0)); 292 | assert_eq!(pv(1.0, 0.0, 0.0) * 3.0, pv(3.0, 0.0, 0.0)); 293 | assert_eq!(pv(1.0, -3.0, 5.0) * -1.0, pv(-1.0, 3.0, -5.0)); 294 | assert_eq!(pv(1.0, -3.0, 5.0) * 2.0, pv(2.0, -6.0, 10.0)); 295 | } 296 | 297 | #[test] 298 | pub fn test_precise_dot() { 299 | assert_eq!( 300 | pv(1.0, 0.0, 0.0).dot(pv(1.0, 0.0, 0.0)), 301 | precisevector::prec_float(1.0) 302 | ); 303 | assert_eq!( 304 | pv(0.0, 1.0, 0.0).dot(pv(0.0, 1.0, 0.0)), 305 | precisevector::prec_float(1.0) 306 | ); 307 | assert_eq!( 308 | pv(0.0, 0.0, 1.0).dot(pv(0.0, 0.0, 1.0)), 309 | precisevector::prec_float(1.0) 310 | ); 311 | assert_eq!( 312 | pv(1.0, 0.0, 0.0).dot(pv(0.0, 1.0, 0.0)), 313 | precisevector::prec_float(0.0) 314 | ); 315 | assert_eq!( 316 | pv(1.0, 0.0, 0.0).dot(pv(0.0, 1.0, 1.0)), 317 | precisevector::prec_float(0.0) 318 | ); 319 | assert_eq!( 320 | pv(1.0, 1.0, 1.0).dot(pv(-1.0, -1.0, -1.0)), 321 | precisevector::prec_float(-3.0) 322 | ); 323 | } 324 | 325 | #[test] 326 | pub fn test_precise_cross() { 327 | assert_eq!( 328 | pv(1.0, 0.0, 0.0).cross(pv(1.0, 0.0, 0.0)), 329 | pv(0.0, 0.0, 0.0) 330 | ); 331 | assert_eq!( 332 | pv(1.0, 0.0, 0.0).cross(pv(0.0, 1.0, 0.0)), 333 | pv(0.0, 0.0, 1.0) 334 | ); 335 | assert_eq!( 336 | pv(0.0, 1.0, 0.0).cross(pv(0.0, 0.0, 1.0)), 337 | pv(1.0, 0.0, 0.0) 338 | ); 339 | assert_eq!( 340 | pv(0.0, 0.0, 1.0).cross(pv(1.0, 0.0, 0.0)), 341 | pv(0.0, 1.0, 0.0) 342 | ); 343 | assert_eq!( 344 | pv(0.0, 1.0, 0.0).cross(pv(1.0, 0.0, 0.0)), 345 | pv(0.0, 0.0, -1.0) 346 | ); 347 | assert_eq!( 348 | pv(1.0, 2.0, 3.0).cross(pv(-4.0, 5.0, -6.0)), 349 | pv(-27.0, -6.0, 13.0) 350 | ); 351 | } 352 | 353 | fn test_precise_identities_case(v1: PreciseVector, v2: PreciseVector) { 354 | let c1 = v1.cross(v2.clone()); 355 | let c2 = v2.cross(v1.clone()); 356 | let d1 = v1.dot(v2.clone()); 357 | let d2 = v2.dot(v1.clone()); 358 | assert_eq!(d1, d2); 359 | assert_eq!(c1, c2 * -1.0); 360 | assert_eq!(v1.dot(c1.clone()), precisevector::prec_float(0.0)); 361 | assert_eq!(v2.dot(c1.clone()), precisevector::prec_float(0.0)); 362 | } 363 | 364 | #[test] 365 | pub fn test_precise_identities() { 366 | let v1 = pv(0.0, 0.0, 0.0); 367 | let v2 = pv(0.0, 0.0, 0.0); 368 | test_precise_identities_case(v1, v2); 369 | 370 | let v1 = pv(0.0, 0.0, 0.0); 371 | let v2 = pv(0.0, 1.0, 2.0); 372 | test_precise_identities_case(v1, v2); 373 | 374 | let v1 = pv(1.0, 0.0, 0.0); 375 | let v2 = pv(0.0, 1.0, 0.0); 376 | test_precise_identities_case(v1, v2); 377 | 378 | let v1 = pv(1.0, 0.0, 0.0); 379 | let v2 = pv(0.0, 1.0, 1.0); 380 | test_precise_identities_case(v1, v2); 381 | 382 | let v1 = pv(1.0, 1.0, 1.0); 383 | let v2 = pv(-1.0, -1.0, -1.0); 384 | test_precise_identities_case(v1, v2); 385 | 386 | let v1 = pv(1.0, 2.0, 2.0); 387 | let v2 = pv(-0.3, 0.4, -1.2); 388 | test_precise_identities_case(v1, v2); 389 | } 390 | 391 | #[test] 392 | pub fn test_precise_largest_smallest_components() { 393 | let v1 = pv(0.0, 0.0, 0.0); 394 | assert_eq!(v1.largest_component(), Axis::Z); 395 | assert_eq!(v1.smallest_component(), Axis::Z); 396 | let v1 = pv(1.0, 0.0, 0.0); 397 | assert_eq!(v1.largest_component(), Axis::X); 398 | assert_eq!(v1.smallest_component(), Axis::Z); 399 | let v1 = pv(1.0, -1.0, 0.0); 400 | assert_eq!(v1.largest_component(), Axis::Y); 401 | assert_eq!(v1.smallest_component(), Axis::Z); 402 | let v1 = pv(-1.0, -1.1, -1.1); 403 | assert_eq!(v1.largest_component(), Axis::Z); 404 | assert_eq!(v1.smallest_component(), Axis::X); 405 | let v1 = pv(0.5, -0.4, -0.5); 406 | assert_eq!(v1.largest_component(), Axis::Z); 407 | assert_eq!(v1.smallest_component(), Axis::Y); 408 | let v1 = pv(1e-15, 1e-14, 1e-13); 409 | assert_eq!(v1.largest_component(), Axis::Z); 410 | assert_eq!(v1.smallest_component(), Axis::X); 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/r3/vector.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use std; 19 | 20 | use crate::consts::EPSILON; 21 | use crate::s1::angle::*; 22 | 23 | /// Vector represents a point in ℝ³. 24 | #[derive(Clone, Copy, Default, PartialEq)] 25 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 26 | pub struct Vector { 27 | pub x: f64, 28 | pub y: f64, 29 | pub z: f64, 30 | } 31 | 32 | impl std::fmt::Debug for Vector { 33 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 34 | write!(f, "({:0.24}, {:0.24}, {:0.24})", self.x, self.y, self.z) 35 | } 36 | } 37 | 38 | impl std::ops::Add for Vector { 39 | type Output = Vector; 40 | fn add(self, other: Vector) -> Self::Output { 41 | &self + &other 42 | } 43 | } 44 | impl<'a, 'b> std::ops::Add<&'b Vector> for &'a Vector { 45 | type Output = Vector; 46 | /// add returns the standard vector sum of v and ov. 47 | fn add(self, other: &'b Vector) -> Self::Output { 48 | Vector { 49 | x: self.x + other.x, 50 | y: self.y + other.y, 51 | z: self.z + other.z, 52 | } 53 | } 54 | } 55 | 56 | impl std::ops::Sub for Vector { 57 | type Output = Vector; 58 | /// sub returns the standard vector difference of v and ov. 59 | fn sub(self, other: Vector) -> Self::Output { 60 | &self - &other 61 | } 62 | } 63 | impl<'a, 'b> std::ops::Sub<&'b Vector> for &'a Vector { 64 | type Output = Vector; 65 | fn sub(self, other: &'b Vector) -> Self::Output { 66 | Vector { 67 | x: self.x - other.x, 68 | y: self.y - other.y, 69 | z: self.z - other.z, 70 | } 71 | } 72 | } 73 | 74 | impl std::ops::Mul for Vector { 75 | type Output = Vector; 76 | fn mul(self, other: Vector) -> Self::Output { 77 | &self * &other 78 | } 79 | } 80 | impl<'a, 'b> std::ops::Mul<&'a Vector> for &'b Vector { 81 | type Output = Vector; 82 | fn mul(self, other: &'a Vector) -> Self::Output { 83 | Vector { 84 | x: self.x * other.x, 85 | y: self.y * other.y, 86 | z: self.z * other.z, 87 | } 88 | } 89 | } 90 | 91 | impl<'a> std::ops::Mul for &'a Vector { 92 | type Output = Vector; 93 | /// mul returns the standard scalar product of v and m. 94 | fn mul(self, m: f64) -> Self::Output { 95 | Vector { 96 | x: self.x * m, 97 | y: self.y * m, 98 | z: self.z * m, 99 | } 100 | } 101 | } 102 | impl std::ops::Mul for Vector { 103 | type Output = Vector; 104 | fn mul(self, m: f64) -> Self::Output { 105 | &self * m 106 | } 107 | } 108 | 109 | use std::cmp::*; 110 | 111 | impl Eq for Vector {} 112 | 113 | impl PartialOrd for Vector { 114 | fn partial_cmp(&self, ov: &Vector) -> Option { 115 | Some(self.cmp(ov)) 116 | } 117 | } 118 | 119 | impl Ord for Vector { 120 | fn cmp(&self, ov: &Vector) -> Ordering { 121 | if self.x < ov.x { 122 | return Ordering::Less; 123 | } 124 | if self.x > ov.x { 125 | return Ordering::Greater; 126 | } 127 | 128 | // First elements were the same, try the next. 129 | if self.y < ov.y { 130 | return Ordering::Less; 131 | } 132 | if self.y > ov.y { 133 | return Ordering::Greater; 134 | } 135 | 136 | // Second elements were the same return the final compare. 137 | if self.z < ov.z { 138 | return Ordering::Less; 139 | } 140 | if self.z > ov.z { 141 | return Ordering::Greater; 142 | } 143 | 144 | // Both are equal 145 | Ordering::Equal 146 | } 147 | } 148 | 149 | impl Vector { 150 | pub fn new(x: f64, y: f64, z: f64) -> Self { 151 | Vector { x, y, z } 152 | } 153 | 154 | /// approx_eq reports whether v and ov are equal within a small epsilon. 155 | pub fn approx_eq(&self, other: &Vector) -> bool { 156 | (self.x - other.x).abs() < EPSILON 157 | && (self.y - other.y).abs() < EPSILON 158 | && (self.z - other.z).abs() < EPSILON 159 | } 160 | 161 | /// norm returns the vector's norm. 162 | pub fn norm(&self) -> f64 { 163 | self.norm2().sqrt() 164 | } 165 | 166 | /// norm2 returns the square of the norm. 167 | pub fn norm2(&self) -> f64 { 168 | self.dot(self) 169 | } 170 | 171 | /// normalize returns a unit vector in the same direction as v. 172 | pub fn normalize(&self) -> Self { 173 | if self.x == 0. && self.y == 0. && self.z == 0. { 174 | *self 175 | } else { 176 | self * (1.0 / self.norm()) 177 | } 178 | } 179 | 180 | /// dot returns the standard dot product of v and ov. 181 | pub fn dot(&self, other: &Self) -> f64 { 182 | self.x * other.x + self.y * other.y + self.z * other.z 183 | } 184 | 185 | /// is_unit returns whether this vector is of approximately unit length. 186 | pub fn is_unit(&self) -> bool { 187 | const EPSILON2: f64 = 5e-14; 188 | (self.norm2() - 1.).abs() < EPSILON2 189 | } 190 | 191 | /// abs returns the vector with nonnegative components. 192 | pub fn abs(&self) -> Self { 193 | Vector { 194 | x: self.x.abs(), 195 | y: self.y.abs(), 196 | z: self.z.abs(), 197 | } 198 | } 199 | 200 | /// cross returns the standard cross product of v and ov. 201 | pub fn cross(&self, other: &Self) -> Self { 202 | Vector { 203 | x: self.y * other.z - self.z * other.y, 204 | y: self.z * other.x - self.x * other.z, 205 | z: self.x * other.y - self.y * other.x, 206 | } 207 | } 208 | 209 | /// distance returns the Euclidean distance between v and ov. 210 | pub fn distance(&self, other: &Self) -> f64 { 211 | (self - other).norm() 212 | } 213 | 214 | /// angle returns the angle between v and ov. 215 | pub fn angle(&self, other: &Self) -> Angle { 216 | Angle::from(Rad(self.cross(other).norm().atan2(self.dot(other)))) 217 | } 218 | 219 | /// ortho returns a unit vector that is orthogonal to v. 220 | /// ortho(-v) = -ortho(v) for all v. 221 | pub fn ortho(&self) -> Self { 222 | let mut ov = Self { 223 | x: 0.012, 224 | y: 0.0053, 225 | z: 0.00457, 226 | }; 227 | match self.largest_component() { 228 | Axis::X => ov.z = 1., 229 | Axis::Y => ov.x = 1., 230 | Axis::Z => ov.y = 1., 231 | }; 232 | self.cross(&ov).normalize() 233 | } 234 | 235 | /// largest_component returns the axis that represents the largest component in this vector. 236 | pub fn largest_component(&self) -> Axis { 237 | let a = self.abs(); 238 | if a.x > a.y { 239 | if a.x > a.z { 240 | Axis::X 241 | } else { 242 | Axis::Z 243 | } 244 | } else { 245 | if a.y > a.z { 246 | Axis::Y 247 | } else { 248 | Axis::Z 249 | } 250 | } 251 | } 252 | 253 | /// smallest_component returns the axis that represents the smallest component in this vector. 254 | pub fn smallest_component(&self) -> Axis { 255 | let t = self.abs(); 256 | if t.x < t.y { 257 | if t.x < t.z { 258 | Axis::X 259 | } else { 260 | Axis::Z 261 | } 262 | } else { 263 | if t.y < t.z { 264 | Axis::Y 265 | } else { 266 | Axis::Z 267 | } 268 | } 269 | } 270 | } 271 | 272 | /// Axis enumerates the 3 axes of ℝ³. 273 | #[derive(PartialEq, Eq, Debug)] 274 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 275 | pub enum Axis { 276 | X, 277 | Y, 278 | Z, 279 | } 280 | 281 | #[cfg(test)] 282 | mod tests { 283 | use super::*; 284 | use std::cmp::Ordering; 285 | use std::f64::consts::PI; 286 | 287 | macro_rules! V { 288 | ($x:expr, $y:expr, $z:expr) => { 289 | Vector { 290 | x: $x, 291 | y: $y, 292 | z: $z, 293 | } 294 | }; 295 | } 296 | 297 | #[test] 298 | fn test_vector_default() { 299 | assert_eq!(Vector::default(), V!(0., 0., 0.)); 300 | } 301 | 302 | #[test] 303 | fn test_vector_norm() { 304 | assert_eq!(V!(0., 0., 0.).norm(), 0.); 305 | assert_eq!(V!(0., 1., 0.).norm(), 1.); 306 | assert_eq!(V!(3., -4., 12.).norm(), 13.); 307 | assert_eq!(V!(1., 1e-16, 1e-32).norm(), 1.); 308 | } 309 | 310 | #[test] 311 | fn test_vector_norm2() { 312 | assert_eq!(V!(0., 0., 0.).norm2(), 0.); 313 | assert_eq!(V!(0., 1., 0.).norm2(), 1.); 314 | assert_eq!(V!(1., 1., 1.).norm2(), 3.); 315 | assert_eq!(V!(1., 2., 3.).norm2(), 14.); 316 | assert_eq!(V!(3., -4., 12.).norm2(), 169.); 317 | assert_eq!(V!(1., 1e-16, 1e-32).norm2(), 1.); 318 | } 319 | 320 | fn test_vec_norm(v: Vector) { 321 | let n = v.normalize(); 322 | assert_f64_eq!(v.x * n.y, v.y * n.x); 323 | assert_f64_eq!(v.x * n.z, v.z * n.x); 324 | 325 | assert_f64_eq!(n.norm(), 1.); 326 | } 327 | 328 | #[test] 329 | fn test_vector_normalize() { 330 | test_vec_norm(V!(1., 0., 0.)); 331 | test_vec_norm(V!(0., 1., 0.)); 332 | test_vec_norm(V!(0., 0., 1.)); 333 | test_vec_norm(V!(1., 1., 1.)); 334 | test_vec_norm(V!(1., 1e-16, 1e-32)); 335 | test_vec_norm(V!(12.34, 56.78, 91.01)); 336 | } 337 | 338 | #[test] 339 | fn test_vector_is_unit() { 340 | assert_eq!(false, V!(0., 0., 0.).is_unit()); 341 | assert_eq!(true, V!(0., 1., 0.).is_unit()); 342 | assert_eq!(true, V!(1. + 2. * EPSILON, 0., 0.).is_unit()); 343 | assert_eq!(true, V!(1. * (1. + EPSILON), 0., 0.).is_unit()); 344 | assert_eq!(false, V!(1., 1., 1.).is_unit()); 345 | assert_eq!(true, V!(1., 1e-16, 1e-32).is_unit()); 346 | } 347 | 348 | fn test_vector_dot_case(expected: f64, v1: Vector, v2: Vector) { 349 | assert_f64_eq!(expected, v1.dot(&v2)); 350 | assert_f64_eq!(expected, v2.dot(&v1)); 351 | } 352 | 353 | #[test] 354 | fn test_vector_dot() { 355 | test_vector_dot_case(1., V!(1., 0., 0.), V!(1., 0., 0.)); 356 | test_vector_dot_case(0., V!(1., 0., 0.), V!(0., 1., 0.)); 357 | test_vector_dot_case(0., V!(1., 0., 0.), V!(0., 1., 1.)); 358 | test_vector_dot_case(-3., V!(1., 1., 1.), V!(-1., -1., -1.)); 359 | test_vector_dot_case(-1.9, V!(1., 2., 2.), V!(-0.3, 0.4, -1.2)); 360 | } 361 | 362 | #[test] 363 | fn test_vector_cross() { 364 | assert!(V!(1., 0., 0.) 365 | .cross(&V!(1., 0., 0.)) 366 | .approx_eq(&V!(0., 0., 0.))); 367 | assert!(V!(1., 0., 0.) 368 | .cross(&V!(0., 1., 0.)) 369 | .approx_eq(&V!(0., 0., 1.))); 370 | assert!(V!(0., 1., 0.) 371 | .cross(&V!(1., 0., 0.)) 372 | .approx_eq(&V!(0., 0., -1.))); 373 | assert!(V!(1., 2., 3.) 374 | .cross(&V!(-4., 5., -6.)) 375 | .approx_eq(&V!(-27., -6., 13.))); 376 | } 377 | 378 | #[test] 379 | fn test_vector_add() { 380 | assert!((V!(0., 0., 0.) + V!(0., 0., 0.)).approx_eq(&V!(0., 0., 0.))); 381 | assert!((V!(1., 0., 0.) + V!(0., 0., 0.)).approx_eq(&V!(1., 0., 0.))); 382 | assert!((V!(1., 2., 3.) + V!(4., 5., 7.)).approx_eq(&V!(5., 7., 10.))); 383 | assert!((V!(1., -3., 5.) + V!(1., -6., -6.)).approx_eq(&V!(2., -9., -1.))); 384 | } 385 | 386 | #[test] 387 | fn test_vector_sub() { 388 | assert!((V!(0., 0., 0.) - V!(0., 0., 0.)).approx_eq(&V!(0., 0., 0.))); 389 | assert!((V!(1., 0., 0.) - V!(0., 0., 0.)).approx_eq(&V!(1., 0., 0.))); 390 | assert!((V!(1., 2., 3.) - V!(4., 5., 7.)).approx_eq(&V!(-3., -3., -4.))); 391 | assert!((V!(1., -3., 5.) - V!(1., -6., -6.)).approx_eq(&V!(0., 3., 11.))); 392 | } 393 | 394 | #[test] 395 | fn test_vector_distance() { 396 | assert_f64_eq!(V!(1., 0., 0.).distance(&V!(1., 0., 0.)), 0.); 397 | assert_f64_eq!(V!(1., 0., 0.).distance(&V!(0., 1., 0.)), 1.41421356237310); 398 | assert_f64_eq!(V!(1., 0., 0.).distance(&V!(0., 1., 1.)), 1.73205080756888); 399 | assert_f64_eq!( 400 | V!(1., 1., 1.).distance(&V!(-1., -1., -1.)), 401 | 3.46410161513775 402 | ); 403 | assert_f64_eq!( 404 | V!(1., 2., 2.).distance(&V!(-0.3, 0.4, -1.2)), 405 | 3.80657326213486 406 | ); 407 | } 408 | 409 | #[test] 410 | fn test_vector_mul() { 411 | assert!((V!(0., 0., 0.) * 3.).approx_eq(&V!(0., 0., 0.))); 412 | assert!((V!(1., 0., 0.) * 1.).approx_eq(&V!(1., 0., 0.))); 413 | assert!((V!(1., 0., 0.) * 0.).approx_eq(&V!(0., 0., 0.))); 414 | assert!((V!(1., 0., 0.) * 3.).approx_eq(&V!(3., 0., 0.))); 415 | assert!((V!(1., -3., 5.) * -1.).approx_eq(&V!(-1., 3., -5.))); 416 | assert!((V!(1., -3., 5.) * 2.).approx_eq(&V!(2., -6., 10.))); 417 | } 418 | 419 | #[test] 420 | fn test_vector_angle() { 421 | assert_f64_eq!(V!(1., 0., 0.).angle(&V!(1., 0., 0.)).rad(), 0.); 422 | assert_f64_eq!(V!(1., 0., 0.).angle(&V!(0., 1., 0.)).rad(), PI / 2.); 423 | assert_f64_eq!(V!(1., 0., 0.).angle(&V!(0., 1., 1.)).rad(), PI / 2.); 424 | assert_f64_eq!(V!(1., 0., 0.).angle(&V!(-1., 0., 0.)).rad(), PI); 425 | assert_f64_eq!( 426 | V!(1., 2., 3.).angle(&V!(2., 3., -1.)).rad(), 427 | 1.2055891055045298 428 | ); 429 | } 430 | 431 | fn test_vector_ortho_case(v: Vector) { 432 | assert_f64_eq!(v.dot(&v.ortho()), 0.); 433 | assert_f64_eq!(v.ortho().norm(), 1.); 434 | } 435 | 436 | #[test] 437 | fn test_vector_ortho() { 438 | test_vector_ortho_case(V!(1., 0., 0.)); 439 | test_vector_ortho_case(V!(1., 1., 0.)); 440 | test_vector_ortho_case(V!(1., 2., 3.)); 441 | test_vector_ortho_case(V!(1., -2., -5.)); 442 | test_vector_ortho_case(V!(0.012, 0.0053, 0.00457)); 443 | test_vector_ortho_case(V!(-0.012, -1., -0.00457)); 444 | } 445 | 446 | fn test_vector_identities_case(v1: Vector, v2: Vector) { 447 | let a1 = v1.angle(&v2).rad(); 448 | let a2 = v2.angle(&v1).rad(); 449 | let c1 = v1.cross(&v2); 450 | let c2 = v2.cross(&v1); 451 | let d1 = v1.dot(&v2); 452 | let d2 = v2.dot(&v1); 453 | 454 | // angle commuts 455 | assert_f64_eq!(a1, a2); 456 | // dot commutes 457 | assert_f64_eq!(d1, d2); 458 | // cross anti-commuts 459 | assert!(c1.approx_eq(&(c2 * -1.))); 460 | 461 | // cross is orthogonal to original vectors 462 | assert_f64_eq!(v1.dot(&c1), 0.); 463 | assert_f64_eq!(v2.dot(&c1), 0.); 464 | assert_f64_eq!(v1.dot(&c2), 0.); 465 | assert_f64_eq!(v2.dot(&c2), 0.); 466 | } 467 | 468 | #[test] 469 | fn test_vector_identities() { 470 | test_vector_identities_case(V!(0., 0., 0.), V!(0., 0., 0.)); 471 | test_vector_identities_case(V!(0., 0., 0.), V!(0., 1., 2.)); 472 | test_vector_identities_case(V!(1., 0., 0.), V!(0., 1., 0.)); 473 | test_vector_identities_case(V!(1., 0., 0.), V!(0., 1., 1.)); 474 | test_vector_identities_case(V!(1., 1., 1.), V!(-1., -1., -1.)); 475 | test_vector_identities_case(V!(1., 2., 2.), V!(-0.3, 0.4, -1.2)); 476 | } 477 | 478 | fn test_ls(v: Vector, largest: Axis, smallest: Axis) { 479 | assert_eq!(v.largest_component(), largest); 480 | assert_eq!(v.smallest_component(), smallest); 481 | } 482 | 483 | #[test] 484 | fn test_vector_largest_smallest_components() { 485 | test_ls(V!(0., 0., 0.), Axis::Z, Axis::Z); 486 | test_ls(V!(1., 0., 0.), Axis::X, Axis::Z); 487 | test_ls(V!(1., -1., 0.), Axis::Y, Axis::Z); 488 | test_ls(V!(-1., -1.1, -1.1), Axis::Z, Axis::X); 489 | test_ls(V!(0.5, -0.4, -0.5), Axis::Z, Axis::Y); 490 | test_ls(V!(1e-15, 1e-14, 1e-13), Axis::Z, Axis::X); 491 | } 492 | 493 | fn test_cmp(v1: Vector, v2: Vector, expected: Ordering) { 494 | assert_eq!(v1.partial_cmp(&v2), Some(expected)); 495 | } 496 | 497 | #[test] 498 | fn test_vector_cmp() { 499 | // let's hope derived PartialCmp compares element in order 500 | test_cmp(V!(0., 0., 0.), V!(0., 0., 0.), Ordering::Equal); 501 | test_cmp(V!(0., 0., 0.), V!(1., 0., 0.), Ordering::Less); 502 | test_cmp(V!(0., 1., 0.), V!(0., 0., 0.), Ordering::Greater); 503 | 504 | test_cmp(V!(1., 2., 3.), V!(3., 2., 1.), Ordering::Less); 505 | test_cmp(V!(-1., 0., 0.), V!(0., 0., -1.), Ordering::Less); 506 | test_cmp(V!(8., 6., 4.), V!(7., 5., 3.), Ordering::Greater); 507 | test_cmp(V!(-1., -0.5, 0.), V!(0., 0., 0.1), Ordering::Less); 508 | test_cmp(V!(1., 2., 3.), V!(2., 3., 4.), Ordering::Less); 509 | test_cmp(V!(1.23, 4.56, 7.89), V!(1.23, 4.56, 7.89), Ordering::Equal); 510 | } 511 | } 512 | -------------------------------------------------------------------------------- /src/s1/angle.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use std; 19 | use std::f64::consts::PI; 20 | 21 | /// Angle represents a 1D angle. The internal representation is a double precision 22 | /// value in radians, so conversion to and from radians is exact. 23 | /// Conversions between E5, E6, E7, and Degrees are not always 24 | /// exact. For example, Deg(3.1) is different from E6(3100000) or E7(310000000). 25 | /// 26 | /// use s2::s1::angle::*; 27 | /// use std::f64::consts::PI; 28 | /// 29 | /// // The following conversions between degrees and radians are exact: 30 | /// assert_eq!(Deg(180.), Deg::from(Rad(PI))); 31 | /// for n in 0..9 { 32 | /// // for n == 0..8 33 | /// assert_eq!(Rad(PI/(n as f64)), Deg(180./(n as f64)).into()); 34 | /// } 35 | /// 36 | /// // These identities hold when the arguments are scaled up or down by any power 37 | /// // of 2. Some similar identities are also true, for example, 38 | /// assert_eq!(Rad(PI/3.), Deg(60.).into()); 39 | /// 40 | /// // But be aware that this type of identity does not hold in general. For example, 41 | /// assert_ne!(Rad(PI/60.), Deg(3.).into()); 42 | /// 43 | /// // Similarly, the conversion to radians means that Deg::from(Angle::from(Deg(x))) 44 | /// // does not always equal x. For example, 45 | /// 46 | /// // for n == 0..8 47 | /// for n in 0..9 { 48 | /// let x = 45. * (n as f64); 49 | /// assert_eq!(Deg(x), Deg::from(Angle::from(Deg(x)))); 50 | /// } 51 | /// 52 | /// // but 53 | /// assert_ne!(Deg(60.), Deg::from(Angle::from(Deg(60.)))); 54 | /// 55 | /// When testing for equality, you should allow for numerical errors (float64Eq) 56 | /// or convert to discrete E5/E6/E7 values first. 57 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd)] 58 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 59 | pub struct Angle(f64); 60 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd, Debug)] 61 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 62 | pub struct Rad(pub f64); 63 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd, Debug)] 64 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 65 | pub struct Deg(pub f64); 66 | 67 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd, Debug)] 68 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 69 | pub struct E5(pub i32); 70 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd, Debug)] 71 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 72 | pub struct E6(pub i32); 73 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd, Debug)] 74 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 75 | pub struct E7(pub i32); 76 | 77 | impl Angle { 78 | /// rad returns the angle in radians. 79 | pub fn rad(&self) -> f64 { 80 | self.0 81 | } 82 | 83 | /// deg returns the angle in degrees. 84 | pub fn deg(&self) -> f64 { 85 | Deg::from(self).0 86 | } 87 | 88 | /// inf returns an angle larger than any finite angle. 89 | pub fn inf() -> Self { 90 | Angle(std::f64::INFINITY) 91 | } 92 | 93 | /// is_infinite reports whether this Angle is infinite. 94 | pub fn is_infinite(&self) -> bool { 95 | self.0.is_infinite() 96 | } 97 | 98 | /// abs returns the absolute value of the angle. 99 | pub fn abs(&self) -> Self { 100 | Angle(self.0.abs()) 101 | } 102 | 103 | /// normalized returns an equivalent angle in [0, 2π). 104 | pub fn normalized(&self) -> Self { 105 | let rem = self.0 % (2f64 * PI); 106 | if rem < 0. { 107 | Angle(rem + 2f64 * PI) 108 | } else { 109 | Angle(rem) 110 | } 111 | } 112 | 113 | pub fn max(self, other: Angle) -> Self { 114 | if self.0 < other.0 { 115 | return other; 116 | } else { 117 | return self; 118 | } 119 | } 120 | 121 | pub fn min(self, other: Angle) -> Self { 122 | if self.0 > other.0 { 123 | return other; 124 | } else { 125 | return self; 126 | } 127 | } 128 | } 129 | 130 | impl std::ops::Mul for Angle { 131 | type Output = Self; 132 | fn mul(self, other: Self) -> Self { 133 | Angle(self.0 * other.0) 134 | } 135 | } 136 | 137 | impl std::ops::Mul for Angle { 138 | type Output = Self; 139 | fn mul(self, other: f64) -> Self { 140 | Angle(self.0 * other) 141 | } 142 | } 143 | 144 | impl std::ops::Mul for f64 { 145 | type Output = Angle; 146 | fn mul(self, other: Angle) -> Angle { 147 | Angle(self * other.0) 148 | } 149 | } 150 | 151 | impl std::ops::MulAssign for Angle { 152 | fn mul_assign(self: &mut Self, other: Self) { 153 | self.0 *= other.0 154 | } 155 | } 156 | 157 | impl std::ops::MulAssign for f64 { 158 | fn mul_assign(self: &mut f64, other: Angle) { 159 | *self *= other.0 160 | } 161 | } 162 | 163 | impl std::ops::MulAssign for Angle { 164 | fn mul_assign(self: &mut Self, other: f64) { 165 | self.0 *= other 166 | } 167 | } 168 | 169 | impl std::ops::Div for Angle { 170 | type Output = Self; 171 | fn div(self, other: Self) -> Self { 172 | Angle(self.0 / other.0) 173 | } 174 | } 175 | 176 | impl std::ops::Div for Angle { 177 | type Output = Self; 178 | fn div(self, other: f64) -> Self { 179 | Angle(self.0 / other) 180 | } 181 | } 182 | 183 | impl std::ops::Div for f64 { 184 | type Output = Angle; 185 | fn div(self, other: Angle) -> Angle { 186 | Angle(self / other.0) 187 | } 188 | } 189 | 190 | impl std::ops::DivAssign for Angle { 191 | fn div_assign(self: &mut Self, other: Self) { 192 | self.0 /= other.0 193 | } 194 | } 195 | 196 | impl std::ops::DivAssign for f64 { 197 | fn div_assign(self: &mut f64, other: Angle) { 198 | *self /= other.0 199 | } 200 | } 201 | 202 | impl std::ops::DivAssign for Angle { 203 | fn div_assign(self: &mut Self, other: f64) { 204 | self.0 /= other 205 | } 206 | } 207 | 208 | impl std::ops::Neg for Angle { 209 | type Output = Self; 210 | fn neg(self: Self) -> Self { 211 | Angle(-self.0) 212 | } 213 | } 214 | 215 | impl std::ops::Add for Angle { 216 | type Output = Self; 217 | fn add(self, other: Self) -> Self { 218 | Angle(self.0 + other.0) 219 | } 220 | } 221 | 222 | impl std::ops::Add for f64 { 223 | type Output = Angle; 224 | fn add(self, other: Angle) -> Angle { 225 | Angle(self + other.0) 226 | } 227 | } 228 | 229 | impl std::ops::Add for Angle { 230 | type Output = Self; 231 | fn add(self, other: f64) -> Self { 232 | Angle(self.0 + other) 233 | } 234 | } 235 | 236 | impl std::ops::AddAssign for Angle { 237 | fn add_assign(self: &mut Self, other: Self) { 238 | self.0 += other.0 239 | } 240 | } 241 | 242 | impl std::ops::AddAssign for f64 { 243 | fn add_assign(self: &mut f64, other: Angle) { 244 | *self += other.0 245 | } 246 | } 247 | 248 | impl std::ops::AddAssign for Angle { 249 | fn add_assign(self: &mut Self, other: f64) { 250 | self.0 += other 251 | } 252 | } 253 | 254 | impl std::ops::Sub for Angle { 255 | type Output = Self; 256 | fn sub(self, other: Self) -> Self { 257 | Angle(self.0 - other.0) 258 | } 259 | } 260 | 261 | impl std::ops::Sub for Angle { 262 | type Output = Self; 263 | fn sub(self, other: f64) -> Self { 264 | Angle(self.0 - other) 265 | } 266 | } 267 | 268 | impl std::ops::Sub for f64 { 269 | type Output = Angle; 270 | fn sub(self, other: Angle) -> Angle { 271 | Angle(self - other.0) 272 | } 273 | } 274 | 275 | impl std::ops::SubAssign for Angle { 276 | fn sub_assign(self: &mut Self, other: Self) { 277 | self.0 -= other.0 278 | } 279 | } 280 | 281 | impl std::ops::SubAssign for f64 { 282 | fn sub_assign(self: &mut f64, other: Angle) { 283 | *self -= other.0 284 | } 285 | } 286 | 287 | impl std::ops::SubAssign for Angle { 288 | fn sub_assign(self: &mut Self, other: f64) { 289 | self.0 -= other 290 | } 291 | } 292 | 293 | impl std::fmt::Debug for Angle { 294 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 295 | write!(f, "{:.7}", Deg::from(self).0) 296 | } 297 | } 298 | 299 | macro_rules! convert { 300 | ($tyname1:ident, $tyname2:ident, $mul:expr) => { 301 | impl From<$tyname1> for $tyname2 { 302 | fn from(a: $tyname1) -> Self { 303 | $tyname2(a.0 * $mul) 304 | } 305 | } 306 | impl<'a> From<&'a $tyname1> for $tyname2 { 307 | fn from(a: &$tyname1) -> Self { 308 | $tyname2(a.0 * $mul) 309 | } 310 | } 311 | impl From<$tyname2> for $tyname1 { 312 | fn from(a: $tyname2) -> Self { 313 | $tyname1(a.0 / $mul) 314 | } 315 | } 316 | impl<'a> From<&'a $tyname2> for $tyname1 { 317 | fn from(a: &$tyname2) -> Self { 318 | $tyname1(a.0 / $mul) 319 | } 320 | } 321 | }; 322 | } 323 | 324 | macro_rules! convert_i32 { 325 | ($tyname1:ident, $tyname2:ident, $mul:expr) => { 326 | impl From<$tyname1> for $tyname2 { 327 | fn from(a: $tyname1) -> Self { 328 | $tyname2(f64::from(a.0) * $mul) 329 | } 330 | } 331 | impl<'a> From<&'a $tyname1> for $tyname2 { 332 | fn from(a: &$tyname1) -> Self { 333 | $tyname2(f64::from(a.0) * $mul) 334 | } 335 | } 336 | impl From<$tyname2> for $tyname1 { 337 | fn from(a: $tyname2) -> Self { 338 | $tyname1((a.0 / $mul).round() as i32) 339 | } 340 | } 341 | impl<'a> From<&'a $tyname2> for $tyname1 { 342 | fn from(a: &$tyname2) -> Self { 343 | $tyname1((a.0 / $mul).round() as i32) 344 | } 345 | } 346 | }; 347 | } 348 | 349 | convert!(Rad, Angle, 1.); 350 | convert!(Deg, Rad, PI / 180.); 351 | convert!(Deg, Angle, PI / 180.); 352 | 353 | convert_i32!(E5, Angle, PI / 180. / 1e5); 354 | convert_i32!(E6, Angle, PI / 180. / 1e6); 355 | convert_i32!(E7, Angle, PI / 180. / 1e7); 356 | 357 | convert_i32!(E5, Deg, 1. / 1e5); 358 | convert_i32!(E6, Deg, 1. / 1e6); 359 | convert_i32!(E7, Deg, 1. / 1e7); 360 | 361 | #[cfg(test)] 362 | mod tests { 363 | use super::*; 364 | use crate::consts::*; 365 | 366 | #[test] 367 | fn test_deg_rad() { 368 | let deg = Deg(180.); 369 | let rad = Angle(PI); 370 | assert_eq!(rad, deg.clone().into()); 371 | assert_eq!(deg, rad.clone().into()); 372 | } 373 | 374 | #[test] 375 | fn test_empty_value() { 376 | assert_eq!(Deg::from(Angle(0.)).0, 0.); 377 | assert_eq!(Angle::default().0, 0.); 378 | assert_eq!(Rad::default().0, 0.); 379 | assert_eq!(Deg::default().0, 0.); 380 | assert_eq!(E5::default().0, 0); 381 | assert_eq!(E6::default().0, 0); 382 | assert_eq!(E7::default().0, 0); 383 | } 384 | 385 | #[test] 386 | fn test_pi_radians_exactly_180_degrees() { 387 | assert_eq!(Angle(PI).rad(), PI); 388 | assert_eq!(Angle(PI).deg(), 180.); 389 | 390 | assert_eq!(Angle::from(Deg(180.)).rad(), PI); 391 | assert_eq!(Angle::from(Deg(180.)).deg(), 180.); 392 | 393 | assert_eq!(Angle(PI / 2.).deg(), 90.); 394 | 395 | assert_eq!(Angle(PI / -2.).deg(), -90.); 396 | assert_eq!(Angle::from(Deg(-45.)).rad(), PI / -4.); 397 | } 398 | 399 | fn test_rounding(have: f64, want: i32) { 400 | assert_eq!(E5::from(Deg(have * 1e-5)).0, want); 401 | assert_eq!(E6::from(Deg(have * 1e-6)).0, want); 402 | assert_eq!(E7::from(Deg(have * 1e-7)).0, want); 403 | } 404 | 405 | #[test] 406 | fn test_e5e6e7_representation() { 407 | // NOTE(dsymonds): This first test gives a variance in the 16th decimal place. I should track that down. 408 | assert_f64_eq!(Angle::from(Deg(-45.)).0, Angle::from(E5(-4500000)).0); 409 | 410 | assert_eq!(Angle::from(Deg(-60.)), Angle::from(E6(-60000000))); 411 | assert_eq!(Angle::from(Deg(-75.)), Angle::from(E7(-750000000))); 412 | 413 | assert_eq!(-17256123, E5::from(Angle::from(Deg(-172.56123))).0); 414 | assert_eq!(12345678, E6::from(Angle::from(Deg(12.345678))).0); 415 | assert_eq!(-123456789, E7::from(Angle::from(Deg(-12.3456789))).0); 416 | 417 | test_rounding(0.500000001, 1); 418 | test_rounding(-0.500000001, -1); 419 | test_rounding(0.499999999, 0); 420 | test_rounding(-0.499999999, 0); 421 | } 422 | 423 | #[test] 424 | fn test_normalize_correctly_canonicalize_angles() { 425 | assert_eq!(Angle::from(Deg(0.)), Angle::from(Deg(360.)).normalized()); 426 | assert_eq!(Angle::from(Deg(180.)), Angle::from(Deg(-180.)).normalized()); 427 | assert_eq!(Angle::from(Deg(180.)), Angle::from(Deg(180.)).normalized()); 428 | assert_eq!(Angle::from(Deg(180.)), Angle::from(Deg(540.)).normalized()); 429 | assert_eq!(Angle::from(Deg(90.)), Angle::from(Deg(-270.)).normalized()); 430 | } 431 | 432 | #[test] 433 | fn test_abs() { 434 | assert_f64_eq!(Angle::from(Rad(-0.3)).abs().rad(), 0.3); 435 | assert_f64_eq!(Angle::from(Rad(-0.3)).rad().abs(), 0.3); 436 | } 437 | 438 | #[test] 439 | fn test_neg() { 440 | assert_f64_eq!((-Angle::from(Rad(0.1))).rad(), -0.1); 441 | } 442 | 443 | #[test] 444 | fn test_add() { 445 | assert_f64_eq!((Angle::from(Rad(0.1)) + Angle::from(Rad(0.3))).rad(), 0.4); 446 | assert_f64_eq!((0.1 + Angle::from(Rad(0.3))).rad(), 0.4); 447 | assert_f64_eq!((Angle::from(Rad(0.1)) + 0.3).rad(), 0.4); 448 | } 449 | 450 | #[test] 451 | fn test_add_assign() { 452 | let mut a = Angle::from(Rad(1.0)); 453 | a += Angle::from(Rad(0.5)); 454 | assert_f64_eq!(a.rad(), 1.5); 455 | a += 0.2; 456 | assert_f64_eq!(a.rad(), 1.7); 457 | 458 | let mut b = 0.3; 459 | b += Angle::from(Rad(0.4)); 460 | assert_f64_eq!(b, 0.7); 461 | } 462 | 463 | #[test] 464 | fn test_sub() { 465 | assert_f64_eq!((Angle::from(Rad(0.1)) - Angle::from(Rad(0.3))).rad(), -0.2); 466 | assert_f64_eq!((0.1 - Angle::from(Rad(0.3))).rad(), -0.2); 467 | assert_f64_eq!((Angle::from(Rad(0.1)) - 0.3).rad(), -0.2); 468 | } 469 | 470 | #[test] 471 | fn test_sub_assign() { 472 | let mut a = Angle::from(Rad(1.5)); 473 | a -= Angle::from(Rad(0.5)); 474 | assert_f64_eq!(a.rad(), 1.0); 475 | a -= 0.2; 476 | assert_f64_eq!(a.rad(), 0.8); 477 | 478 | let mut b = 0.7; 479 | b -= Angle::from(Rad(0.4)); 480 | assert_f64_eq!(b, 0.3); 481 | } 482 | 483 | #[test] 484 | fn test_mul() { 485 | assert_f64_eq!((Angle::from(Rad(0.3)) * Angle::from(Rad(2.0))).rad(), 0.6); 486 | assert_f64_eq!((Angle::from(Rad(0.3)) * 2.0).rad(), 0.6); 487 | assert_f64_eq!((0.3 * Angle::from(Rad(2.0))).rad(), 0.6); 488 | } 489 | 490 | #[test] 491 | fn test_mul_assign() { 492 | let mut a = Angle::from(Rad(0.5)); 493 | a *= Angle::from(Rad(5.0)); 494 | assert_f64_eq!(a.rad(), 2.5); 495 | a *= 0.1; 496 | assert_f64_eq!(a.rad(), 0.25); 497 | 498 | let mut b = 0.5; 499 | b *= Angle::from(Rad(5.0)); 500 | assert_f64_eq!(b, 2.5); 501 | } 502 | 503 | #[test] 504 | fn test_div() { 505 | assert_f64_eq!((Angle::from(Rad(0.3)) / Angle::from(Rad(2.0))).rad(), 0.15); 506 | assert_f64_eq!((Angle::from(Rad(0.3)) / 2.0).rad(), 0.15); 507 | assert_f64_eq!((0.3 / Angle::from(Rad(2.0))).rad(), 0.15); 508 | } 509 | 510 | #[test] 511 | fn test_div_assign() { 512 | let mut a = Angle::from(Rad(2.5)); 513 | a /= Angle::from(Rad(2.0)); 514 | assert_f64_eq!(a.rad(), 1.25); 515 | a /= 2.0; 516 | assert_f64_eq!(a.rad(), 0.625); 517 | 518 | let mut b = 0.4; 519 | b /= Angle::from(Rad(2.0)); 520 | assert_f64_eq!(b, 0.2); 521 | } 522 | 523 | #[test] 524 | fn test_degrees_vs_radians() { 525 | // This test tests the exactness of specific values between degrees and radians. 526 | let mut k = -8.; 527 | while k <= 8. { 528 | assert_eq!(Deg(45. * k), Deg::from(Angle(k * PI / 4.))); 529 | assert_eq!(Angle::from(Deg(45. * k)), Angle(k * PI / 4.)); 530 | k += 1.; 531 | } 532 | 533 | for k in 0..30 { 534 | let n = (1i32 << k) as f64; 535 | assert_eq!(Angle::from(Deg(180. / n)), Angle(PI / (1. * n))); 536 | assert_eq!(Angle::from(Deg(60. / n)), Angle(PI / (3. * n))); 537 | assert_eq!(Angle::from(Deg(36. / n)), Angle(PI / (5. * n))); 538 | assert_eq!(Angle::from(Deg(20. / n)), Angle(PI / (9. * n))); 539 | assert_eq!(Angle::from(Deg(4. / n)), Angle(PI / (45. * n))); 540 | } 541 | 542 | // We also spot check a non-identity. 543 | assert_f64_eq!(Deg::from(Angle::from(Deg(60.))).0, Deg(60.).0); 544 | } 545 | } 546 | 547 | // BUG(dsymonds): The major differences from the C++ version are: 548 | // - no unsigned E5/E6/E7 methods 549 | // - no S2Point or S2LatLng constructors 550 | -------------------------------------------------------------------------------- /src/s1/chordangle.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use std; 19 | use std::f64::consts::PI; 20 | 21 | use crate::consts::*; 22 | use crate::s1::angle::*; 23 | 24 | #[cfg(feature = "float_extras")] 25 | use float_extras::f64::nextafter; 26 | 27 | /// ChordAngle represents the angle subtended by a chord (i.e., the straight 28 | /// line segment connecting two points on the sphere). Its representation 29 | /// makes it very efficient for computing and comparing distances, but unlike 30 | /// Angle it is only capable of representing angles between 0 and π radians. 31 | /// Generally, ChordAngle should only be used in loops where many angles need 32 | /// to be calculated and compared. Otherwise it is simpler to use Angle. 33 | /// 34 | /// ChordAngle loses some accuracy as the angle approaches π radians. 35 | /// Specifically, the representation of (π - x) radians has an error of about 36 | /// (1e-15 / x), with a maximum error of about 2e-8 radians (about 13cm on the 37 | /// Earth's surface). For comparison, for angles up to π/2 radians (10000km) 38 | /// the worst-case representation error is about 2e-16 radians (1 nanonmeter), 39 | /// which is about the same as Angle. 40 | /// 41 | /// ChordAngles are represented by the squared chord length, which can 42 | /// range from 0 to 4. Positive infinity represents an infinite squared length. 43 | #[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)] 44 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 45 | pub struct ChordAngle(pub f64); 46 | 47 | /// NEGATIVE represents a chord angle smaller than the zero angle. 48 | /// The only valid operations on a NegativeChordAngle are comparisons and 49 | /// Angle conversions. 50 | pub const NEGATIVE: ChordAngle = ChordAngle(-1f64); 51 | 52 | /// RIGHT represents a chord angle of 90 degrees (a "right angle"). 53 | pub const RIGHT: ChordAngle = ChordAngle(2f64); 54 | 55 | /// STRAIGHT represents a chord angle of 180 degrees (a "straight angle"). 56 | /// This is the maximum finite chord angle. 57 | pub const STRAIGHT: ChordAngle = ChordAngle(4f64); 58 | 59 | // MAXLENGTH2 is the square of the maximum length allowed in a ChordAngle. 60 | pub const MAXLENGTH2: f64 = 4.0; 61 | 62 | impl<'a> From<&'a Angle> for ChordAngle { 63 | /// returns a ChordAngle from the given Angle. 64 | fn from(a: &'a Angle) -> Self { 65 | if a.rad() < 0. { 66 | NEGATIVE 67 | } else if a.is_infinite() { 68 | ChordAngle::inf() 69 | } else { 70 | let l = 2. * (0.5 * a.rad().min(PI)).sin(); 71 | ChordAngle(l * l) 72 | } 73 | } 74 | } 75 | impl From for ChordAngle { 76 | /// returns a ChordAngle from the given Angle. 77 | fn from(a: Angle) -> Self { 78 | ChordAngle::from(&a) 79 | } 80 | } 81 | 82 | impl<'a> From<&'a Deg> for ChordAngle { 83 | fn from(a: &'a Deg) -> Self { 84 | Angle::from(a).into() 85 | } 86 | } 87 | impl From for ChordAngle { 88 | fn from(a: Deg) -> Self { 89 | Angle::from(&a).into() 90 | } 91 | } 92 | 93 | impl<'a> From<&'a ChordAngle> for Angle { 94 | /// converts this ChordAngle to an Angle. 95 | fn from(ca: &'a ChordAngle) -> Self { 96 | if ca.0 < 0. { 97 | Rad(-1.).into() 98 | } else if ca.is_infinite() { 99 | Angle::inf() 100 | } else { 101 | Rad(2. * (0.5 * ca.0.sqrt()).asin()).into() 102 | } 103 | } 104 | } 105 | impl From for Angle { 106 | /// converts this ChordAngle to an Angle. 107 | fn from(ca: ChordAngle) -> Self { 108 | Angle::from(&ca) 109 | } 110 | } 111 | 112 | impl<'a, 'b> std::ops::Add<&'a ChordAngle> for &'b ChordAngle { 113 | type Output = ChordAngle; 114 | /// add adds the other ChordAngle to this one and returns the resulting value. 115 | /// This method assumes the ChordAngles are not special. 116 | fn add(self, other: &'a ChordAngle) -> Self::Output { 117 | // Note that this method (and Sub) is much more efficient than converting 118 | // the ChordAngle to an Angle and adding those and converting back. It 119 | // requires only one square root plus a few additions and multiplications. 120 | 121 | if other.0 == 0.0 { 122 | // Optimization for the common case where b is an error tolerance 123 | // parameter that happens to be set to zero. 124 | *self 125 | } else if self.0 + other.0 >= 4. { 126 | // Clamp the angle sum to at most 180 degrees. 127 | STRAIGHT 128 | } else { 129 | // Let a and b be the (non-squared) chord lengths, and let c = a+b. 130 | // Let A, B, and C be the corresponding half-angles (a = 2*sin(A), etc). 131 | // Then the formula below can be derived from c = 2 * sin(A+B) and the 132 | // relationships sin(A+B) = sin(A)*cos(B) + sin(B)*cos(A) 133 | // cos(X) = sqrt(1 - sin^2(X)) 134 | let x = self.0 * (1. - 0.25 * other.0); 135 | let y = other.0 * (1. - 0.25 * self.0); 136 | ChordAngle(4f64.min(x + y + 2f64 * (x * y).sqrt())) 137 | } 138 | } 139 | } 140 | 141 | impl std::ops::Add for ChordAngle { 142 | type Output = ChordAngle; 143 | fn add(self, other: ChordAngle) -> Self::Output { 144 | &self + &other 145 | } 146 | } 147 | 148 | impl std::ops::Sub for ChordAngle { 149 | type Output = ChordAngle; 150 | /// sub subtracts the other ChordAngle from this one and returns the resulting 151 | /// value. This method assumes the ChordAngles are not special. 152 | fn sub(self, other: ChordAngle) -> Self::Output { 153 | if other.0 == 0.0 { 154 | self 155 | } else if self.0 <= other.0 { 156 | ChordAngle(0f64) 157 | } else { 158 | let x = self.0 * (1. - 0.25 * other.0); 159 | let y = other.0 * (1. - 0.25 * self.0); 160 | ChordAngle(0f64.max(x + y - 2. * (x * y).sqrt())) 161 | } 162 | } 163 | } 164 | 165 | impl ChordAngle { 166 | /// inf returns a chord angle larger than any finite chord angle. 167 | /// The only valid operations on an InfChordAngle are comparisons and Angle conversions. 168 | pub fn inf() -> Self { 169 | ChordAngle(std::f64::INFINITY) 170 | } 171 | 172 | /// is_infinite reports whether this ChordAngle is infinite. 173 | pub fn is_infinite(&self) -> bool { 174 | self.0.is_infinite() 175 | } 176 | 177 | /// from_squared_length returns a ChordAngle from the squared chord length. 178 | /// Note that the argument is automatically clamped to a maximum of 4.0 to 179 | /// handle possible roundoff errors. The argument must be non-negative. 180 | pub fn from_squared_length(length2: f64) -> Self { 181 | if length2 > 4. { 182 | STRAIGHT 183 | } else { 184 | ChordAngle(length2) 185 | } 186 | } 187 | 188 | /// expanded returns a new ChordAngle that has been adjusted by the given error 189 | /// bound (which can be positive or negative). Error should be the value 190 | /// returned by either MaxPointError or MaxAngleError. For example: 191 | /// let a = ChordAngle::from_points(x, y) 192 | /// let a1 = a.expanded(a.max_point_error()) 193 | pub fn expanded(&self, e: f64) -> Self { 194 | // If the angle is special, don't change it. Otherwise clamp it to the valid range. 195 | if self.is_special() { 196 | *self 197 | } else { 198 | ChordAngle(0f64.max(4f64.min(self.0 + e))) 199 | } 200 | } 201 | 202 | /// is_special reports whether this ChordAngle is one of the special cases. 203 | pub fn is_special(&self) -> bool { 204 | self.0 < 0. || self.0.is_infinite() 205 | } 206 | 207 | /// is_valid reports whether this ChordAngle is valid or not. 208 | pub fn is_valid(&self) -> bool { 209 | self.0 >= 0. && self.0 <= 4. || self.is_special() 210 | } 211 | 212 | pub fn max(self, other: Self) -> Self { 213 | if self.0 < other.0 { 214 | return other; 215 | } else { 216 | return self; 217 | } 218 | } 219 | 220 | /// max_point_error returns the maximum error size for a ChordAngle constructed 221 | /// from 2 Points x and y, assuming that x and y are normalized to within the 222 | /// bounds guaranteed by s2.Point.Normalize. The error is defined with respect to 223 | /// the true distance after the points are projected to lie exactly on the sphere. 224 | pub fn max_point_error(&self) -> f64 { 225 | // There is a relative error of (2.5*DBL_EPSILON) when computing the squared 226 | // distance, plus an absolute error of (16 * DBL_EPSILON**2) because the 227 | // lengths of the input points may differ from 1 by up to (2*DBL_EPSILON) each. 228 | 2.5 * DBL_EPSILON * self.0 + 16. * DBL_EPSILON * DBL_EPSILON 229 | } 230 | 231 | /// max_angle_error returns the maximum error for a ChordAngle constructed 232 | /// as an Angle distance. 233 | pub fn max_angle_error(&self) -> f64 { 234 | DBL_EPSILON * self.0 235 | } 236 | 237 | /// sin returns the sine of this chord angle. This method is more efficient 238 | /// than converting to Angle and performing the computation. 239 | pub fn sin(&self) -> f64 { 240 | self.sin2().sqrt() 241 | } 242 | 243 | /// sin2 returns the square of the sine of this chord angle. 244 | /// It is more efficient than Sin. 245 | pub fn sin2(&self) -> f64 { 246 | // Let a be the (non-squared) chord length, and let A be the corresponding 247 | // half-angle (a = 2*sin(A)). The formula below can be derived from: 248 | // sin(2*A) = 2 * sin(A) * cos(A) 249 | // cos^2(A) = 1 - sin^2(A) 250 | // This is much faster than converting to an angle and computing its sine. 251 | self.0 * (1. - 0.25 * self.0) 252 | } 253 | 254 | /// cos returns the cosine of this chord angle. This method is more efficient 255 | /// than converting to Angle and performing the computation. 256 | pub fn cos(&self) -> f64 { 257 | // cos(2*A) = cos^2(A) - sin^2(A) = 1 - 2*sin^2(A) 258 | 1.0 - 0.5 * self.0 259 | } 260 | 261 | /// tan returns the tangent of this chord angle. 262 | pub fn tan(&self) -> f64 { 263 | self.sin() / self.cos() 264 | } 265 | 266 | #[cfg(feature = "float_extras")] 267 | pub fn successor(&self) -> Self { 268 | if self.0 >= MAXLENGTH2 { 269 | return ChordAngle::inf(); 270 | } else if self.0 < 0. { 271 | return ChordAngle(0.); 272 | } else { 273 | return ChordAngle(nextafter(self.0, 10.)); 274 | } 275 | } 276 | } 277 | 278 | #[cfg(test)] 279 | mod tests { 280 | use super::*; 281 | 282 | fn test_chordangle_basics_case(ca1: ChordAngle, ca2: ChordAngle, less_than: bool, equal: bool) { 283 | assert_eq!(less_than, ca1 < ca2); 284 | assert_eq!(equal, ca1 == ca2); 285 | } 286 | 287 | #[test] 288 | fn test_chordangle_basics() { 289 | let zero = ChordAngle::default(); 290 | 291 | test_chordangle_basics_case(NEGATIVE, NEGATIVE, false, true); 292 | test_chordangle_basics_case(NEGATIVE, zero, true, false); 293 | test_chordangle_basics_case(NEGATIVE, STRAIGHT, true, false); 294 | test_chordangle_basics_case(NEGATIVE, ChordAngle::inf(), true, false); 295 | 296 | test_chordangle_basics_case(zero, zero, false, true); 297 | test_chordangle_basics_case(zero, STRAIGHT, true, false); 298 | test_chordangle_basics_case(zero, ChordAngle::inf(), true, false); 299 | 300 | test_chordangle_basics_case(STRAIGHT, STRAIGHT, false, true); 301 | test_chordangle_basics_case(STRAIGHT, ChordAngle::inf(), true, false); 302 | 303 | test_chordangle_basics_case(ChordAngle::inf(), ChordAngle::inf(), false, true); 304 | test_chordangle_basics_case(ChordAngle::inf(), ChordAngle::inf(), false, true); 305 | } 306 | 307 | fn test_chordangle_is_functions_case( 308 | ca: ChordAngle, 309 | is_neg: bool, 310 | is_zero: bool, 311 | is_inf: bool, 312 | is_special: bool, 313 | ) { 314 | assert_eq!(is_neg, ca.0 < 0.); 315 | assert_eq!(is_zero, ca.0 == 0.); 316 | assert_eq!(is_inf, ca.is_infinite()); 317 | assert_eq!(is_special, ca.is_special()); 318 | } 319 | 320 | #[test] 321 | fn test_chordangle_is_functions() { 322 | let zero: ChordAngle = Default::default(); 323 | test_chordangle_is_functions_case(zero, false, true, false, false); 324 | test_chordangle_is_functions_case(NEGATIVE, true, false, false, true); 325 | test_chordangle_is_functions_case(zero, false, true, false, false); 326 | test_chordangle_is_functions_case(STRAIGHT, false, false, false, false); 327 | test_chordangle_is_functions_case(ChordAngle::inf(), false, false, true, true); 328 | } 329 | 330 | #[test] 331 | fn test_chordangle_from_angle() { 332 | let angles = vec![ 333 | Angle::from(Rad(0.)), 334 | Angle::from(Rad(1.)), 335 | Angle::from(Rad(-1.)), 336 | Angle::from(Rad(PI)), 337 | ]; 338 | 339 | for angle in angles.into_iter() { 340 | let ca = ChordAngle::from(angle); 341 | let got = Angle::from(ca); 342 | assert_eq!(got, angle); 343 | } 344 | 345 | assert_eq!(STRAIGHT, ChordAngle::from(Angle::from(Rad(PI)))); 346 | assert_eq!(Angle::inf(), Angle::from(ChordAngle::from(Angle::inf()))); 347 | } 348 | 349 | fn chordangle_eq(a: ChordAngle, b: ChordAngle) { 350 | assert_f64_eq!(a.0, b.0); 351 | } 352 | 353 | #[test] 354 | fn test_chordangle_arithmetic() { 355 | let zero = ChordAngle::default(); 356 | let deg_30 = ChordAngle::from(Deg(30.)); 357 | let deg_60 = ChordAngle::from(Deg(60.)); 358 | let deg_90 = ChordAngle::from(Deg(90.)); 359 | let deg_120 = ChordAngle::from(Deg(120.)); 360 | let deg_180 = STRAIGHT; 361 | 362 | chordangle_eq(zero + zero, zero); 363 | chordangle_eq(deg_60 + zero, deg_60); 364 | chordangle_eq(zero + deg_60, deg_60); 365 | chordangle_eq(deg_30 + deg_60, deg_90); 366 | chordangle_eq(deg_60 + deg_30, deg_90); 367 | chordangle_eq(deg_180 + zero, deg_180); 368 | chordangle_eq(deg_60 + deg_30, deg_90); 369 | chordangle_eq(deg_90 + deg_90, deg_180); 370 | chordangle_eq(deg_120 + deg_90, deg_180); 371 | chordangle_eq(deg_120 + deg_120, deg_180); 372 | chordangle_eq(deg_30 + deg_180, deg_180); 373 | chordangle_eq(deg_180 + deg_180, deg_180); 374 | 375 | chordangle_eq(zero - zero, zero); 376 | chordangle_eq(deg_60 - deg_60, zero); 377 | chordangle_eq(deg_180 - deg_180, zero); 378 | chordangle_eq(zero - deg_60, zero); 379 | chordangle_eq(deg_30 - deg_90, zero); 380 | chordangle_eq(deg_90 - deg_30, deg_60); 381 | chordangle_eq(deg_90 - deg_60, deg_30); 382 | chordangle_eq(deg_180 - zero, deg_180); 383 | } 384 | 385 | #[test] 386 | fn test_chordangle_trigonometry() { 387 | let iters = 40usize; 388 | for i in 0..(iters + 1) { 389 | let radians = PI * (i as f64) / (iters as f64); 390 | let chordangle = ChordAngle::from(Angle::from(Rad(radians))); 391 | 392 | assert_f64_eq!(radians.sin(), chordangle.sin()); 393 | assert_f64_eq!(radians.cos(), chordangle.cos()); 394 | 395 | // Since tan(x) is unbounded near pi/4, we map the result back to an 396 | // angle before comparing. The assertion is that the result is equal to 397 | // the tangent of a nearby angle. 398 | assert_f64_eq!(radians.tan().atan(), chordangle.tan().atan()); 399 | } 400 | 401 | let angle_90 = ChordAngle::from_squared_length(2.); 402 | let angle_180 = ChordAngle::from_squared_length(4.); 403 | 404 | assert_f64_eq!(1., angle_90.sin()); 405 | assert_f64_eq!(0., angle_90.cos()); 406 | assert!(angle_90.tan().is_infinite()); 407 | 408 | assert_f64_eq!(0., angle_180.sin()); 409 | assert_f64_eq!(-1., angle_180.cos()); 410 | assert_f64_eq!(0., angle_180.tan()); 411 | } 412 | 413 | #[test] 414 | fn test_chordangle_expanded() { 415 | let zero = ChordAngle::default(); 416 | assert_eq!(NEGATIVE.expanded(5.), NEGATIVE.expanded(5.)); 417 | assert_eq!(ChordAngle::inf().expanded(-5.), ChordAngle::inf()); 418 | assert_eq!(zero.expanded(-5.), zero); 419 | assert_eq!( 420 | ChordAngle::from_squared_length(1.25).expanded(0.25), 421 | ChordAngle::from_squared_length(1.5) 422 | ); 423 | assert_eq!( 424 | ChordAngle::from_squared_length(0.75).expanded(0.25), 425 | ChordAngle::from_squared_length(1.) 426 | ); 427 | } 428 | } 429 | -------------------------------------------------------------------------------- /src/s1/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod angle; 2 | pub mod chordangle; 3 | pub mod interval; 4 | 5 | pub use self::angle::{Angle, Deg, Rad, E5, E6, E7}; 6 | pub use self::chordangle::ChordAngle; 7 | pub use self::interval::Interval; 8 | -------------------------------------------------------------------------------- /src/s2/latlng.rs: -------------------------------------------------------------------------------- 1 | use crate::consts::remainder; 2 | use crate::r3::vector::Vector; 3 | use crate::s1::*; 4 | use crate::s2::point::Point; 5 | use std; 6 | use std::f64::consts::{FRAC_PI_2, PI}; 7 | 8 | const NORTH_POLE_LAT: f64 = FRAC_PI_2; 9 | const SOUTH_POLE_LAT: f64 = -FRAC_PI_2; 10 | 11 | #[derive(Clone, Copy, Default, PartialEq)] 12 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 13 | pub struct LatLng { 14 | pub lat: Angle, 15 | pub lng: Angle, 16 | } 17 | 18 | impl std::fmt::Debug for LatLng { 19 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 20 | write!(f, "[{:?}, {:?}]", self.lat, self.lng) 21 | } 22 | } 23 | 24 | impl LatLng { 25 | pub fn new(lat: Angle, lng: Angle) -> Self { 26 | LatLng { lat, lng } 27 | } 28 | 29 | pub fn from_degrees(lat: f64, lng: f64) -> Self { 30 | LatLng { 31 | lat: Angle::from(Deg(lat)), 32 | lng: Angle::from(Deg(lng)), 33 | } 34 | } 35 | 36 | pub fn from_radians(lat: f64, lng: f64) -> Self { 37 | LatLng { 38 | lat: Angle::from(Rad(lat)), 39 | lng: Angle::from(Rad(lng)), 40 | } 41 | } 42 | 43 | pub fn is_valid(&self) -> bool { 44 | self.lat.rad().abs() <= PI / 2. && self.lng.rad().abs() <= PI 45 | } 46 | 47 | pub fn normalized(&self) -> Self { 48 | let lat = if self.lat.rad() > NORTH_POLE_LAT { 49 | Rad(NORTH_POLE_LAT).into() 50 | } else if self.lat.rad() < SOUTH_POLE_LAT { 51 | Rad(SOUTH_POLE_LAT).into() 52 | } else { 53 | self.lat 54 | }; 55 | 56 | LatLng { 57 | lat, 58 | lng: Rad(remainder(self.lng.rad(), PI * 2.)).into(), 59 | } 60 | } 61 | 62 | pub fn distance(&self, other: &Self) -> Angle { 63 | let dlat = (0.5 * (other.lat.rad() - self.lat.rad())).sin(); 64 | let dlng = (0.5 * (other.lng.rad() - self.lng.rad())).sin(); 65 | 66 | let x = dlat * dlat + dlng * dlng * self.lat.rad().cos() * other.lat.rad().cos(); 67 | Rad(2. * x.sqrt().atan2((1. - x).max(0.).sqrt())).into() 68 | } 69 | } 70 | 71 | impl Point { 72 | pub fn latitude(&self) -> Angle { 73 | let v = &self.0; 74 | let l = (v.x * v.x + v.y * v.y).sqrt(); 75 | Rad(v.z.atan2(l)).into() 76 | } 77 | 78 | pub fn longitude(&self) -> Angle { 79 | let v = &self.0; 80 | Rad(v.y.atan2(v.x)).into() 81 | } 82 | } 83 | 84 | impl<'a> From<&'a LatLng> for Point { 85 | fn from(ll: &'a LatLng) -> Self { 86 | let phi = ll.lat.rad(); 87 | let theta = ll.lng.rad(); 88 | let cosphi = phi.cos(); 89 | Point(Vector { 90 | x: theta.cos() * cosphi, 91 | y: theta.sin() * cosphi, 92 | z: phi.sin(), 93 | }) 94 | } 95 | } 96 | impl From for Point { 97 | fn from(ll: LatLng) -> Self { 98 | Self::from(&ll) 99 | } 100 | } 101 | 102 | impl<'a> From<&'a Point> for LatLng { 103 | fn from(p: &'a Point) -> Self { 104 | LatLng { 105 | lat: p.latitude(), 106 | lng: p.longitude(), 107 | } 108 | } 109 | } 110 | impl From for LatLng { 111 | fn from(p: Point) -> Self { 112 | LatLng::from(&p) 113 | } 114 | } 115 | 116 | impl std::fmt::Display for LatLng { 117 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 118 | write!(f, "{:.15}:{:.15}", self.lat.deg(), self.lng.deg()) 119 | } 120 | } 121 | 122 | #[cfg(test)] 123 | mod tests { 124 | use super::*; 125 | use crate::r3::vector::Vector; 126 | use crate::s1; 127 | use crate::s2::point::Point; 128 | use std::f64::consts::FRAC_PI_4; 129 | 130 | macro_rules! ll { 131 | ($lat:expr, $lng:expr) => { 132 | LatLng { 133 | lat: s1::Deg($lat).into(), 134 | lng: s1::Deg($lng).into(), 135 | } 136 | }; 137 | } 138 | macro_rules! p { 139 | ($x:expr, $y:expr, $z:expr) => { 140 | Point(Vector { 141 | x: $x as f64, 142 | y: $y as f64, 143 | z: $z as f64, 144 | }) 145 | }; 146 | } 147 | 148 | #[test] 149 | fn test_latlng_default() { 150 | // Same as geo.s2.LatLng{} in Go. 151 | assert_eq!(LatLng::default(), ll!(0.0, 0.0)); 152 | } 153 | 154 | #[test] 155 | fn test_latlng_basic() { 156 | let ll_rad = LatLng::from_radians(FRAC_PI_4, FRAC_PI_2); 157 | assert_eq!(FRAC_PI_4, ll_rad.lat.rad()); 158 | assert_eq!(FRAC_PI_2, ll_rad.lng.rad()); 159 | assert!(ll_rad.is_valid()); 160 | let ll_deg = LatLng::from_degrees(45., 90.); 161 | assert_eq!(ll_rad, ll_deg); 162 | assert!(ll_deg.is_valid()); 163 | assert!(!LatLng::from_degrees(-91., 0.).is_valid()); 164 | assert!(!LatLng::from_degrees(0., 181.).is_valid()); 165 | } 166 | 167 | fn test_latlng_normalized_case(descr: &str, pos: LatLng, want: LatLng) { 168 | let desc: String = descr.into(); 169 | let normalized = pos.normalized(); 170 | assert!(normalized.is_valid(), "{}", desc); 171 | 172 | let distance = normalized.distance(&want); 173 | assert!(distance < s1::Deg(1e-13).into(), "{}", desc); 174 | } 175 | 176 | #[test] 177 | fn test_latlng_normalized() { 178 | test_latlng_normalized_case( 179 | &"Valid lat/lng", 180 | ll!(21.8275043, 151.1979675), 181 | ll!(21.8275043, 151.1979675), 182 | ); 183 | test_latlng_normalized_case( 184 | &"Valid lat/lng in the West", 185 | ll!(21.8275043, -151.1979675), 186 | ll!(21.8275043, -151.1979675), 187 | ); 188 | test_latlng_normalized_case( 189 | &"Beyond the North pole", 190 | ll!(95., 151.1979675), 191 | ll!(90., 151.1979675), 192 | ); 193 | test_latlng_normalized_case( 194 | "Beyond the South pole", 195 | ll!(-95., 151.1979675), 196 | ll!(-90., 151.1979675), 197 | ); 198 | test_latlng_normalized_case( 199 | "At the date line (from East)", 200 | ll!(21.8275043, 180.), 201 | ll!(21.8275043, 180.), 202 | ); 203 | test_latlng_normalized_case( 204 | "At the date line (from West)", 205 | ll!(21.8275043, -180.), 206 | ll!(21.8275043, -180.), 207 | ); 208 | test_latlng_normalized_case( 209 | "Across the date line going East", 210 | ll!(21.8275043, 181.0012), 211 | ll!(21.8275043, -178.9988), 212 | ); 213 | test_latlng_normalized_case( 214 | "Across the date line going West", 215 | ll!(21.8275043, -181.0012), 216 | ll!(21.8275043, 178.9988), 217 | ); 218 | test_latlng_normalized_case("All wrong", ll!(256., 256.), ll!(90., -104.)); 219 | } 220 | 221 | fn test_approx_eq(a: f64, b: f64) { 222 | assert!((a - b).abs() < 1e-14); 223 | } 224 | 225 | fn test_latlng_point_conversion_case(ll: LatLng, p: Point) { 226 | //TODO 227 | let llp: Point = ll.clone().into(); 228 | test_approx_eq(llp.0.x, p.0.x); 229 | test_approx_eq(llp.0.y, p.0.y); 230 | test_approx_eq(llp.0.z, p.0.z); 231 | 232 | let pll: LatLng = p.into(); 233 | test_approx_eq(pll.lat.rad(), ll.lat.rad()); 234 | let is_polar = ll.lng.rad() == PI / 2. || ll.lng.rad() == PI / -2.; 235 | if !is_polar { 236 | test_approx_eq(pll.lng.rad(), ll.lng.rad()); 237 | } 238 | } 239 | 240 | #[test] 241 | fn test_latlng_point_conversion() { 242 | test_latlng_point_conversion_case(ll!(0., 0.), p!(1, 0, 0)); 243 | test_latlng_point_conversion_case(ll!(90., 0.), p!(6.12323e-17, 0, 1)); 244 | test_latlng_point_conversion_case(ll!(-90., 0.), p!(6.12323e-17, 0, -1)); 245 | test_latlng_point_conversion_case(ll!(0., 180.), p!(-1, 1.22465e-16, 0)); 246 | test_latlng_point_conversion_case(ll!(0., -180.), p!(-1, -1.22465e-16, 0)); 247 | test_latlng_point_conversion_case(ll!(90., 180.), p!(-6.12323e-17, 7.4988e-33, 1)); 248 | test_latlng_point_conversion_case(ll!(90., -180.), p!(-6.12323e-17, -7.4988e-33, 1)); 249 | test_latlng_point_conversion_case(ll!(-90., 180.), p!(-6.12323e-17, 7.4988e-33, -1)); 250 | test_latlng_point_conversion_case(ll!(-90., -180.), p!(-6.12323e-17, -7.4988e-33, -1)); 251 | test_latlng_point_conversion_case( 252 | ll!(-81.82750430354997, 151.19796752929685), 253 | p!(-0.12456788151479525, 0.0684875268284729, -0.989844584550441), 254 | ); 255 | } 256 | 257 | fn test_latlng_distance_case(ll1: LatLng, ll2: LatLng, want: f64, tolerance: f64) { 258 | let distance: s1::Deg = ll1.distance(&ll2).into(); 259 | assert!((distance.0 - want).abs() <= tolerance); 260 | } 261 | 262 | #[test] 263 | fn test_latlng_distance() { 264 | test_latlng_distance_case(ll!(90., 0.), ll!(90., 0.), 0., 0.); 265 | test_latlng_distance_case(ll!(-37., 25.), ll!(-66., -155.), 77., 1e-13); 266 | test_latlng_distance_case(ll!(0., 165.), ll!(0., -80.), 115., 1e-13); 267 | test_latlng_distance_case(ll!(47., -127.), ll!(-47., 53.), 180., 2e-6); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/s2/metric.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | Copyright 2017 Jihyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | //! This file implements functions for various S2 measurements. 19 | 20 | use libm::{ilogb, ldexp}; 21 | use std::f64::consts::{PI, SQRT_2}; 22 | 23 | use crate::s2::cellid::MAX_LEVEL; 24 | 25 | // A metric is a measure for cells. It is used to describe the shape and size 26 | // of cells. They are useful for deciding which cell level to use in order to 27 | // satisfy a given condition (e.g. that cell vertices must be no further than 28 | // "x" apart). You can use the Value(level) method to compute the corresponding 29 | // length or area on the unit sphere for cells at a given level. The minimum 30 | // and maximum bounds are valid for cells at all levels, but they may be 31 | // somewhat conservative for very large cells (e.g. face cells). 32 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 33 | pub struct Metric { 34 | // Dim is either 1 or 2, for a 1D or 2D metric respectively. 35 | dim: u8, 36 | // Deriv is the scaling factor for the metric. 37 | deriv: f64, 38 | } 39 | 40 | macro_rules! metric { 41 | ($name:ident, $dim:expr, $deriv:expr) => { 42 | pub const $name: Metric = Metric { 43 | dim: $dim, 44 | deriv: $deriv, 45 | }; 46 | }; 47 | } 48 | 49 | // Defined metrics. 50 | // Of the projection methods defined in C++, Go only supports the quadratic projection. 51 | 52 | // Each cell is bounded by four planes passing through its four edges and 53 | // the center of the sphere. These metrics relate to the angle between each 54 | // pair of opposite bounding planes, or equivalently, between the planes 55 | // corresponding to two different s-values or two different t-values. 56 | metric!(MIN_ANGLE_SPANMETRIC, 1, 4.0 / 3.); 57 | metric!(AVG_ANGLE_SPANMETRIC, 1, PI / 2.); 58 | metric!(MAX_ANGLE_SPANMETRIC, 1, 1.704897179199218452); 59 | 60 | // The width of geometric figure is defined as the distance between two 61 | // parallel bounding lines in a given direction. For cells, the minimum 62 | // width is always attained between two opposite edges, and the maximum 63 | // width is attained between two opposite vertices. However, for our 64 | // purposes we redefine the width of a cell as the perpendicular distance 65 | // between a pair of opposite edges. A cell therefore has two widths, one 66 | // in each direction. The minimum width according to this definition agrees 67 | // with the classic geometric one, but the maximum width is different. (The 68 | // maximum geometric width corresponds to MAXDiag defined below.) 69 | // 70 | // The average width in both directions for all cells at level k is approximately 71 | // AVG_WIDTHMETRIC.Value(k). 72 | // 73 | // The width is useful for bounding the minimum or maximum distance from a 74 | // point on one edge of a cell to the closest point on the opposite edge. 75 | // For example, this is useful when growing regions by a fixed distance. 76 | metric!(MIN_WIDTHMETRIC, 1, 2. * SQRT_2 / 3.); 77 | metric!(AVG_WIDTHMETRIC, 1, 1.434523672886099389); 78 | metric!(MAX_WIDTHMETRIC, 1, MAX_ANGLE_SPANMETRIC.deriv); 79 | 80 | // The edge length metrics can be used to bound the minimum, maximum, 81 | // or average distance from the center of one cell to the center of one of 82 | // its edge neighbors. In particular, it can be used to bound the distance 83 | // between adjacent cell centers along the space-filling Hilbert curve for 84 | // cells at any given level. 85 | metric!(MIN_EDGEMETRIC, 1, 2. * SQRT_2 / 3.); 86 | metric!(AVG_EDGEMETRIC, 1, 1.459213746386106062); 87 | metric!(MAX_EDGEMETRIC, 1, MAX_ANGLE_SPANMETRIC.deriv); 88 | 89 | /// MAX_EDGE_ASPECT is the maximum edge aspect ratio over all cells at any level, 90 | /// where the edge aspect ratio of a cell is defined as the ratio of its longest 91 | /// edge length to its shortest edge length. 92 | pub const MAX_EDGE_ASPECT: f64 = 1.442615274452682920; 93 | 94 | metric!(MIN_AREAMETRIC, 2, 8. * SQRT_2 / 9.); 95 | metric!(AVG_AREAMETRIC, 2, 4. * PI / 6.); 96 | metric!(MAX_AREAMETRIC, 2, 2.635799256963161491); 97 | 98 | // The maximum diagonal is also the maximum diameter of any cell, 99 | // and also the maximum geometric width (see the comment for widths). For 100 | // example, the distance from an arbitrary point to the closest cell center 101 | // at a given level is at most half the maximum diagonal length. 102 | metric!(MIN_DIAGMETRIC, 1, 8. * SQRT_2 / 9.); 103 | metric!(AVG_DIAGMETRIC, 1, 2.060422738998471683); 104 | metric!(MAX_DIAGMETRIC, 1, 2.438654594434021032); 105 | 106 | /// MAX_DIAG_ASPECT is the maximum diagonal aspect ratio over all cells at any 107 | /// level, where the diagonal aspect ratio of a cell is defined as the ratio 108 | /// of its longest diagonal length to its shortest diagonal length. 109 | ///TODO: 3f64.sqrt() 110 | pub const MAX_DIAG_ASPECT: f64 = 1.7320508075688772; 111 | 112 | impl Metric { 113 | /// value returns the value of the metric at the given level. 114 | pub fn value(&self, level: u8) -> f64 { 115 | // return math.Ldexp(m.Deriv, -m.Dim*level) 116 | ldexp(self.deriv, -1 * (self.dim as i32) * (level as i32)) 117 | } 118 | 119 | /// min_level returns the minimum level such that the metric is at most 120 | /// the given value, or maxLevel (30) if there is no such level. 121 | /// 122 | /// For example, MINLevel(0.1) returns the minimum level such that all cell diagonal 123 | /// lengths are 0.1 or smaller. The returned value is always a valid level. 124 | /// 125 | /// In C++, this is called GetLevelForMAXValue. 126 | pub fn min_level(&self, val: f64) -> u64 { 127 | if val < 0. { 128 | return MAX_LEVEL; 129 | } 130 | 131 | let level = -ilogb(val / self.deriv) >> u64::from(self.dim - 1); 132 | if level > (MAX_LEVEL as i32) { 133 | MAX_LEVEL 134 | } else if level < 0 { 135 | 0 136 | } else { 137 | level as u64 138 | } 139 | } 140 | 141 | /// max_level returns the maximum level such that the metric is at least 142 | /// the given value, or zero if there is no such level. 143 | /// 144 | /// For example, MAXLevel(0.1) returns the maximum level such that all cells have a 145 | /// minimum width of 0.1 or larger. The returned value is always a valid level. 146 | /// 147 | /// In C++, this is called GetLevelForMINValue. 148 | pub fn max_level(&self, val: f64) -> u64 { 149 | if val <= 0. { 150 | return MAX_LEVEL; 151 | } 152 | 153 | let level = ilogb(self.deriv / val) >> u64::from(self.dim - 1); 154 | if level > (MAX_LEVEL as i32) { 155 | MAX_LEVEL 156 | } else if level < 0 { 157 | 0 158 | } else { 159 | level as u64 160 | } 161 | } 162 | 163 | /// closest_level returns the level at which the metric has approximately the given 164 | /// value. The return value is always a valid level. For example, 165 | /// AVG_EDGEMETRIC.closest_level(0.1) returns the level at which the average cell edge 166 | /// length is approximately 0.1. 167 | pub fn closest_level(&self, val: f64) -> u64 { 168 | let x = if self.dim == 2 { 2. } else { SQRT_2 }; 169 | self.min_level(x * val) 170 | } 171 | } 172 | 173 | #[cfg(test)] 174 | mod tests { 175 | use super::*; 176 | 177 | #[test] 178 | fn test_metric() { 179 | assert_eq!(9, MIN_WIDTHMETRIC.max_level(0.001256)); 180 | 181 | // Check that the maximum aspect ratio of an individual cell is consistent 182 | // with the global minimums and maximums. 183 | assert!(MAX_EDGE_ASPECT >= 1.); 184 | assert!(MAX_EDGE_ASPECT <= MAX_EDGEMETRIC.deriv / MIN_EDGEMETRIC.deriv); 185 | 186 | assert!(MAX_DIAG_ASPECT >= 1.); 187 | assert!(MAX_DIAG_ASPECT <= MAX_DIAGMETRIC.deriv / MIN_DIAGMETRIC.deriv); 188 | 189 | // Check that area is consistent with edge and width. 190 | assert!(MIN_AREAMETRIC.deriv >= MIN_WIDTHMETRIC.deriv * MIN_EDGEMETRIC.deriv - 1e-15); 191 | assert!(MAX_AREAMETRIC.deriv <= MAX_WIDTHMETRIC.deriv * MAX_EDGEMETRIC.deriv + 1e-15); 192 | 193 | for level in -2..(MAX_LEVEL as i32 + 4) { 194 | let mut width = MIN_WIDTHMETRIC.deriv * 2.0_f64.powf(-level as f64); 195 | if width >= (MAX_LEVEL + 3) as f64 { 196 | width = 0.0; 197 | } 198 | 199 | // Check boundary cases (exactly equal to a threshold value). 200 | let expected = level.clamp(0, MAX_LEVEL as i32) as u64; 201 | assert!(MIN_WIDTHMETRIC.min_level(width) == expected); 202 | assert!(MIN_WIDTHMETRIC.max_level(width) == expected); 203 | assert!(MIN_WIDTHMETRIC.closest_level(width) == expected); 204 | 205 | // Also check non-boundary cases. 206 | assert!(MIN_WIDTHMETRIC.min_level(1.2 * width) == expected); 207 | assert!(MIN_WIDTHMETRIC.max_level(0.8 * width) == expected); 208 | assert!(MIN_WIDTHMETRIC.closest_level(1.2 * width) == expected); 209 | assert!(MIN_WIDTHMETRIC.closest_level(0.8 * width) == expected); 210 | } 211 | } 212 | 213 | #[test] 214 | fn test_metric_size_relations() { 215 | // Check that min <= avg <= max for each metric. 216 | let tests = vec![ 217 | ( 218 | MIN_ANGLE_SPANMETRIC, 219 | AVG_ANGLE_SPANMETRIC, 220 | MAX_ANGLE_SPANMETRIC, 221 | ), 222 | (MIN_WIDTHMETRIC, AVG_WIDTHMETRIC, MAX_WIDTHMETRIC), 223 | (MIN_EDGEMETRIC, AVG_EDGEMETRIC, MAX_EDGEMETRIC), 224 | (MIN_DIAGMETRIC, AVG_DIAGMETRIC, MAX_DIAGMETRIC), 225 | (MIN_AREAMETRIC, AVG_AREAMETRIC, MAX_AREAMETRIC), 226 | ]; 227 | for test in tests { 228 | assert!(test.0.deriv <= test.1.deriv); 229 | assert!(test.1.deriv <= test.2.deriv); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/s2/mod.rs: -------------------------------------------------------------------------------- 1 | mod stuv; 2 | 3 | pub mod cell; 4 | pub mod cellid; 5 | pub mod cellunion; 6 | 7 | pub mod cap; 8 | pub mod latlng; 9 | pub mod point; 10 | pub mod rect; 11 | pub mod rect_bounder; 12 | 13 | pub mod region; 14 | 15 | pub mod edgeutil; 16 | pub mod metric; 17 | pub mod predicates; 18 | 19 | pub mod shape; 20 | 21 | #[cfg(test)] 22 | mod random; 23 | -------------------------------------------------------------------------------- /src/s2/predicates.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Google Inc. All rights reserved. 3 | Copyright 2017 Jhyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | //! This file contains various predicates that are guaranteed to produce 19 | //! correct, consistent results. They are also relatively efficient. This is 20 | //! achieved by computing conservative error bounds and falling back to high 21 | //! precision or even exact arithmetic when the result is uncertain. Such 22 | //! predicates are useful in implementing robust algorithms. 23 | //! 24 | //! See also EdgeCrosser, which implements various exact 25 | //! edge-crossing predicates more efficiently than can be done here. 26 | 27 | use crate::consts::*; 28 | use crate::s2::point::Point; 29 | 30 | /// MAX_DETERMINANT_ERROR is the maximum error in computing (AxB).C where all vectors 31 | /// are unit length. Using standard inequalities, it can be shown that 32 | /// 33 | /// fl(AxB) = AxB + D where |D| <= (|AxB| + (2/sqrt(3))*|A|*|B|) * e 34 | /// 35 | /// where "fl()" denotes a calculation done in floating-point arithmetic, 36 | /// |x| denotes either absolute value or the L2-norm as appropriate, and 37 | /// e is a reasonably small value near the noise level of floating point 38 | /// number accuracy. Similarly, 39 | /// 40 | /// fl(B.C) = B.C + d where |d| <= (|B.C| + 2*|B|*|C|) * e . 41 | /// 42 | /// Applying these bounds to the unit-length vectors A,B,C and neglecting 43 | /// relative error (which does not affect the sign of the result), we get 44 | /// 45 | /// fl((AxB).C) = (AxB).C + d where |d| <= (3 + 2/sqrt(3)) * e 46 | const MAX_DETERMINANT_ERROR: f64 = 1.8274 * DBL_EPSILON; 47 | 48 | /// DET_ERROR_MULTIPLIER is the factor to scale the magnitudes by when checking 49 | /// for the sign of set of points with certainty. Using a similar technique to 50 | /// the one used for MAX_DETERMINANT_ERROR, the error is at most: 51 | /// 52 | /// |d| <= (3 + 6/sqrt(3)) * |A-C| * |B-C| * e 53 | /// 54 | /// If the determinant magnitude is larger than this value then we know 55 | /// its sign with certainty. 56 | const DET_ERROR_MULTIPLIER: f64 = 3.2321 * DBL_EPSILON; 57 | 58 | #[derive(PartialEq, Eq)] 59 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 60 | pub enum Direction { 61 | Clockwise, 62 | Indeterminate, 63 | CounterClockwise, 64 | } 65 | 66 | /// sign returns true if the points A, B, C are strictly counterclockwise, 67 | /// and returns false if the points are clockwise or collinear (i.e. if they are all 68 | /// contained on some great circle). 69 | /// 70 | /// Due to numerical errors, situations may arise that are mathematically 71 | /// impossible, e.g. ABC may be considered strictly CCW while BCA is not. 72 | /// However, the implementation guarantees the following: 73 | /// 74 | /// If Sign(a,b,c), then !Sign(c,b,a) for all a,b,c. 75 | pub fn sign(a: &Point, b: &Point, c: &Point) -> bool { 76 | // NOTE(dnadasi): In the C++ API the equivalent method here was known as "SimpleSign". 77 | 78 | // We compute the signed volume of the parallelepiped ABC. The usual 79 | // formula for this is (A ⨯ B) · C, but we compute it here using (C ⨯ A) · B 80 | // in order to ensure that ABC and CBA are not both CCW. This follows 81 | // from the following identities (which are true numerically, not just 82 | // mathematically): 83 | // 84 | // (1) x ⨯ y == -(y ⨯ x) 85 | // (2) -x · y == -(x · y) 86 | c.0.cross(&a.0).dot(&b.0) > 0f64 87 | } 88 | 89 | /// robust_sign returns a Direction representing the ordering of the points. 90 | /// CounterClockwise is returned if the points are in counter-clockwise order, 91 | /// Clockwise for clockwise, and Indeterminate if any two points are the same (collinear), 92 | /// or the sign could not completely be determined. 93 | /// 94 | /// This function has additional logic to make sure that the above properties hold even 95 | /// when the three points are coplanar, and to deal with the limitations of 96 | /// floating-point arithmetic. 97 | /// 98 | /// RobustSign satisfies the following conditions: 99 | /// 100 | /// (1) RobustSign(a,b,c) == Indeterminate if and only if a == b, b == c, or c == a 101 | /// (2) RobustSign(b,c,a) == RobustSign(a,b,c) for all a,b,c 102 | /// (3) RobustSign(c,b,a) == -RobustSign(a,b,c) for all a,b,c 103 | /// 104 | /// In other words: 105 | /// 106 | /// (1) The result is Indeterminate if and only if two points are the same. 107 | /// (2) Rotating the order of the arguments does not affect the result. 108 | /// (3) Exchanging any two arguments inverts the result. 109 | /// 110 | /// On the other hand, note that it is not true in general that 111 | /// RobustSign(-a,b,c) == -RobustSign(a,b,c), or any similar identities 112 | /// involving antipodal points. 113 | pub fn robust_sign(a: &Point, b: &Point, c: &Point) -> Direction { 114 | let sign = triage_sign(a, b, c); 115 | if sign == Direction::Indeterminate { 116 | expensive_sign(a, b, c) 117 | } else { 118 | sign 119 | } 120 | } 121 | 122 | /// stable_sign reports the direction sign of the points in a numerically stable way. 123 | /// Unlike triageSign, this method can usually compute the correct determinant sign 124 | /// even when all three points are as collinear as possible. For example if three 125 | /// points are spaced 1km apart along a random line on the Earth's surface using 126 | /// the nearest representable points, there is only a 0.4% chance that this method 127 | /// will not be able to find the determinant sign. The probability of failure 128 | /// decreases as the points get closer together; if the collinear points are 1 meter 129 | /// apart, the failure rate drops to 0.0004%. 130 | /// 131 | /// This method could be extended to also handle nearly-antipodal points, but antipodal 132 | /// points are rare in practice so it seems better to simply fall back to 133 | /// exact arithmetic in that case. 134 | pub fn stable_sign(a: &Point, b: &Point, c: &Point) -> Direction { 135 | let ab = b.0 - a.0; 136 | let ab2 = ab.norm2(); 137 | let bc = c.0 - b.0; 138 | let bc2 = bc.norm2(); 139 | let ca = a.0 - c.0; 140 | let ca2 = ca.norm2(); 141 | 142 | // Now compute the determinant ((A-C)x(B-C)).C, where the vertices have been 143 | // cyclically permuted if necessary so that AB is the longest edge. (This 144 | // minimizes the magnitude of cross product.) At the same time we also 145 | // compute the maximum error in the determinant. 146 | 147 | // The two shortest edges, pointing away from their common point. 148 | let (e1, e2, op) = if ab2 >= bc2 && ab2 >= ca2 { 149 | // AB is the longest edge. 150 | (ca, bc, &c.0) 151 | } else if bc2 >= ca2 { 152 | // BC is the longest edge. 153 | (ab, ca, &a.0) 154 | } else { 155 | // CA is the longest edge. 156 | (bc, ab, &b.0) 157 | }; 158 | 159 | let det = -1. * e1.cross(&e2).dot(&op); 160 | let max_err = DET_ERROR_MULTIPLIER * (e1.norm2() * e2.norm2()).sqrt(); 161 | 162 | // If the determinant isn't zero, within maxErr, we know definitively the point ordering. 163 | if det > max_err { 164 | Direction::CounterClockwise 165 | } else if det < -max_err { 166 | Direction::Clockwise 167 | } else { 168 | Direction::Indeterminate 169 | } 170 | } 171 | 172 | /// triage_sign returns the direction sign of the points. It returns Indeterminate if two 173 | /// points are identical or the result is uncertain. Uncertain cases can be resolved, if 174 | /// desired, by calling expensiveSign. 175 | /// 176 | /// The purpose of this method is to allow additional cheap tests to be done without 177 | /// calling expensiveSign. 178 | pub fn triage_sign(a: &Point, b: &Point, c: &Point) -> Direction { 179 | let det = a.0.cross(&b.0).dot(&c.0); 180 | if det > MAX_DETERMINANT_ERROR { 181 | Direction::CounterClockwise 182 | } else if det < -MAX_DETERMINANT_ERROR { 183 | Direction::Clockwise 184 | } else { 185 | Direction::Indeterminate 186 | } 187 | } 188 | 189 | /// expensive_sign reports the direction sign of the points. It returns Indeterminate 190 | /// if two of the input points are the same. It uses multiple-precision arithmetic 191 | /// to ensure that its results are always self-consistent. 192 | fn expensive_sign(a: &Point, b: &Point, c: &Point) -> Direction { 193 | // Return Indeterminate if and only if two points are the same. 194 | // This ensures RobustSign(a,b,c) == Indeterminate if and only if a == b, b == c, or c == a. 195 | // ie. Property 1 of RobustSign. 196 | if a == b || b == c || c == a { 197 | return Direction::Indeterminate; 198 | } 199 | 200 | // Next we try recomputing the determinant still using floating-point 201 | // arithmetic but in a more precise way. This is more expensive than the 202 | // simple calculation done by triageSign, but it is still *much* cheaper 203 | // than using arbitrary-precision arithmetic. This optimization is able to 204 | // compute the correct determinant sign in virtually all cases except when 205 | // the three points are truly collinear (e.g., three points on the equator). 206 | let det_sign = stable_sign(a, b, c); 207 | if det_sign != Direction::Indeterminate { 208 | det_sign 209 | } else { 210 | // Otherwise fall back to exact arithmetic and symbolic permutations. 211 | exact_sign(a, b, c, false) 212 | } 213 | } 214 | 215 | /// exact-sign reports the direction sign of the points using exact precision arithmetic. 216 | fn exact_sign(_: &Point, _: &Point, _: &Point, _: bool) -> Direction { 217 | // In the C++ version, the final computation is performed using OpenSSL's 218 | // Bignum exact precision math library. The existence of an equivalent 219 | // library in Go is indeterminate. In C++, using the exact precision library 220 | // to solve this stage is ~300x slower than the above checks. 221 | // TODO(roberts): Select and incorporate an appropriate Go exact precision 222 | // floating point library for the remaining calculations. 223 | Direction::Indeterminate 224 | } 225 | 226 | /* 227 | package s2 228 | 229 | import ( 230 | "math" 231 | "testing" 232 | 233 | "github.com/golang/geo/r3" 234 | ) 235 | 236 | func TestPredicatesSign(t *testing.T) { 237 | tests := []struct { 238 | p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z float64 239 | want bool 240 | }{ 241 | {1, 0, 0, 0, 1, 0, 0, 0, 1, true}, 242 | {0, 1, 0, 0, 0, 1, 1, 0, 0, true}, 243 | {0, 0, 1, 1, 0, 0, 0, 1, 0, true}, 244 | {1, 1, 0, 0, 1, 1, 1, 0, 1, true}, 245 | {-3, -1, 4, 2, -1, -3, 1, -2, 0, true}, 246 | 247 | // All degenerate cases of Sign(). Let M_1, M_2, ... be the sequence of 248 | // submatrices whose determinant sign is tested by that function. Then the 249 | // i-th test below is a 3x3 matrix M (with rows A, B, C) such that: 250 | // 251 | // det(M) = 0 252 | // det(M_j) = 0 for j < i 253 | // det(M_i) != 0 254 | // A < B < C in lexicographic order. 255 | // det(M_1) = b0*c1 - b1*c0 256 | {-3, -1, 0, -2, 1, 0, 1, -2, 0, false}, 257 | // det(M_2) = b2*c0 - b0*c2 258 | {-6, 3, 3, -4, 2, -1, -2, 1, 4, false}, 259 | // det(M_3) = b1*c2 - b2*c1 260 | {0, -1, -1, 0, 1, -2, 0, 2, 1, false}, 261 | // From this point onward, B or C must be zero, or B is proportional to C. 262 | // det(M_4) = c0*a1 - c1*a0 263 | {-1, 2, 7, 2, 1, -4, 4, 2, -8, false}, 264 | // det(M_5) = c0 265 | {-4, -2, 7, 2, 1, -4, 4, 2, -8, false}, 266 | // det(M_6) = -c1 267 | {0, -5, 7, 0, -4, 8, 0, -2, 4, false}, 268 | // det(M_7) = c2*a0 - c0*a2 269 | {-5, -2, 7, 0, 0, -2, 0, 0, -1, false}, 270 | // det(M_8) = c2 271 | {0, -2, 7, 0, 0, 1, 0, 0, 2, false}, 272 | } 273 | 274 | for _, test := range tests { 275 | p1 := Point{r3.Vector{test.p1x, test.p1y, test.p1z}} 276 | p2 := Point{r3.Vector{test.p2x, test.p2y, test.p2z}} 277 | p3 := Point{r3.Vector{test.p3x, test.p3y, test.p3z}} 278 | result := Sign(p1, p2, p3) 279 | if result != test.want { 280 | t.Errorf("Sign(%v, %v, %v) = %v, want %v", p1, p2, p3, result, test.want) 281 | } 282 | if test.want { 283 | // For these cases we can test the reversibility condition 284 | result = Sign(p3, p2, p1) 285 | if result == test.want { 286 | t.Errorf("Sign(%v, %v, %v) = %v, want %v", p3, p2, p1, result, !test.want) 287 | } 288 | } 289 | } 290 | } 291 | 292 | // Points used in the various RobustSign tests. 293 | var ( 294 | // The following points happen to be *exactly collinear* along a line that it 295 | // approximate tangent to the surface of the unit sphere. In fact, C is the 296 | // exact midpoint of the line segment AB. All of these points are close 297 | // enough to unit length to satisfy r3.Vector.IsUnit(). 298 | poA = Point{r3.Vector{0.72571927877036835, 0.46058825605889098, 0.51106749730504852}} 299 | poB = Point{r3.Vector{0.7257192746638208, 0.46058826573818168, 0.51106749441312738}} 300 | poC = Point{r3.Vector{0.72571927671709457, 0.46058826089853633, 0.51106749585908795}} 301 | 302 | // The points "x1" and "x2" are exactly proportional, i.e. they both lie 303 | // on a common line through the origin. Both points are considered to be 304 | // normalized, and in fact they both satisfy (x == x.Normalize()). 305 | // Therefore the triangle (x1, x2, -x1) consists of three distinct points 306 | // that all lie on a common line through the origin. 307 | x1 = Point{r3.Vector{0.99999999999999989, 1.4901161193847655e-08, 0}} 308 | x2 = Point{r3.Vector{1, 1.4901161193847656e-08, 0}} 309 | 310 | // Here are two more points that are distinct, exactly proportional, and 311 | // that satisfy (x == x.Normalize()). 312 | x3 = Point{r3.Vector{1, 1, 1}.Normalize()} 313 | x4 = Point{x3.Mul(0.99999999999999989)} 314 | 315 | // The following three points demonstrate that Normalize() is not idempotent, i.e. 316 | // y0.Normalize() != y0.Normalize().Normalize(). Both points are exactly proportional. 317 | y0 = Point{r3.Vector{1, 1, 0}} 318 | y1 = Point{y0.Normalize()} 319 | y2 = Point{y1.Normalize()} 320 | ) 321 | 322 | func TestPredicatesRobustSignEqualities(t *testing.T) { 323 | tests := []struct { 324 | p1, p2 Point 325 | want bool 326 | }{ 327 | {Point{poC.Sub(poA.Vector)}, Point{poB.Sub(poC.Vector)}, true}, 328 | {x1, Point{x1.Normalize()}, true}, 329 | {x2, Point{x2.Normalize()}, true}, 330 | {x3, Point{x3.Normalize()}, true}, 331 | {x4, Point{x4.Normalize()}, true}, 332 | {x3, x4, false}, 333 | {y1, y2, false}, 334 | {y2, Point{y2.Normalize()}, true}, 335 | } 336 | 337 | for _, test := range tests { 338 | if got := test.p1.Vector == test.p2.Vector; got != test.want { 339 | t.Errorf("Testing equality for RobustSign. %v = %v, got %v want %v", test.p1, test.p2, got, test.want) 340 | } 341 | } 342 | } 343 | 344 | func TestPredicatesRobustSign(t *testing.T) { 345 | x := Point{r3.Vector{1, 0, 0}} 346 | y := Point{r3.Vector{0, 1, 0}} 347 | z := Point{r3.Vector{0, 0, 1}} 348 | 349 | tests := []struct { 350 | p1, p2, p3 Point 351 | want Direction 352 | }{ 353 | // Simple collinear points test cases. 354 | // a == b != c 355 | {x, x, z, Indeterminate}, 356 | // a != b == c 357 | {x, y, y, Indeterminate}, 358 | // c == a != b 359 | {z, x, z, Indeterminate}, 360 | // CCW 361 | {x, y, z, CounterClockwise}, 362 | // CW 363 | {z, y, x, Clockwise}, 364 | 365 | // Edge cases: 366 | // The following points happen to be *exactly collinear* along a line that it 367 | // approximate tangent to the surface of the unit sphere. In fact, C is the 368 | // exact midpoint of the line segment AB. All of these points are close 369 | // enough to unit length to satisfy S2::IsUnitLength(). 370 | { 371 | // Until we get ExactSign, this will only return Indeterminate. 372 | // It should be Clockwise. 373 | poA, poB, poC, Indeterminate, 374 | }, 375 | 376 | // The points "x1" and "x2" are exactly proportional, i.e. they both lie 377 | // on a common line through the origin. Both points are considered to be 378 | // normalized, and in fact they both satisfy (x == x.Normalize()). 379 | // Therefore the triangle (x1, x2, -x1) consists of three distinct points 380 | // that all lie on a common line through the origin. 381 | { 382 | // Until we get ExactSign, this will only return Indeterminate. 383 | // It should be CounterClockwise. 384 | x1, x2, Point{x1.Mul(-1.0)}, Indeterminate, 385 | }, 386 | 387 | // Here are two more points that are distinct, exactly proportional, and 388 | // that satisfy (x == x.Normalize()). 389 | { 390 | // Until we get ExactSign, this will only return Indeterminate. 391 | // It should be Clockwise. 392 | x3, x4, Point{x3.Mul(-1.0)}, Indeterminate, 393 | }, 394 | 395 | // The following points demonstrate that Normalize() is not idempotent, 396 | // i.e. y0.Normalize() != y0.Normalize().Normalize(). Both points satisfy 397 | // S2::IsNormalized(), though, and the two points are exactly proportional. 398 | { 399 | // Until we get ExactSign, this will only return Indeterminate. 400 | // It should be CounterClockwise. 401 | y1, y2, Point{y1.Mul(-1.0)}, Indeterminate, 402 | }, 403 | } 404 | 405 | for _, test := range tests { 406 | result := RobustSign(test.p1, test.p2, test.p3) 407 | if result != test.want { 408 | t.Errorf("RobustSign(%v, %v, %v) got %v, want %v", 409 | test.p1, test.p2, test.p3, result, test.want) 410 | } 411 | // Test RobustSign(b,c,a) == RobustSign(a,b,c) for all a,b,c 412 | rotated := RobustSign(test.p2, test.p3, test.p1) 413 | if rotated != result { 414 | t.Errorf("RobustSign(%v, %v, %v) vs Rotated RobustSign(%v, %v, %v) got %v, want %v", 415 | test.p1, test.p2, test.p3, test.p2, test.p3, test.p1, rotated, result) 416 | } 417 | // Test RobustSign(c,b,a) == -RobustSign(a,b,c) for all a,b,c 418 | want := Clockwise 419 | if result == Clockwise { 420 | want = CounterClockwise 421 | } else if result == Indeterminate { 422 | want = Indeterminate 423 | } 424 | reversed := RobustSign(test.p3, test.p2, test.p1) 425 | if reversed != want { 426 | t.Errorf("RobustSign(%v, %v, %v) vs Reversed RobustSign(%v, %v, %v) got %v, want %v", 427 | test.p1, test.p2, test.p3, test.p3, test.p2, test.p1, reversed, -1*result) 428 | } 429 | } 430 | 431 | // Test cases that should not be indeterminate. 432 | /* 433 | Uncomment these tests once RobustSign is completed. 434 | if got := RobustSign(poA, poB, poC); got == Indeterminate { 435 | t.Errorf("RobustSign(%v,%v,%v) = %v, want not Indeterminate", poA, poA, poA, got) 436 | } 437 | if got := RobustSign(x1, x2, Point{x1.Mul(-1)}); got == Indeterminate { 438 | t.Errorf("RobustSign(%v,%v,%v) = %v, want not Indeterminate", x1, x2, x1.Mul(-1), got) 439 | } 440 | if got := RobustSign(x3, x4, Point{x3.Mul(-1)}); got == Indeterminate { 441 | t.Errorf("RobustSign(%v,%v,%v) = %v, want not Indeterminate", x3, x4, x3.Mul(-1), got) 442 | } 443 | if got := RobustSign(y1, y2, Point{y1.Mul(-1)}); got == Indeterminate { 444 | t.Errorf("RobustSign(%v,%v,%v) = %v, want not Indeterminate", x1, x2, y1.Mul(-1), got) 445 | } 446 | */ 447 | } 448 | 449 | func TestPredicatesStableSignFailureRate(t *testing.T) { 450 | const earthRadiusKm = 6371.01 451 | const iters = 1000 452 | 453 | // Verify that stableSign is able to handle most cases where the three 454 | // points are as collinear as possible. (For reference, triageSign fails 455 | // almost 100% of the time on this test.) 456 | // 457 | // Note that the failure rate *decreases* as the points get closer together, 458 | // and the decrease is approximately linear. For example, the failure rate 459 | // is 0.4% for collinear points spaced 1km apart, but only 0.0004% for 460 | // collinear points spaced 1 meter apart. 461 | // 462 | // 1km spacing: < 1% (actual is closer to 0.4%) 463 | // 10km spacing: < 10% (actual is closer to 4%) 464 | want := 0.01 465 | spacing := 1.0 466 | 467 | // Estimate the probability that stableSign will not be able to compute 468 | // the determinant sign of a triangle A, B, C consisting of three points 469 | // that are as collinear as possible and spaced the given distance apart 470 | // by counting up the times it returns Indeterminate. 471 | failureCount := 0 472 | m := math.Tan(spacing / earthRadiusKm) 473 | for iter := 0; iter < iters; iter++ { 474 | f := randomFrame() 475 | a := f.col(0) 476 | x := f.col(1) 477 | 478 | b := Point{a.Sub(x.Mul(m)).Normalize()} 479 | c := Point{a.Add(x.Mul(m)).Normalize()} 480 | sign := stableSign(a, b, c) 481 | if sign != Indeterminate { 482 | // TODO(roberts): Once exactSign is implemented, uncomment this case. 483 | //if got := exactSign(a, b, c, true); got != sign { 484 | // t.Errorf("exactSign(%v, %v, %v, true) = %v, want %v", a, b, c, got, sign) 485 | //} 486 | } else { 487 | failureCount++ 488 | } 489 | } 490 | 491 | rate := float64(failureCount) / float64(iters) 492 | if rate >= want { 493 | t.Errorf("stableSign failure rate for spacing %v km = %v, want %v", spacing, rate, want) 494 | } 495 | } 496 | 497 | func BenchmarkSign(b *testing.B) { 498 | p1 := Point{r3.Vector{-3, -1, 4}} 499 | p2 := Point{r3.Vector{2, -1, -3}} 500 | p3 := Point{r3.Vector{1, -2, 0}} 501 | for i := 0; i < b.N; i++ { 502 | Sign(p1, p2, p3) 503 | } 504 | } 505 | 506 | // BenchmarkRobustSignSimple runs the benchmark for points that satisfy the first 507 | // checks in RobustSign to compare the performance to that of Sign(). 508 | func BenchmarkRobustSignSimple(b *testing.B) { 509 | p1 := Point{r3.Vector{-3, -1, 4}} 510 | p2 := Point{r3.Vector{2, -1, -3}} 511 | p3 := Point{r3.Vector{1, -2, 0}} 512 | for i := 0; i < b.N; i++ { 513 | RobustSign(p1, p2, p3) 514 | } 515 | } 516 | 517 | // BenchmarkRobustSignNearCollinear runs the benchmark for points that are almost but not 518 | // quite collinear, so the tests have to use most of the calculations of RobustSign 519 | // before getting to an answer. 520 | func BenchmarkRobustSignNearCollinear(b *testing.B) { 521 | for i := 0; i < b.N; i++ { 522 | RobustSign(poA, poB, poC) 523 | } 524 | } 525 | */ 526 | -------------------------------------------------------------------------------- /src/s2/random.rs: -------------------------------------------------------------------------------- 1 | use cgmath; 2 | use std::f64::consts::PI; 3 | 4 | use crate::s2::cap::Cap; 5 | use crate::s2::cellid::*; 6 | use crate::s2::latlng::LatLng; 7 | use crate::s2::point::{self, Point}; 8 | use crate::s2::rect::Rect; 9 | use rand; 10 | use rand::Rng; 11 | 12 | pub fn rng() -> rand::rngs::ThreadRng { 13 | rand::thread_rng() 14 | } 15 | 16 | /// skewed_int returns a number in the range [0,2^max_log-1] with bias towards smaller numbers. 17 | pub fn skewed_int(rng: &mut R, max_log: usize) -> usize 18 | where 19 | R: Rng, 20 | { 21 | let base = rng.gen_range(0..max_log + 1); 22 | rng.gen_range(0..1 << 31) & ((1 << base) - 1) 23 | } 24 | 25 | /// cap returns a cap with a random axis such that the log of its area is 26 | /// uniformly distributed between the logs of the two given values. The log of 27 | /// the cap angle is also approximately uniformly distributed. 28 | pub fn cap(rng: &mut R, min_area: f64, max_area: f64) -> Cap 29 | where 30 | R: Rng, 31 | { 32 | let cap_area = max_area * (min_area / max_area).powf(rng.gen_range(0.0..1.0)); 33 | Cap::from_center_area(&point(rng), cap_area) 34 | } 35 | 36 | /// point returns a random unit-length vector. 37 | pub fn point(rng: &mut R) -> Point { 38 | Point::from_coords( 39 | rng.gen_range(-1.0..1.0), 40 | rng.gen_range(-1.0..1.0), 41 | rng.gen_range(-1.0..1.0), 42 | ) 43 | } 44 | 45 | pub fn latlng(rng: &mut R) -> LatLng { 46 | LatLng::from(point::(rng)) 47 | } 48 | 49 | pub fn rect(rng: &mut R) -> Rect { 50 | use std::ops::Add; 51 | Rect::from(latlng::(rng)).add(&latlng::(rng)) 52 | } 53 | 54 | pub fn frame(rng: &mut R) -> cgmath::Matrix3 { 55 | let z = point(rng); 56 | frame_at_point(rng, z) 57 | } 58 | 59 | pub fn frame_at_point(rng: &mut R, z: Point) -> cgmath::Matrix3 { 60 | let p = point(rng); 61 | let x = z.cross(&p).normalize(); 62 | let y = z.cross(&x).normalize(); 63 | 64 | cgmath::Matrix3::from_cols(x.into(), y.into(), z.into()) 65 | } 66 | 67 | pub fn cellid(rng: &mut R) -> CellID 68 | where 69 | R: Rng, 70 | { 71 | let level = rng.gen_range(0..(MAX_LEVEL + 1)); 72 | cellid_for_level(rng, level) 73 | } 74 | 75 | pub fn cellid_for_level(rng: &mut R, level: u64) -> CellID 76 | where 77 | R: Rng, 78 | { 79 | let face = rng.gen_range(0..NUM_FACES as u64); 80 | let pos = rng.next_u64() & ((1 << POS_BITS) - 1); 81 | let cellid = CellID::from_face_pos_level(face, pos, level); 82 | assert_eq!(face, cellid.face() as u64); 83 | assert_eq!(level, cellid.level()); 84 | 85 | cellid 86 | } 87 | 88 | pub fn one_in(rng: &mut R, n: u64) -> bool 89 | where 90 | R: Rng, 91 | { 92 | rng.gen_range(0..n) == 0 93 | } 94 | 95 | // sample_point_from_cap returns a point chosen uniformly at random (with respect 96 | // to area) from the given cap. 97 | pub fn sample_point_from_cap(rng: &mut R, c: Cap) -> Point 98 | where 99 | R: Rng, 100 | { 101 | // We consider the cap axis to be the "z" axis. We choose two other axes to 102 | // complete the coordinate frame. 103 | let center = c.center(); 104 | let m = center.frame(); 105 | 106 | // The surface area of a spherical cap is directly proportional to its 107 | // height. First we choose a random height, and then we choose a random 108 | // point along the circle at that height. 109 | let h = rng.gen_range(0.0..1.0) * c.height(); 110 | let theta = 2. * PI * rng.gen_range(0.0..1.0); 111 | let r = (h * (2. - h)).sqrt(); 112 | 113 | // The result should already be very close to unit-length, but we might as 114 | // well make it accurate as possible. 115 | point::from_frame( 116 | &m, 117 | &Point::from_coords(theta.cos() * r, theta.sin() * r, 1. - h).normalize(), 118 | ) 119 | } 120 | -------------------------------------------------------------------------------- /src/s2/region.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Google Inc. All rights reserved. 3 | Copyright 2017 Jhyun Yu. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | use std; 19 | use std::cmp::min; 20 | use std::collections::BinaryHeap; 21 | 22 | use crate::consts::clamp; 23 | use crate::s2::cap::Cap; 24 | use crate::s2::cell::Cell; 25 | use crate::s2::cellid::*; 26 | use crate::s2::cellunion::CellUnion; 27 | use crate::s2::rect::Rect; 28 | 29 | /// A Region represents a two-dimensional region on the unit sphere. 30 | /// 31 | /// The purpose of this interface is to allow complex regions to be 32 | /// approximated as simpler regions. The interface is restricted to methods 33 | /// that are useful for computing approximations. 34 | pub trait Region { 35 | /// cap_bound returns a bounding spherical cap. This is not guaranteed to be exact. 36 | fn cap_bound(&self) -> Cap; 37 | 38 | /// rect_bound returns a bounding latitude-longitude rectangle that contains 39 | /// the region. The bounds are not guaranteed to be tight. 40 | fn rect_bound(&self) -> Rect { 41 | let cap = self.cap_bound(); 42 | cap.rect_bound() 43 | } 44 | 45 | /// contains_cell reports whether the region completely contains the given region. 46 | /// It returns false if containment could not be determined. 47 | fn contains_cell(&self, cell: &Cell) -> bool { 48 | self.cap_bound().contains_cell(cell) 49 | } 50 | 51 | /// intersects_cell reports whether the region intersects the given cell or 52 | /// if intersection could not be determined. It returns false if the region 53 | /// does not intersect. 54 | fn intersects_cell(&self, cell: &Cell) -> bool { 55 | self.cap_bound().intersects_cell(cell) 56 | } 57 | 58 | fn cell_union_bound(&self) -> Vec { 59 | self.cap_bound().cell_union_bound() 60 | } 61 | } 62 | 63 | /// RegionCoverer allows arbitrary regions to be approximated as unions of cells (CellUnion). 64 | /// This is useful for implementing various sorts of search and precomputation operations. 65 | /// 66 | /// Typical usage: 67 | /// 68 | /// rc := &s2.RegionCoverer{MaxLevel: 30, MaxCells: 5} 69 | /// r := s2.Region(CapFromCenterArea(center, area)) 70 | /// covering := rc.Covering(r) 71 | /// 72 | /// This yields a CellUnion of at most 5 cells that is guaranteed to cover the 73 | /// given region (a disc-shaped region on the sphere). 74 | /// 75 | /// For covering, only cells where (level - MinLevel) is a multiple of LevelMod will be used. 76 | /// This effectively allows the branching factor of the S2 CellID hierarchy to be increased. 77 | /// Currently the only parameter values allowed are 0/1, 2, or 3, corresponding to 78 | /// branching factors of 4, 16, and 64 respectively. 79 | /// 80 | /// Note the following: 81 | /// 82 | /// - MinLevel takes priority over MaxCells, i.e. cells below the given level will 83 | /// never be used even if this causes a large number of cells to be returned. 84 | /// 85 | /// - For any setting of MaxCells, up to 6 cells may be returned if that 86 | /// is the minimum number of cells required (e.g. if the region intersects 87 | /// all six face cells). Up to 3 cells may be returned even for very tiny 88 | /// convex regions if they happen to be located at the intersection of 89 | /// three cube faces. 90 | /// 91 | /// - For any setting of MaxCells, an arbitrary number of cells may be 92 | /// returned if MinLevel is too high for the region being approximated. 93 | /// 94 | /// - If MaxCells is less than 4, the area of the covering may be 95 | /// arbitrarily large compared to the area of the original region even if 96 | /// the region is convex (e.g. a Cap or Rect). 97 | /// 98 | /// The approximation algorithm is not optimal but does a pretty good job in 99 | /// practice. The output does not always use the maximum number of cells 100 | /// allowed, both because this would not always yield a better approximation, 101 | /// and because MaxCells is a limit on how much work is done exploring the 102 | /// possible covering as well as a limit on the final output size. 103 | /// 104 | /// Because it is an approximation algorithm, one should not rely on the 105 | /// stability of the output. In particular, the output of the covering algorithm 106 | /// may change across different versions of the library. 107 | /// 108 | /// One can also generate interior coverings, which are sets of cells which 109 | /// are entirely contained within a region. Interior coverings can be 110 | /// empty, even for non-empty regions, if there are no cells that satisfy 111 | /// the provided constraints and are contained by the region. Note that for 112 | /// performance reasons, it is wise to specify a MaxLevel when computing 113 | /// interior coverings - otherwise for regions with small or zero area, the 114 | /// algorithm may spend a lot of time subdividing cells all the way to leaf 115 | /// level to try to find contained cells. 116 | #[derive(Clone, Debug)] 117 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 118 | pub struct RegionCoverer { 119 | /// the minimum cell level to be used. 120 | pub min_level: u8, 121 | /// the maximum cell level to be used. 122 | pub max_level: u8, 123 | /// the level_mod to be used. 124 | pub level_mod: u8, 125 | /// the maximum desired number of cells in the approximation. 126 | pub max_cells: usize, 127 | } 128 | 129 | struct Coverer<'a, R> 130 | where 131 | R: Region + 'static, 132 | { 133 | constraint: &'a RegionCoverer, 134 | region: &'a R, 135 | result: Vec, 136 | pq: BinaryHeap, 137 | interior_covering: bool, 138 | } 139 | 140 | #[derive(Debug)] 141 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 142 | struct Candidate { 143 | cell: Cell, 144 | terminal: bool, 145 | children: Vec, 146 | priority: isize, 147 | } 148 | 149 | impl std::cmp::PartialEq for Candidate { 150 | fn eq(&self, other: &Candidate) -> bool { 151 | self.cell.id == other.cell.id 152 | } 153 | } 154 | impl std::cmp::Eq for Candidate {} 155 | impl std::cmp::PartialOrd for Candidate { 156 | fn partial_cmp(&self, other: &Candidate) -> Option { 157 | Some(self.priority.cmp(&other.priority)) 158 | } 159 | } 160 | impl std::cmp::Ord for Candidate { 161 | fn cmp(&self, other: &Candidate) -> std::cmp::Ordering { 162 | self.priority.cmp(&other.priority) 163 | } 164 | } 165 | 166 | impl<'a, R> Coverer<'a, R> 167 | where 168 | R: Region, 169 | { 170 | // new_candidate returns a new candidate with no children if the cell intersects the given region. 171 | // The candidate is marked as terminal if it should not be expanded further. 172 | fn new_candidate(&self, cell: Cell) -> Option { 173 | if !self.region.intersects_cell(&cell) { 174 | return None; 175 | } 176 | 177 | let level = cell.level(); 178 | let mut cand = Candidate { 179 | cell: cell.clone(), 180 | terminal: false, 181 | children: Vec::new(), 182 | priority: 0, 183 | }; 184 | 185 | if level >= self.constraint.min_level { 186 | if self.interior_covering { 187 | if self.region.contains_cell(&cell) { 188 | cand.terminal = true; 189 | } else if level + self.constraint.level_mod > self.constraint.max_level { 190 | return None; 191 | } 192 | } else if level + self.constraint.level_mod > self.constraint.max_level 193 | || self.region.contains_cell(&cell) 194 | { 195 | cand.terminal = true; 196 | } 197 | }; 198 | 199 | Some(cand) 200 | } 201 | 202 | /// expand_children populates the children of the candidate by expanding the given number of 203 | /// levels from the given cell. Returns the number of children that were marked "terminal". 204 | fn expand_children(&self, cand: &mut Candidate, cell: Cell, mut num_levels: i32) -> usize { 205 | num_levels -= 1; 206 | let mut num_terminals = 0; 207 | for ci in cell.id.child_iter() { 208 | let child_cell = Cell::from(ci); 209 | if num_levels > 0 { 210 | if self.region.intersects_cell(&child_cell) { 211 | num_terminals += self.expand_children(cand, child_cell, num_levels); 212 | } 213 | continue; 214 | } 215 | 216 | if let Some(child) = self.new_candidate(child_cell) { 217 | if child.terminal { 218 | num_terminals += 1; 219 | } 220 | cand.children.push(child); 221 | } 222 | } 223 | num_terminals 224 | } 225 | 226 | /// add_candidate adds the given candidate to the result if it is marked as "terminal", 227 | /// otherwise expands its children and inserts it into the priority queue. 228 | /// Passing an argument of nil does nothing. 229 | fn add_candidate(&mut self, mut cand: Candidate) { 230 | if cand.terminal { 231 | self.result.push(cand.cell.id); 232 | return; 233 | } 234 | 235 | // Expand one level at a time until we hit minLevel to ensure that we don't skip over it. 236 | let level = cand.cell.level(); 237 | let num_levels = if level < self.constraint.min_level { 238 | 1 239 | } else { 240 | self.constraint.level_mod 241 | }; 242 | 243 | let cand_cell = cand.cell.clone(); 244 | let num_terminals = self.expand_children(&mut cand, cand_cell, i32::from(num_levels)); 245 | let max_children_shift = self.constraint.level_mod * 2; 246 | if cand.children.is_empty() { 247 | return; 248 | } 249 | 250 | if !self.interior_covering 251 | && num_terminals == (1 << max_children_shift) 252 | && level >= self.constraint.min_level 253 | { 254 | // Optimization: add the parent cell rather than all of its children. 255 | // We can't do this for interior coverings, since the children just 256 | // intersect the region, but may not be contained by it - we need to 257 | // subdivide them further. 258 | cand.terminal = true; 259 | self.add_candidate(cand); 260 | return; 261 | } 262 | 263 | // We negate the priority so that smaller absolute priorities are returned 264 | // first. The heuristic is designed to refine the largest cells first, 265 | // since those are where we have the largest potential gain. Among cells 266 | // of the same size, we prefer the cells with the fewest children. 267 | // Finally, among cells with equal numbers of children we prefer those 268 | // with the smallest number of children that cannot be refined further. 269 | cand.priority = -((((((level as usize) << max_children_shift) + cand.children.len()) 270 | << max_children_shift) 271 | + num_terminals) as isize); 272 | self.pq.push(cand) 273 | } 274 | 275 | /// adjust_cell_levels ensures that all cells with level > minLevel also satisfy levelMod, 276 | /// by replacing them with an ancestor if necessary. Cell levels smaller 277 | /// than minLevel are not modified (see AdjustLevel). The output is 278 | /// then normalized to ensure that no redundant cells are present. 279 | fn adjust_cell_levels(&self, cells: &mut Vec) { 280 | if self.constraint.level_mod == 1 { 281 | return; 282 | } 283 | let mut out: Vec = Vec::new(); 284 | 285 | for &ci in cells.iter() { 286 | let level = ci.level() as u8; 287 | let new_level = self.constraint.adjust_level(level); 288 | let cur = if new_level != level { 289 | ci.parent(new_level.into()) 290 | } else { 291 | ci 292 | }; 293 | 294 | let mut pop_last = false; 295 | if let Some(last) = out.last() { 296 | if last.contains(&cur) { 297 | continue; 298 | } 299 | if cur.contains(&last) { 300 | pop_last = true; 301 | } 302 | } 303 | if pop_last { 304 | out.pop(); 305 | } 306 | out.push(cur); 307 | } 308 | cells.clear(); 309 | cells.extend_from_slice(&out); 310 | } 311 | 312 | /// initial_candidates computes a set of initial candidates that cover the given region. 313 | fn initial_candidates(&mut self) { 314 | // Optimization: start with a small (usually 4 cell) covering of the region's bounding cap. 315 | let temp = RegionCoverer { 316 | min_level: 0, 317 | max_level: self.constraint.max_level, 318 | level_mod: 1, 319 | max_cells: min(self.constraint.max_cells, 4), 320 | }; 321 | 322 | let mut cells = temp.fast_covering(self.region); 323 | let mut v = &mut cells.0; 324 | self.adjust_cell_levels(&mut v); 325 | for ci in v.iter() { 326 | if let Some(cand) = self.new_candidate(Cell::from(ci)) { 327 | self.add_candidate(cand); 328 | } 329 | } 330 | } 331 | 332 | /// covering_internal generates a covering and stores it in result. 333 | /// Strategy: Start with the 6 faces of the cube. Discard any 334 | /// that do not intersect the shape. Then repeatedly choose the 335 | /// largest cell that intersects the shape and subdivide it. 336 | /// 337 | /// result contains the cells that will be part of the output, while pq 338 | /// contains cells that we may still subdivide further. Cells that are 339 | /// entirely contained within the region are immediately added to the output, 340 | /// while cells that do not intersect the region are immediately discarded. 341 | /// Therefore pq only contains cells that partially intersect the region. 342 | /// Candidates are prioritized first according to cell size (larger cells 343 | /// first), then by the number of intersecting children they have (fewest 344 | /// children first), and then by the number of fully contained children 345 | /// (fewest children first). 346 | fn covering_internal(&mut self) { 347 | self.initial_candidates(); 348 | 349 | while let Some(mut cand) = self.pq.pop() { 350 | if !(!self.interior_covering || self.result.len() < self.constraint.max_cells) { 351 | break; 352 | } 353 | 354 | // For interior covering we keep subdividing no matter how many children 355 | // candidate has. If we reach MaxCells before expanding all children, 356 | // we will just use some of them. 357 | // For exterior covering we cannot do this, because result has to cover the 358 | // whole region, so all children have to be used. 359 | // candidate.numChildren == 1 case takes care of the situation when we 360 | // already have more then MaxCells in result (minLevel is too high). 361 | // Subdividing of the candidate with one child does no harm in this case. 362 | if self.interior_covering 363 | || cand.cell.level() < self.constraint.min_level 364 | || cand.children.len() == 1 365 | || self.result.len() + self.pq.len() + cand.children.len() 366 | <= self.constraint.max_cells 367 | { 368 | for child in cand.children { 369 | if !self.interior_covering || self.result.len() < self.constraint.max_cells { 370 | self.add_candidate(child) 371 | } 372 | } 373 | } else { 374 | cand.terminal = true; 375 | self.add_candidate(cand) 376 | } 377 | } 378 | 379 | self.pq.clear(); 380 | } 381 | } 382 | 383 | impl RegionCoverer { 384 | fn new_coverer<'a, R>(&'a self, region: &'a R) -> Coverer<'a, R> 385 | where 386 | R: Region, 387 | { 388 | Coverer { 389 | constraint: self, 390 | 391 | region, 392 | result: Vec::new(), 393 | pq: BinaryHeap::new(), 394 | interior_covering: false, 395 | } 396 | } 397 | 398 | /// covering returns a CellUnion that covers the given region and satisfies the various 399 | /// restrictions. 400 | pub fn covering(&self, region: &R) -> CellUnion 401 | where 402 | R: Region + 'static, 403 | { 404 | let mut covering = self.cellunion(region); 405 | covering.denormalize( 406 | clamp(self.min_level, 0, MAX_LEVEL as u8).into(), 407 | clamp(self.level_mod, 1, 3).into(), 408 | ); 409 | covering 410 | } 411 | 412 | /// interior_covering returns a CellUnion that is contained within the given region and 413 | /// satisfies the various restrictions. 414 | pub fn interior_covering(&self, region: &R) -> CellUnion 415 | where 416 | R: Region + 'static, 417 | { 418 | let mut int_covering = self.interior_cellunion(region); 419 | int_covering.denormalize( 420 | clamp(self.min_level, 0, MAX_LEVEL as u8).into(), 421 | clamp(self.level_mod, 1, 3).into(), 422 | ); 423 | int_covering 424 | } 425 | 426 | /// cellunion returns a normalized CellUnion that covers the given region and 427 | /// satisfies the restrictions except for minLevel and levelMod. These criteria 428 | /// cannot be satisfied using a cell union because cell unions are 429 | /// automatically normalized by replacing four child cells with their parent 430 | /// whenever possible. (Note that the list of cell ids passed to the CellUnion 431 | /// constructor does in fact satisfy all the given restrictions.) 432 | fn cellunion<'a, R>(&self, region: &'a R) -> CellUnion 433 | where 434 | R: Region + 'static, 435 | { 436 | let mut c = self.new_coverer(region); 437 | c.covering_internal(); 438 | let mut cu = CellUnion(c.result); 439 | cu.normalize(); 440 | cu 441 | } 442 | 443 | /// interior_cellunion returns a normalized CellUnion that is contained within the given region and 444 | /// satisfies the restrictions except for minLevel and levelMod. These criteria 445 | /// cannot be satisfied using a cell union because cell unions are 446 | /// automatically normalized by replacing four child cells with their parent 447 | /// whenever possible. (Note that the list of cell ids passed to the CellUnion 448 | /// constructor does in fact satisfy all the given restrictions.) 449 | pub fn interior_cellunion<'a, R>(&self, region: &'a R) -> CellUnion 450 | where 451 | R: Region + 'static, 452 | { 453 | let mut c = self.new_coverer(region); 454 | c.interior_covering = true; 455 | c.covering_internal(); 456 | let mut cu = CellUnion(c.result); 457 | cu.normalize(); 458 | cu 459 | } 460 | 461 | /// FastCovering returns a CellUnion that covers the given region similar to Covering, 462 | /// except that this method is much faster and the coverings are not as tight. 463 | /// All of the usual parameters are respected (MaxCells, MinLevel, MaxLevel, and LevelMod), 464 | /// except that the implementation makes no attempt to take advantage of large values of 465 | /// MaxCells. (A small number of cells will always be returned.) 466 | /// 467 | /// This function is useful as a starting point for algorithms that 468 | /// recursively subdivide cells. 469 | pub fn fast_covering<'a, R>(&self, region: &'a R) -> CellUnion 470 | where 471 | R: Region, 472 | { 473 | let mut cu = CellUnion(region.cell_union_bound()); 474 | self.normalize_covering(&mut cu); 475 | cu 476 | } 477 | } 478 | 479 | impl RegionCoverer { 480 | /// adjustLevel returns the reduced "level" so that it satisfies levelMod. Levels smaller than minLevel 481 | /// are not affected (since cells at these levels are eventually expanded). 482 | fn adjust_level(&self, level: u8) -> u8 { 483 | if self.level_mod > 1 && level > self.min_level { 484 | level - ((level - self.min_level) % self.level_mod) 485 | } else { 486 | level 487 | } 488 | } 489 | 490 | /// normalizeCovering normalizes the "covering" so that it conforms to the current covering 491 | /// parameters (MaxCells, minLevel, maxLevel, and levelMod). 492 | /// This method makes no attempt to be optimal. In particular, if 493 | /// minLevel > 0 or levelMod > 1 then it may return more than the 494 | /// desired number of cells even when this isn't necessary. 495 | /// 496 | /// Note that when the covering parameters have their default values, almost 497 | /// all of the code in this function is skipped. 498 | fn normalize_covering(&self, covering: &mut CellUnion) { 499 | // If any cells are too small, or don't satisfy level_mod, then replace them with ancestors. 500 | if self.max_level < (MAX_LEVEL as u8) || self.level_mod > 1 { 501 | for ci in covering.0.iter_mut() { 502 | let level = ci.level() as u8; 503 | let new_level = self.adjust_level(min(level, self.max_level)); 504 | if new_level != level { 505 | *ci = ci.parent(new_level.into()); 506 | } 507 | } 508 | } 509 | 510 | // If there are still too many cells, then repeatedly replace two adjacent 511 | // cells in CellID order by their lowest common ancestor. 512 | covering.normalize(); 513 | while covering.0.len() > self.max_cells { 514 | { 515 | let mut best_index = -1isize; 516 | let mut best_level = -1isize; 517 | 518 | let v = &mut covering.0; 519 | for i in 0..(v.len() - 1) { 520 | if let Some(level) = v[i].common_ancestor_level(&v[i + 1]) { 521 | let level = self.adjust_level(level as u8) as isize; 522 | if level > best_level { 523 | best_level = level; 524 | best_index = i as isize; 525 | } 526 | } 527 | } 528 | 529 | if best_level < self.min_level as isize { 530 | break; 531 | } 532 | v[best_index as usize] = v[best_index as usize].parent(best_level as u64); 533 | } 534 | 535 | covering.normalize(); 536 | } 537 | 538 | // Make sure that the covering satisfies minLevel and levelMod, 539 | // possibly at the expense of satisfying MaxCells. 540 | if self.min_level > 0 || self.level_mod > 1 { 541 | covering.denormalize(self.min_level.into(), self.level_mod.into()); 542 | } 543 | } 544 | } 545 | 546 | // BUG(akashagrawal): The differences from the C++ version FloodFill, SimpleCovering 547 | 548 | #[cfg(test)] 549 | mod tests { 550 | use super::*; 551 | use crate::s2::cell::*; 552 | use crate::s2::metric::*; 553 | use crate::s2::random; 554 | use rand::Rng; 555 | use std::f64::consts::PI; 556 | 557 | #[test] 558 | fn test_coverer_random_cells() { 559 | let mut rng = random::rng(); 560 | let rc = RegionCoverer { 561 | min_level: 0, 562 | max_level: 30, 563 | level_mod: 1, 564 | max_cells: 1, 565 | }; 566 | 567 | for _ in 0..10000 { 568 | let id = random::cellid(&mut rng); 569 | 570 | let covering = rc.covering(&Cell::from(&id)); 571 | assert_eq!(covering.0.len(), 1); 572 | assert_eq!(covering.0[0], id); 573 | } 574 | } 575 | 576 | use std::collections::{hash_map, HashMap}; 577 | 578 | fn check_covering(rc: &RegionCoverer, r: &R, covering: &CellUnion, interior: bool) 579 | where 580 | R: Region, 581 | { 582 | // Keep track of how many cells have the same rc.min_level ancestor. 583 | let mut min_level_cells = HashMap::new(); 584 | for ci in covering.0.iter() { 585 | let level = ci.level() as u8; 586 | assert!(rc.min_level <= level && level <= rc.max_level); 587 | assert_eq!((level - rc.min_level) % rc.level_mod, 0); 588 | 589 | let parent = ci.parent(rc.min_level.into()); 590 | match min_level_cells.entry(parent) { 591 | hash_map::Entry::Occupied(mut e) => { 592 | let v = e.get_mut(); 593 | *v = *v + 1; 594 | } 595 | hash_map::Entry::Vacant(e) => { 596 | e.insert(1); 597 | } 598 | } 599 | } 600 | 601 | if covering.0.len() > rc.max_cells { 602 | // If the covering has more than the requested number of cells, then check 603 | // that the cell count cannot be reduced by using the parent of some cell. 604 | for (_, count) in min_level_cells.iter() { 605 | assert_eq!(*count, 1); 606 | } 607 | } 608 | if interior { 609 | for ci in covering.0.iter() { 610 | assert_eq!(true, r.contains_cell(&Cell::from(ci))); 611 | } 612 | } else { 613 | let mut temp_coverer = covering.clone(); 614 | temp_coverer.normalize(); 615 | check_covering_tight(r, &temp_coverer, true, CellID(0)); 616 | } 617 | } 618 | 619 | // check_covering_tight checks that "cover" completely covers the given region. 620 | // If "check_tight" is true, also checks that it does not contain any cells that 621 | // do not intersect the given region. ("id" is only used internally.) 622 | fn check_covering_tight(r: &R, cover: &CellUnion, check_tight: bool, id: CellID) 623 | where 624 | R: Region, 625 | { 626 | if !id.is_valid() { 627 | for f in 0..6 { 628 | check_covering_tight(r, cover, check_tight, CellID::from_face(f)); 629 | } 630 | return; 631 | } 632 | 633 | let cell = Cell::from(&id); 634 | if !r.intersects_cell(&cell) { 635 | // If region does not intersect id, then neither should the covering. 636 | if check_tight { 637 | assert_eq!(false, cover.intersects_cellid(&id)); 638 | } 639 | } else if !cover.contains_cellid(&id) { 640 | // The region may intersect id, but we can't assert that the covering 641 | // intersects id because we may discover that the region does not actually 642 | // intersect upon further subdivision. (IntersectsCell is not exact.) 643 | assert_eq!(false, r.contains_cell(&cell)); 644 | assert_eq!(false, id.is_leaf()); 645 | 646 | for child in id.child_iter() { 647 | check_covering_tight(r, cover, check_tight, child); 648 | } 649 | } 650 | } 651 | 652 | #[test] 653 | fn test_coverer_random_caps() { 654 | let mut rng = random::rng(); 655 | for _ in 0..1000 { 656 | let mut min_level = rng.gen_range(0..(MAX_LEVEL + 1) as u8); 657 | let mut max_level = rng.gen_range(0..(MAX_LEVEL + 1) as u8); 658 | if min_level > max_level { 659 | let tmp = max_level; 660 | max_level = min_level; 661 | min_level = tmp; 662 | } 663 | assert!(min_level <= max_level); 664 | 665 | let level_mod = rng.gen_range(1..4); 666 | let max_cells = random::skewed_int(&mut rng, 10); 667 | 668 | let rc = RegionCoverer { 669 | min_level, 670 | max_level, 671 | level_mod, 672 | max_cells, 673 | }; 674 | 675 | let max_area = 676 | (4. * PI).min((3. * (max_cells as f64) + 1.) * AVG_AREAMETRIC.value(min_level)); 677 | 678 | let r = random::cap(&mut rng, 0.1 * AVG_AREAMETRIC.value(max_level), max_area); 679 | 680 | let mut covering = rc.covering(&r); 681 | check_covering(&rc, &r, &covering, false); 682 | 683 | let interior = rc.interior_covering(&r); 684 | check_covering(&rc, &r, &interior, true); 685 | 686 | // Check that Covering is deterministic. 687 | let covering2 = rc.covering(&r); 688 | assert_eq!(covering, covering2); 689 | 690 | // Also check Denormalize. The denormalized covering 691 | // may still be different and smaller than "covering" because 692 | // s2.RegionCoverer does not guarantee that it will not output all four 693 | // children of the same parent. 694 | covering.denormalize(min_level as u64, level_mod as u64); 695 | check_covering(&rc, &r, &covering, false); 696 | } 697 | } 698 | } 699 | -------------------------------------------------------------------------------- /src/s2/shape.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | use crate::r2::point::Point; 14 | use crate::s2::point::Point as s2Point; 15 | use std::cmp::*; 16 | 17 | // Edge represents a geodesic edge consisting of two vertices. Zero-length edges are 18 | // allowed, and can be used to represent points. 19 | 20 | pub struct Edge { 21 | v0: Point, 22 | v1: Point, 23 | } 24 | 25 | impl Eq for Edge {} 26 | 27 | impl Ord for Edge { 28 | fn cmp(&self, other: &Edge) -> Ordering { 29 | let v0cmp = self.v0.cmp(&other.v0); 30 | if v0cmp != Ordering::Equal { 31 | v0cmp 32 | } else { 33 | self.v0.cmp(&other.v1) 34 | } 35 | } 36 | } 37 | 38 | impl PartialOrd for Edge { 39 | fn partial_cmp(&self, other: &Edge) -> Option { 40 | Some(self.cmp(other)) 41 | } 42 | } 43 | 44 | impl PartialEq for Edge { 45 | fn eq(&self, other: &Edge) -> bool { 46 | self.cmp(other) == Ordering::Equal 47 | } 48 | } 49 | 50 | // edges implements the Sort interface for slices of Edge. 51 | #[allow(unused)] 52 | type Edges = Vec; 53 | 54 | /// ShapeEdgeID is a unique identifier for an Edge within an ShapeIndex, 55 | /// consisting of a (shape_id, edge_id) pair. 56 | #[derive(Clone, Copy, PartialEq, Eq)] 57 | pub struct ShapeEdgeID { 58 | shape_id: i32, 59 | edge_id: i32, 60 | } 61 | 62 | impl PartialOrd for ShapeEdgeID { 63 | fn partial_cmp(&self, other: &Self) -> Option { 64 | Some(self.cmp(other)) 65 | } 66 | } 67 | impl Ord for ShapeEdgeID { 68 | fn cmp(&self, other: &Self) -> Ordering { 69 | self.shape_id 70 | .cmp(&other.shape_id) 71 | .then(self.edge_id.cmp(&other.edge_id)) 72 | } 73 | } 74 | 75 | /// ShapeEdge represents a Shapeedge_id with the two endpoints of that Edge. 76 | pub struct ShapeEdge { 77 | #[allow(unused)] 78 | id: ShapeEdgeID, 79 | #[allow(unused)] 80 | edge: Edge, 81 | } 82 | 83 | /// Chain represents a range of edge IDs corresponding to a chain of connected 84 | /// edges, specified as a (start, length) pair. The chain is defined to consist of 85 | /// edge IDs {start, start + 1, ..., start + length - 1}. 86 | pub struct Chain { 87 | #[allow(unused)] 88 | start: i64, 89 | #[allow(unused)] 90 | length: i64, 91 | } 92 | 93 | /// ChainPosition represents the position of an edge within a given edge chain, 94 | /// specified as a (chainID, offset) pair. Chains are numbered sequentially 95 | /// starting from zero, and offsets are measured from the start of each chain. 96 | pub struct ChainPosition { 97 | #[allow(unused)] 98 | chain_id: i64, 99 | #[allow(unused)] 100 | offset: i64, 101 | } 102 | 103 | /// A ReferencePoint consists of a point and a boolean indicating whether the point 104 | /// is contained by a particular shape. 105 | pub struct ReferencePoint { 106 | #[allow(unused)] 107 | point: s2Point, 108 | #[allow(unused)] 109 | contained: bool, 110 | } 111 | 112 | impl ReferencePoint { 113 | /// origin returns a ReferencePoint with the given value for 114 | /// contained and the origin point. It should be used when all points or no 115 | /// points are contained. 116 | pub fn origin(contained: bool) -> Self { 117 | ReferencePoint { 118 | point: s2Point::origin(), 119 | contained: contained, 120 | } 121 | } 122 | } 123 | 124 | /// Shape represents polygonal geometry in a flexible way. It is organized as a 125 | /// collection of edges that optionally defines an interior. All geometry 126 | /// represented by a given Shape must have the same dimension, which means that 127 | /// an Shape can represent either a set of points, a set of polylines, or a set 128 | /// of polygons. 129 | /// 130 | /// Shape is defined as an interface in order to give clients control over the 131 | /// underlying data representation. Sometimes an Shape does not have any data of 132 | /// its own, but instead wraps some other type. 133 | /// 134 | /// Shape operations are typically defined on a ShapeIndex rather than 135 | /// individual shapes. An ShapeIndex is simply a collection of Shapes, 136 | /// possibly of different dimensions (e.g. 10 points and 3 polygons), organized 137 | /// into a data structure for efficient edge access. 138 | /// 139 | /// The edges of a Shape are indexed by a contiguous range of edge IDs 140 | /// starting at 0. The edges are further subdivided into chains, where each 141 | /// chain consists of a sequence of edges connected end-to-end (a polyline). 142 | /// For example, a Shape representing two polylines AB and CDE would have 143 | /// three edges (AB, CD, DE) grouped into two chains: (AB) and (CD, DE). 144 | /// Similarly, an Shape representing 5 points would have 5 chains consisting 145 | /// of one edge each. 146 | /// 147 | /// Shape has methods that allow edges to be accessed either using the global 148 | /// numbering (edge ID) or within a particular chain. The global numbering is 149 | /// sufficient for most purposes, but the chain representation is useful for 150 | /// certain algorithms such as intersection (see BooleanOperation). 151 | pub trait Shape { 152 | /// num_edges returns the number of edges in this shape. 153 | fn num_edges(&self) -> i64; 154 | 155 | /// edge returns the edge for the given edge index. 156 | fn edge(&self, i: i64) -> Edge; 157 | 158 | /// reference_point returns an arbitrary reference point for the shape. (The 159 | /// containment boolean value must be false for shapes that do not have an interior.) 160 | /// 161 | /// This reference point may then be used to compute the containment of other 162 | /// points by counting edge crossings. 163 | fn reference_point(&self) -> ReferencePoint; 164 | 165 | /// num_chains reports the number of contiguous edge chains in the shape. 166 | /// For example, a shape whose edges are [AB, BC, CD, AE, EF] would consist 167 | /// of two chains (AB,BC,CD and AE,EF). Every chain is assigned a chain Id 168 | /// numbered sequentially starting from zero. 169 | /// 170 | /// Note that it is always acceptable to implement this method by returning 171 | /// NumEdges, i.e. every chain consists of a single edge, but this may 172 | /// reduce the efficiency of some algorithms. 173 | fn num_chains(&self) -> i64; 174 | 175 | /// chain returns the range of edge IDs corresponding to the given edge chain. 176 | /// Edge chains must form contiguous, non-overlapping ranges that cover 177 | /// the entire range of edge IDs. This is spelled out more formally below: 178 | /// 179 | /// 0 <= i < NumChains() 180 | /// Chain(i).length > 0, for all i 181 | /// Chain(0).start == 0 182 | /// Chain(i).start + Chain(i).length == Chain(i+1).start, for i < NumChains()-1 183 | /// Chain(i).start + Chain(i).length == NumEdges(), for i == NumChains()-1 184 | fn chain(&self, chain_id: i64) -> Chain; 185 | 186 | /// chain_edge returns the edge at offset "offset" within edge chain "chainID". 187 | /// Equivalent to "shape.Edge(shape.Chain(chainID).start + offset)" 188 | /// but more efficient. 189 | fn chain_edge(&self, chain_id: i64, offset: i64) -> Edge; 190 | 191 | /// chain_position finds the chain containing the given edge, and returns the 192 | /// position of that edge as a ChainPosition(chainID, offset) pair. 193 | /// 194 | /// shape.Chain(pos.chainID).start + pos.offset == edge_id 195 | /// shape.Chain(pos.chainID+1).start > edge_id 196 | /// 197 | /// where pos == shape.ChainPosition(edge_id). 198 | fn chain_position(&self, edge_id: i64) -> ChainPosition; 199 | 200 | /// dimension returns the dimension of the geometry represented by this shape, 201 | /// either 0, 1 or 2 for point, polyline and polygon geometry respectively. 202 | /// 203 | /// 0 - Point geometry. Each point is represented as a degenerate edge. 204 | /// 205 | /// 1 - Polyline geometry. Polyline edges may be degenerate. A shape may 206 | /// represent any number of polylines. Polylines edges may intersect. 207 | /// 208 | /// 2 - Polygon geometry. Edges should be oriented such that the polygon 209 | /// interior is always on the left. In theory the edges may be returned 210 | /// in any order, but typically the edges are organized as a collection 211 | /// of edge chains where each chain represents one polygon loop. 212 | /// Polygons may have degeneracies (e.g., degenerate edges or sibling 213 | /// pairs consisting of an edge and its corresponding reversed edge). 214 | /// A polygon loop may also be full (containing all points on the 215 | /// sphere); by convention this is represented as a chain with no edges. 216 | /// (See laxPolygon for details.) 217 | /// 218 | /// This method allows degenerate geometry of different dimensions 219 | /// to be distinguished, e.g. it allows a point to be distinguished from a 220 | /// polyline or polygon that has been simplified to a single point. 221 | fn dimension(&self) -> i64; 222 | 223 | /// is_empty reports whether the Shape contains no points. (Note that the full 224 | /// polygon is represented as a chain with zero edges.) 225 | fn is_empty(&self) -> bool { 226 | self.num_edges() == 0 && (self.dimension() != 2 || self.num_chains() == 0) 227 | } 228 | 229 | /// is_full reports whether the Shape contains all points on the sphere. 230 | fn is_full(&self) -> bool { 231 | self.num_edges() == 0 && self.dimension() == 2 && self.num_chains() > 0 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/s2/stuv.rs: -------------------------------------------------------------------------------- 1 | use crate::r3::vector::Vector; 2 | use crate::s2::cellid::*; 3 | use crate::s2::point::Point; 4 | 5 | const MAX_SITI: u64 = MAX_SIZE << 1; 6 | 7 | pub fn siti_to_st(si: u64) -> f64 { 8 | if si > MAX_SITI { 9 | 1f64 10 | } else { 11 | (si as f64) / (MAX_SITI as f64) 12 | } 13 | } 14 | 15 | #[cfg(test)] 16 | fn st_to_siti(s: f64) -> u64 { 17 | if s < 0. { 18 | (s * (MAX_SITI as f64) - 0.5) as u64 19 | } else { 20 | (s * (MAX_SITI as f64) + 0.5) as u64 21 | } 22 | } 23 | 24 | pub fn st_to_uv(s: f64) -> f64 { 25 | if s >= 0.5 { 26 | (1. / 3.) * (4. * s * s - 1.) 27 | } else { 28 | (1. / 3.) * (1. - 4. * (1. - s) * (1. - s)) 29 | } 30 | } 31 | 32 | pub fn uv_to_st(u: f64) -> f64 { 33 | if u >= 0. { 34 | 0.5 * (1. + 3. * u).sqrt() 35 | } else { 36 | 1. - 0.5 * (1. - 3. * u).sqrt() 37 | } 38 | } 39 | 40 | pub fn face(r: &Vector) -> u8 { 41 | let abs = r.abs(); 42 | let mut id = 0; 43 | let mut value = r.x; 44 | if abs.y > abs.x { 45 | id = 1; 46 | value = r.y; 47 | } 48 | if abs.z > value.abs() { 49 | id = 2; 50 | value = r.z; 51 | } 52 | if value < 0. { 53 | id += 3; 54 | } 55 | id 56 | } 57 | 58 | pub fn valid_face_xyz_to_uv(face: u8, r: &Vector) -> (f64, f64) { 59 | match face { 60 | 0 => (r.y / r.x, r.z / r.x), 61 | 1 => (-r.x / r.y, r.z / r.y), 62 | 2 => (-r.x / r.z, -r.y / r.z), 63 | 3 => (r.z / r.x, r.y / r.x), 64 | 4 => (r.z / r.y, -r.x / r.y), 65 | 5 => (-r.y / r.z, -r.x / r.z), 66 | _ => unimplemented!(), 67 | } 68 | } 69 | 70 | pub fn xyz_to_face_uv(r: &Vector) -> (u8, f64, f64) { 71 | let f = face(r); 72 | let (u, v) = valid_face_xyz_to_uv(f, r); 73 | (f, u, v) 74 | } 75 | 76 | pub fn face_uv_to_xyz(face: u8, u: f64, v: f64) -> Vector { 77 | match face { 78 | 0 => Vector::new(1., u, v), 79 | 1 => Vector::new(-u, 1., v), 80 | 2 => Vector::new(-u, -v, 1.), 81 | 3 => Vector::new(-1., -v, -u), 82 | 4 => Vector::new(v, -1., -u), 83 | 5 => Vector::new(v, u, -1.), 84 | _ => unimplemented!(), 85 | } 86 | } 87 | 88 | pub fn face_xyz_to_uv(face: u8, p: &Point) -> Option<(f64, f64)> { 89 | let invalid = match face { 90 | 0 => p.0.x <= 0., 91 | 1 => p.0.y <= 0., 92 | 2 => p.0.z <= 0., 93 | 3 => p.0.x >= 0., 94 | 4 => p.0.y >= 0., 95 | 5 => p.0.z >= 0., 96 | _ => unimplemented!(), 97 | }; 98 | if invalid { 99 | None 100 | } else { 101 | Some(valid_face_xyz_to_uv(face, &p.0)) 102 | } 103 | } 104 | 105 | #[cfg(test)] 106 | fn face_xyz_to_uvw(face: u8, p: &Point) -> Point { 107 | let v = &p.0; 108 | match face { 109 | 0 => Point(Vector::new(v.y, v.z, v.x)), 110 | 1 => Point(Vector::new(-v.x, v.z, v.y)), 111 | 2 => Point(Vector::new(-v.x, -v.y, v.z)), 112 | 3 => Point(Vector::new(-v.z, -v.y, -v.x)), 113 | 4 => Point(Vector::new(-v.z, v.x, -v.y)), 114 | 5 => Point(Vector::new(v.y, v.x, -v.z)), 115 | _ => unimplemented!(), 116 | } 117 | } 118 | 119 | #[cfg(test)] 120 | fn face_siti_to_xyz(face: u8, si: u64, ti: u64) -> Point { 121 | Point(face_uv_to_xyz( 122 | face, 123 | st_to_uv(siti_to_st(si)), 124 | st_to_uv(siti_to_st(ti)), 125 | )) 126 | } 127 | 128 | #[cfg(test)] 129 | fn siti_to_level(si: u64) -> i8 { 130 | (MAX_LEVEL as i8) - ((si | MAX_SITI).trailing_zeros() as i8) 131 | } 132 | 133 | #[cfg(test)] 134 | /// xyz_to_face_siti transforms the (not necessarily unit length) Point to 135 | /// (face, si, ti) coordinates and the level the Point is at. 136 | fn xyz_to_face_siti(p: &Point) -> (u8, u64, u64, i8) { 137 | let (face, u, v) = xyz_to_face_uv(&p.0); 138 | let si = st_to_siti(uv_to_st(u)); 139 | let ti = st_to_siti(uv_to_st(v)); 140 | 141 | // If the levels corresponding to si,ti are not equal, then p is not a cell 142 | // center. The si,ti values of 0 and maxSiTi need to be handled specially 143 | // because they do not correspond to cell centers at any valid level; they 144 | // are mapped to level -1 by the code at the end. 145 | let level = siti_to_level(si); 146 | if level < 0 || level != siti_to_level(ti) { 147 | return (face, si, ti, -1); 148 | } 149 | 150 | // In infinite precision, this test could be changed to ST == SiTi. However, 151 | // due to rounding errors, uvToST(xyzToFaceUV(faceUVToXYZ(stToUV(...)))) is 152 | // not idempotent. On the other hand, the center is computed exactly the same 153 | // way p was originally computed (if it is indeed the center of a Cell); 154 | // the comparison can be exact. 155 | if p.0 == face_siti_to_xyz(face, si, ti).0.normalize() { 156 | (face, si, ti, level) 157 | } else { 158 | (face, si, ti, -1) 159 | } 160 | } 161 | 162 | pub fn unorm(face: u8, u: f64) -> Vector { 163 | match face { 164 | 0 => Vector::new(u, -1., 0.), 165 | 1 => Vector::new(1., u, 0.), 166 | 2 => Vector::new(1., 0., u), 167 | 3 => Vector::new(-u, 0., 1.), 168 | 4 => Vector::new(0., -u, 1.), 169 | 5 => Vector::new(0., -1., -u), 170 | _ => unimplemented!(), 171 | } 172 | } 173 | 174 | pub fn vnorm(face: u8, v: f64) -> Vector { 175 | match face { 176 | 0 => Vector::new(-v, 0., 1.), 177 | 1 => Vector::new(0., -v, 1.), 178 | 2 => Vector::new(0., -1., -v), 179 | 3 => Vector::new(v, -1., 0.), 180 | 4 => Vector::new(1., v, 0.), 181 | 5 => Vector::new(1., 0., v), 182 | _ => unimplemented!(), 183 | } 184 | } 185 | 186 | macro_rules! V { 187 | ($x:expr, $y:expr, $z:expr) => { 188 | Vector { 189 | x: $x as f64, 190 | y: $y as f64, 191 | z: $z as f64, 192 | } 193 | }; 194 | } 195 | macro_rules! P { 196 | ($x:expr, $y:expr, $z:expr) => { 197 | Point(V!($x, $y, $z)) 198 | }; 199 | } 200 | 201 | const FACE_UVW_AXES: [[Point; 3]; 6] = [ 202 | [P!(0, 1, 0), P!(0, 0, 1), P!(1, 0, 0)], 203 | [P!(-1, 0, 0), P!(0, 0, 1), P!(0, 1, 0)], 204 | [P!(-1, 0, 0), P!(0, -1, 0), P!(0, 0, 1)], 205 | [P!(0, 0, -1), P!(0, -1, 0), P!(-1, 0, 0)], 206 | [P!(0, 0, -1), P!(1, 0, 0), P!(0, -1, 0)], 207 | [P!(0, 1, 0), P!(1, 0, 0), P!(0, 0, -1)], 208 | ]; 209 | 210 | #[cfg(test)] 211 | const FACE_UVW_FACES: [[[u8; 2]; 3]; 6] = [ 212 | [[4, 1], [5, 2], [3, 0]], 213 | [[0, 3], [5, 2], [4, 1]], 214 | [[0, 3], [1, 4], [5, 2]], 215 | [[2, 5], [1, 4], [0, 3]], 216 | [[2, 5], [3, 0], [1, 4]], 217 | [[4, 1], [3, 0], [2, 5]], 218 | ]; 219 | 220 | fn uvw_axis(face: u8, axis: u8) -> Point { 221 | FACE_UVW_AXES[face as usize][axis as usize] 222 | } 223 | 224 | #[cfg(test)] 225 | fn uvw_face(face: u8, axis: u8, direction: u8) -> u8 { 226 | FACE_UVW_FACES[face as usize][axis as usize][direction as usize] 227 | } 228 | 229 | pub fn u_axis(face: u8) -> Point { 230 | uvw_axis(face, 0) 231 | } 232 | 233 | pub fn v_axis(face: u8) -> Point { 234 | uvw_axis(face, 1) 235 | } 236 | 237 | #[cfg(test)] 238 | fn unit_norm(face: u8) -> Point { 239 | uvw_axis(face, 2) 240 | } 241 | 242 | pub const SWAP_MASK: u8 = 0x01; 243 | 244 | #[cfg(test)] 245 | mod tests { 246 | extern crate rand; 247 | 248 | use self::rand::Rng; 249 | use super::*; 250 | use crate::r3::vector::Axis; 251 | use crate::s2::random; 252 | use std; 253 | 254 | #[test] 255 | fn st_uv() { 256 | assert_eq!(0.125, st_to_uv(uv_to_st(0.125))); 257 | assert_eq!(0.125, uv_to_st(st_to_uv(0.125))); 258 | } 259 | 260 | #[test] 261 | fn test_uv_norms() { 262 | let step = 1. / 1024.; 263 | for face in 0..6 { 264 | let mut x = -1.; 265 | loop { 266 | let angle = face_uv_to_xyz(face, x, -1.) 267 | .cross(&face_uv_to_xyz(face, x, 1.)) 268 | .angle(&unorm(face, x)); 269 | assert!(angle.rad() < std::f64::EPSILON); 270 | 271 | x += step; 272 | if x > 1. { 273 | break; 274 | } 275 | } 276 | } 277 | } 278 | 279 | #[test] 280 | fn test_face_uv_to_xyz() { 281 | let mut sum = V!(0., 0., 0.); 282 | 283 | for face in 0..6 { 284 | let center = face_uv_to_xyz(face, 0., 0.); 285 | assert!(center.approx_eq(&unit_norm(face).0)); 286 | 287 | match center.largest_component() { 288 | Axis::X => assert_eq!(center.x.abs(), 1.0), 289 | Axis::Y => assert_eq!(center.y.abs(), 1.0), 290 | Axis::Z => assert_eq!(center.z.abs(), 1.0), 291 | } 292 | 293 | //TODO: implement += operator? 294 | sum = sum + center.abs(); 295 | 296 | assert_eq!( 297 | u_axis(face) 298 | .0 299 | .cross(&v_axis(face).0) 300 | .dot(&unit_norm(face).0), 301 | 1.0 302 | ); 303 | 304 | let sign = if face & SWAP_MASK == 1 { 305 | -1.0f64 306 | } else { 307 | 1.0f64 308 | }; 309 | 310 | assert_eq!( 311 | face_uv_to_xyz(face, sign, -sign), 312 | face_uv_to_xyz((face + 1) % 6, -1., -1.) 313 | ); 314 | } 315 | 316 | assert!(sum.approx_eq(&V!(2., 2., 2.))); 317 | } 318 | 319 | fn test_face_xyz_to_uv_case(face: u8, point: &Point, expected: (f64, f64, bool)) { 320 | if let Some((u, v)) = face_xyz_to_uv(face, point) { 321 | assert!((u - expected.0) <= std::f64::EPSILON); 322 | assert!((v - expected.1) <= std::f64::EPSILON); 323 | assert_eq!(true, expected.2); 324 | } else { 325 | assert_eq!(false, expected.2); 326 | } 327 | } 328 | 329 | #[test] 330 | fn test_face_xyz_to_uv() { 331 | let point = P!(1.1, 1.2, 1.3); 332 | let point_neg = P!(-1.1, -1.2, -1.3); 333 | 334 | test_face_xyz_to_uv_case(0, &point, (1. + 1. / 11., 1. + 2. / 11., true)); 335 | test_face_xyz_to_uv_case(0, &point_neg, (0., 0., false)); 336 | test_face_xyz_to_uv_case(1, &point, (-11. / 12., 1. + 1. / 12., true)); 337 | test_face_xyz_to_uv_case(1, &point_neg, (0., 0., false)); 338 | test_face_xyz_to_uv_case(2, &point, (-11. / 13., -12. / 13., true)); 339 | test_face_xyz_to_uv_case(2, &point_neg, (0., 0., false)); 340 | test_face_xyz_to_uv_case(3, &point, (0., 0., false)); 341 | test_face_xyz_to_uv_case(3, &point_neg, (1. + 2. / 11., 1. + 1. / 11., true)); 342 | test_face_xyz_to_uv_case(4, &point, (0., 0., false)); 343 | test_face_xyz_to_uv_case(4, &point_neg, (1. + 1. / 12., -11. / 12., true)); 344 | test_face_xyz_to_uv_case(5, &point, (0., 0., false)); 345 | test_face_xyz_to_uv_case(5, &point_neg, (-12. / 13., -11. / 13., true)); 346 | } 347 | 348 | #[test] 349 | fn test_face_xyz_to_uvw() { 350 | let origin = P!(0., 0., 0.); 351 | let pos_x = P!(1., 0., 0.); 352 | let neg_x = P!(-1., 0., 0.); 353 | let pos_y = P!(0., 1., 0.); 354 | let neg_y = P!(0., -1., 0.); 355 | let pos_z = P!(0., 0., 1.); 356 | let neg_z = P!(0., 0., -1.); 357 | 358 | for face in 0..6 { 359 | assert_eq!(origin, face_xyz_to_uvw(face, &origin)); 360 | 361 | assert_eq!(pos_x, face_xyz_to_uvw(face, &u_axis(face))); 362 | assert_eq!(neg_x, face_xyz_to_uvw(face, &(u_axis(face) * -1.))); 363 | 364 | assert_eq!(pos_y, face_xyz_to_uvw(face, &v_axis(face))); 365 | assert_eq!(neg_y, face_xyz_to_uvw(face, &(v_axis(face) * -1.))); 366 | 367 | assert_eq!(pos_z, face_xyz_to_uvw(face, &unit_norm(face))); 368 | assert_eq!(neg_z, face_xyz_to_uvw(face, &(unit_norm(face) * -1.))); 369 | } 370 | } 371 | 372 | #[test] 373 | fn test_uvw_axis() { 374 | for face in 0..6 { 375 | assert_eq!( 376 | face_uv_to_xyz(face, 1., 0.) - face_uv_to_xyz(face, 0., 0.), 377 | u_axis(face).0 378 | ); 379 | assert_eq!( 380 | face_uv_to_xyz(face, 0., 1.) - face_uv_to_xyz(face, 0., 0.), 381 | v_axis(face).0 382 | ); 383 | assert_eq!(face_uv_to_xyz(face, 0., 0.), unit_norm(face).0); 384 | 385 | assert_eq!( 386 | 1., 387 | u_axis(face) 388 | .0 389 | .cross(&v_axis(face).0) 390 | .dot(&unit_norm(face).0) 391 | ); 392 | assert_eq!(u_axis(face), uvw_axis(face, 0)); 393 | assert_eq!(v_axis(face), uvw_axis(face, 1)); 394 | assert_eq!(unit_norm(face), uvw_axis(face, 2)); 395 | } 396 | } 397 | 398 | #[test] 399 | fn test_siti_st_roundtrip() { 400 | let mut rng = random::rng(); 401 | for _ in 0..1000 { 402 | let si = rng.gen_range(0..MAX_SITI); 403 | assert_eq!(st_to_siti(siti_to_st(si)), si); 404 | } 405 | 406 | for _ in 0..1000 { 407 | let st = rng.gen_range(0.0..1f64); 408 | let error = siti_to_st(st_to_siti(st)) - st; 409 | assert!(error.abs() < (1. / MAX_LEVEL as f64)); 410 | } 411 | } 412 | 413 | #[test] 414 | fn test_uvw_face() { 415 | for f in 0..6 { 416 | for axis in 0..3 { 417 | assert_eq!(face(&(uvw_axis(f, axis).0 * -1.)), uvw_face(f, axis, 0)); 418 | assert_eq!(face(&uvw_axis(f, axis).0), uvw_face(f, axis, 1)); 419 | } 420 | } 421 | } 422 | 423 | #[test] 424 | fn test_xyz_to_face_siti() { 425 | let mut rng = random::rng(); 426 | let shift = Point(Vector::new(1e-13, 1e-13, 1e-13)); 427 | 428 | for level in 0..MAX_LEVEL { 429 | for _ in 0..1000 { 430 | let ci = random::cellid_for_level(&mut rng, level); 431 | 432 | let (f, si, ti, gotlevel) = xyz_to_face_siti(&Point::from(&ci)); 433 | assert_eq!(gotlevel, level as i8); 434 | 435 | assert_eq!( 436 | ci, 437 | CellID::from_face_ij(f, (si / 2) as i32, (ti / 2) as i32).parent(level) 438 | ); 439 | 440 | //TODO: add by ref 441 | let p_moved = Point::from(ci) + shift.clone(); 442 | let (f_moved, si_moved, ti_moved, gotlevel_moved) = xyz_to_face_siti(&p_moved); 443 | 444 | assert_eq!(gotlevel_moved, -1); 445 | assert_eq!(f, f_moved); 446 | 447 | assert_eq!(si, si_moved); 448 | assert_eq!(ti, ti_moved); 449 | } 450 | 451 | { 452 | let face_random = rng.gen_range(0..NUM_FACES); 453 | let mask = (std::u64::MAX << (MAX_LEVEL - level)) & (MAX_SITI - 1); 454 | let si_random = rng.gen_range(0..MAX_SITI) & mask; 455 | let ti_random = rng.gen_range(0..MAX_SITI) & mask; 456 | 457 | let lower_mask = (1 << (MAX_LEVEL - level)) - 1; 458 | assert!(si_random < MAX_SITI && (si_random & lower_mask) == 0); 459 | assert!(ti_random < MAX_SITI && (ti_random & lower_mask) == 0); 460 | 461 | let p_random = face_siti_to_xyz(face_random, si_random, ti_random); 462 | let (f, si, ti, gotlevel) = xyz_to_face_siti(&p_random); 463 | 464 | if f != face_random { 465 | assert_eq!(-1, gotlevel); 466 | assert!(si == 0 || si == MAX_SITI || ti == 0 || ti == MAX_SITI); 467 | } else { 468 | assert_eq!(si, si_random); 469 | assert_eq!(ti, ti_random); 470 | if gotlevel >= 0 { 471 | assert!(p_random.approx_eq( 472 | &CellID::from_face_ij(f, si as i32 / 2, ti as i32 / 2) 473 | .parent(gotlevel as u64) 474 | .into() 475 | )); 476 | } 477 | } 478 | } 479 | } 480 | } 481 | 482 | #[test] 483 | fn test_xyz_face_siti_roundtrip() { 484 | let mut rng = random::rng(); 485 | 486 | for level in 0..MAX_LEVEL { 487 | for _ in 0..1000 { 488 | let ci = random::cellid_for_level(&mut rng, level); 489 | let p = Point::from(&ci); 490 | let (f, si, ti, _) = xyz_to_face_siti(&p); 491 | let op = face_siti_to_xyz(f, si, ti); 492 | assert!(op.approx_eq(&p)); 493 | } 494 | } 495 | } 496 | } 497 | --------------------------------------------------------------------------------