| 1 | // pathfinder/geometry/src/basic/line_segment.rs |
| 2 | // |
| 3 | // Copyright © 2019 The Pathfinder Project Developers. |
| 4 | // |
| 5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | // option. This file may not be copied, modified, or distributed |
| 9 | // except according to those terms. |
| 10 | |
| 11 | //! Line segment types, optimized with SIMD. |
| 12 | |
| 13 | use crate::transform2d::Matrix2x2F; |
| 14 | use crate::vector::{Vector2F, vec2f}; |
| 15 | use crate::util; |
| 16 | use pathfinder_simd::default::F32x4; |
| 17 | use std::ops::{Add, Mul, MulAssign, Sub}; |
| 18 | |
| 19 | #[derive (Clone, Copy, Debug, PartialEq, Default)] |
| 20 | pub struct LineSegment2F(pub F32x4); |
| 21 | |
| 22 | impl LineSegment2F { |
| 23 | #[inline ] |
| 24 | pub fn new(from: Vector2F, to: Vector2F) -> LineSegment2F { |
| 25 | LineSegment2F(from.0.concat_xy_xy(to.0)) |
| 26 | } |
| 27 | |
| 28 | #[inline ] |
| 29 | pub fn from(self) -> Vector2F { |
| 30 | Vector2F(self.0.xy()) |
| 31 | } |
| 32 | |
| 33 | #[inline ] |
| 34 | pub fn to(self) -> Vector2F { |
| 35 | Vector2F(self.0.zw()) |
| 36 | } |
| 37 | |
| 38 | #[inline ] |
| 39 | pub fn set_from(&mut self, point: Vector2F) { |
| 40 | self.0 = point.0.to_f32x4().concat_xy_zw(self.0) |
| 41 | } |
| 42 | |
| 43 | #[inline ] |
| 44 | pub fn set_to(&mut self, point: Vector2F) { |
| 45 | self.0 = self.0.concat_xy_xy(point.0.to_f32x4()) |
| 46 | } |
| 47 | |
| 48 | #[allow (clippy::wrong_self_convention)] |
| 49 | #[inline ] |
| 50 | pub fn from_x(self) -> f32 { |
| 51 | self.0[0] |
| 52 | } |
| 53 | |
| 54 | #[allow (clippy::wrong_self_convention)] |
| 55 | #[inline ] |
| 56 | pub fn from_y(self) -> f32 { |
| 57 | self.0[1] |
| 58 | } |
| 59 | |
| 60 | #[inline ] |
| 61 | pub fn to_x(self) -> f32 { |
| 62 | self.0[2] |
| 63 | } |
| 64 | |
| 65 | #[inline ] |
| 66 | pub fn to_y(self) -> f32 { |
| 67 | self.0[3] |
| 68 | } |
| 69 | |
| 70 | #[inline ] |
| 71 | pub fn set_from_x(&mut self, x: f32) { |
| 72 | self.0[0] = x |
| 73 | } |
| 74 | |
| 75 | #[inline ] |
| 76 | pub fn set_from_y(&mut self, y: f32) { |
| 77 | self.0[1] = y |
| 78 | } |
| 79 | |
| 80 | #[inline ] |
| 81 | pub fn set_to_x(&mut self, x: f32) { |
| 82 | self.0[2] = x |
| 83 | } |
| 84 | |
| 85 | #[inline ] |
| 86 | pub fn set_to_y(&mut self, y: f32) { |
| 87 | self.0[3] = y |
| 88 | } |
| 89 | |
| 90 | #[inline ] |
| 91 | pub fn split(self, t: f32) -> (LineSegment2F, LineSegment2F) { |
| 92 | debug_assert!(t >= 0.0 && t <= 1.0); |
| 93 | let (from_from, to_to) = (self.0.xyxy(), self.0.zwzw()); |
| 94 | let d_d = to_to - from_from; |
| 95 | let mid_mid = from_from + d_d * F32x4::splat(t); |
| 96 | ( |
| 97 | LineSegment2F(from_from.concat_xy_xy(mid_mid)), |
| 98 | LineSegment2F(mid_mid.concat_xy_xy(to_to)), |
| 99 | ) |
| 100 | } |
| 101 | |
| 102 | // Returns the left segment first, followed by the right segment. |
| 103 | #[inline ] |
| 104 | pub fn split_at_x(self, x: f32) -> (LineSegment2F, LineSegment2F) { |
| 105 | let (min_part, max_part) = self.split(self.solve_t_for_x(x)); |
| 106 | if min_part.from_x() < max_part.from_x() { |
| 107 | (min_part, max_part) |
| 108 | } else { |
| 109 | (max_part, min_part) |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | // Returns the upper segment first, followed by the lower segment. |
| 114 | #[inline ] |
| 115 | pub fn split_at_y(self, y: f32) -> (LineSegment2F, LineSegment2F) { |
| 116 | let (min_part, max_part) = self.split(self.solve_t_for_y(y)); |
| 117 | |
| 118 | // Make sure we compare `from_y` and `to_y` to properly handle the case in which one of the |
| 119 | // two segments is zero-length. |
| 120 | if min_part.from_y() < max_part.to_y() { |
| 121 | (min_part, max_part) |
| 122 | } else { |
| 123 | (max_part, min_part) |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | #[inline ] |
| 128 | pub fn solve_t_for_x(self, x: f32) -> f32 { |
| 129 | (x - self.from_x()) / (self.to_x() - self.from_x()) |
| 130 | } |
| 131 | |
| 132 | #[inline ] |
| 133 | pub fn solve_t_for_y(self, y: f32) -> f32 { |
| 134 | (y - self.from_y()) / (self.to_y() - self.from_y()) |
| 135 | } |
| 136 | |
| 137 | #[inline ] |
| 138 | pub fn solve_x_for_y(self, y: f32) -> f32 { |
| 139 | util::lerp(self.from_x(), self.to_x(), self.solve_t_for_y(y)) |
| 140 | } |
| 141 | |
| 142 | #[inline ] |
| 143 | pub fn solve_y_for_x(self, x: f32) -> f32 { |
| 144 | util::lerp(self.from_y(), self.to_y(), self.solve_t_for_x(x)) |
| 145 | } |
| 146 | |
| 147 | #[inline ] |
| 148 | pub fn reversed(self) -> LineSegment2F { |
| 149 | LineSegment2F(self.0.zwxy()) |
| 150 | } |
| 151 | |
| 152 | #[inline ] |
| 153 | pub fn upper_point(self) -> Vector2F { |
| 154 | if self.from_y() < self.to_y() { |
| 155 | self.from() |
| 156 | } else { |
| 157 | self.to() |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | #[inline ] |
| 162 | pub fn min_x(self) -> f32 { |
| 163 | f32::min(self.from_x(), self.to_x()) |
| 164 | } |
| 165 | |
| 166 | #[inline ] |
| 167 | pub fn max_x(self) -> f32 { |
| 168 | f32::max(self.from_x(), self.to_x()) |
| 169 | } |
| 170 | |
| 171 | #[inline ] |
| 172 | pub fn min_y(self) -> f32 { |
| 173 | f32::min(self.from_y(), self.to_y()) |
| 174 | } |
| 175 | |
| 176 | #[inline ] |
| 177 | pub fn max_y(self) -> f32 { |
| 178 | f32::max(self.from_y(), self.to_y()) |
| 179 | } |
| 180 | |
| 181 | #[inline ] |
| 182 | pub fn y_winding(self) -> i32 { |
| 183 | if self.from_y() < self.to_y() { |
| 184 | 1 |
| 185 | } else { |
| 186 | -1 |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // Reverses if necessary so that the from point is above the to point. Calling this method |
| 191 | // again will undo the transformation. |
| 192 | #[inline ] |
| 193 | pub fn orient(self, y_winding: i32) -> LineSegment2F { |
| 194 | if y_winding >= 0 { |
| 195 | self |
| 196 | } else { |
| 197 | self.reversed() |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | // TODO(pcwalton): Optimize with SIMD. |
| 202 | #[inline ] |
| 203 | pub fn square_length(self) -> f32 { |
| 204 | let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y()); |
| 205 | dx * dx + dy * dy |
| 206 | } |
| 207 | |
| 208 | #[inline ] |
| 209 | pub fn vector(self) -> Vector2F { |
| 210 | self.to() - self.from() |
| 211 | } |
| 212 | |
| 213 | // http://www.cs.swan.ac.uk/~cssimon/line_intersection.html |
| 214 | pub fn intersection_t(self, other: LineSegment2F) -> Option<f32> { |
| 215 | let p0p1 = self.vector(); |
| 216 | let matrix = Matrix2x2F(other.vector().0.concat_xy_xy((-p0p1).0)); |
| 217 | if f32::abs(matrix.det()) < EPSILON { |
| 218 | return None; |
| 219 | } |
| 220 | return Some((matrix.inverse() * (self.from() - other.from())).y()); |
| 221 | |
| 222 | const EPSILON: f32 = 0.0001; |
| 223 | } |
| 224 | |
| 225 | #[inline ] |
| 226 | pub fn sample(self, t: f32) -> Vector2F { |
| 227 | self.from() + self.vector() * t |
| 228 | } |
| 229 | |
| 230 | #[inline ] |
| 231 | pub fn midpoint(self) -> Vector2F { |
| 232 | self.sample(0.5) |
| 233 | } |
| 234 | |
| 235 | #[inline ] |
| 236 | pub fn offset(self, distance: f32) -> LineSegment2F { |
| 237 | if self.is_zero_length() { |
| 238 | self |
| 239 | } else { |
| 240 | self + self.vector().yx().normalize() * vec2f(-distance, distance) |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | #[inline ] |
| 245 | pub fn is_zero_length(self) -> bool { |
| 246 | self.vector().is_zero() |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | impl Add<Vector2F> for LineSegment2F { |
| 251 | type Output = LineSegment2F; |
| 252 | #[inline ] |
| 253 | fn add(self, point: Vector2F) -> LineSegment2F { |
| 254 | LineSegment2F(self.0 + point.0.to_f32x4().xyxy()) |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | impl Sub<Vector2F> for LineSegment2F { |
| 259 | type Output = LineSegment2F; |
| 260 | #[inline ] |
| 261 | fn sub(self, point: Vector2F) -> LineSegment2F { |
| 262 | LineSegment2F(self.0 - point.0.to_f32x4().xyxy()) |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | impl Mul<Vector2F> for LineSegment2F { |
| 267 | type Output = LineSegment2F; |
| 268 | #[inline ] |
| 269 | fn mul(self, factors: Vector2F) -> LineSegment2F { |
| 270 | LineSegment2F(self.0 * factors.0.to_f32x4().xyxy()) |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | impl Mul<f32> for LineSegment2F { |
| 275 | type Output = LineSegment2F; |
| 276 | #[inline ] |
| 277 | fn mul(self, factor: f32) -> LineSegment2F { |
| 278 | LineSegment2F(self.0 * F32x4::splat(factor)) |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | impl MulAssign<Vector2F> for LineSegment2F { |
| 283 | #[inline ] |
| 284 | fn mul_assign(&mut self, factors: Vector2F) { |
| 285 | *self = *self * factors |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | #[derive (Clone, Copy, Debug, Default)] |
| 290 | #[repr (C)] |
| 291 | pub struct LineSegmentU4 { |
| 292 | pub from: u8, |
| 293 | pub to: u8, |
| 294 | } |
| 295 | |
| 296 | #[derive (Clone, Copy, Debug, Default)] |
| 297 | #[repr (C)] |
| 298 | pub struct LineSegmentU8 { |
| 299 | pub from_x: u8, |
| 300 | pub from_y: u8, |
| 301 | pub to_x: u8, |
| 302 | pub to_y: u8, |
| 303 | } |
| 304 | |