#![deny(
clippy::all,
clippy::cargo,
clippy::nursery,
// clippy::restriction,
// clippy::pedantic
)]
#![allow(
clippy::fallible_impl_from,
clippy::needless_doctest_main,
clippy::redundant_pub_crate,
clippy::suboptimal_flops
)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(rustdoc::all)]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
#[macro_use]
extern crate alloc;
use alloc::vec::Vec;
#[cfg(feature = "std")]
use crate::setup::Max7219;
#[cfg(feature = "std")]
use std::{thread::sleep, time::Duration};
#[cfg(feature = "std")]
use crate::encoding::encode_string;
use crate::mappings::SingleDisplayData;
#[cfg(feature = "std")]
use max7219::DecodeMode;
pub const LED_SQUARE_MATRIX_DIM: usize = 8;
pub const MAX_DISPLAYS: usize = 16;
pub mod encoding;
pub mod mappings;
#[cfg(feature = "std")]
mod setup;
#[cfg(feature = "std")]
pub use setup::{setup as setup_adapter, Max7219 as Max7219Adapter};
pub fn shift_all_rows_one_bit_left(moving_bits: &mut [SingleDisplayData] ) {
let len = moving_bits.len();
for display_i in 0..len {
for row_i in 0..8 {
if moving_bits[display_i][row_i] & 0b10000000 != 0 {
if display_i == 0
{
moving_bits[len - 1][row_i] |= 1;
} else {
moving_bits[display_i - 1][row_i] |= 1;
}
}
moving_bits[display_i][row_i] <<= 1;
}
}
}
#[cfg(feature = "std")]
pub fn prepare_display(display: &mut Max7219, display_count: usize, intensity: u8) {
let display_count = display_count % MAX_DISPLAYS;
display.power_on().unwrap();
for i in 0..display_count {
display.set_decode_mode(i, DecodeMode::NoDecode).unwrap();
display.clear_display(i).unwrap();
display.set_intensity(i, intensity).unwrap();
}
}
#[cfg(feature = "std")]
pub fn show_moving_text_in_loop(
display: &mut Max7219,
text: &str,
display_count: usize,
ms_sleep: u64,
gap_width: Option<usize>,
) {
let display_count = display_count % MAX_DISPLAYS;
let raw_bits = encode_string(text);
let mut display_data_vec: Vec<SingleDisplayData> = if let Some(gap_width) = gap_width {
remove_gaps_in_display_text(&raw_bits, gap_width)
} else {
raw_bits
};
loop {
for (i, display_data) in display_data_vec.iter().enumerate().take(display_count) {
display.write_raw(i, display_data).unwrap();
}
sleep(Duration::from_millis(ms_sleep));
shift_all_rows_one_bit_left(&mut display_data_vec);
}
}
pub fn remove_gaps_in_display_text(
display_data_arr: &[SingleDisplayData],
min_gap_size: usize,
) -> Vec<SingleDisplayData> {
let display_data_expanded: Vec<u8> = display_data_arr
.iter()
.map(transpose_single_display_data)
.flat_map(|display_data| display_data.into_iter())
.collect();
let preserve_at_begin = display_data_expanded
.iter()
.take_while(|col| **col == 0)
.count();
let preserve_at_end = display_data_expanded
.iter()
.rev()
.take_while(|col| **col == 0)
.count();
let mut shrinked_display_data_expanded = vec![0_u8; preserve_at_begin];
let mut count_since_last_not_empty = 0;
let skip_begin = preserve_at_begin;
let skip_end = preserve_at_end;
let take_elements = display_data_expanded.len() - skip_end - skip_begin + 1;
for col in display_data_expanded.iter().take(take_elements).copied() {
if col == 0 {
count_since_last_not_empty += 1;
} else {
count_since_last_not_empty = 0;
}
if count_since_last_not_empty <= min_gap_size {
shrinked_display_data_expanded.push(col);
}
}
shrinked_display_data_expanded.extend_from_slice(&vec![0; preserve_at_end]);
let add_to_multiple_of_8 = 8 - (shrinked_display_data_expanded.len() % 8);
shrinked_display_data_expanded.extend_from_slice(&vec![0; add_to_multiple_of_8]);
let shrinked_display_data = shrinked_display_data_expanded
.as_slice()
.chunks_exact(8)
.map(|cols_8| {
let mut transposed_display: SingleDisplayData = [0; 8];
cols_8
.iter()
.copied()
.enumerate()
.for_each(|(i, col)| transposed_display[i] = col);
transpose_single_display_data(&transposed_display)
})
.collect();
shrinked_display_data
}
#[allow(clippy::needless_range_loop)]
pub fn transpose_single_display_data(data: &SingleDisplayData) -> SingleDisplayData {
let mut transposed_data: SingleDisplayData = [0; 8];
for col_i in 0..8 {
let mut col = 0;
for row_i in 0..8 {
let col_bit = (data[row_i] >> (7 - col_i)) & 1;
col = (col << 1) | col_bit;
}
transposed_data[col_i] = col;
}
transposed_data
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_move_all_bits_one_col_left() {
let data_dis_0 = [
0b01000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000000,
];
let data_dis_1 = [
0b11000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000000,
];
let mut data = Vec::new();
data.push(data_dis_0);
data.push(data_dis_1);
shift_all_rows_one_bit_left(&mut data);
let first_row_dis_0_expected = 0b10000001;
let first_row_dis_1_expected = 0b10000000;
let first_row_dis_0_actual = data[0][0];
let first_row_dis_1_actual = data[1][0];
assert_eq!(first_row_dis_0_actual, first_row_dis_0_expected);
assert_eq!(first_row_dis_1_actual, first_row_dis_1_expected);
}
#[test]
fn test_transpose_single_display_data() {
let input = [
0b0100_0000,
0b0100_0000,
0b0100_0000,
0b0100_0000,
0b0000_0011,
0b0000_0011,
0b0000_0011,
0b0000_0011,
];
let expected = [
0b0000_0000,
0b1111_0000,
0b0000_0000,
0b0000_0000,
0b0000_0000,
0b0000_0000,
0b0000_1111,
0b0000_1111,
];
let actual = transpose_single_display_data(&input);
for i in 0..input.len() {
assert_eq!(
actual[i], expected[i],
"swap_cols_to_rows() doesn't transposed the matrix properly at index {}! is={:#b}, expected={:#b}",
i, actual[i], expected[i]
);
}
}
#[test]
fn test_remove_gaps_in_display_text() {
let vec = vec![
[
0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000,
0b10000000,
],
[
0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000,
0b10000000,
],
];
let expected = vec![
[
0b10010000, 0b10010000, 0b10010000, 0b10010000, 0b10010000, 0b10010000, 0b10010000,
0b10010000,
],
[
0, 0, 0, 0, 0, 0, 0, 0,
],
];
let actual = remove_gaps_in_display_text(&vec, 2);
for i in 0..2 {
for j in 0..8 {
assert_eq!(
actual[i][j], expected[i][j],
"expected: {:#b}, is: {:#b}",
expected[i][j], vec[i][j]
);
}
}
}
}