pub struct LedLayout<const N: usize, const W: usize, const H: usize> { /* private fields */ }Expand description
Compile-time description of panel geometry and wiring, including dimensions (with examples).
LedLayout defines how a rectangular (x, y) panel of LEDs maps to the linear
wiring order of LEDs on a NeoPixel-style (WS2812) panel.
For examples of LedLayout in use, see the led2d module,
Frame2d, and the example below.
What LedLayout does:
- Lets you describe panel wiring once
- Enables drawing text, graphics, and animations in
(x, y)space - Hides LED strip order from rendering code
Coordinates use a screen-style convention:
(0, 0)is the top-left cornerxincreases to the rightyincreases downward
Most users should start with one of the constructors below and then apply transforms (rotate_cw, flip_h, combine_v, etc.) as needed.
§Constructing layouts
Prefer the built-in constructors when possible:
For unusual wiring, you can construct a layout directly with LedLayout::new
by listing (x, y) for each LED in the order the strip is wired.
The example below shows both construction methods. Also, the documentation for every constructor and method includes illustrations of use.
§Transforming layouts
You can adapt a layout without rewriting it:
- rotate:
rotate_cw,rotate_ccw,rotate_180 - flip:
flip_h,flip_v - combine:
combine_h,combine_v(join two layouts into a larger one)
§Validation
Layouts are validated at compile time:
- coordinates must be in-bounds
- every
(x, y)cell must appear exactly once
If you want the final mapping, use index_to_xy.
§Example
Rotate a serpentine-wired 3×2 panel into a 2×3 layout and verify the result at compile time:
use device_envoy_core::led2d::layout::LedLayout;
const ROTATED: LedLayout<6, 2, 3> = LedLayout::serpentine_column_major().rotate_cw();
const EXPECTED: LedLayout<6, 2, 3> =
LedLayout::new([(1, 0), (0, 0), (0, 1), (1, 1), (1, 2), (0, 2)]);
const _: () = assert!(ROTATED.equals(&EXPECTED)); // Compile-time assertSerpentine 3×2 rotated to 2×3:
Before: After:
LED0 LED3 LED4 LED1 LED0
LED1 LED2 LED5 LED2 LED3
LED5 LED4Implementations§
Source§impl<const N: usize, const W: usize, const H: usize> LedLayout<N, W, H>
impl<const N: usize, const W: usize, const H: usize> LedLayout<N, W, H>
Sourcepub const fn index_to_xy(&self) -> &[(u16, u16); N]
pub const fn index_to_xy(&self) -> &[(u16, u16); N]
Return the array mapping LED wiring order to (x, y) coordinates.
Sourcepub const fn xy_to_index(&self) -> [u16; N]
pub const fn xy_to_index(&self) -> [u16; N]
Return the inverse mapping: (x, y) coordinates to LED wiring index.
The returned array is indexed by y * W + x and contains the LED wiring
index for each pixel position. This is the inverse of index_to_xy.
Sourcepub const fn equals(&self, other: &Self) -> bool
pub const fn equals(&self, other: &Self) -> bool
Const equality helper for doctests/examples.
use device_envoy_core::led2d::layout::LedLayout;
const LINEAR: LedLayout<4, 4, 1> = LedLayout::linear_h();
const ROTATED: LedLayout<4, 4, 1> = LedLayout::linear_v().rotate_cw();
const _: () = assert!(LINEAR.equals(&LINEAR)); // assert equal
const _: () = assert!(!LINEAR.equals(&ROTATED)); // assert not equalLINEAR: LED0 LED1 LED2 LED3
ROTATED: LED3 LED2 LED1 LED0Sourcepub const fn new(map: [(u16, u16); N]) -> Self
pub const fn new(map: [(u16, u16); N]) -> Self
Construct a LedLayout by explicitly specifying the wiring order.
Use this constructor when your panel wiring does not match one of the
built-in patterns (linear, serpentine, etc.). You provide the (x, y)
coordinate for each LED in strip order, and LedLayout derives the
panel geometry from that mapping.
This constructor is const and is intended to be used in a const
definition, so layout errors are caught at compile time, not at runtime.
use device_envoy_core::led2d::layout::LedLayout;
// 3×2 panel (landscape, W×H)
const MAP: LedLayout<6, 3, 2> =
LedLayout::new([(0, 0), (1, 0), (2, 0), (2, 1), (1, 1), (0, 1)]);
// Rotate to portrait (CW)
const ROTATED: LedLayout<6, 2, 3> = MAP.rotate_cw();
// Expected: 2×3 panel (W×H)
const EXPECTED: LedLayout<6, 2, 3> =
LedLayout::new([(1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 0)]);
const _: () = assert!(ROTATED.equals(&EXPECTED));3×2 input (col,row by LED index):
LED0 LED1 LED2
LED5 LED4 LED3
After rotate to 2×3:
LED1 LED0
LED2 LED3
LED5 LED4Sourcepub const fn linear_h() -> Self
pub const fn linear_h() -> Self
Linear row-major mapping for a single-row strip (cols increase left-to-right).
use device_envoy_core::led2d::layout::LedLayout;
const LINEAR: LedLayout<6, 6, 1> = LedLayout::linear_h();
const EXPECTED: LedLayout<6, 6, 1> =
LedLayout::new([(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)]);
const _: () = assert!(LINEAR.equals(&EXPECTED));6×1 strip maps to single row:
LED0 LED1 LED2 LED3 LED4 LED5Sourcepub const fn linear_v() -> Self
pub const fn linear_v() -> Self
Linear column-major mapping for a single-column strip (rows increase top-to-bottom).
use device_envoy_core::led2d::layout::LedLayout;
const LINEAR: LedLayout<6, 1, 6> = LedLayout::linear_v();
const EXPECTED: LedLayout<6, 1, 6> =
LedLayout::new([(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]);
const _: () = assert!(LINEAR.equals(&EXPECTED));1×6 strip maps to single column:
LED0
LED1
LED2
LED3
LED4
LED5Sourcepub const fn serpentine_column_major() -> Self
pub const fn serpentine_column_major() -> Self
Serpentine column-major mapping returned as a checked LedLayout.
use device_envoy_core::led2d::layout::LedLayout;
const MAP: LedLayout<6, 3, 2> = LedLayout::serpentine_column_major();
const EXPECTED: LedLayout<6, 3, 2> =
LedLayout::new([(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 1)]);
const _: () = assert!(MAP.equals(&EXPECTED));Strip snakes down columns (3×2 example):
LED0 LED3 LED4
LED1 LED2 LED5Sourcepub const fn serpentine_row_major() -> Self
pub const fn serpentine_row_major() -> Self
Serpentine row-major mapping (alternating left-to-right and right-to-left across rows).
use device_envoy_core::led2d::layout::LedLayout;
const MAP: LedLayout<6, 3, 2> = LedLayout::serpentine_row_major();
const EXPECTED: LedLayout<6, 3, 2> =
LedLayout::new([(0, 0), (1, 0), (2, 0), (2, 1), (1, 1), (0, 1)]);
const _: () = assert!(MAP.equals(&EXPECTED));Strip snakes across rows (3×2 example):
LED0 LED1 LED2
LED5 LED4 LED3Sourcepub const fn rotate_cw(self) -> LedLayout<N, H, W>
pub const fn rotate_cw(self) -> LedLayout<N, H, W>
Rotate 90° clockwise (dims swap).
use device_envoy_core::led2d::layout::LedLayout;
const ROTATED: LedLayout<6, 2, 3> = LedLayout::serpentine_column_major().rotate_cw();
const EXPECTED: LedLayout<6, 2, 3> =
LedLayout::new([(1, 0), (0, 0), (0, 1), (1, 1), (1, 2), (0, 2)]);
const _: () = assert!(ROTATED.equals(&EXPECTED));Before (3×2 serpentine): After (2×3):
LED0 LED3 LED4 LED1 LED0
LED1 LED2 LED5 LED2 LED3
LED5 LED4Sourcepub const fn flip_h(self) -> Self
pub const fn flip_h(self) -> Self
Flip horizontally (mirror columns).
use device_envoy_core::led2d::layout::LedLayout;
const FLIPPED: LedLayout<6, 3, 2> = LedLayout::serpentine_column_major().flip_h();
const EXPECTED: LedLayout<6, 3, 2> =
LedLayout::new([(2, 0), (2, 1), (1, 1), (1, 0), (0, 0), (0, 1)]);
const _: () = assert!(FLIPPED.equals(&EXPECTED));Before (serpentine): After:
LED0 LED3 LED4 LED4 LED3 LED0
LED1 LED2 LED5 LED5 LED2 LED1Sourcepub const fn rotate_180(self) -> Self
pub const fn rotate_180(self) -> Self
Rotate 180° derived from rotate_cw.
use device_envoy_core::led2d::layout::LedLayout;
const ROTATED: LedLayout<6, 3, 2> = LedLayout::serpentine_column_major().rotate_180();
const EXPECTED: LedLayout<6, 3, 2> =
LedLayout::new([(2, 1), (2, 0), (1, 0), (1, 1), (0, 1), (0, 0)]);
const _: () = assert!(ROTATED.equals(&EXPECTED));Before (3×2 serpentine): After 180°:
LED0 LED3 LED4 LED5 LED2 LED1
LED1 LED2 LED5 LED4 LED3 LED0Sourcepub const fn rotate_ccw(self) -> LedLayout<N, H, W>
pub const fn rotate_ccw(self) -> LedLayout<N, H, W>
Rotate 90° counter-clockwise derived from rotate_cw.
use device_envoy_core::led2d::layout::LedLayout;
const ROTATED: LedLayout<6, 2, 3> = LedLayout::serpentine_column_major().rotate_ccw();
const EXPECTED: LedLayout<6, 2, 3> =
LedLayout::new([(0, 2), (1, 2), (1, 1), (0, 1), (0, 0), (1, 0)]);
const _: () = assert!(ROTATED.equals(&EXPECTED));Before (3×2 serpentine): After (2×3):
LED0 LED3 LED4 LED4 LED5
LED1 LED2 LED5 LED3 LED2
LED0 LED1Sourcepub const fn flip_v(self) -> Self
pub const fn flip_v(self) -> Self
Flip vertically derived from rotation + horizontal flip.
use device_envoy_core::led2d::layout::LedLayout;
const FLIPPED: LedLayout<6, 3, 2> = LedLayout::serpentine_column_major().flip_v();
const EXPECTED: LedLayout<6, 3, 2> =
LedLayout::new([(0, 1), (0, 0), (1, 0), (1, 1), (2, 1), (2, 0)]);
const _: () = assert!(FLIPPED.equals(&EXPECTED));Before (serpentine): After:
LED0 LED3 LED4 LED1 LED2 LED5
LED1 LED2 LED5 LED0 LED3 LED4Sourcepub const fn combine_h<const N2: usize, const OUT_N: usize, const W2: usize, const OUT_W: usize>(
self,
right: LedLayout<N2, W2, H>,
) -> LedLayout<OUT_N, OUT_W, H>
pub const fn combine_h<const N2: usize, const OUT_N: usize, const W2: usize, const OUT_W: usize>( self, right: LedLayout<N2, W2, H>, ) -> LedLayout<OUT_N, OUT_W, H>
Concatenate horizontally with another mapping sharing the same rows.
use device_envoy_core::led2d::layout::LedLayout;
const LED_LAYOUT: LedLayout<6, 3, 2> = LedLayout::serpentine_column_major();
const COMBINED: LedLayout<12, 6, 2> = LED_LAYOUT.combine_h::<6, 12, 3, 6>(LED_LAYOUT);
const EXPECTED: LedLayout<12, 6, 2> = LedLayout::new([
(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (4, 1),
(4, 0), (5, 0), (5, 1),
]);
const _: () = assert!(COMBINED.equals(&EXPECTED));Left serpentine (3×2): Right serpentine (3×2):
0 3 4 6 9 10
1 2 5 7 8 11
Combined (6×2):
0 3 4 6 9 10
1 2 5 7 8 11Sourcepub const fn combine_v<const N2: usize, const OUT_N: usize, const H2: usize, const OUT_H: usize>(
self,
bottom: LedLayout<N2, W, H2>,
) -> LedLayout<OUT_N, W, OUT_H>
pub const fn combine_v<const N2: usize, const OUT_N: usize, const H2: usize, const OUT_H: usize>( self, bottom: LedLayout<N2, W, H2>, ) -> LedLayout<OUT_N, W, OUT_H>
Concatenate vertically with another mapping sharing the same columns.
use device_envoy_core::led2d::layout::LedLayout;
const LED_LAYOUT: LedLayout<6, 3, 2> = LedLayout::serpentine_column_major();
const COMBINED: LedLayout<12, 3, 4> = LED_LAYOUT.combine_v::<6, 12, 2, 4>(LED_LAYOUT);
const EXPECTED: LedLayout<12, 3, 4> = LedLayout::new([
(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 1), (0, 2), (0, 3), (1, 3),
(1, 2), (2, 2), (2, 3),
]);
const _: () = assert!(COMBINED.equals(&EXPECTED));Top serpentine (3×2): Bottom serpentine (3×2):
0 3 4 6 9 10
1 2 5 7 8 11
Combined (3×4):
0 3 4
1 2 5
6 9 10
7 8 11