use std::any::Any;
use mkgraphic::prelude::*;
use mkgraphic::support::canvas::Canvas;
use super::config::RenderConfig;
use super::{STAFF_HEIGHT, STAFF_SPACE};
use crate::notation::{Clef, ClefSign};
pub struct ClefElement {
clef: Clef,
x: f32,
staff_y: f32,
scale: f32,
}
impl ClefElement {
pub fn new(clef: Clef) -> Self {
Self {
clef,
x: 0.0,
staff_y: 0.0,
scale: 1.0,
}
}
pub fn set_position(&mut self, x: f32, staff_y: f32) {
self.x = x;
self.staff_y = staff_y;
}
pub fn set_scale(&mut self, scale: f32) {
self.scale = scale;
}
pub fn width(&self) -> f32 {
match self.clef.sign() {
ClefSign::G => STAFF_SPACE * 3.0 * self.scale,
ClefSign::F => STAFF_SPACE * 2.5 * self.scale,
ClefSign::C => STAFF_SPACE * 2.5 * self.scale,
ClefSign::Percussion => STAFF_SPACE * 2.0 * self.scale,
ClefSign::Tab => STAFF_SPACE * 2.5 * self.scale,
ClefSign::None | ClefSign::Jianpu => 0.0,
}
}
pub fn draw_to_canvas(&self, canvas: &mut Canvas, config: &RenderConfig) {
let colors = &config.colors.clefs;
let color = Color::new(colors.0, colors.1, colors.2, colors.3);
canvas.stroke_style(color);
canvas.fill_style(color);
match self.clef.sign() {
ClefSign::G => self.draw_treble_clef(canvas),
ClefSign::F => self.draw_bass_clef(canvas),
ClefSign::C => self.draw_c_clef(canvas),
ClefSign::Percussion => self.draw_percussion_clef(canvas),
ClefSign::Tab => self.draw_tab_clef(canvas),
ClefSign::None | ClefSign::Jianpu => {}
}
let octave_change = self.clef.octave_change();
if octave_change != 0 {
self.draw_octave_marker(canvas, octave_change);
}
}
fn draw_treble_clef(&self, canvas: &mut Canvas) {
let s = STAFF_SPACE * self.scale;
let cx = self.x + s * 1.5;
let _g_line_y = self.staff_y + s * 0.5;
canvas.line_width(2.0 * self.scale);
canvas.begin_path();
let bottom_y = self.staff_y + s * 2.5;
let top_y = self.staff_y - s * 3.0;
canvas.move_to(Point::new(cx, bottom_y));
canvas.line_to(Point::new(cx - s * 0.8, self.staff_y + s * 1.5));
canvas.line_to(Point::new(cx - s * 0.5, self.staff_y + s * 0.5));
canvas.line_to(Point::new(cx + s * 0.3, self.staff_y));
canvas.line_to(Point::new(cx + s * 0.5, self.staff_y - s * 0.5));
canvas.line_to(Point::new(cx + s * 0.3, self.staff_y - s * 1.0));
canvas.line_to(Point::new(cx - s * 0.2, self.staff_y - s * 1.2));
canvas.line_to(Point::new(cx - s * 0.5, self.staff_y - s * 1.0));
canvas.line_to(Point::new(cx, self.staff_y - s * 0.3));
canvas.line_to(Point::new(cx + s * 0.2, top_y + s));
canvas.line_to(Point::new(cx, top_y));
canvas.stroke();
canvas.begin_path();
canvas.move_to(Point::new(cx, bottom_y));
canvas.line_to(Point::new(cx + s * 0.3, bottom_y - s * 0.3));
canvas.line_to(Point::new(cx + s * 0.2, bottom_y - s * 0.6));
canvas.line_to(Point::new(cx - s * 0.1, bottom_y - s * 0.4));
canvas.stroke();
}
fn draw_bass_clef(&self, canvas: &mut Canvas) {
let s = STAFF_SPACE * self.scale;
let cx = self.x + s;
let f_line_y = self.staff_y - s * 0.5;
canvas.line_width(2.0 * self.scale);
canvas.begin_path();
canvas.move_to(Point::new(cx, f_line_y - s * 0.3));
canvas.line_to(Point::new(cx + s * 0.8, f_line_y - s * 0.5));
canvas.line_to(Point::new(cx + s, f_line_y));
canvas.line_to(Point::new(cx + s * 0.8, f_line_y + s * 0.5));
canvas.line_to(Point::new(cx + s * 0.3, f_line_y + s));
canvas.line_to(Point::new(cx - s * 0.2, f_line_y + s * 1.2));
canvas.line_to(Point::new(cx - s * 0.4, f_line_y + s));
canvas.stroke();
canvas.begin_path();
canvas.add_circle(mkgraphic::support::circle::Circle::new(
Point::new(cx - s * 0.2, f_line_y),
s * 0.25,
));
canvas.fill();
let dot_x = cx + s * 1.3;
canvas.begin_path();
canvas.add_circle(mkgraphic::support::circle::Circle::new(
Point::new(dot_x, f_line_y - s * 0.5),
s * 0.15,
));
canvas.fill();
canvas.begin_path();
canvas.add_circle(mkgraphic::support::circle::Circle::new(
Point::new(dot_x, f_line_y + s * 0.5),
s * 0.15,
));
canvas.fill();
}
fn draw_c_clef(&self, canvas: &mut Canvas) {
let s = STAFF_SPACE * self.scale;
let cx = self.x + s * 0.5;
let line_offset = (3 - self.clef.line() as i8) as f32;
let c_line_y = self.staff_y + line_offset * s * 0.5;
canvas.line_width(2.0 * self.scale);
canvas.begin_path();
canvas.move_to(Point::new(cx, c_line_y - s * 2.0));
canvas.line_to(Point::new(cx, c_line_y + s * 2.0));
canvas.stroke();
canvas.line_width(4.0 * self.scale);
canvas.begin_path();
canvas.move_to(Point::new(cx + s * 0.3, c_line_y - s * 2.0));
canvas.line_to(Point::new(cx + s * 0.3, c_line_y + s * 2.0));
canvas.stroke();
canvas.line_width(2.0 * self.scale);
canvas.begin_path();
canvas.move_to(Point::new(cx + s * 0.5, c_line_y - s * 2.0));
canvas.line_to(Point::new(cx + s * 1.5, c_line_y - s * 1.5));
canvas.line_to(Point::new(cx + s * 1.8, c_line_y - s * 0.5));
canvas.line_to(Point::new(cx + s * 1.5, c_line_y));
canvas.stroke();
canvas.begin_path();
canvas.move_to(Point::new(cx + s * 0.5, c_line_y + s * 2.0));
canvas.line_to(Point::new(cx + s * 1.5, c_line_y + s * 1.5));
canvas.line_to(Point::new(cx + s * 1.8, c_line_y + s * 0.5));
canvas.line_to(Point::new(cx + s * 1.5, c_line_y));
canvas.stroke();
}
fn draw_percussion_clef(&self, canvas: &mut Canvas) {
let s = STAFF_SPACE * self.scale;
let cx = self.x + s;
canvas.line_width(4.0 * self.scale);
canvas.begin_path();
canvas.move_to(Point::new(cx - s * 0.3, self.staff_y - s));
canvas.line_to(Point::new(cx - s * 0.3, self.staff_y + s));
canvas.stroke();
canvas.begin_path();
canvas.move_to(Point::new(cx + s * 0.3, self.staff_y - s));
canvas.line_to(Point::new(cx + s * 0.3, self.staff_y + s));
canvas.stroke();
}
fn draw_tab_clef(&self, canvas: &mut Canvas) {
let s = STAFF_SPACE * self.scale;
let cx = self.x + s;
canvas.line_width(2.0 * self.scale);
let t_y = self.staff_y - s * 1.5;
canvas.begin_path();
canvas.move_to(Point::new(cx - s * 0.4, t_y));
canvas.line_to(Point::new(cx + s * 0.4, t_y));
canvas.stroke();
canvas.begin_path();
canvas.move_to(Point::new(cx, t_y));
canvas.line_to(Point::new(cx, t_y + s * 0.8));
canvas.stroke();
let a_y = self.staff_y;
canvas.begin_path();
canvas.move_to(Point::new(cx - s * 0.3, a_y + s * 0.4));
canvas.line_to(Point::new(cx, a_y - s * 0.4));
canvas.line_to(Point::new(cx + s * 0.3, a_y + s * 0.4));
canvas.stroke();
canvas.begin_path();
canvas.move_to(Point::new(cx - s * 0.15, a_y));
canvas.line_to(Point::new(cx + s * 0.15, a_y));
canvas.stroke();
let b_y = self.staff_y + s * 1.5;
canvas.begin_path();
canvas.move_to(Point::new(cx - s * 0.2, b_y - s * 0.4));
canvas.line_to(Point::new(cx - s * 0.2, b_y + s * 0.4));
canvas.stroke();
canvas.begin_path();
canvas.move_to(Point::new(cx - s * 0.2, b_y - s * 0.4));
canvas.line_to(Point::new(cx + s * 0.2, b_y - s * 0.2));
canvas.line_to(Point::new(cx - s * 0.2, b_y));
canvas.line_to(Point::new(cx + s * 0.2, b_y + s * 0.2));
canvas.line_to(Point::new(cx - s * 0.2, b_y + s * 0.4));
canvas.stroke();
}
fn draw_octave_marker(&self, canvas: &mut Canvas, octave_change: i8) {
let s = STAFF_SPACE * self.scale;
let marker_y = if octave_change > 0 {
self.staff_y - STAFF_HEIGHT / 2.0 - s * 1.5
} else {
self.staff_y + STAFF_HEIGHT / 2.0 + s * 0.5
};
canvas.line_width(1.0);
let eight_x = self.x + self.width() * 0.5;
canvas.begin_path();
canvas.add_circle(mkgraphic::support::circle::Circle::new(
Point::new(eight_x, marker_y - s * 0.15),
s * 0.2,
));
canvas.stroke();
canvas.begin_path();
canvas.add_circle(mkgraphic::support::circle::Circle::new(
Point::new(eight_x, marker_y + s * 0.15),
s * 0.25,
));
canvas.stroke();
}
}
impl Element for ClefElement {
fn limits(&self, _ctx: &BasicContext) -> ViewLimits {
ViewLimits::fixed(self.width(), STAFF_HEIGHT)
}
fn draw(&self, _ctx: &Context) {
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clef_element_creation() {
let clef = Clef::treble();
let element = ClefElement::new(clef);
assert!(element.width() > 0.0);
}
#[test]
fn test_clef_widths() {
let treble = ClefElement::new(Clef::treble());
let bass = ClefElement::new(Clef::bass());
let alto = ClefElement::new(Clef::alto());
assert!(treble.width() > 0.0);
assert!(bass.width() > 0.0);
assert!(alto.width() > 0.0);
}
}