-
alexdevteam authoredc3867665
objects.rs 6.82 KiB
use std::{collections::HashMap, io::Read};
use xml::{attribute::OwnedAttribute, EventReader};
use crate::{
error::TiledError,
properties::{parse_properties, Colour, Properties},
util::{get_attrs, parse_tag},
};
#[derive(Debug, PartialEq, Clone)]
pub struct ObjectGroup {
pub name: String,
pub opacity: f32,
pub visible: bool,
pub objects: Vec<Object>,
pub colour: Option<Colour>,
/**
* Layer index is not preset for tile collision boxes
*/
pub layer_index: Option<u32>,
pub properties: Properties,
}
impl ObjectGroup {
pub(crate) fn new<R: Read>(
parser: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
layer_index: Option<u32>,
) -> Result<ObjectGroup, TiledError> {
let ((o, v, c, n), ()) = get_attrs!(
attrs,
optionals: [
("opacity", opacity, |v:String| v.parse().ok()),
("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
("color", colour, |v:String| v.parse().ok()),
("name", name, |v:String| v.into()),
],
required: [],
TiledError::MalformedAttributes("object groups must have a name".to_string())
);
let mut objects = Vec::new();
let mut properties = HashMap::new();
parse_tag!(parser, "objectgroup", {
"object" => |attrs| {
objects.push(Object::new(parser, attrs)?);
Ok(())
},
"properties" => |_| {
properties = parse_properties(parser)?;
Ok(())
},
});
Ok(ObjectGroup {
name: n.unwrap_or(String::new()),
opacity: o.unwrap_or(1.0),
visible: v.unwrap_or(true),
objects: objects,
colour: c,
layer_index,
properties,
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ObjectShape {
Rect { width: f32, height: f32 },
Ellipse { width: f32, height: f32 },
Polyline { points: Vec<(f32, f32)> },
Polygon { points: Vec<(f32, f32)> },
Point(f32, f32),
}
#[derive(Debug, PartialEq, Clone)]
pub struct Object {
pub id: u32,
pub gid: u32,
pub name: String,
pub obj_type: String,
pub width: f32,
pub height: f32,
pub x: f32,
pub y: f32,
pub rotation: f32,
pub visible: bool,
pub shape: ObjectShape,
pub properties: Properties,
}
impl Object {
fn new<R: Read>(
parser: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
) -> Result<Object, TiledError> {
let ((id, gid, n, t, w, h, v, r), (x, y)) = get_attrs!(
attrs,
optionals: [
("id", id, |v:String| v.parse().ok()),
("gid", gid, |v:String| v.parse().ok()),
("name", name, |v:String| v.parse().ok()),
("type", obj_type, |v:String| v.parse().ok()),
("width", width, |v:String| v.parse().ok()),
("height", height, |v:String| v.parse().ok()),
("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
("rotation", rotation, |v:String| v.parse().ok()),
],
required: [
("x", x, |v:String| v.parse().ok()),
("y", y, |v:String| v.parse().ok()),
],
TiledError::MalformedAttributes("objects must have an x and a y number".to_string())
);
let v = v.unwrap_or(true);
let w = w.unwrap_or(0f32);
let h = h.unwrap_or(0f32);
let r = r.unwrap_or(0f32);
let id = id.unwrap_or(0u32);
let gid = gid.unwrap_or(0u32);
let n = n.unwrap_or(String::new());
let t = t.unwrap_or(String::new());
let mut shape = None;
let mut properties = HashMap::new();
parse_tag!(parser, "object", {
"ellipse" => |_| {
shape = Some(ObjectShape::Ellipse {
width: w,
height: h,
});
Ok(())
},
"polyline" => |attrs| {
shape = Some(Object::new_polyline(attrs)?);
Ok(())
},
"polygon" => |attrs| {
shape = Some(Object::new_polygon(attrs)?);
Ok(())
},
"point" => |_| {
shape = Some(Object::new_point(x, y)?);
Ok(())
},
"properties" => |_| {
properties = parse_properties(parser)?;
Ok(())
},
});
let shape = shape.unwrap_or(ObjectShape::Rect {
width: w,
height: h,
});
Ok(Object {
id: id,
gid: gid,
name: n.clone(),
obj_type: t.clone(),
width: w,
height: h,
x: x,
y: y,
rotation: r,
visible: v,
shape: shape,
properties: properties,
})
}
fn new_polyline(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> {
let ((), s) = get_attrs!(
attrs,
optionals: [],
required: [
("points", points, |v| Some(v)),
],
TiledError::MalformedAttributes("A polyline must have points".to_string())
);
let points = Object::parse_points(s)?;
Ok(ObjectShape::Polyline { points: points })
}
fn new_polygon(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> {
let ((), s) = get_attrs!(
attrs,
optionals: [],
required: [
("points", points, |v| Some(v)),
],
TiledError::MalformedAttributes("A polygon must have points".to_string())
);
let points = Object::parse_points(s)?;
Ok(ObjectShape::Polygon { points: points })
}
fn new_point(x: f32, y: f32) -> Result<ObjectShape, TiledError> {
Ok(ObjectShape::Point(x, y))
}
fn parse_points(s: String) -> Result<Vec<(f32, f32)>, TiledError> {
let pairs = s.split(' ');
let mut points = Vec::new();
for v in pairs.map(|p| p.split(',')) {
let v: Vec<&str> = v.collect();
if v.len() != 2 {
return Err(TiledError::MalformedAttributes(
"one of a polyline's points does not have an x and y coordinate".to_string(),
));
}
let (x, y) = (v[0].parse().ok(), v[1].parse().ok());
if x.is_none() || y.is_none() {
return Err(TiledError::MalformedAttributes(
"one of polyline's points does not have i32eger coordinates".to_string(),
));
}
points.push((x.unwrap(), y.unwrap()));
}
Ok(points)
}
}