lset/
line.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use super::*;
4use core::{cmp::Ordering, ops::*};
5
6/// Expresses a linear set by its starting and termination points
7///
8/// This type is fully isomorphic with `core::ops::Range` and `Span`. However,
9/// unlike `core::ops::Range`, this type is not an iterator and therefore can
10/// implement `Copy`. Points may have any number of dimensions.
11#[repr(C)]
12#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
13pub struct Line<T> {
14    /// The start point
15    pub start: T,
16
17    /// The first point excluded by the set
18    pub end: T,
19}
20
21impl<T> Line<T> {
22    /// Create a new line
23    ///
24    /// # Example
25    ///
26    /// ```
27    /// let line = lset::Line::new(5, 10);
28    /// assert_eq!(line.start, 5);
29    /// assert_eq!(line.end, 10);
30    /// ```
31    #[inline(always)]
32    pub const fn new(start: T, end: T) -> Self {
33        Self { start, end }
34    }
35
36    /// Indicates whether the line is empty
37    ///
38    /// # Example
39    ///
40    /// ```
41    /// use lset::*;
42    /// assert!(Line::from(2..2).is_empty());
43    /// assert!(!Line::from(2..3).is_empty());
44    /// ```
45    #[inline(always)]
46    pub fn is_empty(&self) -> bool
47    where
48        T: PartialEq,
49    {
50        self.start == self.end
51    }
52}
53
54impl<T: PartialOrd> Line<T> {
55    /// Returns the intersection between the sets, if any.
56    ///
57    /// ```
58    /// use lset::*;
59    ///
60    /// let a = Line::new(0, 5);
61    /// let b = Line::new(2, 7);
62    /// let c = Line::new(5, 10);
63    ///
64    /// assert_eq!(a.intersection(b), Some(Line::new(2, 5)));
65    /// assert_eq!(b.intersection(c), Some(Line::new(5, 7)));
66    /// assert_eq!(a.intersection(a), Some(a));
67    /// assert_eq!(b.intersection(b), Some(b));
68    /// assert_eq!(c.intersection(c), Some(c));
69    /// assert_eq!(a.intersection(c), None);
70    /// ```
71    pub fn intersection(self, other: Self) -> Option<Self> {
72        let start = if other.start >= self.start && other.start < self.end {
73            Some(other.start)
74        } else {
75            None
76        };
77
78        let end = if other.end > self.start && other.end <= self.end {
79            Some(other.end)
80        } else {
81            None
82        };
83
84        match (start, end) {
85            (Some(start), Some(end)) => Some(Line::new(start, end)),
86            (Some(start), None) => Some(Line::new(start, self.end)),
87            (None, Some(end)) => Some(Line::new(self.start, end)),
88            (None, None) => None,
89        }
90    }
91}
92
93impl<T: Add<T, Output = T>> Add<T> for Line<T> {
94    type Output = Self;
95
96    /// Grows the line by the size of the operand
97    ///
98    /// # Example
99    ///
100    /// ```
101    /// let before = lset::Line::new(5, 10);
102    /// let after = before + 5;
103    /// assert_eq!(after.start, 5);
104    /// assert_eq!(after.end, 15);
105    /// ```
106    #[inline(always)]
107    fn add(self, rhs: T) -> Self::Output {
108        Self {
109            start: self.start,
110            end: self.end + rhs,
111        }
112    }
113}
114
115impl<T: AddAssign<T>> AddAssign<T> for Line<T> {
116    /// Grows the line by the size of the operand
117    ///
118    /// # Example
119    ///
120    /// ```
121    /// let mut line = lset::Line::new(5, 10);
122    /// line += 5;
123    /// assert_eq!(line.start, 5);
124    /// assert_eq!(line.end, 15);
125    /// ```
126    #[inline(always)]
127    fn add_assign(&mut self, rhs: T) {
128        self.end += rhs;
129    }
130}
131
132impl<T: Sub<T, Output = T>> Sub<T> for Line<T> {
133    type Output = Self;
134
135    /// Shrinks the line by the size of the operand
136    ///
137    /// # Example
138    ///
139    /// ```
140    /// let before = lset::Line::new(5, 10);
141    /// let after = before - 5;
142    /// assert_eq!(after.start, 5);
143    /// assert_eq!(after.end, 5);
144    /// ```
145    #[inline(always)]
146    fn sub(self, rhs: T) -> Self::Output {
147        Self {
148            start: self.start,
149            end: self.end - rhs,
150        }
151    }
152}
153
154impl<T: SubAssign<T>> SubAssign<T> for Line<T> {
155    /// Shrinks the line by the size of the operand
156    ///
157    /// # Example
158    ///
159    /// ```
160    /// let mut line = lset::Line::new(5, 10);
161    /// line -= 5;
162    /// assert_eq!(line.start, 5);
163    /// assert_eq!(line.end, 5);
164    /// ```
165    #[inline(always)]
166    fn sub_assign(&mut self, rhs: T) {
167        self.end -= rhs;
168    }
169}
170
171impl<T: Copy + Sub<T, Output = T>> Shl<T> for Line<T> {
172    type Output = Self;
173
174    /// Shifts the line downwards without changing size
175    ///
176    /// # Example
177    ///
178    /// ```
179    /// let before = lset::Line::new(5, 10);
180    /// let after = before << 5;
181    /// assert_eq!(after.start, 0);
182    /// assert_eq!(after.end, 5);
183    /// ```
184    #[inline(always)]
185    fn shl(self, rhs: T) -> Self::Output {
186        Self {
187            start: self.start - rhs,
188            end: self.end - rhs,
189        }
190    }
191}
192
193impl<T: Copy + SubAssign<T>> ShlAssign<T> for Line<T> {
194    /// Shifts the line downwards without changing size
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// let mut line = lset::Line::new(5, 10);
200    /// line <<= 5;
201    /// assert_eq!(line.start, 0);
202    /// assert_eq!(line.end, 5);
203    /// ```
204    #[inline(always)]
205    fn shl_assign(&mut self, rhs: T) {
206        self.start -= rhs;
207        self.end -= rhs;
208    }
209}
210
211impl<T: Copy + Add<T, Output = T>> Shr<T> for Line<T> {
212    type Output = Self;
213
214    /// Shifts the line upwards without changing size
215    ///
216    /// # Example
217    ///
218    /// ```
219    /// let before = lset::Line::new(5, 10);
220    /// let after = before >> 5;
221    /// assert_eq!(after.start, 10);
222    /// assert_eq!(after.end, 15);
223    /// ```
224    #[inline(always)]
225    fn shr(self, rhs: T) -> Self::Output {
226        Self {
227            start: self.start + rhs,
228            end: self.end + rhs,
229        }
230    }
231}
232
233impl<T: Copy + AddAssign<T>> ShrAssign<T> for Line<T> {
234    /// Shifts the line upwards without changing size
235    ///
236    /// # Example
237    ///
238    /// ```
239    /// let mut line = lset::Line::new(5, 10);
240    /// line >>= 5;
241    /// assert_eq!(line.start, 10);
242    /// assert_eq!(line.end, 15);
243    /// ```
244    #[inline(always)]
245    fn shr_assign(&mut self, rhs: T) {
246        self.start += rhs;
247        self.end += rhs;
248    }
249}
250
251impl<T: PartialOrd> PartialOrd for Line<T> {
252    /// Compares two `Line` types.
253    ///
254    /// # Example
255    ///
256    /// ```
257    /// use lset::*;
258    /// assert!(Line::new(5, 10) <= Line::new(5, 10));
259    /// assert!(Line::new(5, 10) >= Line::new(5, 10));
260    /// assert!(Line::new(5, 10) < Line::new(10, 15));
261    /// assert!(Line::new(10, 15) > Line::new(5, 10));
262    /// ```
263    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
264        if self == other {
265            Some(Ordering::Equal)
266        } else if self.start >= other.end {
267            Some(Ordering::Greater)
268        } else if self.end <= other.start {
269            Some(Ordering::Less)
270        } else {
271            None
272        }
273    }
274}
275
276impl<T> From<Range<T>> for Line<T> {
277    /// Converts a `Range` into a `Line`
278    ///
279    /// # Example
280    ///
281    /// ```
282    /// use lset::*;
283    /// assert_eq!(Line::new(5, 10), Line::from(5..10));
284    /// assert_eq!(Line::new(5, 10), (5..10).into());
285    /// ```
286    #[inline(always)]
287    fn from(value: Range<T>) -> Self {
288        Self {
289            start: value.start,
290            end: value.end,
291        }
292    }
293}
294
295impl<T> From<Line<T>> for Range<T> {
296    /// Converts a `Line` into a `Range`
297    ///
298    /// # Example
299    ///
300    /// ```
301    /// use lset::*;
302    /// assert_eq!(5..10, std::ops::Range::from(Line::new(5, 10)));
303    /// assert_eq!(5..10, Line::new(5, 10).into());
304    /// ```
305    #[inline(always)]
306    fn from(value: Line<T>) -> Self {
307        Self {
308            start: value.start,
309            end: value.end,
310        }
311    }
312}
313
314impl<T: Clone + Add<U, Output = T>, U> From<Span<T, U>> for Line<T> {
315    /// Converts a `Span` into a `Line`
316    ///
317    /// # Example
318    ///
319    /// ```
320    /// use lset::*;
321    /// assert_eq!(Line::new(5, 10), Line::from(Span::new(5, 5)));
322    /// assert_eq!(Line::new(5, 10), Span::new(5, 5).into());
323    /// ```
324    #[inline(always)]
325    fn from(value: Span<T, U>) -> Self {
326        Self {
327            start: value.start.clone(),
328            end: value.start + value.count,
329        }
330    }
331}
332
333impl<T: PartialOrd> Contains<T> for Line<T> {
334    /// Indicates whether the line contains a point
335    ///
336    /// # Example
337    ///
338    /// ```
339    /// use lset::*;
340    /// assert!(!Line::from(2..3).contains(&1));
341    /// assert!(Line::from(2..3).contains(&2));
342    /// assert!(!Line::from(2..3).contains(&3));
343
344    /// assert!(!Line::from(3..2).contains(&1));
345    /// assert!(!Line::from(3..2).contains(&2));
346    /// assert!(!Line::from(3..2).contains(&3));
347    /// ```
348    #[inline(always)]
349    fn contains(&self, value: &T) -> bool {
350        if self.start <= self.end {
351            &self.start <= value && value < &self.end
352        } else {
353            false
354        }
355    }
356}
357
358impl<T: PartialOrd> Contains<Self> for Line<T> {
359    /// Indicates whether the line contains another line
360    ///
361    /// # Example
362    ///
363    /// ```
364    /// use lset::*;
365    /// assert!(Line::from(4..8).contains(&Line::from(5..7)));
366    /// assert!(Line::from(4..8).contains(&Line::from(4..7)));
367    /// assert!(Line::from(4..8).contains(&Line::from(5..8)));
368    /// assert!(Line::from(4..8).contains(&Line::from(4..8)));
369    /// assert!(!Line::from(4..8).contains(&Line::from(3..8)));
370    /// assert!(!Line::from(4..8).contains(&Line::from(4..9)));
371    /// assert!(!Line::from(4..8).contains(&Line::from(3..9)));
372    /// assert!(!Line::from(4..8).contains(&Line::from(2..10)));
373    /// assert!(!Line::from(4..8).contains(&Line::from(6..5)));
374    /// assert!(!Line::from(7..3).contains(&Line::from(5..6)));
375    /// ```
376    #[inline(always)]
377    fn contains(&self, value: &Self) -> bool {
378        if self.start <= self.end && value.start <= value.end {
379            self.start <= value.start && value.end <= self.end
380        } else {
381            false
382        }
383    }
384}
385
386impl<T: PartialOrd> Split<Self> for Line<T> {
387    /// Splits a line by another line
388    ///
389    /// # Example
390    ///
391    /// ```
392    /// use lset::*;
393    /// let line = Line::from(2..5);
394    /// assert_eq!(line.split(Line::from(1..4)), None);
395    /// assert_eq!(line.split(Line::from(3..6)), None);
396    /// assert_eq!(line.split(Line::from(2..2)), None);
397    /// assert_eq!(line.split(Line::from(2..3)), Some((Line::from(2..2), Line::from(3..5))));
398    /// assert_eq!(line.split(Line::from(3..3)), None);
399    /// assert_eq!(line.split(Line::from(3..4)), Some((Line::from(2..3), Line::from(4..5))));
400    /// assert_eq!(line.split(Line::from(4..4)), None);
401    /// assert_eq!(line.split(Line::from(4..5)), Some((Line::from(2..4), Line::from(5..5))));
402    /// assert_eq!(line.split(Line::from(5..5)), None);
403    /// assert_eq!(line.split(line), Some((Line::from(2..2), Line::from(5..5))));
404    /// ```
405    #[inline(always)]
406    fn split(self, at: Self) -> Option<(Self, Self)> {
407        if at.start == at.end {
408            return None;
409        }
410
411        if !self.contains(&at) {
412            return None;
413        }
414
415        let l = Self {
416            start: self.start,
417            end: at.start,
418        };
419
420        let r = Self {
421            start: at.end,
422            end: self.end,
423        };
424
425        Some((l, r))
426    }
427}
428
429impl<T: PartialOrd + Copy> Split<T> for Line<T> {
430    /// Splits a line at a point
431    ///
432    /// # Example
433    ///
434    /// ```
435    /// use lset::*;
436    /// let line = Line::from(2..4);
437    /// assert_eq!(line.split(1), None);
438    /// assert_eq!(line.split(2), Some((Line::from(2..2), line)));
439    /// assert_eq!(line.split(3), Some((Line::from(2..3), Line::from(3..4))));
440    /// assert_eq!(line.split(4), Some((line, Line::from(4..4))));
441    /// assert_eq!(line.split(5), None);
442    /// ```
443    #[inline(always)]
444    fn split(self, at: T) -> Option<(Self, Self)> {
445        if at < self.start || at > self.end {
446            return None;
447        }
448
449        let l = Self {
450            start: self.start,
451            end: at,
452        };
453
454        let r = Self {
455            start: at,
456            end: self.end,
457        };
458
459        Some((l, r))
460    }
461}