<commit_before>use msgpack::decode::{read_u32, read_u8, read_str_len, read_str_data, read_u64_loosely};
use msgpack::decode::{read_array_size, read_bin_len, ValueReadError, ReadError};
use msgpack::encode::{write_u32, write_u8, write_str, write_uint, write_array_len, write_bin};

use std::io::{Write, Read, Seek, SeekFrom};
use std::error::Error;
use std::sync::Arc;
use std::cell::RefCell;
use errors::Result;
use tree::Tree;
use iterators::{Iter, IterNode, IterContent};
use node::{Node, Content, Edge};
use containers::{Container, Blob, Queue, Set};

// Types of Content - Used for msgpack serialization
const DIRECTORY_TYPE_ID: u8 = 0;
const CONTAINER_TYPE_ID: u8 = 1;

// Types of containers - Used for msgpack serialization
const BLOB_TYPE_ID: u8 = 0;
const QUEUE_TYPE_ID: u8 = 1;
const SET_TYPE_ID: u8 = 2;

/// Write a snapshot to a Writer `W`
///
/// Return the number of nodes written on success
pub fn write<W: Write>(writer: &mut W, depth: u32, iter: Iter) -> Result<()> {
    try!(write_u32(writer, depth));
    for node in iter {
        try!(write_node(writer, &node));
    }
    Ok(())
}

/// Load a tree from a Reader `R`
pub fn load<R>(reader: &mut R) -> Result<Tree> where R: Read + Seek {
    let depth = try!(read_u32(reader));
    let root = try!(read_node(reader));
    if root.is_none() {
        return Err("empty tree".into());
    }
    let root = Arc::new(RefCell::new(root.unwrap()));
    try!(read_inner_nodes(&root, reader, depth));
    // We are done reading the file
    return Ok(Tree { root: root, depth: depth });
}

fn read_inner_nodes<R>(root: &Arc<RefCell<Node>>, reader: &mut R, depth: u32) -> Result<()>
    where R: Read + Seek
{
    // A stack of directories for a depth first traversal
    let mut stack = Vec::with_capacity(depth as usize);
    stack.push(root);
    loop {
        match read_node(reader) {
            Ok(Some(node)) => {
                let node = Arc::new(RefCell::new(node));
                let node_depth = node.borrow().path.split("/").skip_while(|&s| s == "").count();
                while node_depth != stack.len() {
                    stack.pop();
                }
                // This node is a child of the current top of the stack
                insert_edge(&mut stack, node);

                // If the last node pushed is a directory, then add it to the stack
                // I can't figure out a way to get around this unsafe.
                unsafe {
                    let parent = stack.last().unwrap().as_ptr();
                    let node_ref = &(*parent).content.get_dir_edges_mut().unwrap().last().unwrap().node;
                    if node_ref.borrow().content.is_dir() {
                        stack.push(node_ref);
                    }
                }
            },
            Ok(None) => return Ok(()),
            Err(e) => return Err(e)
        }
    }
}

/// Serialize an IterNode and write it to `W`
///
/// This function uses the low level msgpack functions directly to avoid allocation
fn write_node<W: Write>(writer: &mut W, node: &IterNode) -> Result<()> {
    try!(write_str(writer, node.path));
    try!(write_uint(writer, node.version as u64));
    match node.content {
        IterContent::Directory(ref labels) => {
            try!(write_u8(writer, DIRECTORY_TYPE_ID));
            try!(write_array_len(writer, labels.len() as u32));
            for label in labels {
                try!(write_str(writer, label));
            }
        },
        IterContent::Container(ref container) => {
            try!(write_u8(writer, CONTAINER_TYPE_ID));
            try!(write_container(writer, &container));
        }
    }
    Ok(())
}

/// Read a msgpack encoded IterNode from a file and return a Node based on it
///
/// Since directories contain child pointers in Nodes, but not in IterNodes, this function allocates
/// a properly sized vector for directory entries (edges) but leaves it empty. It will be filled in
/// when reconstructing the tree.
fn read_node<R>(reader: &mut R) -> Result<Option<Node>> where R: Read + Seek {
    match read_str_len(reader) {
        Ok(path_len) => {
            let mut path_buf = vec![0u8; path_len as usize];
            let path = try!(read_str_data(reader, path_len, &mut path_buf).map_err(|e| {
                e.cause().unwrap().to_string()
            }));
            let version = try!(read_u64_loosely(reader));
            let content = try!(read_content(reader));
            return Ok(Some(Node {
                path: path.to_string(),
                version: version as usize,
                content: content
            }))
        },
        Err(ValueReadError::InvalidMarkerRead(ReadError::UnexpectedEOF)) => return Ok(None),
        Err(e) => return Err(e.into())
    }
}

fn read_content<R>(reader: &mut R) -> Result<Content> where R: Read + Seek {
    let content_type = try!(read_u8(reader));
    match content_type {
        DIRECTORY_TYPE_ID => {
            let len = try!(read_array_size(reader));
            for _ in 0..len {
                // Discard the labels. We will proprly fill in the edges and create pointers when we
                // reconstuct the tree. Reading them in now and decoding as utf-8 is wasteful.
                let str_len = try!(read_str_len(reader));
                try!(reader.seek(SeekFrom::Current(str_len as i64)));
            }
            Ok(Content::Directory(Vec::with_capacity(len as usize)))
        },
        CONTAINER_TYPE_ID => {
            let container = try!(read_container(reader));
            Ok(Content::Container(container))
        },
        _ => unreachable!()
    }
}

fn read_container<R: Read>(reader: &mut R) -> Result<Container> {
    let container_type = try!(read_u8(reader));
    match container_type {
        BLOB_TYPE_ID => {
            let blob = try!(read_blob(reader));
            Ok(Container::Blob(blob))
        },
        QUEUE_TYPE_ID => {
            let len = try!(read_array_size(reader));
            let mut queue = Queue::with_capacity(len as usize);
            for _ in 0..len {
                let blob = try!(read_blob(reader));
                queue.push(blob)
            }
            Ok(Container::Queue(queue))
        },
        SET_TYPE_ID => {
            let len = try!(read_array_size(reader));
            let mut set = Set::with_capacity(len as usize);
            for _ in 0..len {
                let blob = try!(read_blob(reader));
                let _ = set.insert(blob);
            }
            Ok(Container::Set(set))
        },
        _ => unreachable!()
    }
}

fn read_blob<R: Read>(reader: &mut R) -> Result<Blob> {
    let len = try!(read_bin_len(reader));
    let mut buf = vec![0u8; len as usize];
    try!(reader.read_exact(&mut buf));
    Ok(Blob::fill(buf))
}

/// Serialize a Container and write it to the given file
fn write_container<W: Write>(writer: &mut W, container: &Container) -> Result<()> {
    match *container {
        Container::Blob(ref blob) => {
            try!(write_u8(writer, BLOB_TYPE_ID));
            write_blob(writer, blob)
        },
        Container::Queue(ref queue) => {
            try!(write_u8(writer, QUEUE_TYPE_ID));
            try!(write_array_len(writer, queue.len() as u32));
            for blob in queue.data.iter() {
                try!(write_blob(writer, blob));
            }
            Ok(())
        },
        Container::Set(ref set) => {
            try!(write_u8(writer, SET_TYPE_ID));
            try!(write_array_len(writer, set.data.len() as u32));
            for blob in set.data.iter() {
                try!(write_blob(writer, blob));
            }
            Ok(())
        }
    }
}

fn write_blob<W: Write>(writer: &mut W, blob: &Blob) -> Result<()> {
    write_bin(writer, &blob.data).map_err(|e| e.into())
}

fn insert_edge(stack: &mut Vec<&Arc<RefCell<Node>>>, node: Arc<RefCell<Node>>) {
    let mut parent = stack.last_mut().unwrap().borrow_mut();
    let mut edges = parent.content.get_dir_edges_mut().unwrap();
    let label = node.borrow().path.split("/").last().unwrap().to_string();
    let edge = Edge {
        label: label,
        node: node
    };
    edges.push(edge);
}

#[cfg(test)]
mod tests {

    use quickcheck::{Arbitrary, Gen};
    use tree::Tree;
    use node::NodeType;
    use rand::distributions::range::Range;
    use rand::distributions::IndependentSample;

    #[derive(Debug, Clone)]
    struct Path(String);

    impl Arbitrary for Path {
        fn arbitrary<G: Gen>(g: &mut G) -> Path {
            let range = Range::new(1u8, 11u8);
            let depth = range.ind_sample(g);
            let labels = ['a', 'b', 'c', 'd', 'e'];
            let mut path = String::with_capacity((depth*2 - 1) as usize);
            path = (0..depth).fold(path, |mut acc, _| {
                acc.push('/');
                acc.push(*g.choose(&labels).unwrap());
                acc
            });
            Path(path)
        }
    }

    impl Arbitrary for NodeType {
        fn arbitrary<G: Gen>(g: &mut G) -> NodeType {
            g.choose(&[NodeType::Directory, NodeType::Queue, NodeType::Set, NodeType::Blob])
             .unwrap().clone()
        }
    }

    #[quickcheck]
    /// Create a random tree, take a snapshot, and ensure reading it back in gives the same tree
    fn prop_rountrip(node_specs: Vec<(Path, NodeType)>) -> bool {
        let tree = node_specs.iter().fold(Tree::new(), |acc, &(ref path, ref node_type)| {
            // Ignore failures. We may try to insert a node into a non-directory due to randomness
            // of generation. It doesn't matter for this property.
            match acc.create(&path.0, node_type.clone()) {
                Ok(tree) => tree,
                _ => acc
            }
        });
        let filename = tree.snapshot("/tmp").unwrap();
        let loaded_tree = Tree::load_snapshot(&filename).unwrap();
        if tree.depth != loaded_tree.depth {
            return false;
        }
        tree.iter().zip(loaded_tree.iter()).all(|(node1, node2)| node1 == node2)
    }
}
<commit_msg>delete temp snapshot file in tests<commit_after>use msgpack::decode::{read_u32, read_u8, read_str_len, read_str_data, read_u64_loosely};
use msgpack::decode::{read_array_size, read_bin_len, ValueReadError, ReadError};
use msgpack::encode::{write_u32, write_u8, write_str, write_uint, write_array_len, write_bin};

use std::io::{Write, Read, Seek, SeekFrom};
use std::error::Error;
use std::sync::Arc;
use std::cell::RefCell;
use errors::Result;
use tree::Tree;
use iterators::{Iter, IterNode, IterContent};
use node::{Node, Content, Edge};
use containers::{Container, Blob, Queue, Set};

// Types of Content - Used for msgpack serialization
const DIRECTORY_TYPE_ID: u8 = 0;
const CONTAINER_TYPE_ID: u8 = 1;

// Types of containers - Used for msgpack serialization
const BLOB_TYPE_ID: u8 = 0;
const QUEUE_TYPE_ID: u8 = 1;
const SET_TYPE_ID: u8 = 2;

/// Write a snapshot to a Writer `W`
///
/// Return the number of nodes written on success
pub fn write<W: Write>(writer: &mut W, depth: u32, iter: Iter) -> Result<()> {
    try!(write_u32(writer, depth));
    for node in iter {
        try!(write_node(writer, &node));
    }
    Ok(())
}

/// Load a tree from a Reader `R`
pub fn load<R>(reader: &mut R) -> Result<Tree> where R: Read + Seek {
    let depth = try!(read_u32(reader));
    let root = try!(read_node(reader));
    if root.is_none() {
        return Err("empty tree".into());
    }
    let root = Arc::new(RefCell::new(root.unwrap()));
    try!(read_inner_nodes(&root, reader, depth));
    // We are done reading the file
    return Ok(Tree { root: root, depth: depth });
}

fn read_inner_nodes<R>(root: &Arc<RefCell<Node>>, reader: &mut R, depth: u32) -> Result<()>
    where R: Read + Seek
{
    // A stack of directories for a depth first traversal
    let mut stack = Vec::with_capacity(depth as usize);
    stack.push(root);
    loop {
        match read_node(reader) {
            Ok(Some(node)) => {
                let node = Arc::new(RefCell::new(node));
                let node_depth = node.borrow().path.split("/").skip_while(|&s| s == "").count();
                while node_depth != stack.len() {
                    stack.pop();
                }
                // This node is a child of the current top of the stack
                insert_edge(&mut stack, node);

                // If the last node pushed is a directory, then add it to the stack
                // I can't figure out a way to get around this unsafe.
                unsafe {
                    let parent = stack.last().unwrap().as_ptr();
                    let node_ref = &(*parent).content.get_dir_edges_mut().unwrap().last().unwrap().node;
                    if node_ref.borrow().content.is_dir() {
                        stack.push(node_ref);
                    }
                }
            },
            Ok(None) => return Ok(()),
            Err(e) => return Err(e)
        }
    }
}

/// Serialize an IterNode and write it to `W`
///
/// This function uses the low level msgpack functions directly to avoid allocation
fn write_node<W: Write>(writer: &mut W, node: &IterNode) -> Result<()> {
    try!(write_str(writer, node.path));
    try!(write_uint(writer, node.version as u64));
    match node.content {
        IterContent::Directory(ref labels) => {
            try!(write_u8(writer, DIRECTORY_TYPE_ID));
            try!(write_array_len(writer, labels.len() as u32));
            for label in labels {
                try!(write_str(writer, label));
            }
        },
        IterContent::Container(ref container) => {
            try!(write_u8(writer, CONTAINER_TYPE_ID));
            try!(write_container(writer, &container));
        }
    }
    Ok(())
}

/// Read a msgpack encoded IterNode from a file and return a Node based on it
///
/// Since directories contain child pointers in Nodes, but not in IterNodes, this function allocates
/// a properly sized vector for directory entries (edges) but leaves it empty. It will be filled in
/// when reconstructing the tree.
fn read_node<R>(reader: &mut R) -> Result<Option<Node>> where R: Read + Seek {
    match read_str_len(reader) {
        Ok(path_len) => {
            let mut path_buf = vec![0u8; path_len as usize];
            let path = try!(read_str_data(reader, path_len, &mut path_buf).map_err(|e| {
                e.cause().unwrap().to_string()
            }));
            let version = try!(read_u64_loosely(reader));
            let content = try!(read_content(reader));
            return Ok(Some(Node {
                path: path.to_string(),
                version: version as usize,
                content: content
            }))
        },
        Err(ValueReadError::InvalidMarkerRead(ReadError::UnexpectedEOF)) => return Ok(None),
        Err(e) => return Err(e.into())
    }
}

fn read_content<R>(reader: &mut R) -> Result<Content> where R: Read + Seek {
    let content_type = try!(read_u8(reader));
    match content_type {
        DIRECTORY_TYPE_ID => {
            let len = try!(read_array_size(reader));
            for _ in 0..len {
                // Discard the labels. We will proprly fill in the edges and create pointers when we
                // reconstuct the tree. Reading them in now and decoding as utf-8 is wasteful.
                let str_len = try!(read_str_len(reader));
                try!(reader.seek(SeekFrom::Current(str_len as i64)));
            }
            Ok(Content::Directory(Vec::with_capacity(len as usize)))
        },
        CONTAINER_TYPE_ID => {
            let container = try!(read_container(reader));
            Ok(Content::Container(container))
        },
        _ => unreachable!()
    }
}

fn read_container<R: Read>(reader: &mut R) -> Result<Container> {
    let container_type = try!(read_u8(reader));
    match container_type {
        BLOB_TYPE_ID => {
            let blob = try!(read_blob(reader));
            Ok(Container::Blob(blob))
        },
        QUEUE_TYPE_ID => {
            let len = try!(read_array_size(reader));
            let mut queue = Queue::with_capacity(len as usize);
            for _ in 0..len {
                let blob = try!(read_blob(reader));
                queue.push(blob)
            }
            Ok(Container::Queue(queue))
        },
        SET_TYPE_ID => {
            let len = try!(read_array_size(reader));
            let mut set = Set::with_capacity(len as usize);
            for _ in 0..len {
                let blob = try!(read_blob(reader));
                let _ = set.insert(blob);
            }
            Ok(Container::Set(set))
        },
        _ => unreachable!()
    }
}

fn read_blob<R: Read>(reader: &mut R) -> Result<Blob> {
    let len = try!(read_bin_len(reader));
    let mut buf = vec![0u8; len as usize];
    try!(reader.read_exact(&mut buf));
    Ok(Blob::fill(buf))
}

/// Serialize a Container and write it to the given file
fn write_container<W: Write>(writer: &mut W, container: &Container) -> Result<()> {
    match *container {
        Container::Blob(ref blob) => {
            try!(write_u8(writer, BLOB_TYPE_ID));
            write_blob(writer, blob)
        },
        Container::Queue(ref queue) => {
            try!(write_u8(writer, QUEUE_TYPE_ID));
            try!(write_array_len(writer, queue.len() as u32));
            for blob in queue.data.iter() {
                try!(write_blob(writer, blob));
            }
            Ok(())
        },
        Container::Set(ref set) => {
            try!(write_u8(writer, SET_TYPE_ID));
            try!(write_array_len(writer, set.data.len() as u32));
            for blob in set.data.iter() {
                try!(write_blob(writer, blob));
            }
            Ok(())
        }
    }
}

fn write_blob<W: Write>(writer: &mut W, blob: &Blob) -> Result<()> {
    write_bin(writer, &blob.data).map_err(|e| e.into())
}

fn insert_edge(stack: &mut Vec<&Arc<RefCell<Node>>>, node: Arc<RefCell<Node>>) {
    let mut parent = stack.last_mut().unwrap().borrow_mut();
    let mut edges = parent.content.get_dir_edges_mut().unwrap();
    let label = node.borrow().path.split("/").last().unwrap().to_string();
    let edge = Edge {
        label: label,
        node: node
    };
    edges.push(edge);
}

#[cfg(test)]
mod tests {

    use std::fs;
    use quickcheck::{Arbitrary, Gen};
    use tree::Tree;
    use node::NodeType;
    use rand::distributions::range::Range;
    use rand::distributions::IndependentSample;

    #[derive(Debug, Clone)]
    struct Path(String);

    impl Arbitrary for Path {
        fn arbitrary<G: Gen>(g: &mut G) -> Path {
            let range = Range::new(1u8, 11u8);
            let depth = range.ind_sample(g);
            let labels = ['a', 'b', 'c', 'd', 'e'];
            let mut path = String::with_capacity((depth*2 - 1) as usize);
            path = (0..depth).fold(path, |mut acc, _| {
                acc.push('/');
                acc.push(*g.choose(&labels).unwrap());
                acc
            });
            Path(path)
        }
    }

    impl Arbitrary for NodeType {
        fn arbitrary<G: Gen>(g: &mut G) -> NodeType {
            g.choose(&[NodeType::Directory, NodeType::Queue, NodeType::Set, NodeType::Blob])
             .unwrap().clone()
        }
    }

    #[quickcheck]
    /// Create a random tree, take a snapshot, and ensure reading it back in gives the same tree
    fn prop_rountrip(node_specs: Vec<(Path, NodeType)>) -> bool {
        let tree = node_specs.iter().fold(Tree::new(), |acc, &(ref path, ref node_type)| {
            // Ignore failures. We may try to insert a node into a non-directory due to randomness
            // of generation. It doesn't matter for this property.
            match acc.create(&path.0, node_type.clone()) {
                Ok(tree) => tree,
                _ => acc
            }
        });
        let filename = tree.snapshot("/tmp").unwrap();
        let loaded_tree = Tree::load_snapshot(&filename).unwrap();
        fs::remove_file(filename).unwrap();

        if tree.depth != loaded_tree.depth {
            return false;
        }
        tree.iter().zip(loaded_tree.iter()).all(|(node1, node2)| node1 == node2)
    }
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate sdl2_ttf;
use sdl2::render::{Renderer, BlendMode};
use sdl2::pixels::Color;
use std::env;
use font::*;
use display::{self, Display};
use subtitles::Subtitles;
use utils::*;

pub struct Displayer<'a> {
    fonts: FontList,
    renderer: Renderer<'a>,
    #[allow(dead_code)]
    ttf_context: sdl2_ttf::Sdl2TtfContext,
    subtitles: Option<Subtitles>,
}

impl<'a> Displayer<'a> {
    pub fn new(mut renderer: Renderer<'a>,
               subtitles: Option<Subtitles>)
               -> Result<Displayer<'a>, ()> {
        renderer.set_blend_mode(BlendMode::Blend);
        let ttf_context = sdl2_ttf::init().unwrap();
        let mut font_path = env::current_exe().unwrap().parent().unwrap().to_path_buf();
        font_path.push("res/DejaVuSansMono-Bold.ttf");
        let font_path = font_path.as_path();
        let font_list = FontList::new(font_path, &ttf_context).unwrap();
        let displayer = Displayer {
            fonts: font_list,
            ttf_context: ttf_context,
            renderer: renderer,
            subtitles: subtitles,
        };
        Ok(displayer)
    }

    pub fn render_subtitles(&mut self, frame_number: u32) {
        const FADE_INTERVAL: u32 = 10;
        const TRANSITION_INTERVAL: u32 = FADE_INTERVAL + 5;
        let sub_colors = (Color::RGB(0, 0xFF, 0xFF),
                          Color::RGB(0xFF, 0, 0),
                          Color::RGB(0xFF, 0xFF, 0));
        let mut text2d_vec = vec![];
        match self.subtitles {
            Some(ref subtitles) => {
                let subtitles = subtitles.get_subtitles();
                let iter = subtitles.iter().enumerate().filter(|&(_, line)| {
                    match (line.first(), line.last()) {
                        (Some(&(_, (frame_begin, _))), Some(&(_, (_, frame_end)))) => {
                            frame_begin.saturating_sub(TRANSITION_INTERVAL) < frame_number &&
                            frame_end.saturating_add(TRANSITION_INTERVAL) > frame_number
                        }
                        _ => false,
                    }
                });
                for (i, line) in iter {
                    let text_pos_y = match i % 2 {
                        0 => display::PosY::FromTopPercent(0.02),
                        1 => display::PosY::FromTopPercent(0.12),
                        _ => unreachable!(),
                    };
                    let alpha: f32 = match (line.first(), line.last()) {
                        (Some(&(_, (frame_begin, _))), Some(&(_, (_, frame_end)))) => {
                            let finished_fade_interval = TRANSITION_INTERVAL - FADE_INTERVAL;
                            let begin_first_fade_frame =
                                frame_begin.saturating_sub(TRANSITION_INTERVAL);
                            let end_first_fade_frame =
                                frame_begin.saturating_sub(finished_fade_interval);
                            let begin_second_fade_frame =
                                frame_end.saturating_add(finished_fade_interval);
                            let end_second_fade_frame =
                                frame_end.saturating_add(TRANSITION_INTERVAL);
                            assert!(end_second_fade_frame - begin_second_fade_frame ==
                                    FADE_INTERVAL);
                            if (end_first_fade_frame < frame_number &&
                                begin_second_fade_frame > frame_number) {
                                1.0
                            } else if begin_first_fade_frame <= frame_number &&
                               end_first_fade_frame >= frame_number {
                                (frame_number - begin_first_fade_frame) as f32 /
                                (end_first_fade_frame - begin_first_fade_frame) as f32
                            } else if begin_second_fade_frame <= frame_number &&
                               end_second_fade_frame >= frame_number {
                                1.0 -
                                ((frame_number - begin_second_fade_frame) as f32 /
                                 (end_second_fade_frame - begin_second_fade_frame) as f32)
                            } else {
                                0.0
                            }
                        }
                        _ => 0.0,
                    };
                    let text_elts = line.iter().fold(vec![], |mut accu_vec,
                                                      &(ref syllabus, (frame_begin, frame_end))| {
                        if (frame_number < frame_begin) {
                            // not yet

                            let text_2d = display::TextElement {
                                text: syllabus.clone(),
                                color: fade_color(sub_colors.2, alpha),
                                outline: Some(Color::RGB(0, 0, 0)),
                                shadow: None,
                            };
                            accu_vec.push(text_2d);

                        } else if (frame_begin <= frame_number) && (frame_number <= frame_end) {
                            let percent = (frame_number - frame_begin) as f32 /
                                          (frame_end - frame_begin) as f32;
                            // lets ease the percent a lil bits
                            let percent = 1.0 - (1.0 - percent).sqrt();
                            // let percent = (percent * consts::PI / 2.0).sin();
                            let transition_color = mix_colors(sub_colors.1, sub_colors.0, percent);
                            let text_2d = display::TextElement {
                                text: syllabus.clone(),
                                color: transition_color,
                                outline: Some(Color::RGB(0, 0, 0)),
                                shadow: None,
                            };
                            accu_vec.push(text_2d);
                        } else {
                            if (accu_vec.is_empty()) {
                                let text_2d = display::TextElement {
                                    text: syllabus.clone(),
                                    color: fade_color(sub_colors.0, alpha),
                                    outline: Some(Color::RGB(0, 0, 0)),
                                    shadow: None,
                                };
                                accu_vec.push(text_2d);
                            } else {
                                let mut text_2d = accu_vec.last_mut().unwrap();
                                text_2d.text.push_str(&syllabus);
                            }
                        }
                        accu_vec
                    });
                    let text_2d = display::Text2D {
                        text: text_elts,
                        size: display::Size::FitPercent(Some(0.95), Some(0.1)),
                        pos: (display::PosX::Centered, text_pos_y),
                        anchor: (0.5, 0.0),
                    };
                    text2d_vec.push(text_2d);
                }
            }
            None => {}
        };
        for text_2d in text2d_vec.into_iter() {
            text_2d.draw(self);
        }
    }

    // width and height must be between 0 and 1
    pub fn sub_screen_dims(&self,
                           width: Option<f32>,
                           height: Option<f32>)
                           -> (Option<u32>, Option<u32>) {
        let dims: (u32, u32) = self.renderer.window().unwrap().size();
        (width.and_then(|width| Some((width * (dims.0 as f32)) as u32)),
         height.and_then(|height| Some((height * (dims.1 as f32)) as u32)))
    }

    pub fn render(&mut self) {
        self.sdl_renderer_mut().window().unwrap().gl_swap_window();
    }

    pub fn sdl_renderer_mut(&mut self) -> &mut Renderer<'a> {
        &mut self.renderer
    }

    pub fn sdl_renderer(&self) -> &Renderer<'a> {
        &self.renderer
    }

    pub fn fonts(&self) -> &FontList {
        &self.fonts
    }
}
<commit_msg>changed assert into a debug assert<commit_after>extern crate sdl2;
extern crate sdl2_ttf;
use sdl2::render::{Renderer, BlendMode};
use sdl2::pixels::Color;
use std::env;
use font::*;
use display::{self, Display};
use subtitles::Subtitles;
use utils::*;

pub struct Displayer<'a> {
    fonts: FontList,
    renderer: Renderer<'a>,
    #[allow(dead_code)]
    ttf_context: sdl2_ttf::Sdl2TtfContext,
    subtitles: Option<Subtitles>,
}

impl<'a> Displayer<'a> {
    pub fn new(mut renderer: Renderer<'a>,
               subtitles: Option<Subtitles>)
               -> Result<Displayer<'a>, ()> {
        renderer.set_blend_mode(BlendMode::Blend);
        let ttf_context = sdl2_ttf::init().unwrap();
        let mut font_path = env::current_exe().unwrap().parent().unwrap().to_path_buf();
        font_path.push("res/DejaVuSansMono-Bold.ttf");
        let font_path = font_path.as_path();
        let font_list = FontList::new(font_path, &ttf_context).unwrap();
        let displayer = Displayer {
            fonts: font_list,
            ttf_context: ttf_context,
            renderer: renderer,
            subtitles: subtitles,
        };
        Ok(displayer)
    }

    pub fn render_subtitles(&mut self, frame_number: u32) {
        const FADE_INTERVAL: u32 = 10;
        const TRANSITION_INTERVAL: u32 = FADE_INTERVAL + 5;
        let sub_colors = (Color::RGB(0, 0xFF, 0xFF),
                          Color::RGB(0xFF, 0, 0),
                          Color::RGB(0xFF, 0xFF, 0));
        let mut text2d_vec = vec![];
        match self.subtitles {
            Some(ref subtitles) => {
                let subtitles = subtitles.get_subtitles();
                let iter = subtitles.iter().enumerate().filter(|&(_, line)| {
                    match (line.first(), line.last()) {
                        (Some(&(_, (frame_begin, _))), Some(&(_, (_, frame_end)))) => {
                            frame_begin.saturating_sub(TRANSITION_INTERVAL) < frame_number &&
                            frame_end.saturating_add(TRANSITION_INTERVAL) > frame_number
                        }
                        _ => false,
                    }
                });
                for (i, line) in iter {
                    let text_pos_y = match i % 2 {
                        0 => display::PosY::FromTopPercent(0.02),
                        1 => display::PosY::FromTopPercent(0.12),
                        _ => unreachable!(),
                    };
                    let alpha: f32 = match (line.first(), line.last()) {
                        (Some(&(_, (frame_begin, _))), Some(&(_, (_, frame_end)))) => {
                            let finished_fade_interval = TRANSITION_INTERVAL - FADE_INTERVAL;
                            let begin_first_fade_frame =
                                frame_begin.saturating_sub(TRANSITION_INTERVAL);
                            let end_first_fade_frame =
                                frame_begin.saturating_sub(finished_fade_interval);
                            let begin_second_fade_frame =
                                frame_end.saturating_add(finished_fade_interval);
                            let end_second_fade_frame =
                                frame_end.saturating_add(TRANSITION_INTERVAL);
                            debug_assert_eq!(end_second_fade_frame - begin_second_fade_frame,
                                             FADE_INTERVAL);
                            if (end_first_fade_frame < frame_number &&
                                begin_second_fade_frame > frame_number) {
                                1.0
                            } else if begin_first_fade_frame <= frame_number &&
                               end_first_fade_frame >= frame_number {
                                (frame_number - begin_first_fade_frame) as f32 /
                                (end_first_fade_frame - begin_first_fade_frame) as f32
                            } else if begin_second_fade_frame <= frame_number &&
                               end_second_fade_frame >= frame_number {
                                1.0 -
                                ((frame_number - begin_second_fade_frame) as f32 /
                                 (end_second_fade_frame - begin_second_fade_frame) as f32)
                            } else {
                                0.0
                            }
                        }
                        _ => 0.0,
                    };
                    let text_elts = line.iter().fold(vec![], |mut accu_vec,
                                                      &(ref syllabus, (frame_begin, frame_end))| {
                        if (frame_number < frame_begin) {
                            // not yet

                            let text_2d = display::TextElement {
                                text: syllabus.clone(),
                                color: fade_color(sub_colors.2, alpha),
                                outline: Some(Color::RGB(0, 0, 0)),
                                shadow: None,
                            };
                            accu_vec.push(text_2d);

                        } else if (frame_begin <= frame_number) && (frame_number <= frame_end) {
                            let percent = (frame_number - frame_begin) as f32 /
                                          (frame_end - frame_begin) as f32;
                            // lets ease the percent a lil bits
                            let percent = 1.0 - (1.0 - percent).sqrt();
                            // let percent = (percent * consts::PI / 2.0).sin();
                            let transition_color = mix_colors(sub_colors.1, sub_colors.0, percent);
                            let text_2d = display::TextElement {
                                text: syllabus.clone(),
                                color: transition_color,
                                outline: Some(Color::RGB(0, 0, 0)),
                                shadow: None,
                            };
                            accu_vec.push(text_2d);
                        } else {
                            if (accu_vec.is_empty()) {
                                let text_2d = display::TextElement {
                                    text: syllabus.clone(),
                                    color: fade_color(sub_colors.0, alpha),
                                    outline: Some(Color::RGB(0, 0, 0)),
                                    shadow: None,
                                };
                                accu_vec.push(text_2d);
                            } else {
                                let mut text_2d = accu_vec.last_mut().unwrap();
                                text_2d.text.push_str(&syllabus);
                            }
                        }
                        accu_vec
                    });
                    let text_2d = display::Text2D {
                        text: text_elts,
                        size: display::Size::FitPercent(Some(0.95), Some(0.1)),
                        pos: (display::PosX::Centered, text_pos_y),
                        anchor: (0.5, 0.0),
                    };
                    text2d_vec.push(text_2d);
                }
            }
            None => {}
        };
        for text_2d in text2d_vec.into_iter() {
            text_2d.draw(self);
        }
    }

    // width and height must be between 0 and 1
    pub fn sub_screen_dims(&self,
                           width: Option<f32>,
                           height: Option<f32>)
                           -> (Option<u32>, Option<u32>) {
        let dims: (u32, u32) = self.renderer.window().unwrap().size();
        (width.and_then(|width| Some((width * (dims.0 as f32)) as u32)),
         height.and_then(|height| Some((height * (dims.1 as f32)) as u32)))
    }

    pub fn render(&mut self) {
        self.sdl_renderer_mut().window().unwrap().gl_swap_window();
    }

    pub fn sdl_renderer_mut(&mut self) -> &mut Renderer<'a> {
        &mut self.renderer
    }

    pub fn sdl_renderer(&self) -> &Renderer<'a> {
        &self.renderer
    }

    pub fn fonts(&self) -> &FontList {
        &self.fonts
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

use std::rc::*;

use super::component::*;
use super::super::tree::*;

struct FunctionComponent;

impl Component for FunctionComponent {
}

impl Drop for FunctionComponent {
    fn drop(&mut self) {
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
impl<F: Fn(&TreeChange) -> TreeChange> ComponentFactory for F {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn create(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = Box::new(self);

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Function that receives a tree and returns a modified tree
///
impl<F: Fn(&TreeRef) -> TreeRef> ComponentFactory for F {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn create(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = Box::new(self);

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::New(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}
<commit_msg>Can make the 'some functions can be treated as component factories' thing work by forcing the user to box the function<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

use std::rc::*;

use super::component::*;
use super::super::tree::*;

struct FunctionComponent;

impl Component for FunctionComponent {
}

impl Drop for FunctionComponent {
    fn drop(&mut self) {
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
impl ComponentFactory for Box<Fn(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn create(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Function that receives a tree and returns a modified tree
///
impl ComponentFactory for Box<Fn(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn create(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson, Object, Array};
use geojson::{FeatureCollection, Feature, Pos};
use {TopoJsonResult, TopoJsonError};

pub struct Topology(BTreeMap<String, FeatureCollection>);
// TODO TopoJSON can have any top-level geometry in an object, it
// doesn't have to be a FeatureCollection (which is spelled
// GeometryCollection in TopoJSON).

fn parse_transform(json: &Json) -> TopoJsonResult<(f64, f64, f64, f64)> {
    let transform = expect_object!(json);
    let translate = expect_array!(expect_property!(transform, "translate", "Missing 'translate' field"));
    let scale = expect_array!(expect_property!(transform, "scale", "Missing 'scale' field"));
    match (translate.len(), scale.len()) {
        (2, 2) => (),
        _ => return Err(TopoJsonError::new("Both 'translate' and 'scale' must be two-element arrays"))
    };
    let t0 = expect_f64!(translate[0]);
    let t1 = expect_f64!(translate[1]);
    let s0 = expect_f64!(scale[0]);
    let s1 = expect_f64!(scale[1]);
    return Ok((t0, t1, s0, s1));
}

struct Space {
    arcs: Vec<Vec<Pos>>,
    transform: Option<(f64, f64, f64, f64)>,
}

impl Space {
    fn from_topology(topo: &Object) -> TopoJsonResult<Space> {
        let transform = match topo.get("transform") {
            Some(transform_json) => Some(try!(parse_transform(transform_json))),
            None => None
        };
        let mut arcs = vec![];
        for arc_json in expect_array!(expect_property!(topo, "arcs", "Missing 'arcs' field")).iter() {
            let mut arc = vec![];
            match transform {
                Some((t0, t1, s0, s1)) => {
                    let mut x = 0;
                    let mut y = 0;
                    for point_json in expect_array!(arc_json).iter() {
                        let mut p = expect_array!(point_json).clone();
                        if p.len() < 2 {
                            return Err(TopoJsonError::new("Point must have at least two elements"));
                        }
                        x += expect_i64!(p[0]);
                        y += expect_i64!(p[1]);
                        p[0] = Json::F64((x as f64) * s0 + t0);
                        p[1] = Json::F64((y as f64) * s1 + t1);
                        arc.push(Pos::from_json(&p));
                    }
                },
                None => {
                    for point_json in expect_array!(arc_json).iter() {
                        arc.push(Pos::from_json(expect_array!(point_json)));
                    }
                }
            };
            arcs.push(arc);
        }
        return Ok(Space{arcs: arcs, transform: transform});
    }

    pub fn point(&self, p: &Vec<Json>) -> TopoJsonResult<Vec<Json>> {
        if p.len() < 2 {
            return Err(TopoJsonError::new("Point must have at least two elements"));
        }
        let mut rv = p.clone();
        println!("{:?}", self.transform);
        match self.transform {
            Some((t0, t1, s0, s1)) => {
                rv[0] = Json::F64(expect_f64!(rv[0]) * s0 + t0);
                rv[1] = Json::F64(expect_f64!(rv[1]) * s1 + t1);
            },
            None => ()
        };
        return Ok(rv);
    }

    pub fn arc(&self, arc_index: isize) -> TopoJsonResult<Json> {
        let (inverse, offset) = if arc_index > -1 {
            (false, arc_index as usize)
        } else {
            (true, -1 - arc_index as usize)
        };
        if offset >= self.arcs.len() {
            println!("offset = {}", offset);
            return Err(TopoJsonError::new("Arc index too large"));
        }
        let arc = &self.arcs[offset];
        let arcs_json = if inverse {
            arc.iter().rev().map(|pos| pos.to_json()).collect()
        } else {
            arc.iter().map(|pos| pos.to_json()).collect()
        };
        return Ok(Json::Array(arcs_json));
    }
}

fn transform_coordinates(topo_coord: &Array, space: &Space) -> TopoJsonResult<Json> {
    return Ok(Json::Array(try!(space.point(topo_coord))));
}

fn transform_arcs(arcs: &Array, space: &Space, depth: usize) -> TopoJsonResult<Json> {
    let mut rv = vec![];
    for arc in arcs.iter() {
        println!("{:?}", arc);
        if depth > 1 {
            rv.push(try!(transform_arcs(expect_array!(arc), space, depth - 1)));
        }
        else {
            rv.push(try!(space.arc(expect_i64!(arc) as isize)));
        }
    }
    if depth == 1 {
        rv = match rv.pop() {
            Some(Json::Array(v)) => v,
            _ => return Err(TopoJsonError::new("Empty arc list"))
        }
    }
    return Ok(Json::Array(rv));
}

fn parse_geometry(object: &Object, space: &Space) -> TopoJsonResult<Feature> {
    let geometry_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
    let properties = expect_property!(object, "properties", "Missing 'properties' field");

    let mut geojson_geometry = BTreeMap::new();
    geojson_geometry.insert("type".to_string(), Json::String(geometry_type.to_string()));
    let coordinates = match geometry_type {
        "Point" => {
            let topo_coord = expect_property!(object, "coordinates", "Missing 'coordinates' property");
            try!(transform_coordinates(expect_array!(topo_coord), space))
        },
        "LineString" | "Polygon" => {
            let arcs = expect_property!(object, "arcs", "Missing 'arcs' property");
            println!("arcs! {:?}", arcs);
            let depth = match geometry_type {
                "LineString" => 1,
                "Polygon" => 2,
                _ => unreachable!()
            };
            try!(transform_arcs(expect_array!(arcs), space, depth))
        },
        _ => return Err(TopoJsonError::new("Unknown geometry type"))
    };
    geojson_geometry.insert("coordinates".to_string(), coordinates);

    let mut geojson_feature = BTreeMap::new();
    geojson_feature.insert("type".to_string(), Json::String("Feature".to_string()));
    geojson_feature.insert("geometry".to_string(), Json::Object(geojson_geometry));
    geojson_feature.insert("properties".to_string(), properties.clone());
    println!("feature: {}", Json::Object(geojson_feature.clone()));
    return Ok(Feature::from_json(&geojson_feature));
}

fn parse_geometry_collection(object: &Object, space: &Space) -> TopoJsonResult<FeatureCollection> {
    let geometries = expect_array!(expect_property!(object, "geometries", "Missing 'geometries' field"));
    let mut features = vec![];
    for geometry_json in geometries.iter() {
        features.push(try!(parse_geometry(expect_object!(geometry_json), space)));
    }
    return Ok(FeatureCollection{features: features});
}

impl Topology {
    pub fn from_json(json: &Json) -> TopoJsonResult<Topology> {
        let topo = expect_object!(json);
        match expect_property!(topo, "type", "Missing 'type' field").as_string() {
            Some("Topology") => (),
            _ => return Err(TopoJsonError::new("'type' should be 'Topology'"))
        };
        let space = try!(Space::from_topology(topo));
        let objects = expect_object!(expect_property!(topo, "objects", "Missing 'objects' field"));
        let mut map = BTreeMap::new();
        for (name, value) in objects.iter() {
            let object = expect_object!(value);
            let object_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
            let geojson = match object_type {
                "GeometryCollection" => try!(parse_geometry_collection(object, &space)),
                _ => return Err(TopoJsonError::new("'type' should be 'GeometryCollection'"))
            };
            map.insert(name.to_string(), geojson);
        }
        return Ok(Topology(map));
    }

    pub fn object(&self, name: &str) -> Option<&FeatureCollection> {
        let &Topology(ref hash) = self;
        return hash.get(name);
    }
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::{Json, ToJson};
    use super::Topology;

    #[test]
    fn test_invalid_topo() {
        fn expect_err(json: &str) {
            match Topology::from_json(&Json::from_str(json).unwrap()) {
                Err(_) => (),
                _ => panic!()
            }
        }
        expect_err("{}");
        expect_err(r#"{"type": "foo"}"#);
        expect_err(r#"{"type": "Topology"}"#);
    }

    #[test]
    fn test_parse_simple_topology_from_spec() {
        let topo_str = r#"{"type":"Topology","objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[102,0.5]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[-2]]}]}},"arcs":[[[102,0],[103,1],[104,0],[105,1]],[[100,0],[101,0],[101,1],[100,1],[100,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0,0.5],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0,0.0],[103.0,1.0],[104.0,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }

    #[test]
    fn test_parse_quantized_topology_from_spec() {
        let topo_str = r#"{"objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[4000,5000]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[1]]}]}},"type":"Topology","transform":{"translate":[100,0],"scale":[0.0005000500050005,0.00010001000100010001]},"arcs":[[[4000,0],[1999,9999],[2000,-9999],[2000,9999]],[[0,0],[0,9999],[2000,0],[0,-9999],[-2000,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0002,0.50005],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0002,0.0],[102.9998,1.0],[103.9999,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0001,1.0],[101.0001,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }
}
<commit_msg>don't use Pos<commit_after>use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson, Object, Array};
use geojson::{FeatureCollection, Feature};
use {TopoJsonResult, TopoJsonError};

pub struct Topology(BTreeMap<String, FeatureCollection>);
// TODO TopoJSON can have any top-level geometry in an object, it
// doesn't have to be a FeatureCollection (which is spelled
// GeometryCollection in TopoJSON).

fn parse_transform(json: &Json) -> TopoJsonResult<(f64, f64, f64, f64)> {
    let transform = expect_object!(json);
    let translate = expect_array!(expect_property!(transform, "translate", "Missing 'translate' field"));
    let scale = expect_array!(expect_property!(transform, "scale", "Missing 'scale' field"));
    match (translate.len(), scale.len()) {
        (2, 2) => (),
        _ => return Err(TopoJsonError::new("Both 'translate' and 'scale' must be two-element arrays"))
    };
    let t0 = expect_f64!(translate[0]);
    let t1 = expect_f64!(translate[1]);
    let s0 = expect_f64!(scale[0]);
    let s1 = expect_f64!(scale[1]);
    return Ok((t0, t1, s0, s1));
}

struct Space {
    arcs: Vec<Vec<Json>>,
    transform: Option<(f64, f64, f64, f64)>,
}

impl Space {
    fn from_topology(topo: &Object) -> TopoJsonResult<Space> {
        let transform = match topo.get("transform") {
            Some(transform_json) => Some(try!(parse_transform(transform_json))),
            None => None
        };
        let mut arcs = vec![];
        for arc_json in expect_array!(expect_property!(topo, "arcs", "Missing 'arcs' field")).iter() {
            let arc = match transform {
                Some((t0, t1, s0, s1)) => {
                    let mut arc = vec![];
                    let mut x = 0;
                    let mut y = 0;
                    for point_json in expect_array!(arc_json).iter() {
                        let mut p = expect_array!(point_json).clone();
                        if p.len() < 2 {
                            return Err(TopoJsonError::new("Point must have at least two elements"));
                        }
                        x += expect_i64!(p[0]);
                        y += expect_i64!(p[1]);
                        p[0] = Json::F64((x as f64) * s0 + t0);
                        p[1] = Json::F64((y as f64) * s1 + t1);
                        arc.push(Json::Array(p));
                    }
                    arc
                },
                None => expect_array!(arc_json).clone()
            };
            arcs.push(arc);
        }
        return Ok(Space{arcs: arcs, transform: transform});
    }

    pub fn point(&self, p: &Vec<Json>) -> TopoJsonResult<Vec<Json>> {
        if p.len() < 2 {
            return Err(TopoJsonError::new("Point must have at least two elements"));
        }
        let mut rv = p.clone();
        println!("{:?}", self.transform);
        match self.transform {
            Some((t0, t1, s0, s1)) => {
                rv[0] = Json::F64(expect_f64!(rv[0]) * s0 + t0);
                rv[1] = Json::F64(expect_f64!(rv[1]) * s1 + t1);
            },
            None => ()
        };
        return Ok(rv);
    }

    pub fn arc(&self, arc_index: isize) -> TopoJsonResult<Json> {
        let (inverse, offset) = if arc_index > -1 {
            (false, arc_index as usize)
        } else {
            (true, -1 - arc_index as usize)
        };
        if offset >= self.arcs.len() {
            println!("offset = {}", offset);
            return Err(TopoJsonError::new("Arc index too large"));
        }
        let arc = &self.arcs[offset];
        let arcs_json = if inverse {
            arc.iter().rev().map(|p| p.to_json()).collect()
        } else {
            arc.iter().map(|p| p.to_json()).collect()
        };
        return Ok(Json::Array(arcs_json));
    }
}

fn transform_coordinates(topo_coord: &Array, space: &Space) -> TopoJsonResult<Json> {
    return Ok(Json::Array(try!(space.point(topo_coord))));
}

fn transform_arcs(arcs: &Array, space: &Space, depth: usize) -> TopoJsonResult<Json> {
    let mut rv = vec![];
    for arc in arcs.iter() {
        println!("{:?}", arc);
        if depth > 1 {
            rv.push(try!(transform_arcs(expect_array!(arc), space, depth - 1)));
        }
        else {
            rv.push(try!(space.arc(expect_i64!(arc) as isize)));
        }
    }
    if depth == 1 {
        rv = match rv.pop() {
            Some(Json::Array(v)) => v,
            _ => return Err(TopoJsonError::new("Empty arc list"))
        }
    }
    return Ok(Json::Array(rv));
}

fn parse_geometry(object: &Object, space: &Space) -> TopoJsonResult<Feature> {
    let geometry_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
    let properties = expect_property!(object, "properties", "Missing 'properties' field");

    let mut geojson_geometry = BTreeMap::new();
    geojson_geometry.insert("type".to_string(), Json::String(geometry_type.to_string()));
    let coordinates = match geometry_type {
        "Point" => {
            let topo_coord = expect_property!(object, "coordinates", "Missing 'coordinates' property");
            try!(transform_coordinates(expect_array!(topo_coord), space))
        },
        "LineString" | "Polygon" => {
            let arcs = expect_property!(object, "arcs", "Missing 'arcs' property");
            println!("arcs! {:?}", arcs);
            let depth = match geometry_type {
                "LineString" => 1,
                "Polygon" => 2,
                _ => unreachable!()
            };
            try!(transform_arcs(expect_array!(arcs), space, depth))
        },
        _ => return Err(TopoJsonError::new("Unknown geometry type"))
    };
    geojson_geometry.insert("coordinates".to_string(), coordinates);

    let mut geojson_feature = BTreeMap::new();
    geojson_feature.insert("type".to_string(), Json::String("Feature".to_string()));
    geojson_feature.insert("geometry".to_string(), Json::Object(geojson_geometry));
    geojson_feature.insert("properties".to_string(), properties.clone());
    println!("feature: {}", Json::Object(geojson_feature.clone()));
    return Ok(Feature::from_json(&geojson_feature));
}

fn parse_geometry_collection(object: &Object, space: &Space) -> TopoJsonResult<FeatureCollection> {
    let geometries = expect_array!(expect_property!(object, "geometries", "Missing 'geometries' field"));
    let mut features = vec![];
    for geometry_json in geometries.iter() {
        features.push(try!(parse_geometry(expect_object!(geometry_json), space)));
    }
    return Ok(FeatureCollection{features: features});
}

impl Topology {
    pub fn from_json(json: &Json) -> TopoJsonResult<Topology> {
        let topo = expect_object!(json);
        match expect_property!(topo, "type", "Missing 'type' field").as_string() {
            Some("Topology") => (),
            _ => return Err(TopoJsonError::new("'type' should be 'Topology'"))
        };
        let space = try!(Space::from_topology(topo));
        let objects = expect_object!(expect_property!(topo, "objects", "Missing 'objects' field"));
        let mut map = BTreeMap::new();
        for (name, value) in objects.iter() {
            let object = expect_object!(value);
            let object_type = expect_string!(expect_property!(object, "type", "Missing 'type' field"));
            let geojson = match object_type {
                "GeometryCollection" => try!(parse_geometry_collection(object, &space)),
                _ => return Err(TopoJsonError::new("'type' should be 'GeometryCollection'"))
            };
            map.insert(name.to_string(), geojson);
        }
        return Ok(Topology(map));
    }

    pub fn object(&self, name: &str) -> Option<&FeatureCollection> {
        let &Topology(ref hash) = self;
        return hash.get(name);
    }
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::{Json, ToJson};
    use super::Topology;

    #[test]
    fn test_invalid_topo() {
        fn expect_err(json: &str) {
            match Topology::from_json(&Json::from_str(json).unwrap()) {
                Err(_) => (),
                _ => panic!()
            }
        }
        expect_err("{}");
        expect_err(r#"{"type": "foo"}"#);
        expect_err(r#"{"type": "Topology"}"#);
    }

    #[test]
    fn test_parse_simple_topology_from_spec() {
        let topo_str = r#"{"type":"Topology","objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[102,0.5]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[-2]]}]}},"arcs":[[[102,0],[103,1],[104,0],[105,1]],[[100,0],[101,0],[101,1],[100,1],[100,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0,0.5],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0,0.0],[103.0,1.0],[104.0,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }

    #[test]
    fn test_parse_quantized_topology_from_spec() {
        let topo_str = r#"{"objects":{"example":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"prop0":"value0"},"coordinates":[4000,5000]},{"type":"LineString","properties":{"prop0":"value0","prop1":0},"arcs":[0]},{"type":"Polygon","properties":{"prop0":"value0","prop1":{"this":"that"}},"arcs":[[1]]}]}},"type":"Topology","transform":{"translate":[100,0],"scale":[0.0005000500050005,0.00010001000100010001]},"arcs":[[[4000,0],[1999,9999],[2000,-9999],[2000,9999]],[[0,0],[0,9999],[2000,0],[0,-9999],[-2000,0]]]}"#;
        let topo_json = Json::from_str(topo_str).unwrap();
        let topology = match Topology::from_json(&topo_json) {
            Ok(t) => t,
            Err(e) => panic!(e.desc)
        };
        let example = topology.object("example").unwrap();
        let geojson = format!("{}", example.to_json());
        assert_eq!(geojson, r#"{"features":[{"geometry":{"coordinates":[102.0002,0.50005],"type":"Point"},"properties":{"prop0":"value0"},"type":"Feature"},{"geometry":{"coordinates":[[102.0002,0.0],[102.9998,1.0],[103.9999,0.0],[105.0,1.0]],"type":"LineString"},"properties":{"prop0":"value0","prop1":0},"type":"Feature"},{"geometry":{"coordinates":[[[100.0,0.0],[100.0,1.0],[101.0001,1.0],[101.0001,0.0],[100.0,0.0]]],"type":"Polygon"},"properties":{"prop0":"value0","prop1":{"this":"that"}},"type":"Feature"}],"type":"FeatureCollection"}"#);
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
#[macro_use]
extern crate pest;

#[cfg(not(test))]
extern crate pest;

extern crate maplit;

pub mod parser;
pub mod ast;
pub mod validator;

use std::fmt::Display;

pub fn unwrap_or_report<T, E>(res: Result<T, E>) -> T where E: IntoIterator, E::Item: Display {
    res.unwrap_or_else(|e| panic!(e.into_iter()
              .map(|error| { format!("{}", error) })
              .collect::<Vec<_>>()
              .join("\n\n")))
}
<commit_msg>Qualify grammar errors<commit_after>#[cfg(test)]
#[macro_use]
extern crate pest;

#[cfg(not(test))]
extern crate pest;

extern crate maplit;

pub mod parser;
pub mod ast;
pub mod validator;

use std::fmt::Display;

pub fn unwrap_or_report<T, E>(res: Result<T, E>) -> T where E: IntoIterator, E::Item: Display {
    res.unwrap_or_else(|e| panic!("grammar error\n\n".to_owned() + &e.into_iter()
              .map(|error| { format!("{}", error) })
              .collect::<Vec<_>>()
              .join("\n\n")))
}
<|endoftext|>


<commit_before>use cairo;
use cairo::{MatrixTrait, PatternTrait};
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

#[derive(Clone, Default)]
pub struct Pattern {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: Option<RsvgWeakNode>,
}

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Resolve for Pattern {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &Pattern) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.is_some() {
                self.node = fallback.node.clone();
            } else {
                self.node = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl Pattern {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = self.node {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

pub struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl Default for NodePattern {
    fn default() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::default()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        let mut p = self.pattern.borrow_mut();

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => p.units = Some(attr.parse(value)?),

                local_name!("patternContentUnits") => p.content_units = Some(attr.parse(value)?),

                local_name!("viewBox") => p.vbox = Some(Some(attr.parse(value)?)),

                local_name!("preserveAspectRatio") => {
                    p.preserve_aspect_ratio = Some(attr.parse(value)?)
                }

                local_name!("patternTransform") => p.affine = Some(attr.parse(value)?),

                local_name!("xlink:href") => {
                    p.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }

                local_name!("x") => p.x = Some(attr.parse(value)?),

                local_name!("y") => p.y = Some(attr.parse(value)?),

                local_name!("width") => {
                    p.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }

                local_name!("height") => {
                    p.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl PaintSource for NodePattern {
    type Source = Pattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        _bbox: &BoundingBox,
    ) -> Result<Option<Self::Source>, RenderingError> {
        {
            // Initialize pattern.node and release the mutable borrow
            let mut p = self.pattern.borrow_mut();
            p.node = Some(node.downgrade());
        }

        let mut result = node
            .borrow()
            .get_impl::<NodePattern>()
            .pattern
            .borrow()
            .clone();
        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(acquired) = draw_ctx
                .acquired_nodes()
                .get_node_of_type(result.fallback.as_ref(), NodeType::Pattern)
            {
                let a_node = acquired.get();

                if stack.contains(a_node) {
                    rsvg_log!("circular reference in pattern {}", node);
                    return Err(RenderingError::CircularReference);
                }

                let node_data = a_node.borrow();
                let fallback = node_data.get_impl::<NodePattern>().pattern.borrow();
                result.resolve_from_fallback(&fallback);

                stack.push(a_node);
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(Some(result))
    }

    fn set_pattern_on_draw_context(
        &self,
        pattern: &Self::Source,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(pattern.is_resolved());

        if pattern.node.is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = pattern.units.unwrap();
        let content_units = pattern.content_units.unwrap();
        let pattern_affine = pattern.affine.unwrap();
        let vbox = pattern.vbox.unwrap();
        let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = pattern.x.unwrap().normalize(values, &params);
            let pattern_y = pattern.y.unwrap().normalize(values, &params);
            let pattern_width = pattern.width.unwrap().normalize(values, &params);
            let pattern_height = pattern.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = pattern.node.as_ref().unwrap().upgrade().unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&cairo::Pattern::SurfacePattern(surface_pattern));

        res.and_then(|_| Ok(true))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = Pattern::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<commit_msg>pattern: follow the same naming convention of gradient`<commit_after>use cairo;
use cairo::{MatrixTrait, PatternTrait};
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

#[derive(Clone, Default)]
pub struct PatternData {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: Option<RsvgWeakNode>,
}

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Resolve for PatternData {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &PatternData) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.is_some() {
                self.node = fallback.node.clone();
            } else {
                self.node = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl PatternData {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = self.node {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

pub struct NodePattern {
    pattern: RefCell<PatternData>,
}

impl Default for NodePattern {
    fn default() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(PatternData::default()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        let mut p = self.pattern.borrow_mut();

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => p.units = Some(attr.parse(value)?),

                local_name!("patternContentUnits") => p.content_units = Some(attr.parse(value)?),

                local_name!("viewBox") => p.vbox = Some(Some(attr.parse(value)?)),

                local_name!("preserveAspectRatio") => {
                    p.preserve_aspect_ratio = Some(attr.parse(value)?)
                }

                local_name!("patternTransform") => p.affine = Some(attr.parse(value)?),

                local_name!("xlink:href") => {
                    p.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }

                local_name!("x") => p.x = Some(attr.parse(value)?),

                local_name!("y") => p.y = Some(attr.parse(value)?),

                local_name!("width") => {
                    p.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }

                local_name!("height") => {
                    p.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl PaintSource for NodePattern {
    type Source = PatternData;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        _bbox: &BoundingBox,
    ) -> Result<Option<Self::Source>, RenderingError> {
        {
            // Initialize pattern.node and release the mutable borrow
            let mut p = self.pattern.borrow_mut();
            p.node = Some(node.downgrade());
        }

        let mut result = node
            .borrow()
            .get_impl::<NodePattern>()
            .pattern
            .borrow()
            .clone();
        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(acquired) = draw_ctx
                .acquired_nodes()
                .get_node_of_type(result.fallback.as_ref(), NodeType::Pattern)
            {
                let a_node = acquired.get();

                if stack.contains(a_node) {
                    rsvg_log!("circular reference in pattern {}", node);
                    return Err(RenderingError::CircularReference);
                }

                let node_data = a_node.borrow();
                let fallback = node_data.get_impl::<NodePattern>().pattern.borrow();
                result.resolve_from_fallback(&fallback);

                stack.push(a_node);
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(Some(result))
    }

    fn set_pattern_on_draw_context(
        &self,
        pattern: &Self::Source,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(pattern.is_resolved());

        if pattern.node.is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = pattern.units.unwrap();
        let content_units = pattern.content_units.unwrap();
        let pattern_affine = pattern.affine.unwrap();
        let vbox = pattern.vbox.unwrap();
        let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = pattern.x.unwrap().normalize(values, &params);
            let pattern_y = pattern.y.unwrap().normalize(values, &params);
            let pattern_width = pattern.width.unwrap().normalize(values, &params);
            let pattern_height = pattern.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = pattern.node.as_ref().unwrap().upgrade().unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&cairo::Pattern::SurfacePattern(surface_pattern));

        res.and_then(|_| Ok(true))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = PatternData::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<|endoftext|>