|
| 1 | +use geo_types::CoordFloat; |
| 2 | + |
| 3 | +use super::{LineSplitResult, LineSplitTwiceResult}; |
| 4 | + |
| 5 | +/// Defines functions to split a [Line](crate::Line) or [LineString](crate::LineString) |
| 6 | +pub trait LineSplit<Scalar> |
| 7 | +where |
| 8 | + Self: Sized, |
| 9 | + Scalar: CoordFloat, |
| 10 | +{ |
| 11 | + /// Split a [Line](crate::Line) or [LineString](crate::LineString) at some `fraction` of its length. |
| 12 | + /// |
| 13 | + /// `fraction` is any real number. Only values between 0.0 and 1.0 will split the line. |
| 14 | + /// Values outside of this range (including infinite values) will be clamped to 0.0 or 1.0. |
| 15 | + /// |
| 16 | + /// Returns `None` when |
| 17 | + /// - The provided `fraction` is NAN |
| 18 | + /// - The the object being sliced includes NAN or infinite coordinates |
| 19 | + /// |
| 20 | + /// Otherwise returns [`Some(LineSplitResult)`](crate::algorithm::LineSplitResult) |
| 21 | + /// |
| 22 | + /// example |
| 23 | + /// |
| 24 | + /// ``` |
| 25 | + /// use geo::{Line, coord}; |
| 26 | + /// use geo::algorithm::{LineSplit, LineSplitResult}; |
| 27 | + /// let line = Line::new( |
| 28 | + /// coord! {x: 0.0, y:0.0}, |
| 29 | + /// coord! {x:10.0, y:0.0}, |
| 30 | + /// ); |
| 31 | + /// let result = line.line_split(0.6); |
| 32 | + /// assert_eq!( |
| 33 | + /// result, |
| 34 | + /// Some(LineSplitResult::FirstSecond( |
| 35 | + /// Line::new( |
| 36 | + /// coord! {x: 0.0, y:0.0}, |
| 37 | + /// coord! {x: 6.0, y:0.0}, |
| 38 | + /// ), |
| 39 | + /// Line::new( |
| 40 | + /// coord! {x: 6.0, y:0.0}, |
| 41 | + /// coord! {x:10.0, y:0.0}, |
| 42 | + /// ) |
| 43 | + /// )) |
| 44 | + /// ); |
| 45 | + /// |
| 46 | + /// match result { |
| 47 | + /// Some(LineSplitResult::First(line1))=>{}, |
| 48 | + /// Some(LineSplitResult::Second(line2))=>{}, |
| 49 | + /// Some(LineSplitResult::FirstSecond(line1, line2))=>{}, |
| 50 | + /// None=>{}, |
| 51 | + /// } |
| 52 | + /// ``` |
| 53 | + fn line_split(&self, fraction: Scalar) -> Option<LineSplitResult<Self>>; |
| 54 | + |
| 55 | + /// |
| 56 | + /// |
| 57 | + /// example |
| 58 | + /// |
| 59 | + /// ``` |
| 60 | + /// |
| 61 | + /// ``` |
| 62 | + /// > Note: Currently the default implementation of this function provided by the trait is |
| 63 | + /// > inefficient because it uses repeated application of the |
| 64 | + /// > [.line_split()](LineSplit::line_split) function. In future, types implementing this trait |
| 65 | + /// > should override this with a more efficient algorithm if possible. |
| 66 | + fn line_split_many(&self, fractions: &Vec<Scalar>) -> Option<Vec<Option<Self>>> |
| 67 | + where |
| 68 | + Self: Clone, |
| 69 | + { |
| 70 | + match fractions.len() { |
| 71 | + 0 => None, |
| 72 | + 1 => self.line_split(fractions[0]).map(|item| { |
| 73 | + let (a, b) = item.into_tuple(); |
| 74 | + vec![a, b] |
| 75 | + }), |
| 76 | + _ => { |
| 77 | + let mut fractions: Vec<Scalar> = fractions |
| 78 | + .iter() |
| 79 | + .map(|item| item.min(Scalar::one()).max(Scalar::zero())) |
| 80 | + .collect(); |
| 81 | + fractions |
| 82 | + .sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); |
| 83 | + fractions.insert(0, Scalar::zero()); |
| 84 | + fractions.push(Scalar::one()); |
| 85 | + let fractions = fractions; // remove mutability |
| 86 | + let mut output: Vec<Option<Self>> = Vec::new(); |
| 87 | + let mut remaining_self = Some(self.clone()); |
| 88 | + for fraction in fractions.windows(2) { |
| 89 | + // cannot be irrefutably unwrapped in for loop *sad crab noises*: |
| 90 | + let (a, b) = match fraction { |
| 91 | + &[a, b] => (a, b), |
| 92 | + _ => return None, |
| 93 | + }; |
| 94 | + let fraction_interval = b - a; |
| 95 | + let fraction_to_end = Scalar::one() - a; |
| 96 | + let next_fraction = fraction_interval / fraction_to_end; |
| 97 | + remaining_self = if let Some(remaining_self) = remaining_self { |
| 98 | + match remaining_self.line_split(next_fraction) { |
| 99 | + Some(LineSplitResult::FirstSecond(line1, line2)) => { |
| 100 | + output.push(Some(line1)); |
| 101 | + Some(line2) |
| 102 | + } |
| 103 | + Some(LineSplitResult::First(line1)) => { |
| 104 | + output.push(Some(line1)); |
| 105 | + None |
| 106 | + } |
| 107 | + Some(LineSplitResult::Second(line2)) => { |
| 108 | + output.push(None); |
| 109 | + Some(line2) |
| 110 | + } |
| 111 | + None => return None, |
| 112 | + } |
| 113 | + } else { |
| 114 | + output.push(None); |
| 115 | + None |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + Some(output) |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + /// Split a [Line](crate::Line) or [LineString](crate::LineString) |
| 125 | + /// at `fraction_start` and at `fraction_end`. |
| 126 | + /// |
| 127 | + /// `fraction_start`/`fraction_end` are any real numbers. Only values between 0.0 and 1.0 will |
| 128 | + /// split the line. Values outside of this range (including infinite values) will be clamped to |
| 129 | + /// 0.0 or 1.0. |
| 130 | + /// |
| 131 | + /// If `fraction_start > fraction_end`, then the values will be swapped prior splitting. |
| 132 | + /// |
| 133 | + /// Returns [None] when |
| 134 | + /// - Either`fraction_start` or `fraction_end` are NAN |
| 135 | + /// - The the object being sliced includes NAN or infinite coordinates |
| 136 | + /// |
| 137 | + /// Otherwise Returns a [`Some(LineSplitTwiceResult<T>)`](LineSplitTwiceResult) |
| 138 | + /// |
| 139 | + /// A [`LineSplitTwiceResult<T>`](LineSplitTwiceResult) can contain between one and three |
| 140 | + /// line parts where `T` is either [Line](crate::Line) or [LineString](crate::LineString). |
| 141 | + /// |
| 142 | + /// Note that [LineSplitTwiceResult] provides various helper methods to get the desired part(s) |
| 143 | + /// of the output. |
| 144 | + /// |
| 145 | + /// The following example shows how to always obtain the "middle" part between the two splits |
| 146 | + /// using the [`.into_second()`](LineSplitTwiceResult#method.into_second) method: |
| 147 | + /// |
| 148 | + /// ``` |
| 149 | + /// use geo::{LineString, line_string}; |
| 150 | + /// use geo::algorithm::{LineSplit, EuclideanLength}; |
| 151 | + /// use approx::assert_relative_eq; |
| 152 | + /// let my_road_line_string:LineString<f32> = line_string![ |
| 153 | + /// (x: 0.0,y: 0.0), |
| 154 | + /// (x:10.0,y: 0.0), |
| 155 | + /// (x:10.0,y:10.0), |
| 156 | + /// ]; |
| 157 | + /// let my_road_len = my_road_line_string.euclidean_length(); |
| 158 | + /// let fraction_from = 5.0 / my_road_len; |
| 159 | + /// let fraction_to = 12.0 / my_road_len; |
| 160 | + /// // Extract the road section between `fraction_from` and `fraction_to` using `.into_second()` |
| 161 | + /// let my_road_section = match my_road_line_string.line_split_twice(fraction_from, fraction_to) { |
| 162 | + /// Some(result) => match result.into_second() { // get the second part of the result |
| 163 | + /// Some(linestring)=>Some(linestring), |
| 164 | + /// _=>None |
| 165 | + /// }, |
| 166 | + /// _=>None |
| 167 | + /// }; |
| 168 | + /// assert_relative_eq!(my_road_section.unwrap(), line_string![ |
| 169 | + /// (x: 5.0,y: 0.0), |
| 170 | + /// (x:10.0,y: 0.0), |
| 171 | + /// (x:10.0,y: 2.0), |
| 172 | + /// ]); |
| 173 | + /// ``` |
| 174 | + /// |
| 175 | + #[rustfmt::skip] |
| 176 | + fn line_split_twice( |
| 177 | + &self, |
| 178 | + fraction_start: Scalar, |
| 179 | + fraction_end: Scalar, |
| 180 | + ) -> Option<LineSplitTwiceResult<Self>> { |
| 181 | + // import enum variants |
| 182 | + use LineSplitTwiceResult::*; |
| 183 | + // reject nan fractions |
| 184 | + if fraction_start.is_nan() || fraction_end.is_nan() { |
| 185 | + return None; |
| 186 | + } |
| 187 | + // clamp |
| 188 | + let fraction_start = fraction_start.min(Scalar::one()).max(Scalar::zero()); |
| 189 | + let fraction_end = fraction_end.min(Scalar::one()).max(Scalar::zero()); |
| 190 | + |
| 191 | + // swap interval if incorrectly ordered |
| 192 | + let (start_fraction, end_fraction) = if fraction_start > fraction_end { |
| 193 | + (fraction_end, fraction_start) |
| 194 | + } else { |
| 195 | + (fraction_start, fraction_end) |
| 196 | + }; |
| 197 | + |
| 198 | + // find the fraction to split the second portion of the line |
| 199 | + let second_fraction = |
| 200 | + (end_fraction - start_fraction) |
| 201 | + / (Scalar::one() - start_fraction); |
| 202 | + |
| 203 | + match self.line_split(start_fraction) { |
| 204 | + Some(LineSplitResult::FirstSecond(line1, line2)) => match line2.line_split(second_fraction) { |
| 205 | + Some(LineSplitResult::FirstSecond(line2, line3)) => Some(FirstSecondThird(line1, line2, line3)), |
| 206 | + Some(LineSplitResult::First (line2 )) => Some(FirstSecond (line1, line2 )), |
| 207 | + Some(LineSplitResult::Second ( line3)) => Some(FirstThird (line1, line3)), |
| 208 | + None => None, |
| 209 | + }, |
| 210 | + Some(LineSplitResult::First (line1)) => Some(First(line1)), |
| 211 | + Some(LineSplitResult::Second(line2)) => match line2.line_split(second_fraction) { |
| 212 | + Some(LineSplitResult::FirstSecond(line2, line3)) => Some(SecondThird ( line2, line3)), |
| 213 | + Some(LineSplitResult::First (line2 )) => Some(Second ( line2 )), |
| 214 | + Some(LineSplitResult::Second ( line3)) => Some(Third ( line3)), |
| 215 | + None => None, |
| 216 | + }, |
| 217 | + None => None, |
| 218 | + } |
| 219 | + } |
| 220 | +} |
0 commit comments