123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- use serde::Deserialize;
- use serde_yaml::Value;
- use crate::errors::IncompleteMappingError;
- use crate::image::{Image, Pixel};
- use std::collections::{HashMap, HashSet};
- #[derive(Deserialize, Debug)]
- pub struct Mapping(pub HashMap<Pixel, Option<ColorEntry>>);
- #[derive(Deserialize, Debug)]
- pub struct ColorEntry {
- pub color: Pixel,
- pub name: String,
- pub symbol: String,
- }
- impl ColorEntry {
- fn from_entry(
- entry: HashMap<String, Value>,
- ) -> Result<(Pixel, Option<ColorEntry>), Box<dyn std::error::Error>> {
- let color = match entry.get("color") {
- Some(Value::Sequence(v)) => {
- let r = v[0].as_u64().unwrap().try_into().unwrap();
- let g = v[1].as_u64().unwrap().try_into().unwrap();
- let b = v[2].as_u64().unwrap().try_into().unwrap();
- (r, g, b)
- }
- _ => panic!("Missing `color` in entry"),
- };
- if entry.get("blank").is_some() {
- return Ok((color, None));
- }
- let name = entry["name"].as_str().unwrap().to_owned();
- let symbol = entry["symbol"].as_str().unwrap().to_owned();
- Ok((
- color,
- Some(ColorEntry {
- color,
- name,
- symbol,
- }),
- ))
- }
- }
- impl Mapping {
- pub fn load(
- path: impl AsRef<std::path::Path>,
- img: &Image,
- ) -> Result<Mapping, Box<dyn std::error::Error>> {
- let path = path.as_ref().to_path_buf();
- let data: Vec<HashMap<String, Value>> =
- serde_yaml::from_reader(std::fs::File::open(&path)?)?;
- // do validation to make sure all pixel colors are handled
- let color_map = data
- .into_iter()
- .map(|entry| {
- let (c, e) = ColorEntry::from_entry(entry)?;
- Ok((c, e))
- })
- .collect::<Result<HashMap<Pixel, Option<ColorEntry>>, Box<dyn std::error::Error>>>()?;
- let all_image_colors = img
- .iter()
- .map(|(_, color)| color)
- .collect::<HashSet<Pixel>>();
- let missing_colors = all_image_colors
- .into_iter()
- .filter(|c| !color_map.contains_key(c))
- .collect::<Vec<Pixel>>();
- if !missing_colors.is_empty() {
- return Err(Box::new(IncompleteMappingError {
- path,
- missing_colors,
- }));
- }
- Ok(Mapping(color_map))
- }
- pub fn lookup(&self, color: Pixel) -> &str {
- if let Some(ref e) = self.0[&color] {
- &e.symbol
- } else {
- ""
- }
- }
- }
- #[derive(Deserialize)]
- pub struct Config {
- pub grid_every: u32,
- pub line_weight: f64,
- pub major_line_weight: f64,
- pub grid_size: f64,
- pub font: String,
- }
- impl Config {
- pub fn scale(&self, n: u32) -> f64 {
- n as f64 * self.grid_size
- }
- }
- impl Default for Config {
- fn default() -> Config {
- Config {
- grid_every: 10,
- line_weight: 1.0,
- major_line_weight: 3.0,
- grid_size: 24.0,
- font: "Fira Sans 12".to_string(),
- }
- }
- }
|