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
13use crate::transform2d::Matrix2x2F;
14use crate::vector::{Vector2F, vec2f};
15use crate::util;
16use pathfinder_simd::default::F32x4;
17use std::ops::{Add, Mul, MulAssign, Sub};
18
19#[derive(Clone, Copy, Debug, PartialEq, Default)]
20pub struct LineSegment2F(pub F32x4);
21
22impl 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
250impl 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
258impl 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
266impl 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
274impl 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
282impl 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)]
291pub struct LineSegmentU4 {
292 pub from: u8,
293 pub to: u8,
294}
295
296#[derive(Clone, Copy, Debug, Default)]
297#[repr(C)]
298pub struct LineSegmentU8 {
299 pub from_x: u8,
300 pub from_y: u8,
301 pub to_x: u8,
302 pub to_y: u8,
303}
304