mod base;
mod top;
use crate::base::Base;
use crate::top::{Top, TopResultElement};
#[derive(Clone, Debug)]
pub struct Rectangle {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
#[derive(Debug)]
pub enum Element {
WhiteKey {
wide: Rectangle,
small: Rectangle,
blind: Option<Rectangle>,
},
BlackKey(Rectangle),
}
pub struct Keyboard2d {
pub left_white_key: u8,
pub right_white_key: u8,
pub width: u16,
pub height: u16,
perfect: bool,
elements: Vec<Element>,
}
impl Keyboard2d {
pub fn iter(&self) -> std::slice::Iter<Element> {
self.elements.iter()
}
pub fn white_keys(&self, blind_as_white: bool) -> Vec<Rectangle> {
let mut rects = vec![];
for opt_element in self.elements.iter() {
match opt_element {
Element::WhiteKey {
wide: r1,
small: r2,
blind: opt_blind,
} => {
rects.push(r1.clone());
rects.push(r2.clone());
if blind_as_white {
if let Some(blind) = opt_blind {
rects.push(blind.clone());
}
}
}
_ => (),
}
}
rects
}
pub fn black_keys(&self) -> Vec<Rectangle> {
let mut rects = vec![];
for opt_element in self.elements.iter() {
match opt_element {
Element::BlackKey(r) => {
rects.push(r.clone());
}
_ => (),
}
}
rects
}
pub fn is_perfect(&self) -> bool {
self.perfect
}
}
pub struct KeyboardBuilder {
left_white_key: u8,
right_white_key: u8,
width: u16,
dot_ratio_1024: u16,
white_key_wide_width_10um: u32,
white_key_small_width_fb_10um: u32,
white_key_small_width_ga_10um: u32,
black_key_width_10um: u32,
black_key_height_10um: u32,
need_black_gap: bool,
white_key_height_10um: u32,
white_key_wide_height_10um: u32,
}
impl KeyboardBuilder {
pub fn new() -> KeyboardBuilder {
KeyboardBuilder {
left_white_key: 21,
right_white_key: 108,
width: 640,
dot_ratio_1024: 1024,
white_key_wide_width_10um: 22_15,
white_key_small_width_fb_10um: 12_83,
white_key_small_width_ga_10um: 13_08,
black_key_width_10um: 11_00,
black_key_height_10um: 80_00,
need_black_gap: true,
white_key_height_10um: 126_27,
white_key_wide_height_10um: 45_00,
}
}
fn check_width(self) -> Result<KeyboardBuilder, String> {
let nr_of_keys = (self.right_white_key - self.left_white_key) as u16;
if self.width > 65535 - 127 {
Err(format!("Keyboard width {} too big",self.width))
}
else if self.width < nr_of_keys * 3 {
Err(format!("Keyboard width too small {} < {} for {} keys", self.width, nr_of_keys * 3, nr_of_keys))
} else {
Ok(self)
}
}
pub fn standard_piano(mut self, nr_of_keys: u8) -> Result<KeyboardBuilder, String> {
let (left, right) = match nr_of_keys {
88 => (21, 108),
76 => (21, 108 - 12),
73 => (21 + 3, 108 - 12),
64 => (21 + 12, 108 - 12),
61 => (21 + 12 + 3, 108 - 12),
49 => (21 + 12 + 3, 108 - 24),
37 => (21 + 24 + 3, 108 - 24),
25 => (21 + 24 + 3, 108 - 36),
_ => {
return Err(format!(
"size {} not a recognized standard size",
nr_of_keys
))
}
};
assert_eq!(right - left + 1, nr_of_keys);
self.left_white_key = left;
self.right_white_key = right;
self.check_width()
}
pub fn is_rd64(mut self) -> KeyboardBuilder {
self.left_white_key = 21 + 12;
self.right_white_key = 108 - 12;
self
}
pub fn set_most_left_right_white_keys(
mut self,
left_white_key: u8,
right_white_key: u8,
) -> Result<KeyboardBuilder, String> {
if left_white_key > right_white_key {
Err("left white key right from right white key ".to_string())
} else if left_white_key > 127 {
Err("left white key is out of range".to_string())
} else if right_white_key > 127 {
Err("right white key is out of range".to_string())
} else if right_white_key - left_white_key < 11 {
Err("Keyboard must be at least one octave".to_string())
} else if !KeyboardBuilder::is_white(left_white_key) {
Err("left white key is not a white key".to_string())
} else if !KeyboardBuilder::is_white(right_white_key) {
Err("right white key is not a white key".to_string())
} else {
self.left_white_key = left_white_key;
self.right_white_key = right_white_key;
self.check_width()
}
}
pub fn set_width(mut self, width: u16) -> Result<KeyboardBuilder, String> {
self.width = width;
self.check_width()
}
pub fn white_black_gap_present(mut self, gap_present: bool) -> KeyboardBuilder {
self.need_black_gap = gap_present;
self
}
fn is_white(key: u8) -> bool {
match key % 12 {
0 | 2 | 4 | 5 | 7 | 9 | 11 => true,
1 | 3 | 6 | 8 | 10 => false,
_ => panic!("wrong value"),
}
}
pub fn build2d(self) -> Keyboard2d {
let base = Base::calculate(&self);
let top = Top::calculate(&self, &base);
let base_elements = base.get_elements();
let nr_of_white_keys = (self.left_white_key..=self.right_white_key)
.filter(|k| KeyboardBuilder::is_white(*k))
.count() as u16;
let key_gap_10um = self.white_key_height_10um
- self.black_key_height_10um
- self.white_key_wide_height_10um;
let keyboard_width_10um = (self.white_key_wide_width_10um + key_gap_10um)
* nr_of_white_keys as u32
+ key_gap_10um;
let key_gap = ((self.width as u32 * key_gap_10um + keyboard_width_10um / 2)
/ keyboard_width_10um) as u16;
let black_gap = if self.need_black_gap { key_gap } else { 0 };
let max_pure_white_key_width = self.width - key_gap * (nr_of_white_keys + 1) as u16;
let white_key_wide_width = max_pure_white_key_width / nr_of_white_keys;
let black_key_height =
((white_key_wide_width as u64 * self.black_key_height_10um as u64 * 1024
+ self.white_key_wide_width_10um as u64 / 2)
/ self.white_key_wide_width_10um as u64
/ self.dot_ratio_1024 as u64) as u16;
let white_key_wide_height = ((white_key_wide_width as u64
* self.white_key_wide_height_10um as u64
+ self.white_key_wide_width_10um as u64 / 2)
/ self.white_key_wide_width_10um as u64) as u16;
let height = 2 * key_gap + black_gap + black_key_height + white_key_wide_height;
let mut elements = vec![];
let mut white_x = 0;
let n = base_elements.len() - 1;
for (i, el) in base_elements.into_iter().enumerate() {
match el {
base::ResultElement::Key(width, _key) => {
let wide_rect = Rectangle {
x: white_x,
y: black_gap + black_key_height + key_gap,
width: width,
height: white_key_wide_height,
};
let tr = top.get_top_for(&el);
match tr {
TopResultElement::WhiteGapBlack(w, _g, _blk) => {
let small_rect = Rectangle {
x: white_x,
y: key_gap,
width: w,
height: black_gap + black_key_height,
};
let opt_blind = if i == n - 1 {
Some(Rectangle {
x: white_x + w,
y: key_gap,
width: width - w,
height: black_gap + black_key_height,
})
} else {
None
};
elements.push(Element::WhiteKey {
wide: wide_rect,
small: small_rect,
blind: opt_blind,
});
}
TopResultElement::BlindWhiteGapBlack(blind, w, g, _blk) => {
let opt_blind = if i == 1 {
Some(Rectangle {
x: white_x,
y: key_gap,
width: blind,
height: black_gap + black_key_height,
})
} else if i == n - 1 {
Some(Rectangle {
x: white_x + w + g,
y: key_gap,
width: width - w - g,
height: black_gap + black_key_height,
})
} else {
None
};
let small_rect = Rectangle {
x: white_x + blind,
y: key_gap,
width: w,
height: black_gap + black_key_height,
};
elements.push(Element::WhiteKey {
wide: wide_rect,
small: small_rect,
blind: opt_blind,
});
}
TopResultElement::BlindWhite(g, w) => {
let opt_blind = if i == 1 {
Some(Rectangle {
x: white_x,
y: key_gap,
width: g,
height: black_gap + black_key_height,
})
} else {
None
};
let small_rect = Rectangle {
x: white_x + g,
y: key_gap,
width: w,
height: black_gap + black_key_height,
};
elements.push(Element::WhiteKey {
wide: wide_rect,
small: small_rect,
blind: opt_blind,
});
}
};
if i < n - 1 {
match tr {
TopResultElement::WhiteGapBlack(w, g, blk) => {
let rect = Rectangle {
x: white_x + w + g,
y: key_gap,
width: blk,
height: black_key_height,
};
elements.push(Element::BlackKey(rect));
}
TopResultElement::BlindWhiteGapBlack(blind, w, g, blk) => {
let rect = Rectangle {
x: white_x + blind + w + g,
y: key_gap,
width: blk,
height: black_key_height,
};
elements.push(Element::BlackKey(rect));
}
TopResultElement::BlindWhite(_g, _w) => (),
}
};
white_x += width;
}
base::ResultElement::Gap(gap) => {
white_x += gap;
}
}
}
Keyboard2d {
left_white_key: self.left_white_key,
right_white_key: self.right_white_key,
width: self.width,
height,
perfect: base.is_perfect() && top.is_perfect(),
elements,
}
}
}
#[cfg(test)]
mod tests {
use crate::KeyboardBuilder;
#[test]
fn test_standard_pianos() -> Result<(), String> {
let _keyboard = KeyboardBuilder::new()
.standard_piano(88)?
.standard_piano(76)?
.standard_piano(73)?
.standard_piano(64)?
.standard_piano(61)?
.standard_piano(49)?
.standard_piano(37)?
.standard_piano(25)?
.set_width(800)
.unwrap()
.build2d();
Ok(())
}
#[test]
fn test_max_width() {
let _keyboard = KeyboardBuilder::new()
.set_most_left_right_white_keys(0, 127)
.unwrap()
.set_width(65535 - 127)
.unwrap()
.build2d();
}
#[test]
fn test_several_widths() {
for width in 65000..65535 - 127 {
let _keyboard = KeyboardBuilder::new()
.set_most_left_right_white_keys(0, 127)
.unwrap()
.set_width(width)
.unwrap()
.build2d();
}
}
#[test] #[ignore]
fn test_all_pianos() -> Result<(), String> {
for keys in vec![25,37,49,61,64,73,76,88].into_iter() {
for width in 3*keys as u16..65535-127 {
let _keyboard = KeyboardBuilder::new()
.standard_piano(keys)?
.set_width(width)
.unwrap()
.build2d();
}
}
Ok(())
}
}