geometry/
ring.rs

1//! Rectangular ring geometry.
2//!
3//! May be useful for drawing structures that enclose other structures,
4//! such as guard rings.
5
6use array_map::ArrayMap;
7use serde::{Deserialize, Serialize};
8
9use crate::prelude::*;
10use crate::transform::TranslateMut;
11
12/// A rectangular ring surrounding an enclosed rectangle.
13#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
14pub struct Ring {
15    /// Vertical span of top segment.
16    topv: Span,
17    /// Vertical span of bottom segment.
18    botv: Span,
19    /// Horizontal span of left segment.
20    lefth: Span,
21    /// Horizontal span of right segment.
22    righth: Span,
23}
24
25/// Represents the ways [`Ring`] geometry can be specified.
26#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
27pub enum RingContents {
28    /// The ring must fit within the given rectangle.
29    Outer(Rect),
30    /// The ring must enclose the given rectangle.
31    Inner(Rect),
32}
33
34impl RingContents {
35    /// The rectangle stored in this enum variant.
36    pub fn rect(&self) -> Rect {
37        match self {
38            Self::Outer(r) => *r,
39            Self::Inner(r) => *r,
40        }
41    }
42
43    /// Returns true if this is a [`RingContents::Outer`] variant.
44    pub fn is_outer(&self) -> bool {
45        matches!(self, Self::Outer(_))
46    }
47
48    /// Returns true if this is a [`RingContents::Inner`] variant.
49    pub fn is_inner(&self) -> bool {
50        matches!(self, Self::Inner(_))
51    }
52}
53
54/// A utility for constructing a [`Ring`].
55#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
56pub struct RingBuilder {
57    contents: Option<RingContents>,
58    widths: ArrayMap<Side, i64, 4>,
59}
60
61impl Ring {
62    /// Creates a new [`RingBuilder`].
63    #[inline]
64    pub fn builder() -> RingBuilder {
65        RingBuilder::new()
66    }
67
68    /// Checks that the ring is valid.
69    pub(crate) fn is_valid(&self) -> bool {
70        self.topv.start() > self.botv.stop() && self.righth.start() > self.lefth.stop()
71    }
72
73    /// The horizontal span of the annulus of the ring.
74    pub fn outer_hspan(&self) -> Span {
75        Span::new(self.lefth.start(), self.righth.stop())
76    }
77
78    /// The horizontal span of the inner portion of the ring.
79    pub fn inner_hspan(&self) -> Span {
80        Span::new(self.lefth.stop(), self.righth.start())
81    }
82
83    /// The vertical span of the annulus of the ring.
84    pub fn outer_vspan(&self) -> Span {
85        Span::new(self.botv.start(), self.topv.stop())
86    }
87
88    /// The vertical span of the inner portion of the ring.
89    pub fn inner_vspan(&self) -> Span {
90        Span::new(self.botv.stop(), self.topv.start())
91    }
92
93    /// The outer annulus bounding box.
94    pub fn outer(&self) -> Rect {
95        Rect::from_spans(self.outer_hspan(), self.outer_vspan())
96    }
97
98    /// The inner rectangle.
99    pub fn inner(&self) -> Rect {
100        Rect::from_spans(self.inner_hspan(), self.inner_vspan())
101    }
102
103    /// The annular rectangle on the given side.
104    #[inline]
105    pub fn rect(&self, side: Side) -> Rect {
106        match side {
107            Side::Top => Rect::from_spans(self.outer_hspan(), self.topv),
108            Side::Right => Rect::from_spans(self.righth, self.outer_vspan()),
109            Side::Bot => Rect::from_spans(self.outer_hspan(), self.botv),
110            Side::Left => Rect::from_spans(self.lefth, self.outer_vspan()),
111        }
112    }
113
114    /// The annuluar rectangle on the given side, but limited to the width/height of the inner rectangle.
115    #[inline]
116    pub fn inner_rect(&self, side: Side) -> Rect {
117        match side {
118            Side::Top => Rect::from_spans(self.inner_hspan(), self.topv),
119            Side::Right => Rect::from_spans(self.righth, self.inner_vspan()),
120            Side::Bot => Rect::from_spans(self.inner_hspan(), self.botv),
121            Side::Left => Rect::from_spans(self.lefth, self.inner_vspan()),
122        }
123    }
124
125    /// The lower left annular corner.
126    ///
127    /// Shares a corner with the inner rect, but does not have any edges
128    /// in common with the inner rect.
129    #[inline]
130    pub fn corner(&self, corner: Corner) -> Rect {
131        match corner {
132            Corner::LowerLeft => Rect::from_spans(self.lefth, self.botv),
133            Corner::UpperLeft => Rect::from_spans(self.lefth, self.topv),
134            Corner::LowerRight => Rect::from_spans(self.righth, self.botv),
135            Corner::UpperRight => Rect::from_spans(self.righth, self.topv),
136        }
137    }
138
139    /// The left annular rectangle.
140    #[inline]
141    pub fn left(&self) -> Rect {
142        self.rect(Side::Left)
143    }
144
145    /// The right annular rectangle.
146    #[inline]
147    pub fn right(&self) -> Rect {
148        self.rect(Side::Right)
149    }
150
151    /// The top annular rectangle.
152    #[inline]
153    pub fn top(&self) -> Rect {
154        self.rect(Side::Top)
155    }
156
157    /// The bottom annular rectangle.
158    #[inline]
159    pub fn bot(&self) -> Rect {
160        self.rect(Side::Bot)
161    }
162
163    /// All 4 annular rectangles.
164    ///
165    /// The order is subject to change.
166    #[inline]
167    pub fn rects(&self) -> [Rect; 4] {
168        [self.top(), self.right(), self.bot(), self.left()]
169    }
170
171    /// The [`Rect`]s going in the horizontal direction (ie. the bottom and top rectangles).
172    #[inline]
173    pub fn hrects(&self) -> [Rect; 2] {
174        [self.bot(), self.top()]
175    }
176
177    /// The [`Rect`]s going in the vertical direction (ie. the left and right rectangles).
178    #[inline]
179    pub fn vrects(&self) -> [Rect; 2] {
180        [self.left(), self.right()]
181    }
182
183    /// The 4 inner annular rectangles.
184    ///
185    /// The order is subject to change.
186    pub fn inner_rects(&self) -> [Rect; 4] {
187        [
188            self.inner_rect(Side::Top),
189            self.inner_rect(Side::Right),
190            self.inner_rect(Side::Bot),
191            self.inner_rect(Side::Left),
192        ]
193    }
194
195    /// The inner annular vertical-going (i.e. left and right) rectangles.
196    pub fn inner_vrects(&self) -> [Rect; 2] {
197        [self.inner_rect(Side::Left), self.inner_rect(Side::Right)]
198    }
199
200    /// The inner annular horizontal-going (i.e. top and bottom) rectangles.
201    pub fn inner_hrects(&self) -> [Rect; 2] {
202        [self.inner_rect(Side::Bot), self.inner_rect(Side::Top)]
203    }
204
205    /// The [`Rect`]s going in the given direction.
206    ///
207    /// Also see [`Ring::hrects`] and [`Ring::vrects`].
208    pub fn dir_rects(&self, dir: Dir) -> [Rect; 2] {
209        match dir {
210            Dir::Horiz => self.hrects(),
211            Dir::Vert => self.vrects(),
212        }
213    }
214}
215
216impl Bbox for Ring {
217    #[inline]
218    fn bbox(&self) -> Option<Rect> {
219        self.outer().bbox()
220    }
221}
222
223impl Contains<Point> for Ring {
224    fn contains(&self, other: &Point) -> Containment {
225        self.rects()
226            .into_iter()
227            .map(move |r| r.contains(other))
228            .max()
229            .unwrap()
230    }
231}
232
233impl TranslateMut for Ring {
234    fn translate_mut(&mut self, p: Point) {
235        self.lefth.translate(p.x);
236        self.righth.translate(p.x);
237        self.botv.translate(p.y);
238        self.topv.translate(p.y);
239    }
240}
241
242impl From<RingBuilder> for Ring {
243    fn from(value: RingBuilder) -> Self {
244        let contents = value.contents.unwrap();
245        let r = contents.rect();
246
247        let sign = if contents.is_outer() {
248            Sign::Pos
249        } else {
250            Sign::Neg
251        };
252
253        let topv = Span::with_point_and_length(sign, r.top(), value.widths[Side::Top]);
254        let righth = Span::with_point_and_length(sign, r.right(), value.widths[Side::Right]);
255        let lefth = Span::with_point_and_length(!sign, r.left(), value.widths[Side::Left]);
256        let botv = Span::with_point_and_length(!sign, r.bot(), value.widths[Side::Bot]);
257
258        let res = Self {
259            topv,
260            botv,
261            lefth,
262            righth,
263        };
264
265        if contents.is_outer() {
266            assert_eq!(res.outer(), r);
267        } else {
268            assert_eq!(res.inner(), r);
269        }
270
271        assert!(res.is_valid());
272        res
273    }
274}
275
276impl RingBuilder {
277    /// Creates a new [`RingBuilder`].
278    #[inline]
279    pub fn new() -> Self {
280        Self::default()
281    }
282
283    /// Constructs a [`Ring`] from this builder.
284    #[inline]
285    pub fn build(&mut self) -> Ring {
286        Ring::from(*self)
287    }
288
289    /// Set the outer region of the ring.
290    ///
291    /// Only one of inner and outer may be set.
292    pub fn outer(&mut self, rect: Rect) -> &mut Self {
293        self.contents = Some(RingContents::Outer(rect));
294        self
295    }
296
297    /// Set the inner region of the ring.
298    ///
299    /// Only one of inner and outer may be set.
300    pub fn inner(&mut self, rect: Rect) -> &mut Self {
301        self.contents = Some(RingContents::Inner(rect));
302        self
303    }
304
305    /// Set the width of the left side of the ring.
306    pub fn left_width(&mut self, value: i64) -> &mut Self {
307        self.widths[Side::Left] = value;
308        self
309    }
310
311    /// Set the width of the right side of the ring.
312    pub fn right_width(&mut self, value: i64) -> &mut Self {
313        self.widths[Side::Right] = value;
314        self
315    }
316
317    /// Set the height of the bottom of the ring.
318    pub fn bot_height(&mut self, value: i64) -> &mut Self {
319        self.widths[Side::Bot] = value;
320        self
321    }
322
323    /// Set the height of the top of the ring.
324    pub fn top_height(&mut self, value: i64) -> &mut Self {
325        self.widths[Side::Top] = value;
326        self
327    }
328
329    /// Sets the widths of the vertical-going parts of the ring to the given value.
330    pub fn widths(&mut self, value: i64) -> &mut Self {
331        self.left_width(value);
332        self.right_width(value)
333    }
334
335    /// Sets the heights of the horizontal-going parts of the ring to the given value.
336    pub fn heights(&mut self, value: i64) -> &mut Self {
337        self.top_height(value);
338        self.bot_height(value)
339    }
340
341    /// Sets the width of all ring edges to the given value.
342    pub fn uniform_width(&mut self, value: i64) -> &mut Self {
343        self.widths(value);
344        self.heights(value)
345    }
346
347    /// Sets the width of segments running in the given direction.
348    ///
349    /// If `dir` is [`Dir::Vert`], sets the widths of the left/right regions.
350    /// If `dir` is [`Dir::Horiz`], sets the heights of the top/bottom regions.
351    pub fn dir_widths(&mut self, dir: Dir, value: i64) -> &mut Self {
352        match dir {
353            Dir::Vert => self.widths(value),
354            Dir::Horiz => self.heights(value),
355        }
356    }
357
358    /// Set the width of the given side.
359    pub fn side_width(&mut self, side: Side, value: i64) -> &mut Self {
360        use Side::*;
361        match side {
362            Top => self.top_height(value),
363            Bot => self.bot_height(value),
364            Left => self.left_width(value),
365            Right => self.right_width(value),
366        }
367    }
368}