use super::*;
pub struct Tiled<'a, P: Processor> {
default: &'a mut P,
is_tiled: usize,
group_depth: usize,
tile_buf: Vec<Element>,
tile_x0: f32,
tile_y0: f32,
tile_x1: f32,
tile_y1: f32,
x0: f32,
x1: f32,
y0: f32,
y1: f32,
style: TiledStyle
}
#[derive(Clone, Copy, Debug)]
pub struct TiledStyle {
pub h_grid_margin: f32,
pub v_grid_margin: f32,
pub tile_margin: f32,
pub grid_color: Option<[u8; 3]>,
}
impl Default for TiledStyle {
fn default() -> Self {
TiledStyle {
tile_margin: 0.1,
h_grid_margin: 10.,
v_grid_margin: 10.,
grid_color: Some([200; 3]),
}
}
}
impl<'a, P: Processor> Tiled<'a, P> {
pub fn new(p: &'a mut P, style: TiledStyle) -> Self {
Tiled {
default: p,
is_tiled: std::usize::MAX,
group_depth: 0,
tile_buf: Vec::new(),
tile_x0: std::f32::INFINITY,
tile_y0: std::f32::INFINITY,
tile_x1: -std::f32::INFINITY,
tile_y1: -std::f32::INFINITY,
x0: std::f32::INFINITY,
y0: std::f32::INFINITY,
x1: -std::f32::INFINITY,
y1: -std::f32::INFINITY,
style
}
}
fn draw_tile(&mut self, x: f32, y: f32, style: &Style) {
self.tile_buf.push(Element::Rectangle {
style: Style {
fill_color: style.stroke_color.clone(),
stroke_color: Some([0, 0, 0]),
fill_opacity: style.opacity.clone(),
.. Style::default()
},
x0: x - 5. + self.style.tile_margin,
y0: y - 5. + self.style.tile_margin,
x1: x + 5. - self.style.tile_margin,
y1: y + 5. - self.style.tile_margin,
});
self.x1 = self.x1.max(x);
self.y1 = self.y1.max(y);
self.x0 = self.x0.min(x);
self.y0 = self.y0.min(y);
self.tile_x0 = self.tile_x0.min(x - self.style.h_grid_margin);
self.tile_y0 = self.tile_y0.min(y - self.style.v_grid_margin);
self.tile_x1 = self.tile_x1.max(x + self.style.h_grid_margin);
self.tile_y1 = self.tile_y1.max(y + self.style.v_grid_margin);
}
fn line(&mut self, current_is_drawn: &mut bool, mut x0: f32, mut y0: f32, x: f32, y: f32, style: &Style) {
debug!("=> {:?} {:?}", x, y);
if x.round() > x0.round() {
if *current_is_drawn {
x0 += 10.
}
while x0.round() <= x.round() {
self.draw_tile(x0, y0, &style);
x0 += 10.
}
*current_is_drawn = true;
} else if x.round() < x0.round() {
if *current_is_drawn {
x0 -= 10.
}
while x0.round() >= x.round() {
self.draw_tile(x0, y0, &style);
x0 -= 10.
}
*current_is_drawn = true;
}
if y.round() > y0.round() {
if *current_is_drawn {
y0 += 10.
}
while y0.round() <= y.round() {
self.draw_tile(x, y0, &style);
y0 += 10.
}
*current_is_drawn = true;
} else if y.round() < y0.round() {
if *current_is_drawn {
y0 -= 10.
}
while y0.round() >= y.round() {
self.draw_tile(x, y0, &style);
y0 -= 10.
}
*current_is_drawn = true;
}
}
fn path(
&mut self,
close: bool,
path: &[PathElement],
style: &Style,
) -> Result<(), failure::Error> {
let (mut x0, mut y0) = (0., 0.);
let mut initial = None;
let mut current_is_drawn = true;
for elt in path.iter() {
match *elt {
PathElement::Move { x, y } => {
debug!("MV {:?} {:?}", x, y);
if !current_is_drawn {
self.draw_tile(x0, y0, &style);
}
current_is_drawn = false;
if close {
if let Some((mut x, mut y)) = initial.take() {
if x0 > x {
x += 1.
} else if x0 < x {
x -= 1.
}
if y0 > x {
y += 1.
} else if y0 < x {
y -= 1.
}
self.line(&mut current_is_drawn, x0, y0, x, y, style)
}
}
initial = Some((x, y));
x0 = x;
y0 = y;
}
PathElement::Bezier3 { .. } => {}
PathElement::Line { x, y } => {
self.line(&mut current_is_drawn, x0, y0, x, y, style);
x0 = x;
y0 = y;
}
}
}
if close {
if let Some((mut x, mut y)) = initial.take() {
if x0 > x {
x += 1.
} else if x0 < x {
x -= 1.
}
if y0 > x {
y += 1.
} else if y0 < x {
y -= 1.
}
self.line(&mut current_is_drawn, x0, y0, x, y, style)
}
}
Ok(())
}
fn tiled_element(&mut self, element: Element) -> Result<(), failure::Error> {
debug!("tiled_element: {:?}", element);
match element {
Element::Path {
close,
ref style,
ref path,
} => {
if style.stroke_color.is_some() {
self.path(close, path, style)?;
self.tile_buf.push(element);
} else {
for elt in path.iter() {
match *elt {
PathElement::Move { x, y } => {
self.x1 = self.x1.max(x);
self.y1 = self.y1.max(y);
self.x0 = self.x0.min(x);
self.y0 = self.y0.min(y);
}
PathElement::Bezier3 {
x,
y,
x1,
y1,
x2,
y2,
} => {
self.x1 = self.x1.max(x).max(x1).max(x2);
self.y1 = self.y1.max(y).max(y1).max(y2);
self.x0 = self.x0.min(x).min(x1).min(x2);
self.y0 = self.y0.min(y).min(y1).min(y2);
}
PathElement::Line { x, y } => {
self.x1 = self.x1.max(x);
self.y1 = self.y1.max(y);
self.x0 = self.x0.min(x);
self.y0 = self.y0.min(y);
}
}
}
self.tile_buf.push(element)
}
}
e => self.tile_buf.push(e),
}
Ok(())
}
}
impl<'a, P: Processor> Processor for Tiled<'a, P> {
fn process(&mut self, element: Element) -> Result<(), failure::Error> {
debug!("element {:?}", element);
match element {
Element::BeginGroup { id } => {
self.group_depth += 1;
if id.contains("tiled") {
self.is_tiled = self.group_depth;
}
self.tile_buf.push(Element::BeginGroup { id })
}
Element::EndGroup => {
self.group_depth -= 1;
if self.group_depth < self.is_tiled {
self.is_tiled = std::usize::MAX
}
self.tile_buf.push(Element::EndGroup)
}
e => {
if self.group_depth >= self.is_tiled {
self.tiled_element(e)?
} else {
self.tile_buf.push(e)
}
}
}
Ok(())
}
fn finish(&mut self) -> Result<(), failure::Error> {
debug!("finish, outputting grid");
let mut x0 = self.tile_x0 - 5.;
let mut y0 = self.tile_y0 - 5.;
self.x0 = self.x0.min(x0);
self.y0 = self.y0.min(y0);
let xoff = ((x0 - self.x0) / 10.).round() * 10.;
let yoff = ((y0 - self.y0) / 10.).round() * 10.;
x0 -= xoff;
y0 -= yoff;
let x1 = self.x1.max(self.tile_x1);
let y1 = self.y1.max(self.tile_y1);
let w = ((x1 - x0) / 10.).round() * 10.;
let h = ((y1 - y0) / 10.).round() * 10.;
let style = Style {
stroke_color: self.style.grid_color.clone(),
.. Style::default()
};
self.default.process(Element::Rectangle {
style: style.clone(),
x0: self.tile_x0 - 5. - xoff,
y0: self.tile_y0 - 5. - yoff,
x1: self.tile_x0 - 5. - xoff + w,
y1: self.tile_y0 - 5. - yoff + h,
})?;
x0 += 10.;
while x0.round() < x1.round() {
self.default.process(Element::Path {
close: false,
style: style.clone(),
path: vec![
PathElement::Move { x: x0, y: y0 + h },
PathElement::Line { x: x0, y: y0 },
],
})?;
x0 += 10.;
}
y0 += 10.;
while y0.round() < y1.round() {
self.default.process(Element::Path {
close: false,
style: style.clone(),
path: vec![
PathElement::Move {
x: self.tile_x0 - 5. - xoff,
y: y0,
},
PathElement::Line {
x: self.tile_x0 - 5. - xoff + w,
y: y0,
},
],
})?;
y0 += 10.;
}
for elt in self.tile_buf.drain(..) {
self.default.process(elt)?
}
self.default.finish()
}
}