mod beams;
mod geometry;
mod glyphs;
mod render;
mod tuplets;
use acorde_core::Score;
use acorde_layout::{LayoutConfig, LayoutResult, compute_layout};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct SvgRenderOptions {
pub width: f32,
pub staff_size: f32,
pub measures_per_system: usize,
pub interactive: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RenderMetadata {
pub width: f32,
pub height: f32,
pub address_bounds: Vec<AddressBounds>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddressBounds {
pub part: usize,
pub staff: usize,
pub measure: usize,
pub voice: usize,
pub note: usize,
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Default for SvgRenderOptions {
fn default() -> Self {
Self {
width: 900.0,
staff_size: 24.0,
measures_per_system: 4,
interactive: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RenderError {
EmptyScore,
InvalidRow { row: usize },
InvalidLayout { reason: String },
InvalidOptions { reason: String },
UnsupportedClef,
UnsupportedAccidental { alter: i8 },
}
impl std::fmt::Display for RenderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RenderError::EmptyScore => write!(f, "score has no staves to render"),
RenderError::InvalidRow { row } => write!(f, "layout row {row} does not exist"),
RenderError::InvalidLayout { reason } => write!(f, "invalid layout: {reason}"),
RenderError::InvalidOptions { reason } => write!(f, "invalid render options: {reason}"),
RenderError::UnsupportedClef => write!(
f,
"unsupported clef (percussion has no staff-position mapping)"
),
RenderError::UnsupportedAccidental { alter } => {
write!(
f,
"unsupported accidental alter={alter} (supported range is -2..=2)"
)
}
}
}
}
impl std::error::Error for RenderError {}
pub fn render_svg(score: &Score, options: &SvgRenderOptions) -> Result<String, RenderError> {
let config = LayoutConfig {
measures_per_row: options.measures_per_system.max(1),
..Default::default()
};
let layout = compute_layout(score, &config);
render_svg_with_layout(score, &layout, options)
}
pub fn render_svg_with_layout(
score: &Score,
layout: &LayoutResult,
options: &SvgRenderOptions,
) -> Result<String, RenderError> {
render::build_svg(score, layout, options)
}
pub fn render_svg_row(
score: &Score,
layout: &LayoutResult,
row: usize,
options: &SvgRenderOptions,
) -> Result<String, RenderError> {
let mut subset = layout.clone();
subset.rows = vec![
layout
.rows
.get(row)
.cloned()
.ok_or(RenderError::InvalidRow { row })?,
];
render_svg_with_layout(score, &subset, options)
}
pub fn render_svg_metadata(
score: &Score,
layout: &LayoutResult,
options: &SvgRenderOptions,
) -> Result<RenderMetadata, RenderError> {
render::build_svg_with_metadata(score, layout, options).map(|(_, metadata)| metadata)
}