184 lines
3.9 KiB
Rust
Raw Normal View History

2023-12-25 10:30:27 -08:00
use anyhow::{bail, Context, Result};
use cgmath::{Deg, Point3};
2023-12-25 10:30:27 -08:00
use std::collections::{HashMap, HashSet};
2023-12-24 23:03:00 -08:00
2023-12-25 09:01:12 -08:00
use crate::physics::{Pfloat, Polar};
2023-12-24 23:03:00 -08:00
/// Toml file syntax
pub(in crate::content) mod toml {
use super::Pfloat;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Debug, Deserialize)]
pub struct SystemRoot {
pub system: System,
pub object: HashMap<String, Object>,
}
#[derive(Debug, Deserialize)]
pub struct System {
pub name: String,
}
#[derive(Debug, Deserialize)]
pub struct Object {
pub sprite: String,
2023-12-25 10:30:27 -08:00
pub position: Position,
2023-12-24 23:03:00 -08:00
2023-12-25 09:01:12 -08:00
pub size: Pfloat,
2023-12-25 10:30:27 -08:00
pub radius: Option<Pfloat>,
pub angle: Option<Pfloat>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum Position {
Polar(PolarCoords),
Cartesian(CoordinatesThree),
2023-12-25 10:30:27 -08:00
}
#[derive(Debug, Deserialize)]
pub struct PolarCoords {
pub center: CoordinatesTwo,
2023-12-24 23:03:00 -08:00
pub radius: Pfloat,
pub angle: Pfloat,
pub z: Pfloat,
2023-12-24 23:03:00 -08:00
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum CoordinatesTwo {
2023-12-24 23:03:00 -08:00
Label(String),
Coords([Pfloat; 2]),
}
2023-12-25 09:01:12 -08:00
impl ToString for CoordinatesTwo {
2023-12-25 09:01:12 -08:00
fn to_string(&self) -> String {
match self {
Self::Label(s) => s.to_owned(),
Self::Coords(v) => format!("{:?}", v),
}
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum CoordinatesThree {
Label(String),
Coords([Pfloat; 3]),
}
impl ToString for CoordinatesThree {
fn to_string(&self) -> String {
match self {
Self::Label(s) => s.to_owned(),
Self::Coords(v) => format!("{:?}", v),
}
}
}
2023-12-24 23:03:00 -08:00
}
#[derive(Debug)]
pub struct System {
2023-12-25 09:01:12 -08:00
pub name: String,
pub objects: Vec<Object>,
2023-12-24 23:03:00 -08:00
}
#[derive(Debug)]
2023-12-25 09:01:12 -08:00
pub struct Object {
pub sprite: String,
pub position: Point3<f32>,
2023-12-25 09:01:12 -08:00
pub size: Pfloat,
2023-12-25 10:30:27 -08:00
pub angle: Deg<Pfloat>,
2023-12-24 23:03:00 -08:00
}
// Helper function for resolve_position, never called on its own.
2023-12-25 10:30:27 -08:00
fn resolve_coordinates(
2023-12-24 23:03:00 -08:00
objects: &HashMap<String, toml::Object>,
cor: &toml::CoordinatesThree,
2023-12-25 10:30:27 -08:00
mut cycle_detector: HashSet<String>,
) -> Result<Point3<f32>> {
2023-12-25 10:30:27 -08:00
match cor {
toml::CoordinatesThree::Coords(c) => Ok((*c).into()),
toml::CoordinatesThree::Label(l) => {
2023-12-25 10:30:27 -08:00
if cycle_detector.contains(l) {
bail!(
"Found coordinate cycle: `{}`",
cycle_detector.iter().fold(String::new(), |sum, a| {
if sum.is_empty() {
a.to_string()
} else {
sum + " -> " + a
}
})
);
2023-12-25 09:01:12 -08:00
}
2023-12-25 10:30:27 -08:00
cycle_detector.insert(l.to_owned());
let p = match objects.get(l) {
Some(p) => p,
None => bail!("Could not resolve coordinate label `{l}`"),
};
Ok(resolve_position(&objects, &p, cycle_detector)
.with_context(|| format!("In object {:#?}", l))?)
}
}
}
/// Given an object, resolve it's position as a Point3.
2023-12-25 10:30:27 -08:00
fn resolve_position(
objects: &HashMap<String, toml::Object>,
obj: &toml::Object,
cycle_detector: HashSet<String>,
) -> Result<Point3<f32>> {
2023-12-25 10:30:27 -08:00
match &obj.position {
toml::Position::Cartesian(c) => Ok(resolve_coordinates(objects, &c, cycle_detector)?),
toml::Position::Polar(p) => {
2023-12-25 16:15:09 -08:00
let three = match &p.center {
toml::CoordinatesTwo::Label(s) => toml::CoordinatesThree::Label(s.clone()),
toml::CoordinatesTwo::Coords(v) => {
toml::CoordinatesThree::Coords([v[0], v[1], f32::NAN])
}
};
let r = resolve_coordinates(&objects, &three, cycle_detector)?;
let plane = Polar {
center: (r.x, r.y).into(),
radius: p.radius,
angle: Deg(p.angle),
}
.to_cartesian();
Ok(Point3 {
x: plane.x,
y: plane.y,
z: p.z,
})
2023-12-25 10:30:27 -08:00
}
2023-12-24 23:03:00 -08:00
}
}
impl System {
pub fn parse(value: toml::SystemRoot) -> Result<Self> {
let mut objects = Vec::new();
2023-12-25 09:01:12 -08:00
for (label, obj) in &value.object {
2023-12-25 10:30:27 -08:00
let mut cycle_detector = HashSet::new();
cycle_detector.insert(label.to_owned());
2023-12-24 23:03:00 -08:00
objects.push(Object {
sprite: obj.sprite.clone(),
2023-12-25 10:30:27 -08:00
position: resolve_position(&value.object, &obj, cycle_detector)
.with_context(|| format!("In object {:#?}", label))?,
2023-12-25 09:01:12 -08:00
size: obj.size,
2023-12-25 10:30:27 -08:00
angle: Deg(obj.angle.unwrap_or(0.0)),
2023-12-24 23:03:00 -08:00
});
}
return Ok(Self {
name: value.system.name.clone(),
objects,
});
}
}