use crate::*;
#[derive(Clone, Debug)]
pub struct Geometry {
pub id: Option<String>,
pub name: Option<String>,
pub asset: Option<Box<Asset>>,
pub element: GeometryElement,
pub extra: Vec<Extra>,
}
impl Geometry {
pub fn new(id: impl Into<String>, element: GeometryElement) -> Self {
Self {
id: Some(id.into()),
name: None,
asset: None,
element,
extra: vec![],
}
}
pub fn new_mesh(
id: impl Into<String>,
sources: Vec<Source>,
vertices: Vertices,
elements: Vec<Primitive>,
) -> Self {
Self::new(id, Mesh::new(sources, vertices, elements).into())
}
}
impl XNode for Geometry {
const NAME: &'static str = "geometry";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Geometry {
id: element.attr("id").map(Into::into),
name: element.attr("name").map(Into::into),
asset: Asset::parse_opt_box(&mut it)?,
element: parse_one_many(&mut it, GeometryElement::parse)?,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for Geometry {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.opt_attr("id", &self.id);
e.opt_attr("name", &self.name);
let e = e.start(w)?;
self.asset.write_to(w)?;
self.element.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
impl CollectLocalMaps for Geometry {
fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
maps.insert(self);
self.element.collect_local_maps(maps);
}
}
#[derive(Clone, Debug, Default)]
pub struct InstanceGeometryData {
pub bind_material: Option<BindMaterial>,
}
impl XNodeWrite for InstanceGeometryData {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.bind_material.write_to(w)
}
}
impl Instantiate for Geometry {
const INSTANCE: &'static str = "instance_geometry";
type Data = InstanceGeometryData;
fn parse_data(_: &Element, it: &mut ElementIter<'_>) -> Result<Self::Data> {
Ok(InstanceGeometryData {
bind_material: BindMaterial::parse_opt(it)?,
})
}
fn is_empty(data: &Self::Data) -> bool {
data.bind_material.is_none()
}
}
impl Instance<Geometry> {
pub fn instance_materials(&self) -> &[InstanceMaterial] {
match self.data.bind_material {
Some(ref m) => &m.instance_material,
None => &[],
}
}
pub fn get_instance_material(&self, symbol: &str) -> Option<&InstanceMaterial> {
let bm = self.data.bind_material.as_ref()?;
bm.instance_material.iter().find(|mat| mat.symbol == symbol)
}
}
#[derive(Clone, Debug)]
pub enum GeometryElement {
ConvexHullOf(Url),
Mesh(Mesh),
Spline(Spline),
}
impl From<Spline> for GeometryElement {
fn from(v: Spline) -> Self {
Self::Spline(v)
}
}
impl From<Mesh> for GeometryElement {
fn from(v: Mesh) -> Self {
Self::Mesh(v)
}
}
impl CollectLocalMaps for GeometryElement {
fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
match self {
GeometryElement::ConvexHullOf(_) => {}
GeometryElement::Mesh(mesh) => mesh.sources.collect_local_maps(maps),
GeometryElement::Spline(spline) => spline.sources.collect_local_maps(maps),
}
}
}
impl GeometryElement {
pub fn parse(element: &Element) -> Result<Option<Self>> {
Ok(Some(match element.name() {
Mesh::CONVEX => Mesh::parse_convex(element)?,
Mesh::NAME => GeometryElement::Mesh(Mesh::parse(false, element)?),
Spline::NAME => GeometryElement::Spline(Spline::parse(element)?),
_ => return Ok(None),
}))
}
pub fn sources(&self) -> &[Source] {
match self {
GeometryElement::ConvexHullOf(_) => &[],
GeometryElement::Mesh(mesh) => &mesh.sources,
GeometryElement::Spline(spline) => &spline.sources,
}
}
pub fn as_mesh(&self) -> Option<&Mesh> {
match self {
GeometryElement::Mesh(mesh) if !mesh.convex => Some(mesh),
_ => None,
}
}
}
impl XNodeWrite for GeometryElement {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
match self {
GeometryElement::ConvexHullOf(s) => {
let mut e = ElemBuilder::new(Mesh::CONVEX);
e.print_attr("convex_hull_of", s);
e.end(w)
}
GeometryElement::Mesh(e) => e.write_to(w),
GeometryElement::Spline(e) => e.write_to(w),
}
}
}
#[derive(Clone, Debug)]
pub struct Mesh {
pub convex: bool,
pub sources: Vec<Source>,
pub vertices: Option<Vertices>,
pub elements: Vec<Primitive>,
pub extra: Vec<Extra>,
}
impl Mesh {
pub fn new(sources: Vec<Source>, vertices: Vertices, elements: Vec<Primitive>) -> Self {
assert!(!sources.is_empty());
Self {
convex: false,
sources,
vertices: Some(vertices),
elements,
extra: vec![],
}
}
pub fn new_convex(sources: Vec<Source>, vertices: Vertices, elements: Vec<Primitive>) -> Self {
assert!(!sources.is_empty());
Self {
convex: true,
sources,
vertices: Some(vertices),
elements,
extra: vec![],
}
}
pub const CONVEX: &'static str = "convex_mesh";
pub fn parse_convex(element: &Element) -> Result<GeometryElement> {
debug_assert_eq!(element.name(), Self::CONVEX);
if let Some(s) = parse_attr(element.attr("convex_hull_of"))? {
return Ok(GeometryElement::ConvexHullOf(s));
}
Ok(GeometryElement::Mesh(Mesh::parse(true, element)?))
}
pub fn parse(convex: bool, element: &Element) -> Result<Self> {
debug_assert_eq!(
element.name(),
if convex { Self::CONVEX } else { Self::NAME }
);
let mut it = element.children().peekable();
Ok(Mesh {
convex,
sources: Source::parse_list_n::<1>(&mut it)?,
vertices: Vertices::parse_opt(&mut it)?,
elements: parse_list_many(&mut it, Primitive::parse)?,
extra: Extra::parse_many(it)?,
})
}
fn write_inner<W: Write>(&self, e: ElemBuilder, w: &mut XWriter<W>) -> Result<()> {
let e = e.start(w)?;
self.sources.write_to(w)?;
self.vertices.write_to(w)?;
self.elements.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
impl XNode for Mesh {
const NAME: &'static str = "mesh";
fn parse(element: &Element) -> Result<Self> {
Self::parse(false, element)
}
}
impl XNodeWrite for Mesh {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.write_inner(Self::elem(), w)
}
}
#[derive(Clone, Debug)]
pub struct Vertices {
pub id: String,
pub name: Option<String>,
pub inputs: Vec<Input>,
pub position: usize,
pub extra: Vec<Extra>,
}
impl XNode for Vertices {
const NAME: &'static str = "vertices";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
let inputs = Input::parse_list(&mut it)?;
Ok(Vertices {
id: element.attr("id").ok_or("missing 'id' attr")?.into(),
name: element.attr("name").map(Into::into),
position: inputs
.iter()
.position(|i| i.semantic == Semantic::Position)
.ok_or("vertices: missing POSITION input")?,
inputs,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for Vertices {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.attr("id", &self.id);
e.opt_attr("name", &self.name);
let e = e.start(w)?;
self.inputs.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
impl Traversable for Vertices {
fn traverse<'a, E>(
doc: &'a Document,
mut f: impl FnMut(&'a Self) -> Result<(), E>,
) -> Result<(), E>
where
Self: 'a,
{
for lib in doc.iter::<Geometry>() {
if let GeometryElement::Mesh(Mesh {
vertices: Some(v), ..
}) = &lib.element
{
f(v)?
}
}
Ok(())
}
}
impl Vertices {
pub fn new(id: impl Into<String>, inputs: Vec<Input>) -> Self {
Self {
id: id.into(),
name: None,
position: inputs
.iter()
.position(|i| i.semantic == Semantic::Position)
.expect("vertices: missing POSITION input"),
inputs,
extra: vec![],
}
}
pub fn position_input(&self) -> &Input {
&self.inputs[self.position]
}
}
#[derive(Clone, Default, Debug)]
pub struct Geom<T> {
pub name: Option<String>,
pub material: Option<String>,
pub count: usize,
pub inputs: InputList,
pub data: T,
pub extra: Vec<Extra>,
}
impl<T: ParseGeom> Geom<T> {
fn new_geom(material: Option<String>, inputs: InputList, count: usize, data: T) -> Self {
Self {
name: None,
material,
count,
inputs,
data,
extra: vec![],
}
}
}
pub(crate) use private::ParseGeom;
pub(crate) mod private {
use super::*;
pub trait ParseGeom: XNodeWrite + Default {
const NAME: &'static str;
fn parse(it: &mut ElementIter<'_>) -> Result<Self>;
fn validate(_: &Geom<Self>) -> Result<()>;
}
}
impl<T: ParseGeom> XNode for Geom<T> {
const NAME: &'static str = T::NAME;
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
let res = Geom {
name: element.attr("name").map(Into::into),
material: element.attr("material").map(Into::into),
count: parse_attr(element.attr("count"))?.ok_or("expected 'count' attr")?,
inputs: InputList::parse::<0>(&mut it)?,
data: T::parse(&mut it)?,
extra: Extra::parse_many(it)?,
};
T::validate(&res)?;
Ok(res)
}
}
impl<T: ParseGeom> XNodeWrite for Geom<T> {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.opt_attr("name", &self.name);
e.opt_attr("material", &self.material);
e.print_attr("count", self.count);
let e = e.start(w)?;
self.inputs.write_to(w)?;
self.data.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
macro_rules! mk_primitive {
($($(#[$doc:meta])* $name:ident($as:ident),)*) => {
#[derive(Clone, Debug)]
pub enum Primitive {
$($(#[$doc])* $name($name),)*
}
$(
impl From<$name> for Primitive {
fn from(val: $name) -> Self {
Self::$name(val)
}
}
)*
impl Primitive {
pub fn parse(e: &Element) -> Result<Option<Self>> {
Ok(Some(match e.name() {
$($name::NAME => Self::$name(Geom::parse(e)?),)*
_ => return Ok(None),
}))
}
$(
pub fn $as(&self) -> Option<&$name> {
if let Self::$name(x) = self {
Some(x)
} else {
None
}
}
)*
}
impl XNodeWrite for Primitive {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
match self {
$(Self::$name(e) => e.write_to(w),)*
}
}
}
}
}
mk_primitive! {
Lines(as_lines),
LineStrips(as_line_strips),
Polygons(as_polygons),
PolyList(as_polylist),
Triangles(as_triangles),
TriFans(as_trifans),
TriStrips(as_tristrips),
}
#[derive(Clone, Default, Debug)]
pub struct LineGeom {
pub prim: Option<Box<[u32]>>,
}
pub type Lines = Geom<LineGeom>;
impl Lines {
pub fn new(
material: Option<String>,
inputs: Vec<InputS>,
count: usize,
prim: Box<[u32]>,
) -> Self {
let inputs = InputList::new(inputs);
assert!(inputs.len() * 2 * count == prim.len());
Self::new_geom(material, inputs, count, LineGeom { prim: Some(prim) })
}
}
impl Deref for LineGeom {
type Target = Option<Box<[u32]>>;
fn deref(&self) -> &Self::Target {
&self.prim
}
}
impl ParseGeom for LineGeom {
const NAME: &'static str = "lines";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
Ok(LineGeom {
prim: parse_opt("p", it, parse_array)?,
})
}
fn validate(res: &Geom<Self>) -> Result<()> {
if let Some(ref data) = *res.data {
if res.inputs.stride * 2 * res.count != data.len() {
return Err("line count does not match <p> field".into());
}
}
Ok(())
}
}
impl XNodeWrite for LineGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
opt(&self.prim, |e| ElemBuilder::print_arr("p", e, w))
}
}
#[derive(Clone, Default, Debug)]
pub struct LineStripGeom {
pub prim: Vec<Box<[u32]>>,
}
pub type LineStrips = Geom<LineStripGeom>;
impl LineStrips {
pub fn new(material: Option<String>, inputs: Vec<InputS>, prim: Vec<Box<[u32]>>) -> Self {
let inputs = InputList::new(inputs);
debug_assert!(prim.iter().all(|p| inputs.check_prim::<2>(p)));
Self::new_geom(material, inputs, prim.len(), LineStripGeom { prim })
}
}
impl Deref for LineStripGeom {
type Target = Vec<Box<[u32]>>;
fn deref(&self) -> &Self::Target {
&self.prim
}
}
impl ParseGeom for LineStripGeom {
const NAME: &'static str = "line_strips";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
Ok(LineStripGeom {
prim: parse_list("p", it, parse_array)?,
})
}
fn validate(res: &Geom<Self>) -> Result<()> {
if res.count != res.data.len() {
return Err("line strip count does not match <p> fields".into());
}
if !res.data.iter().all(|p| res.inputs.check_prim::<2>(p)) {
return Err("incorrect <p> field in line strips".into());
}
Ok(())
}
}
impl XNodeWrite for LineStripGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.prim
.iter()
.try_for_each(|e| ElemBuilder::print_arr("p", e, w))
}
}
#[derive(Clone, Debug)]
pub struct PolygonHole {
pub verts: Box<[u32]>,
pub hole: Vec<Box<[u32]>>,
}
impl PolygonHole {
pub fn new(verts: Box<[u32]>, hole: Vec<Box<[u32]>>) -> Self {
Self { verts, hole }
}
}
impl XNodeWrite for PolygonHole {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = ElemBuilder::new("ph").start(w)?;
ElemBuilder::print_arr("p", &self.verts, w)?;
many(&self.hole, |h| ElemBuilder::print_arr("h", h, w))?;
e.end(w)
}
}
#[derive(Clone, Default, Debug)]
pub struct PolygonGeom(
pub Vec<PolygonHole>,
);
pub type Polygons = Geom<PolygonGeom>;
impl Polygons {
pub fn new(material: Option<String>, inputs: Vec<InputS>, prim: Vec<PolygonHole>) -> Self {
let inputs = InputList::new(inputs);
debug_assert!(prim.iter().all(|ph| {
inputs.check_prim::<3>(&ph.verts) && ph.hole.iter().all(|h| inputs.check_prim::<3>(h))
}));
Self::new_geom(material, inputs, prim.len(), PolygonGeom(prim))
}
}
impl Deref for PolygonGeom {
type Target = Vec<PolygonHole>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ParseGeom for PolygonGeom {
const NAME: &'static str = "polygon";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
let mut polys = parse_list("p", it, |e| {
Ok(PolygonHole {
verts: parse_array(e)?,
hole: vec![],
})
})?;
let more_polys = parse_list("ph", it, |e| {
let mut it = e.children().peekable();
let verts = parse_one("p", &mut it, parse_array)?;
let hole = parse_list("h", &mut it, parse_array)?;
if hole.is_empty() {
return Err(
"<ph> element can only be used when at least one hole is present".into(),
);
}
finish(PolygonHole { verts, hole }, it)
})?;
polys.extend(more_polys);
Ok(PolygonGeom(polys))
}
fn validate(res: &Geom<Self>) -> Result<()> {
if res.count != res.data.len() {
return Err("polygon count does not match <p> fields".into());
}
if !res.data.iter().all(|ph| {
res.inputs.check_prim::<3>(&ph.verts)
&& ph.hole.iter().all(|h| res.inputs.check_prim::<3>(h))
}) {
return Err("incorrect <p> field in polygon".into());
}
Ok(())
}
}
impl XNodeWrite for PolygonGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.0.write_to(w)
}
}
#[derive(Clone, Default, Debug)]
pub struct PolyListGeom {
pub vcount: Box<[u32]>,
pub prim: Box<[u32]>,
}
pub type PolyList = Geom<PolyListGeom>;
impl PolyList {
pub fn new(
material: Option<String>,
inputs: Vec<InputS>,
vcount: Box<[u32]>,
prim: Box<[u32]>,
) -> Self {
let inputs = InputList::new(inputs);
debug_assert!(inputs.len() * vcount.iter().sum::<u32>() as usize == prim.len());
Self::new_geom(
material,
inputs,
vcount.len(),
PolyListGeom { vcount, prim },
)
}
}
pub(crate) fn validate_vcount<T>(
count: usize,
stride: usize,
vcount: &[u32],
prim: &[T],
) -> Result<()> {
if count != vcount.len() {
return Err("count does not match <vcount> field".into());
}
if stride * vcount.iter().sum::<u32>() as usize != prim.len() {
return Err("vcount does not match <p>/<v> field".into());
}
Ok(())
}
impl ParseGeom for PolyListGeom {
const NAME: &'static str = "polylist";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
Ok(PolyListGeom {
vcount: parse_opt("vcount", it, parse_array)?.unwrap_or_default(),
prim: parse_opt("p", it, parse_array)?.unwrap_or_default(),
})
}
fn validate(res: &Geom<Self>) -> Result<()> {
validate_vcount(
res.count,
res.inputs.stride,
&res.data.vcount,
&res.data.prim,
)
}
}
impl XNodeWrite for PolyListGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
if !self.vcount.is_empty() {
ElemBuilder::print_arr("vcount", &self.vcount, w)?;
}
if !self.prim.is_empty() {
ElemBuilder::print_arr("p", &self.prim, w)?;
}
Ok(())
}
}
#[derive(Clone, Default, Debug)]
pub struct TriangleGeom {
pub prim: Option<Box<[u32]>>,
}
pub type Triangles = Geom<TriangleGeom>;
impl Triangles {
pub fn new(
material: Option<String>,
inputs: Vec<InputS>,
count: usize,
prim: Box<[u32]>,
) -> Self {
let inputs = InputList::new(inputs);
assert!(inputs.len() * 3 * count == prim.len());
Self::new_geom(material, inputs, count, TriangleGeom { prim: Some(prim) })
}
}
impl Deref for TriangleGeom {
type Target = Option<Box<[u32]>>;
fn deref(&self) -> &Self::Target {
&self.prim
}
}
impl ParseGeom for TriangleGeom {
const NAME: &'static str = "triangles";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
Ok(TriangleGeom {
prim: parse_opt("p", it, parse_array)?,
})
}
fn validate(res: &Geom<Self>) -> Result<()> {
if let Some(ref data) = *res.data {
if res.inputs.stride * 3 * res.count != data.len() {
return Err("triangle count does not match <p> field".into());
}
}
Ok(())
}
}
impl XNodeWrite for TriangleGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
opt(&self.prim, |e| ElemBuilder::print_arr("p", e, w))
}
}
#[derive(Clone, Default, Debug)]
pub struct TriFanGeom {
pub prim: Vec<Box<[u32]>>,
}
pub type TriFans = Geom<TriFanGeom>;
impl TriFans {
pub fn new(material: Option<String>, inputs: Vec<InputS>, prim: Vec<Box<[u32]>>) -> Self {
let inputs = InputList::new(inputs);
debug_assert!(prim.iter().all(|p| inputs.check_prim::<3>(p)));
Self::new_geom(material, inputs, prim.len(), TriFanGeom { prim })
}
}
impl Deref for TriFanGeom {
type Target = Vec<Box<[u32]>>;
fn deref(&self) -> &Self::Target {
&self.prim
}
}
impl ParseGeom for TriFanGeom {
const NAME: &'static str = "trifans";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
Ok(TriFanGeom {
prim: parse_list("p", it, parse_array)?,
})
}
fn validate(res: &Geom<Self>) -> Result<()> {
if res.count != res.data.len() {
return Err("triangle fan count does not match <p> fields".into());
}
if !res.data.iter().all(|p| res.inputs.check_prim::<3>(p)) {
return Err("incorrect <p> field in triangle fans".into());
}
Ok(())
}
}
impl XNodeWrite for TriFanGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
many(&self.prim, |e| ElemBuilder::print_arr("p", e, w))
}
}
#[derive(Clone, Default, Debug)]
pub struct TriStripGeom {
pub prim: Vec<Box<[u32]>>,
}
pub type TriStrips = Geom<TriStripGeom>;
impl TriStrips {
pub fn new(material: Option<String>, inputs: Vec<InputS>, prim: Vec<Box<[u32]>>) -> Self {
let inputs = InputList::new(inputs);
debug_assert!(prim.iter().all(|p| inputs.check_prim::<3>(p)));
Self::new_geom(material, inputs, prim.len(), TriStripGeom { prim })
}
}
impl Deref for TriStripGeom {
type Target = Vec<Box<[u32]>>;
fn deref(&self) -> &Self::Target {
&self.prim
}
}
impl ParseGeom for TriStripGeom {
const NAME: &'static str = "tristrips";
fn parse(it: &mut ElementIter<'_>) -> Result<Self> {
Ok(TriStripGeom {
prim: parse_list("p", it, parse_array)?,
})
}
fn validate(res: &Geom<Self>) -> Result<()> {
if res.count != res.data.len() {
return Err("triangle strip count does not match <p> fields".into());
}
if !res.data.iter().all(|p| res.inputs.check_prim::<3>(p)) {
return Err("incorrect <p> field in triangle strips".into());
}
Ok(())
}
}
impl XNodeWrite for TriStripGeom {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
many(&self.prim, |e| ElemBuilder::print_arr("p", e, w))
}
}
#[derive(Clone, Debug)]
pub struct Spline {
pub closed: bool,
pub sources: Vec<Source>,
pub controls: ControlVertices,
pub extra: Vec<Extra>,
}
impl Spline {
pub fn new(sources: Vec<Source>, controls: Vec<Input>) -> Self {
Self {
closed: false,
sources,
controls: ControlVertices::new(controls),
extra: vec![],
}
}
}
impl XNode for Spline {
const NAME: &'static str = "spline";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Spline {
closed: parse_attr(element.attr("closed"))?.unwrap_or(false),
sources: Source::parse_list_n::<1>(&mut it)?,
controls: ControlVertices::parse_one(&mut it)?,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for Spline {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.def_print_attr("closed", self.closed, false);
let e = e.start(w)?;
self.sources.write_to(w)?;
self.controls.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Debug)]
pub struct ControlVertices {
pub inputs: Vec<Input>,
pub position: usize,
pub extra: Vec<Extra>,
}
impl XNode for ControlVertices {
const NAME: &'static str = "control_vertices";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
let inputs = Input::parse_list(&mut it)?;
Ok(ControlVertices {
position: inputs
.iter()
.position(|i| i.semantic == Semantic::Position)
.ok_or("control_vertices: missing POSITION input")?,
inputs,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for ControlVertices {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
self.inputs.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
impl ControlVertices {
pub fn new(inputs: Vec<Input>) -> Self {
Self {
position: inputs
.iter()
.position(|i| i.semantic == Semantic::Position)
.expect("control_vertices: missing POSITION input"),
inputs,
extra: vec![],
}
}
pub fn position_input(&self) -> &Input {
&self.inputs[self.position]
}
}