use energyml_core::value::Reflect;
use crate::error::{Error, Result};
use crate::geometry::Patch;
use crate::geometry::Topology;
use crate::points::Point;
use crate::source::ValueSource;
use crate::values::scalar_f64;
const PLANES: &[&str] = &["Planes"];
const TRIPLES: &[&str] = &["Plane"];
pub(crate) fn read(representation: &dyn Reflect, _source: &dyn ValueSource) -> Result<Vec<Patch>> {
let planes = first_list(representation, PLANES);
if planes.is_empty() {
return Err(Error::malformed(
"PlaneSetRepresentation",
"the representation declares no plane at all",
));
}
let mut out = Vec::new();
let mut node_offset = 0usize;
for (index, plane) in planes.iter().enumerate() {
let at = format!("Planes[{index}]");
let form = plane.type_info().type_key;
let triples = first_list(*plane, TRIPLES);
if triples.is_empty() {
return Err(Error::unbounded(
crate::geometry::leak(form),
at,
"a depth, and nothing about how far the plane extends",
));
}
let mut points: Vec<Point> = Vec::new();
let mut triangles: Vec<[u32; 3]> = Vec::new();
for (which, triple) in triples.iter().enumerate() {
let corners = triple_points(*triple, &format!("{at}.Plane[{which}]"))?;
let base = points.len() as u32;
points.extend(corners);
triangles.push([base, base + 1, base + 2]);
}
let start = node_offset;
node_offset += points.len();
out.push(Patch {
index,
node_offset: start,
points,
topology: Topology::Triangles(triangles),
closed: Vec::new(),
});
}
Ok(out)
}
fn triple_points(triple: &dyn Reflect, at: &str) -> Result<[Point; 3]> {
let listed = crate::geometry::field_list(triple, "Point3d");
if listed.len() != 3 {
return Err(Error::malformed(
at,
format!(
"a `ThreePoint3d` states exactly three points, this one states {}",
listed.len()
),
));
}
let mut corners = [[0.0f64; 3]; 3];
for (index, point) in listed.iter().enumerate() {
corners[index] = [
coordinate(*point, "Coordinate1", at, index)?,
coordinate(*point, "Coordinate2", at, index)?,
coordinate(*point, "Coordinate3", at, index)?,
];
}
Ok(corners)
}
fn coordinate(point: &dyn Reflect, field: &str, at: &str, index: usize) -> Result<f64> {
scalar_f64(point, field).ok_or_else(|| Error::malformed(at, format!("point {index} states no `{field}`")))
}
fn first_list<'a>(object: &'a dyn Reflect, fields: &[&str]) -> Vec<&'a dyn Reflect> {
for field in fields {
let found = crate::geometry::field_list(object, field);
if !found.is_empty() {
return found;
}
}
Vec::new()
}