sensehat_screen/
frame_clip.rs

1//! Support for combining `PixelFrame`s.
2//!
3//! The `Clip` is the type that merges `PixelFrame` by rows or by columns
4//!
5//!
6use super::{
7    clip_pixel_frames_offset_bottom, clip_pixel_frames_offset_left, clip_pixel_frames_offset_right,
8    clip_pixel_frames_offset_top, Offset, PixelFrame,
9};
10
11/// Methods enabled by the `clip` feature.
12impl PixelFrame {
13    /// Shortcut to get a `PixelFrame` from a temporary `Clip`, at a given `Offset`.
14    /// For memory reallocation, such as when rendering a clip at multiple offsets,
15    /// it is better to use `PixelFrame::build_clip`.
16    pub fn clip(&self, other: &PixelFrame, clip_at: Offset) -> Self {
17        self.build_clip(other).offset(clip_at)
18    }
19    /// Create a `Clip` with this and another `PixelFrame`.
20    pub fn build_clip(&self, other: &PixelFrame) -> Clip {
21        Clip::new(*self, *other)
22    }
23}
24
25/// A clip made of two `PixelFrame`s.
26/// # Horizontal `Clip`
27///
28/// In horizontal clips, frames are placed side-by-side, and a `Clip` is specified
29/// using either `Offset::Right(_)`, or `Offset::Left(_)` with the `Clip::offset` method.
30///
31///
32/// `Offset::Right(_)`
33/// ------------------
34///
35/// ```text
36///                 |---------------|
37///                 |    Right(8)   |
38///                 |---------------|
39///               |---------------| |
40///               |    Right(7)   | |
41///               |---------------| |
42///             |---------------| | |
43///             |    Right(6)   | | |
44///             |---------------| | |
45///           |---------------| | | |
46///           |    Right(5)   | | | |
47///           |---------------| | | |
48///         |---------------| | | | |
49///         |    Right(4)   | | | | |
50///         |---------------| | | | |
51///       |---------------| | | | | |
52///       |    Right(3)   | | | | | |
53///       |---------------| | | | | |
54///     |---------------| | | | | | |
55///     |    Right(2)   | | | | | | |
56///     |---------------| | | | | | |
57///   |---------------| | | | | | | |
58///   |    Right(1)   | | | | | | | |
59///   |---------------| | | | | | | |
60/// |---------------| | | | | | | | |
61/// |    Right(0)   | | | | | | | | |
62/// |---------------| | | | | | | | |
63///                 | | | | | | | | |
64/// |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|  <- individual frame COLUMN index
65/// |---------------|---------------|
66/// |    frame 1    |    frame 2    |
67/// |---------------|---------------|
68/// ```
69///
70/// ### Example
71///
72/// ```rust
73/// extern crate sensehat_screen;
74/// use sensehat_screen::{Clip, PixelColor, PixelFrame};
75/// use sensehat_screen::Offset;
76///
77/// fn main() {
78///     let frame_1 = PixelFrame::new(&[PixelColor::YELLOW; 64]);
79///     let frame_2 = PixelFrame::new(&[PixelColor::BLUE; 64]);
80///     let clip = Clip::new(frame_1, frame_2);
81///     // Offset of `0`, shows the first frame.
82///     assert_eq!(clip.offset(Offset::Right(0)), frame_1);
83///
84///     let offset_2_cols = clip.offset(Offset::Right(2)).as_columns();
85///
86///
87///     let expected_cols = &[
88///         [PixelColor::BLUE; 8],
89///         [PixelColor::BLUE; 8],
90///         [PixelColor::YELLOW; 8],
91///         [PixelColor::YELLOW; 8],
92///         [PixelColor::YELLOW; 8],
93///         [PixelColor::YELLOW; 8],
94///         [PixelColor::YELLOW; 8],
95///         [PixelColor::YELLOW; 8],
96///     ];
97///     assert_eq!(&offset_2_cols, expected_cols);
98///
99///     // Offset of `8`, shows the second frame.
100///     assert_eq!(clip.offset(Offset::Right(8)), frame_2);
101/// }
102/// ```
103/// `Offset::Left(_)`
104/// ----------------
105/// ```text
106///                 |---------------|
107///                 |    Left(0)    |
108///                 |---------------|
109///               |---------------| |
110///               |    Left(1)    | |
111///               |---------------| |
112///             |---------------| | |
113///             |    Left(2)    | | |
114///             |---------------| | |
115///           |---------------| | | |
116///           |    Left(3)    | | | |
117///           |---------------| | | |
118///         |---------------| | | | |
119///         |    Left(4)    | | | | |
120///         |---------------| | | | |
121///       |---------------| | | | | |
122///       |    Left(5)    | | | | | |
123///       |---------------| | | | | |
124///     |---------------| | | | | | |
125///     |    Left(6)    | | | | | | |
126///     |---------------| | | | | | |
127///   |---------------| | | | | | | |
128///   |    Left(7)    | | | | | | | |
129///   |---------------| | | | | | | |
130/// |---------------| | | | | | | | |
131/// |    Left(8)    | | | | | | | | |
132/// |---------------| | | | | | | | |
133///                 | | | | | | | | |
134/// |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|  <- individual frame COLUMN index
135/// |---------------|---------------|
136/// |      frame 2  |      frame 1  |
137/// |---------------|---------------|
138///
139/// ```
140///
141/// ### Example
142///
143/// ```rust
144/// extern crate sensehat_screen;
145/// use sensehat_screen::{Clip, PixelColor, PixelFrame};
146/// use sensehat_screen::Offset;
147///
148/// fn main() {
149///     let frame_1 = PixelFrame::new(&[PixelColor::YELLOW; 64]);
150///     let frame_2 = PixelFrame::new(&[PixelColor::BLUE; 64]);
151///     let clip = Clip::new(frame_1, frame_2);
152///     // Offset of `0`, shows the first frame.
153///     assert_eq!(clip.offset(Offset::Left(0)), frame_1);
154///
155///     let offset_4_cols = clip.offset(Offset::Left(4)).as_columns();
156///
157///     let expected_cols = &[
158///         [PixelColor::YELLOW; 8],
159///         [PixelColor::YELLOW; 8],
160///         [PixelColor::YELLOW; 8],
161///         [PixelColor::YELLOW; 8],
162///         [PixelColor::BLUE; 8],
163///         [PixelColor::BLUE; 8],
164///         [PixelColor::BLUE; 8],
165///         [PixelColor::BLUE; 8],
166///     ];
167///     assert_eq!(&offset_4_cols, expected_cols);
168///
169///     // Offset of `8`, shows the second frame.
170///     assert_eq!(clip.offset(Offset::Left(8)), frame_2);
171/// }
172/// ```
173///
174/// Vertical `Clip`
175/// ====================
176///
177/// In vertical clips, frames are placed above-and-belowe each other, and a `Clip` is specified
178/// using either `Offset::Bottom(_)`, or `Offset::Top(_)` with the `Clip::offset` method.
179///
180/// `Offset::Bottom(_)`
181/// -------------------
182/// ```text
183///                 |---------------|
184///                 |   Bottom(0)   |
185///                 |---------------|
186///               |---------------| |
187///               |   Bottom(1)   | |
188///               |---------------| |
189///             |---------------| | |
190///             |   Bottom(2)   | | |
191///             |---------------| | |
192///           |---------------| | | |
193///           |   Bottom(3)   | | | |
194///           |---------------| | | |
195///         |---------------| | | | |
196///         |   Bottom(4)   | | | | |
197///         |---------------| | | | |
198///       |---------------| | | | | |
199///       |   Bottom(5)   | | | | | |
200///       |---------------| | | | | |
201///     |---------------| | | | | | |
202///     |   Bottom(6)   | | | | | | |
203///     |---------------| | | | | | |
204///   |---------------| | | | | | | |
205///   |   Bottom(7)   | | | | | | | |
206///   |---------------| | | | | | | |
207/// |---------------| | | | | | | | |
208/// |   Bottom(8)   | | | | | | | | |
209/// |---------------| | | | | | | | |
210///                 | | | | | | | | |
211/// |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|  <- individual frame ROW index
212/// |---------------|---------------|
213/// |      frame 2  |      frame 1  |
214/// |---------------|---------------|
215///
216/// ```
217///
218/// ### Example
219///
220/// ```rust
221/// extern crate sensehat_screen;
222/// use sensehat_screen::{Clip, PixelColor, PixelFrame};
223/// use sensehat_screen::Offset;
224///
225/// fn main() {
226///     let frame_1 = PixelFrame::new(&[PixelColor::MAGENTA; 64]);
227///     let frame_2 = PixelFrame::new(&[PixelColor::WHITE; 64]);
228///     let clip = Clip::new(frame_1, frame_2);
229///     // Offset of `0`, shows the first frame.
230///     assert_eq!(clip.offset(Offset::Bottom(0)), frame_1);
231///
232///     let offset_2_rows = clip.offset(Offset::Bottom(2)).as_rows();
233///
234///     let expected_rows = &[
235///         [PixelColor::WHITE; 8],
236///         [PixelColor::WHITE; 8],
237///         [PixelColor::MAGENTA; 8],
238///         [PixelColor::MAGENTA; 8],
239///         [PixelColor::MAGENTA; 8],
240///         [PixelColor::MAGENTA; 8],
241///         [PixelColor::MAGENTA; 8],
242///         [PixelColor::MAGENTA; 8],
243///     ];
244///     assert_eq!(&offset_2_rows, expected_rows);
245///
246///     // Offset of `8`, shows the second frame.
247///     assert_eq!(clip.offset(Offset::Bottom(8)), frame_2);
248/// }
249/// ```
250///
251/// `Offset::Top(_)`
252/// ----------------
253/// ```text
254///                 |---------------|
255///                 |    Top(8)     |
256///                 |---------------|
257///               |---------------| |
258///               |    Top(7)     | |
259///               |---------------| |
260///             |---------------| | |
261///             |    Top(6)     | | |
262///             |---------------| | |
263///           |---------------| | | |
264///           |    Top(5)     | | | |
265///           |---------------| | | |
266///         |---------------| | | | |
267///         |    Top(4)     | | | | |
268///         |---------------| | | | |
269///       |---------------| | | | | |
270///       |    Top(3)     | | | | | |
271///       |---------------| | | | | |
272///     |---------------| | | | | | |
273///     |    Top(2)     | | | | | | |
274///     |---------------| | | | | | |
275///   |---------------| | | | | | | |
276///   |    Top(1)     | | | | | | | |
277///   |---------------| | | | | | | |
278/// |---------------| | | | | | | | |
279/// |    Top(0)     | | | | | | | | |
280/// |---------------| | | | | | | | |
281///                 | | | | | | | | |
282/// |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|  <- individual frame ROW index
283/// |---------------|---------------|
284/// |    frame 1    |    frame 2    |
285/// |---------------|---------------|
286/// ```
287///
288/// ### Example
289///
290/// ```rust
291/// extern crate sensehat_screen;
292/// use sensehat_screen::{Clip, PixelColor, PixelFrame};
293/// use sensehat_screen::Offset;
294///
295/// fn main() {
296///     let frame_1 = PixelFrame::new(&[PixelColor::GREEN; 64]);
297///     let frame_2 = PixelFrame::new(&[PixelColor::BLACK; 64]);
298///     let clip = Clip::new(frame_1, frame_2);
299///     // Offset of `0`, shows the first frame.
300///     assert_eq!(clip.offset(Offset::Top(0)), frame_1);
301///
302///     let offset_7_rows = clip.offset(Offset::Top(7)).as_rows();
303///
304///     let expected_rows = &[
305///         [PixelColor::GREEN; 8],
306///         [PixelColor::BLACK; 8],
307///         [PixelColor::BLACK; 8],
308///         [PixelColor::BLACK; 8],
309///         [PixelColor::BLACK; 8],
310///         [PixelColor::BLACK; 8],
311///         [PixelColor::BLACK; 8],
312///         [PixelColor::BLACK; 8],
313///     ];
314///
315///     assert_eq!(&offset_7_rows, expected_rows);
316///
317///     // Offset of `8`, shows the second frame.
318///     assert_eq!(clip.offset(Offset::Left(8)), frame_2);
319/// }
320/// ```
321#[derive(Copy, Clone, Debug, Default, PartialEq)]
322pub struct Clip {
323    first: PixelFrame,
324    second: PixelFrame,
325}
326
327impl Clip {
328    /// Create a new `Clip` from two `PixelFrame`s.
329    pub fn new(first: PixelFrame, second: PixelFrame) -> Self {
330        Clip { first, second }
331    }
332
333    /// Offset position for which to create the clipped `PixelFrame`.
334    pub fn offset(&self, offset: Offset) -> PixelFrame {
335        match offset {
336            Offset::Left(offset) => self.offset_left(offset),
337            Offset::Right(offset) => self.offset_right(offset),
338            Offset::Bottom(offset) => self.offset_bottom(offset),
339            Offset::Top(offset) => self.offset_top(offset),
340        }
341    }
342
343    // # Panics
344    // If `offset` is out of bounds (> 8).
345    fn offset_left(&self, offset: u8) -> PixelFrame {
346        assert!(offset < 9);
347        clip_pixel_frames_offset_left(self.first, self.second, offset)
348    }
349
350    // # Panics
351    // If `offset` is out of bounds (> 8).
352    fn offset_right(&self, offset: u8) -> PixelFrame {
353        assert!(offset < 9);
354        clip_pixel_frames_offset_right(self.first, self.second, offset)
355    }
356
357    fn offset_bottom(&self, offset: u8) -> PixelFrame {
358        assert!(offset < 9);
359        clip_pixel_frames_offset_bottom(self.first, self.second, offset)
360    }
361
362    fn offset_top(&self, offset: u8) -> PixelFrame {
363        assert!(offset < 9);
364        clip_pixel_frames_offset_top(self.first, self.second, offset)
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371    use crate::color::PixelColor;
372
373    const DARK: PixelColor = PixelColor::BLACK;
374    const BLUE: PixelColor = PixelColor::BLUE;
375
376    const FRAME_ONE: [PixelColor; 64] = [
377        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
378        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
379        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
380        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
381        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
382        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
383        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
384        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
385    ];
386
387    const FRAME_TWO: [PixelColor; 64] = [
388        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
389        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
390        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
391        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
392        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
393        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
394        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
395        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
396    ];
397
398    // RIGHT
399    const OFFSET_RIGHT_ONE: [PixelColor; 64] = [
400        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
401        DARK, DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, //
402        DARK, DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, //
403        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
404        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
405        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
406        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
407        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
408    ];
409
410    const OFFSET_RIGHT_TWO: [PixelColor; 64] = [
411        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
412        BLUE, DARK, DARK, BLUE, DARK, DARK, DARK, DARK, //
413        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
414        DARK, DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, //
415        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
416        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
417        BLUE, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
418        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
419    ];
420
421    const OFFSET_RIGHT_THREE: [PixelColor; 64] = [
422        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
423        DARK, BLUE, DARK, DARK, BLUE, DARK, DARK, DARK, //
424        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
425        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
426        DARK, DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, //
427        BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
428        BLUE, BLUE, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
429        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
430    ];
431
432    const OFFSET_RIGHT_FOUR: [PixelColor; 64] = [
433        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
434        DARK, DARK, BLUE, DARK, DARK, BLUE, DARK, DARK, //
435        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
436        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
437        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
438        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
439        BLUE, BLUE, BLUE, DARK, DARK, BLUE, BLUE, BLUE, //
440        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
441    ];
442
443    const OFFSET_RIGHT_FIVE: [PixelColor; 64] = [
444        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
445        DARK, DARK, DARK, BLUE, DARK, DARK, BLUE, DARK, //
446        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
447        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
448        BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, DARK, //
449        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, //
450        BLUE, BLUE, BLUE, BLUE, DARK, DARK, BLUE, BLUE, //
451        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
452    ];
453
454    const OFFSET_RIGHT_SIX: [PixelColor; 64] = [
455        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
456        DARK, DARK, DARK, DARK, BLUE, DARK, DARK, BLUE, //
457        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
458        BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, DARK, //
459        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
460        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
461        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, BLUE, //
462        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
463    ];
464
465    const OFFSET_RIGHT_SEVEN: [PixelColor; 64] = [
466        DARK, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
467        BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, DARK, //
468        BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, DARK, //
469        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
470        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
471        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
472        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
473        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
474    ];
475
476    // LEFT
477    const OFFSET_LEFT_ONE: [PixelColor; 64] = [
478        DARK, DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, //
479        BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
480        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
481        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
482        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
483        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
484        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, BLUE, //
485        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
486    ];
487
488    const OFFSET_LEFT_TWO: [PixelColor; 64] = [
489        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, DARK, //
490        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
491        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
492        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
493        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
494        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
495        BLUE, BLUE, BLUE, BLUE, DARK, BLUE, BLUE, DARK, //
496        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
497    ];
498
499    const OFFSET_LEFT_THREE: [PixelColor; 64] = [
500        DARK, DARK, DARK, DARK, BLUE, BLUE, DARK, DARK, //
501        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, DARK, //
502        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
503        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
504        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
505        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
506        BLUE, BLUE, BLUE, DARK, BLUE, BLUE, DARK, BLUE, //
507        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
508    ];
509
510    const OFFSET_LEFT_FOUR: [PixelColor; 64] = [
511        DARK, DARK, DARK, BLUE, BLUE, DARK, DARK, DARK, //
512        DARK, DARK, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
513        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
514        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
515        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
516        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
517        BLUE, BLUE, DARK, BLUE, BLUE, DARK, BLUE, BLUE, //
518        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
519    ];
520
521    const OFFSET_LEFT_FIVE: [PixelColor; 64] = [
522        DARK, DARK, BLUE, BLUE, DARK, DARK, DARK, DARK, //
523        DARK, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
524        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
525        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
526        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
527        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
528        BLUE, DARK, BLUE, BLUE, DARK, BLUE, BLUE, BLUE, //
529        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
530    ];
531
532    const OFFSET_LEFT_SIX: [PixelColor; 64] = [
533        DARK, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
534        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
535        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
536        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
537        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
538        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
539        DARK, BLUE, BLUE, DARK, BLUE, BLUE, BLUE, BLUE, //
540        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
541    ];
542
543    const OFFSET_LEFT_SEVEN: [PixelColor; 64] = [
544        BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, DARK, //
545        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, //
546        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
547        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
548        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
549        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
550        BLUE, BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
551        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
552    ];
553
554    // TOP
555    const OFFSET_TOP_ONE: [PixelColor; 64] = [
556        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
557        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
558        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
559        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
560        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
561        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
562        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
563        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
564    ];
565
566    const OFFSET_TOP_TWO: [PixelColor; 64] = [
567        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
568        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
569        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
570        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
571        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
572        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
573        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
574        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
575    ];
576
577    const OFFSET_TOP_THREE: [PixelColor; 64] = [
578        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
579        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
580        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
581        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
582        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
583        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
584        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
585        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
586    ];
587
588    const OFFSET_TOP_FOUR: [PixelColor; 64] = [
589        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
590        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
591        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
592        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
593        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
594        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
595        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
596        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
597    ];
598
599    const OFFSET_TOP_FIVE: [PixelColor; 64] = [
600        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
601        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
602        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
603        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
604        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
605        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
606        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
607        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
608    ];
609
610    const OFFSET_TOP_SIX: [PixelColor; 64] = [
611        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
612        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
613        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
614        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
615        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
616        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
617        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
618        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
619    ];
620
621    const OFFSET_TOP_SEVEN: [PixelColor; 64] = [
622        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
623        BLUE, DARK, DARK, DARK, DARK, DARK, DARK, DARK, //
624        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
625        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
626        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
627        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
628        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
629        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
630    ];
631
632    // BOTTOM
633    const OFFSET_BOTTOM_ONE: [PixelColor; 64] = [
634        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
635        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
636        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
637        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
638        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
639        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
640        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
641        DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, BLUE, //
642    ];
643
644    const OFFSET_BOTTOM_TWO: [PixelColor; 64] = [
645        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
646        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
647        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
648        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
649        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
650        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
651        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
652        DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
653    ];
654
655    const OFFSET_BOTTOM_THREE: [PixelColor; 64] = [
656        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
657        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
658        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
659        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
660        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
661        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
662        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
663        DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, //
664    ];
665
666    const OFFSET_BOTTOM_FOUR: [PixelColor; 64] = [
667        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
668        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
669        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
670        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
671        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
672        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
673        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
674        DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, BLUE, //
675    ];
676
677    const OFFSET_BOTTOM_FIVE: [PixelColor; 64] = [
678        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
679        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
680        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
681        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
682        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
683        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
684        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
685        DARK, DARK, DARK, DARK, DARK, BLUE, BLUE, BLUE, //
686    ];
687
688    const OFFSET_BOTTOM_SIX: [PixelColor; 64] = [
689        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
690        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
691        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
692        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
693        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
694        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
695        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
696        DARK, BLUE, DARK, DARK, DARK, DARK, BLUE, BLUE, //
697    ];
698
699    const OFFSET_BOTTOM_SEVEN: [PixelColor; 64] = [
700        BLUE, BLUE, DARK, DARK, DARK, DARK, BLUE, DARK, //
701        BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, DARK, //
702        BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, DARK, //
703        BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, DARK, //
704        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, DARK, //
705        BLUE, DARK, BLUE, BLUE, BLUE, BLUE, BLUE, DARK, //
706        BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, //
707        DARK, DARK, DARK, DARK, DARK, DARK, DARK, BLUE, //
708    ];
709
710    #[test]
711    fn frame_clip_offsets_to_the_left() {
712        let symbol = PixelFrame::new(&FRAME_ONE);
713        let symbol_two = PixelFrame::new(&FRAME_TWO);
714        let clip = symbol.build_clip(&symbol_two);
715        assert_eq!(clip.offset(Offset::left(0)), PixelFrame::new(&FRAME_ONE));
716        assert_eq!(
717            clip.offset(Offset::left(1)),
718            PixelFrame::new(&OFFSET_LEFT_ONE)
719        );
720        assert_eq!(
721            clip.offset(Offset::left(2)),
722            PixelFrame::new(&OFFSET_LEFT_TWO)
723        );
724        assert_eq!(
725            clip.offset(Offset::left(3)),
726            PixelFrame::new(&OFFSET_LEFT_THREE)
727        );
728        assert_eq!(
729            clip.offset(Offset::left(4)),
730            PixelFrame::new(&OFFSET_LEFT_FOUR)
731        );
732        assert_eq!(
733            clip.offset(Offset::left(5)),
734            PixelFrame::new(&OFFSET_LEFT_FIVE)
735        );
736        assert_eq!(
737            clip.offset(Offset::left(6)),
738            PixelFrame::new(&OFFSET_LEFT_SIX)
739        );
740        assert_eq!(
741            clip.offset(Offset::left(7)),
742            PixelFrame::new(&OFFSET_LEFT_SEVEN)
743        );
744        assert_eq!(clip.offset(Offset::left(8)), symbol_two);
745    }
746
747    #[test]
748    #[should_panic]
749    fn frame_clip_panics_when_offset_to_the_left_is_greater_than_8() {
750        let symbol = PixelFrame::new(&FRAME_ONE);
751        let symbol_two = PixelFrame::new(&FRAME_TWO);
752        let clip = symbol.build_clip(&symbol_two);
753        let _ = clip.offset(Offset::left(9));
754    }
755
756    #[test]
757    fn frame_clip_offsets_to_the_right() {
758        let symbol = PixelFrame::new(&FRAME_ONE);
759        let symbol_two = PixelFrame::new(&FRAME_TWO);
760        let clip = symbol.build_clip(&symbol_two);
761        assert_eq!(clip.offset(Offset::right(0)), PixelFrame::new(&FRAME_ONE));
762        assert_eq!(
763            clip.offset(Offset::right(1)),
764            PixelFrame::new(&OFFSET_RIGHT_ONE)
765        );
766        assert_eq!(
767            clip.offset(Offset::right(2)),
768            PixelFrame::new(&OFFSET_RIGHT_TWO)
769        );
770        assert_eq!(
771            clip.offset(Offset::right(3)),
772            PixelFrame::new(&OFFSET_RIGHT_THREE)
773        );
774        assert_eq!(
775            clip.offset(Offset::right(4)),
776            PixelFrame::new(&OFFSET_RIGHT_FOUR)
777        );
778        assert_eq!(
779            clip.offset(Offset::right(5)),
780            PixelFrame::new(&OFFSET_RIGHT_FIVE)
781        );
782        assert_eq!(
783            clip.offset(Offset::right(6)),
784            PixelFrame::new(&OFFSET_RIGHT_SIX)
785        );
786        assert_eq!(
787            clip.offset(Offset::right(7)),
788            PixelFrame::new(&OFFSET_RIGHT_SEVEN)
789        );
790        assert_eq!(clip.offset(Offset::right(8)), symbol_two);
791    }
792
793    #[test]
794    #[should_panic]
795    fn frame_clip_panics_when_offset_to_the_right_is_greater_than_8() {
796        let symbol = PixelFrame::new(&FRAME_ONE);
797        let symbol_two = PixelFrame::new(&FRAME_TWO);
798        let clip = symbol.build_clip(&symbol_two);
799        let _ = clip.offset(Offset::right(9));
800    }
801
802    #[test]
803    fn frame_clip_offsets_to_the_bottom() {
804        let symbol = PixelFrame::new(&FRAME_ONE);
805        let symbol_two = PixelFrame::new(&FRAME_TWO);
806        let clip = symbol.build_clip(&symbol_two);
807        assert_eq!(clip.offset(Offset::bottom(0)), PixelFrame::new(&FRAME_ONE));
808        assert_eq!(
809            clip.offset(Offset::bottom(1)),
810            PixelFrame::new(&OFFSET_BOTTOM_ONE)
811        );
812        assert_eq!(
813            clip.offset(Offset::bottom(2)),
814            PixelFrame::new(&OFFSET_BOTTOM_TWO)
815        );
816        assert_eq!(
817            clip.offset(Offset::bottom(3)),
818            PixelFrame::new(&OFFSET_BOTTOM_THREE)
819        );
820        assert_eq!(
821            clip.offset(Offset::bottom(4)),
822            PixelFrame::new(&OFFSET_BOTTOM_FOUR)
823        );
824        assert_eq!(
825            clip.offset(Offset::bottom(5)),
826            PixelFrame::new(&OFFSET_BOTTOM_FIVE)
827        );
828        assert_eq!(
829            clip.offset(Offset::bottom(6)),
830            PixelFrame::new(&OFFSET_BOTTOM_SIX)
831        );
832        assert_eq!(
833            clip.offset(Offset::bottom(7)),
834            PixelFrame::new(&OFFSET_BOTTOM_SEVEN)
835        );
836        assert_eq!(clip.offset(Offset::bottom(8)), symbol_two);
837    }
838
839    #[test]
840    #[should_panic]
841    fn frame_clip_panics_when_offset_to_the_bottom_is_greater_than_8() {
842        let symbol = PixelFrame::new(&FRAME_ONE);
843        let symbol_two = PixelFrame::new(&FRAME_TWO);
844        let clip = symbol.build_clip(&symbol_two);
845        let _ = clip.offset(Offset::bottom(9));
846    }
847
848    #[test]
849    fn frame_clip_offsets_to_the_top() {
850        let symbol = PixelFrame::new(&FRAME_ONE);
851        let symbol_two = PixelFrame::new(&FRAME_TWO);
852        let clip = symbol.build_clip(&symbol_two);
853        assert_eq!(clip.offset(Offset::top(0)), PixelFrame::new(&FRAME_ONE));
854        assert_eq!(
855            clip.offset(Offset::top(1)),
856            PixelFrame::new(&OFFSET_TOP_ONE)
857        );
858        assert_eq!(
859            clip.offset(Offset::top(2)),
860            PixelFrame::new(&OFFSET_TOP_TWO)
861        );
862        assert_eq!(
863            clip.offset(Offset::top(3)),
864            PixelFrame::new(&OFFSET_TOP_THREE)
865        );
866        assert_eq!(
867            clip.offset(Offset::top(4)),
868            PixelFrame::new(&OFFSET_TOP_FOUR)
869        );
870        assert_eq!(
871            clip.offset(Offset::top(5)),
872            PixelFrame::new(&OFFSET_TOP_FIVE)
873        );
874        assert_eq!(
875            clip.offset(Offset::top(6)),
876            PixelFrame::new(&OFFSET_TOP_SIX)
877        );
878        assert_eq!(
879            clip.offset(Offset::top(7)),
880            PixelFrame::new(&OFFSET_TOP_SEVEN)
881        );
882        assert_eq!(clip.offset(Offset::top(8)), symbol_two);
883    }
884
885    #[test]
886    #[should_panic]
887    fn frame_clip_panics_when_offset_to_the_top_is_greater_than_8() {
888        let symbol = PixelFrame::new(&FRAME_ONE);
889        let symbol_two = PixelFrame::new(&FRAME_TWO);
890        let clip = symbol.build_clip(&symbol_two);
891        let _ = clip.offset(Offset::top(9));
892    }
893}