use crate::dms::config::{MultiCfg, SignCfg, VmsCfg};
use crate::dms::font::FontTable;
use crate::dms::graphic::GraphicTable;
use crate::dms::multi::{ColorCtx, ColorScheme};
use crate::dms::render::{Page, Pages};
use crate::dms::Result;
#[derive(Clone, Default)]
pub struct DmsBuilder {
sign_cfg: SignCfg,
vms_cfg: VmsCfg,
font_definition: FontTable,
multi_cfg: MultiCfg,
graphic_definition: GraphicTable,
}
#[derive(Clone)]
pub struct Dms {
sign_cfg: SignCfg,
vms_cfg: VmsCfg,
font_definition: FontTable,
multi_cfg: MultiCfg,
graphic_definition: GraphicTable,
}
impl DmsBuilder {
pub fn with_sign_cfg(mut self, cfg: SignCfg) -> Self {
self.sign_cfg = cfg;
self
}
pub fn with_vms_cfg(mut self, cfg: VmsCfg) -> Self {
self.vms_cfg = cfg;
self
}
pub fn with_font_definition(mut self, fonts: FontTable) -> Self {
self.font_definition = fonts;
self
}
pub fn with_multi_cfg(mut self, cfg: MultiCfg) -> Self {
self.multi_cfg = cfg;
self
}
pub fn with_graphic_definition(mut self, graphics: GraphicTable) -> Self {
self.graphic_definition = graphics;
self
}
pub fn build(self) -> Dms {
Dms {
sign_cfg: self.sign_cfg,
vms_cfg: self.vms_cfg,
font_definition: self.font_definition,
multi_cfg: self.multi_cfg,
graphic_definition: self.graphic_definition,
}
}
}
impl Dms {
pub fn builder() -> DmsBuilder {
DmsBuilder::default()
}
pub fn into_builder(self) -> DmsBuilder {
DmsBuilder {
sign_cfg: self.sign_cfg,
vms_cfg: self.vms_cfg,
font_definition: self.font_definition,
multi_cfg: self.multi_cfg,
graphic_definition: self.graphic_definition,
}
}
pub fn font_definition(&self) -> &FontTable {
&self.font_definition
}
pub fn graphic_definition(&self) -> &GraphicTable {
&self.graphic_definition
}
fn default_font(&self) -> u8 {
self.multi_cfg.default_font
}
pub fn face_width_mm(&self) -> f32 {
self.sign_cfg.sign_width as f32
}
fn pixel_width(&self) -> u16 {
self.vms_cfg.sign_width_pixels
}
fn border_horiz_mm(&self) -> f32 {
let pix = self.pixel_width() + self.gap_char_count();
let min_width = (pix as f32) * self.pitch_horiz_mm();
let extra = (self.face_width_mm() - min_width).max(0.0);
if self.gap_char_count() > 0 {
let border = self.sign_cfg.horizontal_border as f32;
border.min(extra / 2.0)
} else {
extra / 2.0
}
}
fn pixel_width_mm(&self) -> f32 {
self.face_width_mm() - self.border_horiz_mm() * 2.0
}
fn pitch_horiz(&self) -> u8 {
self.vms_cfg.horizontal_pitch
}
fn pitch_horiz_mm(&self) -> f32 {
let pitch = self.pitch_horiz() as f32;
let pix = self.pixel_width() + self.gap_char_count();
if pix > 0 {
pitch.min(self.face_width_mm() / pix as f32)
} else {
pitch
}
}
fn gap_char_count(&self) -> u16 {
let cw = self.char_width().into();
if cw > 1 && self.pixel_width() > cw {
(self.pixel_width() - 1) / cw
} else {
0
}
}
pub fn pixel_x(&self, x: u32, width: u32) -> f32 {
let border = self.border_horiz_mm();
let offset = self.char_offset_mm(x);
let x = x as f32 + 0.5; let pos = border + offset + x * self.pitch_horiz_mm();
width as f32 * pos / self.face_width_mm()
}
fn char_offset_mm(&self, x: u32) -> f32 {
let cw = u32::from(self.char_width());
if cw > 1 {
let char_num = (x / cw) as f32;
char_num * self.gap_char_mm()
} else {
0.0
}
}
fn gap_char_mm(&self) -> f32 {
let gaps = self.gap_char_count();
if gaps > 0 {
self.excess_char_mm() / gaps as f32
} else {
0.0
}
}
fn excess_char_mm(&self) -> f32 {
let pix_mm = self.pitch_horiz_mm() * self.pixel_width() as f32;
(self.pixel_width_mm() - pix_mm).max(0.0)
}
pub fn face_height_mm(&self) -> f32 {
self.sign_cfg.sign_height as f32
}
fn pixel_height(&self) -> u16 {
self.vms_cfg.sign_height_pixels
}
fn border_vert_mm(&self) -> f32 {
let pix = self.pixel_height() + self.gap_line_count();
let min_height = (pix as f32) * self.pitch_vert_mm();
let extra = (self.face_height_mm() - min_height).max(0.0);
if self.gap_line_count() > 0 {
let border = self.sign_cfg.vertical_border as f32;
border.min(extra / 2.0)
} else {
extra / 2.0
}
}
fn pixel_height_mm(&self) -> f32 {
self.face_height_mm() - self.border_vert_mm() * 2.0
}
fn pitch_vert(&self) -> u8 {
self.vms_cfg.vertical_pitch
}
fn pitch_vert_mm(&self) -> f32 {
let pitch = self.pitch_vert() as f32;
let pix = self.pixel_height() + self.gap_line_count();
if pix > 0 {
pitch.min(self.face_height_mm() / pix as f32)
} else {
pitch
}
}
fn gap_line_count(&self) -> u16 {
let ch = self.char_height().into();
if ch > 1 && self.pixel_height() > ch {
(self.pixel_height() - 1) / ch
} else {
0
}
}
pub fn pixel_y(&self, y: u32, height: u32) -> f32 {
let border = self.border_vert_mm();
let offset = self.line_offset_mm(y);
let y = y as f32 + 0.5; let pos = border + offset + y * self.pitch_vert_mm();
height as f32 * pos / self.face_height_mm()
}
fn line_offset_mm(&self, y: u32) -> f32 {
let ch = u32::from(self.char_height());
if ch > 1 {
let line_num = (y / ch) as f32;
line_num * self.gap_line_mm()
} else {
0.0
}
}
fn gap_line_mm(&self) -> f32 {
let gaps = self.gap_line_count();
if gaps > 0 {
self.excess_line_mm() / gaps as f32
} else {
0.0
}
}
fn excess_line_mm(&self) -> f32 {
let pix_mm = self.pitch_vert_mm() * self.pixel_height() as f32;
(self.pixel_height_mm() - pix_mm).max(0.0)
}
fn char_width(&self) -> u8 {
self.vms_cfg.char_width_pixels
}
fn char_height(&self) -> u8 {
self.vms_cfg.char_height_pixels
}
fn color_scheme(&self) -> ColorScheme {
self.multi_cfg.color_scheme
}
fn foreground_default_rgb(&self) -> (u8, u8, u8) {
match self.color_scheme() {
ColorScheme::ColorClassic | ColorScheme::Color24Bit => {
self.multi_cfg.default_foreground_rgb.rgb()
}
_ => {
let mono = &self.vms_cfg.monochrome_color;
(mono[0], mono[1], mono[2])
}
}
}
fn background_default_rgb(&self) -> (u8, u8, u8) {
match self.color_scheme() {
ColorScheme::ColorClassic | ColorScheme::Color24Bit => {
self.multi_cfg.default_background_rgb.rgb()
}
_ => {
let mono = &self.vms_cfg.monochrome_color;
(mono[3], mono[4], mono[5])
}
}
}
pub fn render_pages<'a>(
&'a self,
multi: &'a str,
) -> impl Iterator<Item = Result<Page>> + 'a {
let width = self.pixel_width();
let height = self.pixel_height();
let color_scheme = self.color_scheme();
let fg_default = self.foreground_default_rgb();
let bg_default = self.background_default_rgb();
let color_ctx = ColorCtx::new(color_scheme, fg_default, bg_default);
let char_width = self.char_width();
let char_height = self.char_height();
let font_num = self.default_font();
Pages::builder(width, height)
.with_color_ctx(color_ctx)
.with_char_size(char_width, char_height)
.with_page_on_time_ds(self.multi_cfg.default_page_on_time)
.with_page_off_time_ds(self.multi_cfg.default_page_off_time)
.with_justification_page(self.multi_cfg.default_justification_page)
.with_justification_line(self.multi_cfg.default_justification_line)
.with_font_num(font_num)
.with_fonts(&self.font_definition)
.with_graphics(&self.graphic_definition)
.build(multi)
}
}