use indicatrix_vault::local::{DesignFileKind, design_file_to_asc_text};
use std::path::Path;
pub use indicatrix_vault::local::converted_asc_file_name;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ForeignFormat {
Gem,
Gcs,
}
impl ForeignFormat {
#[must_use]
pub fn from_path(path: &Path) -> Option<Self> {
match DesignFileKind::from_file_name(&path.file_name()?.to_string_lossy())? {
DesignFileKind::Gem => Some(Self::Gem),
DesignFileKind::Gcs => Some(Self::Gcs),
DesignFileKind::Asc => None,
}
}
#[must_use]
pub const fn dotted_extension(self) -> &'static str {
self.kind().dotted_extension()
}
const fn kind(self) -> DesignFileKind {
match self {
Self::Gem => DesignFileKind::Gem,
Self::Gcs => DesignFileKind::Gcs,
}
}
}
#[derive(Debug, Clone)]
pub struct ConvertedDesign {
pub asc_text: String,
pub warnings: Vec<String>,
}
pub fn convert_foreign_design(
format: ForeignFormat,
bytes: &[u8],
) -> Result<ConvertedDesign, String> {
design_file_to_asc_text(format.dotted_extension(), format.kind(), bytes).map(|file| {
ConvertedDesign {
asc_text: file.asc_text,
warnings: file.warnings,
}
})
}
#[cfg(test)]
pub mod test_gem {
pub type GemTierSpec<'a> = (&'a str, f64, f64, &'a [f64]);
pub const SYNTHETIC_GEM_TIERS: &[GemTierSpec<'static>] = &[
(
"P1",
-41.0,
0.75,
&[96.0, 12.0, 24.0, 36.0, 48.0, 60.0, 72.0, 84.0],
),
(
"G1",
-90.0,
1.0,
&[
96.0, 6.0, 12.0, 18.0, 24.0, 30.0, 36.0, 42.0, 48.0, 54.0, 60.0, 66.0, 72.0, 78.0,
84.0, 90.0,
],
),
(
"C1",
35.0,
0.9,
&[96.0, 12.0, 24.0, 36.0, 48.0, 60.0, 72.0, 84.0],
),
("T", 0.0, 0.6, &[96.0]),
];
fn push_gem_string(out: &mut Vec<u8>, text: &str) {
out.push(u8::try_from(text.len()).expect("test strings are short"));
out.extend_from_slice(text.as_bytes());
}
pub fn encode_gem(tiers: &[GemTierSpec<'_>], gear: i32, title: &str) -> Vec<u8> {
let mut out = Vec::new();
for (tier_number, (name, angle, mast, indices)) in (1_i32..).zip(tiers) {
let theta = angle.abs().to_radians();
let z_sign = if angle.is_sign_negative() { -1.0 } else { 1.0 };
for (k, index) in indices.iter().enumerate() {
let phi = (90.0 - 360.0 * index / f64::from(gear)).to_radians();
let normal = [
theta.sin() * phi.cos(),
theta.sin() * phi.sin(),
z_sign * theta.cos(),
];
for component in normal {
out.extend_from_slice(&(component / mast).to_le_bytes());
}
out.extend_from_slice(&tier_number.to_le_bytes());
let label = if k == 0 {
format!("{name}\t")
} else {
"\t".to_string()
};
push_gem_string(&mut out, &label);
out.extend_from_slice(&0_i32.to_le_bytes());
}
}
out.extend_from_slice(&(-99_999.0_f64).to_le_bytes());
out.extend_from_slice(&8_i32.to_le_bytes());
out.extend_from_slice(&1_i32.to_le_bytes());
out.extend_from_slice(&gear.to_le_bytes());
out.extend_from_slice(&1.54_f64.to_le_bytes());
out.extend_from_slice(&0x7FFF_u32.to_le_bytes());
out.extend_from_slice(&0.0_f64.to_le_bytes());
push_gem_string(&mut out, title);
for _ in 0..7 {
push_gem_string(&mut out, "");
}
out
}
}