<commit_before>use git2_commit;
use std::path::Path;
use semver::Version;
use std::error::Error;
use git2::{self, Repository, Commit, Config};
use commit_analyzer::{self, CommitType};

struct Author {
    pub name: String,
    pub email: String
}

fn range_to_head(commit: &str) -> String {
    format!("{}..HEAD", commit)
}

fn format_commit(commit: Commit) -> String {
    format!("{}\n{}", commit.id(), commit.message().unwrap_or(""))
}

fn add<P: AsRef<Path>>(repo: &str, files: &[P]) -> Result<(), git2::Error> {
    let repo = try!(Repository::open(repo));
    let mut index = try!(repo.index());

    for path in files {
        let _ = try!(index.add_path(path.as_ref()));
    }

    index.write()
}

fn get_signature() -> Result<Author, git2::Error> {
    let config = try!(Config::open_default());
    let author = try!(config.get_string("user.name"));
    let email = try!(config.get_string("user.email"));
    Ok(Author {
        name: author.to_string(),
        email: email.to_string()
    })
}

pub fn latest_tag(path: &str) -> Option<Version> {
    let repo = match Repository::open(path) {
        Ok(repo) => repo,
        Err(_) => return None
    };

    let tags = match repo.tag_names(None) {
        Ok(tags) => tags,
        Err(_) => return None
    };

    tags.iter()
        .map(|tag| tag.unwrap())
        .filter_map(|tag| Version::parse(&tag[1..]).ok())
        .max()
}

pub fn version_bump_since_latest(path: &str) -> CommitType {
    match latest_tag(path) {
        Some(t) => {
            let tag = format!("v{}", t.to_string());
            version_bump_since_tag(path, &tag)
        },
        None => CommitType::Major
    }
}

pub fn version_bump_since_tag(path: &str, tag: &str) -> CommitType {
    let tag = range_to_head(tag);

    let repo = Repository::open(path).expect("Open repository failed");

    let mut walker = repo.revwalk().expect("Creating a revwalk failed");
    walker.push_range(&tag).expect("Adding a range failed");

    let tag = walker.map(|c| repo.find_commit(c).expect("No commit found"))
        .map(|c| format_commit(c))
        .map(|c| commit_analyzer::analyze_single(&c).expect("Analyzing commit failed"))
        .max().unwrap_or(CommitType::Unknown);

    tag
}

pub fn generate_commit_message(new_version: &str) -> String {
    format!("Bump version to {}", new_version).into()
}

pub fn commit_files(repository_path: &str, new_version: &str) -> Result<(), String> {
    let files = vec!["Cargo.toml", "Changelog.md"];
    match add(&repository_path, &files[..]) {
        Ok(_) => {},
        Err(err) => return Err(err.description().into())
    }
    let author = match get_signature() {
        Ok(author) => author,
        Err(err) => return Err(err.description().into())
    };

    match git2_commit::commit(repository_path, &author.name, &author.email, &generate_commit_message(new_version)) {
        Ok(_) => Ok(()),
        Err(err) => Err(err.description().into())
    }
}

pub fn tag(repository_path: &str, tag_name: &str, tag_message: &str) -> Result<(), String> {
    let author = match get_signature() {
        Ok(author) => author,
        Err(err) => return Err(err.description().into())
    };

    git2_commit::tag(repository_path, &author.name, &author.email, &tag_name, &tag_message)
        .map_err(|err| err.description().into())
}
<commit_msg>refactor: move commit function into git module<commit_after>use git2_commit;
use std::path::Path;
use semver::Version;
use std::error::Error;
use git2::{self, Repository, Commit, Config, Signature};
use commit_analyzer::{self, CommitType};

struct Author {
    pub name: String,
    pub email: String
}

fn range_to_head(commit: &str) -> String {
    format!("{}..HEAD", commit)
}

fn format_commit(commit: Commit) -> String {
    format!("{}\n{}", commit.id(), commit.message().unwrap_or(""))
}

fn add<P: AsRef<Path>>(repo: &str, files: &[P]) -> Result<(), git2::Error> {
    let repo = try!(Repository::open(repo));
    let mut index = try!(repo.index());

    for path in files {
        let _ = try!(index.add_path(path.as_ref()));
    }

    index.write()
}

fn get_signature() -> Result<Author, git2::Error> {
    let config = try!(Config::open_default());
    let author = try!(config.get_string("user.name"));
    let email = try!(config.get_string("user.email"));
    Ok(Author {
        name: author.to_string(),
        email: email.to_string()
    })
}

fn commit(repo: &str, name: &str, email: &str, message: &str) -> Result<(), git2::Error> {
    let signature = try!(Signature::now(name, email));
    let update_ref = Some("HEAD");

    let repo = try!(Repository::open(repo));

    let oid = try!(repo.refname_to_id("HEAD"));
    let parent_commit = try!(repo.find_commit(oid));
    let parents = vec![&parent_commit];

    let mut index = try!(repo.index());
    let tree_oid = try!(index.write_tree());
    let tree = try!(repo.find_tree(tree_oid));

    repo
        .commit(update_ref, &signature, &signature, message, &tree, &parents)
        .map(|_| ())
}

pub fn latest_tag(path: &str) -> Option<Version> {
    let repo = match Repository::open(path) {
        Ok(repo) => repo,
        Err(_) => return None
    };

    let tags = match repo.tag_names(None) {
        Ok(tags) => tags,
        Err(_) => return None
    };

    tags.iter()
        .map(|tag| tag.unwrap())
        .filter_map(|tag| Version::parse(&tag[1..]).ok())
        .max()
}

pub fn version_bump_since_latest(path: &str) -> CommitType {
    match latest_tag(path) {
        Some(t) => {
            let tag = format!("v{}", t.to_string());
            version_bump_since_tag(path, &tag)
        },
        None => CommitType::Major
    }
}

pub fn version_bump_since_tag(path: &str, tag: &str) -> CommitType {
    let tag = range_to_head(tag);

    let repo = Repository::open(path).expect("Open repository failed");

    let mut walker = repo.revwalk().expect("Creating a revwalk failed");
    walker.push_range(&tag).expect("Adding a range failed");

    let tag = walker.map(|c| repo.find_commit(c).expect("No commit found"))
        .map(|c| format_commit(c))
        .map(|c| commit_analyzer::analyze_single(&c).expect("Analyzing commit failed"))
        .max().unwrap_or(CommitType::Unknown);

    tag
}

pub fn generate_commit_message(new_version: &str) -> String {
    format!("Bump version to {}", new_version).into()
}

pub fn commit_files(repository_path: &str, new_version: &str) -> Result<(), String> {
    let files = vec!["Cargo.toml", "Changelog.md"];
    match add(&repository_path, &files[..]) {
        Ok(_) => {},
        Err(err) => return Err(err.description().into())
    }
    let author = match get_signature() {
        Ok(author) => author,
        Err(err) => return Err(err.description().into())
    };

    match commit(repository_path, &author.name, &author.email, &generate_commit_message(new_version)) {
        Ok(_) => Ok(()),
        Err(err) => Err(err.description().into())
    }
}

pub fn tag(repository_path: &str, tag_name: &str, tag_message: &str) -> Result<(), String> {
    let author = match get_signature() {
        Ok(author) => author,
        Err(err) => return Err(err.description().into())
    };

    git2_commit::tag(repository_path, &author.name, &author.email, &tag_name, &tag_message)
        .map_err(|err| err.description().into())
}
<|endoftext|>


<commit_before>// Copyright 2018-2019, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::ffi::{CStr, CString};
use std::fmt;
use std::io;
use std::mem;
use std::ops::Deref;
use std::path::Path;
use std::ptr;

#[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))]
use enums::SvgUnit;
use enums::{Status, SvgVersion};
use ffi;
use surface::Surface;

#[cfg(feature = "use_glib")]
use glib::translate::*;

impl SvgVersion {
    pub fn as_str(self) -> Option<&'static str> {
        unsafe {
            let res = ffi::cairo_svg_version_to_string(self.into());
            res.as_ref()
                .and_then(|cstr| CStr::from_ptr(cstr as _).to_str().ok())
        }
    }
}

#[derive(Debug)]
pub struct SvgSurface {
    inner: Surface,
}

impl SvgSurface {
    pub fn new<P: AsRef<Path>>(width: f64, height: f64, path: P) -> Result<SvgSurface, Status> {
        let path = path.as_ref().to_string_lossy().into_owned();
        let path = CString::new(path).unwrap();

        unsafe {
            Ok(Self {
                inner: Surface::from_raw_full(ffi::cairo_svg_surface_create(
                    path.as_ptr(),
                    width,
                    height,
                ))?,
            })
        }
    }

    for_stream_constructors!(cairo_svg_surface_create_for_stream);

    pub fn get_versions() -> impl Iterator<Item = SvgVersion> {
        let vers_slice = unsafe {
            let mut vers_ptr = ptr::null_mut();
            let mut num_vers = mem::MaybeUninit::uninit();
            ffi::cairo_svg_get_versions(&mut vers_ptr, num_vers.as_mut_ptr());

            std::slice::from_raw_parts(vers_ptr, num_vers.assume_init() as _)
        };

        vers_slice.iter().map(|v| SvgVersion::from(*v))
    }

    pub fn restrict(&self, version: SvgVersion) {
        unsafe {
            ffi::cairo_svg_surface_restrict_to_version(self.inner.to_raw_none(), version.into());
        }
    }

    #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))]
    pub fn set_document_unit(&mut self, unit: SvgUnit) {
        unsafe {
            ffi::cairo_svg_surface_set_document_unit(self.inner.to_raw_none(), unit.into());
        }
    }

    #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))]
    pub fn get_document_unit(&self) -> SvgUnit {
        unsafe {
            SvgUnit::from(ffi::cairo_svg_surface_get_document_unit(
                self.inner.to_raw_none(),
            ))
        }
    }
}

impl Deref for SvgSurface {
    type Target = Surface;

    fn deref(&self) -> &Surface {
        &self.inner
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for SvgSurface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        let stash = self.inner.to_glib_none();
        Stash(stash.0, stash.1)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for SvgSurface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> SvgSurface {
        SvgSurface {
            inner: from_glib_borrow(ptr),
        }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for SvgSurface {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> SvgSurface {
        SvgSurface {
            inner: from_glib_borrow(ptr),
        }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for SvgSurface {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> SvgSurface {
        Self {
            inner: Surface::from_raw_full(ptr).unwrap(),
        }
    }
}

impl fmt::Display for SvgSurface {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SvgSurface")
    }
}

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

    fn draw(surface: &Surface) {
        let cr = Context::new(surface);

        cr.set_line_width(25.0);

        cr.set_source_rgba(1.0, 0.0, 0.0, 0.5);
        cr.line_to(0., 0.);
        cr.line_to(100., 100.);
        cr.stroke();

        cr.set_source_rgba(0.0, 0.0, 1.0, 0.5);
        cr.line_to(0., 100.);
        cr.line_to(100., 0.);
        cr.stroke();
    }

    fn draw_in_buffer() -> Vec<u8> {
        let buffer: Vec<u8> = vec![];

        let surface = SvgSurface::for_stream(100., 100., buffer).unwrap();
        draw(&surface);
        *surface.finish_output_stream().unwrap().downcast().unwrap()
    }

    fn assert_len_close_enough(len_a: usize, len_b: usize) {
        // It seems cairo randomizies some element IDs which might make one svg slightly
        // larger than the other. Here we make sure the difference is within ~10%.
        let len_diff = (len_a as isize - len_b as isize).abs() as usize;
        assert!(len_diff < len_b / 10);
    }

    #[test]
    fn versions() {
        assert!(SvgSurface::get_versions().any(|v| v == SvgVersion::_1_1));
    }

    #[test]
    fn version_string() {
        let ver_str = SvgVersion::_1_1.as_str().unwrap();
        assert_eq!(ver_str, "SVG 1.1");
    }

    #[test]
    #[cfg(unix)]
    fn file() {
        let surface = SvgSurface::new(100., 100., "/dev/null").unwrap();
        draw(&surface);
        surface.finish();
    }

    #[test]
    fn writer() {
        let file = tempfile().expect("tempfile failed");
        let surface = SvgSurface::for_stream(100., 100., file).unwrap();

        draw(&surface);
        let stream = surface.finish_output_stream().unwrap();
        let file = stream.downcast::<std::fs::File>().unwrap();

        let buffer = draw_in_buffer();
        let file_size = file.metadata().unwrap().len();

        assert_len_close_enough(file_size as usize, buffer.len());
    }

    #[test]
    fn ref_writer() {
        let mut file = tempfile().expect("tempfile failed");
        let surface = unsafe { SvgSurface::for_raw_stream(100., 100., &mut file).unwrap() };

        draw(&surface);
        surface.finish_output_stream().unwrap();
    }

    #[test]
    fn buffer() {
        let buffer = draw_in_buffer();

        let header = b"<?xml";
        assert_eq!(&buffer[..header.len()], header);
    }

    #[test]
    fn custom_writer() {
        use std::fs;

        struct CustomWriter(usize, fs::File);

        impl io::Write for CustomWriter {
            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                self.1.write(buf)?;

                self.0 += buf.len();
                Ok(buf.len())
            }

            fn flush(&mut self) -> io::Result<()> {
                Ok(())
            }
        }

        let file = tempfile().expect("tempfile failed");
        let custom_writer = CustomWriter(0, file);

        let surface = SvgSurface::for_stream(100., 100., custom_writer).unwrap();
        draw(&surface);
        let stream = surface.finish_output_stream().unwrap();
        let custom_writer = stream.downcast::<CustomWriter>().unwrap();

        let buffer = draw_in_buffer();

        assert_len_close_enough(custom_writer.0, buffer.len());
    }

    fn with_panicky_stream() -> SvgSurface {
        struct PanicWriter;

        impl io::Write for PanicWriter {
            fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
                panic!("panic in writer");
            }
            fn flush(&mut self) -> io::Result<()> {
                Ok(())
            }
        }

        let surface = SvgSurface::for_stream(20., 20., PanicWriter).unwrap();
        surface.finish();
        surface
    }

    #[test]
    #[should_panic]
    fn finish_stream_propagates_panic() {
        let _ = with_panicky_stream().finish_output_stream();
    }
}
<commit_msg>Add SvgSurface::try_from(Surface)<commit_after>// Copyright 2018-2019, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io;
use std::mem;
use std::ops::Deref;
use std::path::Path;
use std::ptr;

#[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))]
use enums::SvgUnit;
use enums::{Status, SurfaceType, SvgVersion};
use ffi;
use surface::Surface;

#[cfg(feature = "use_glib")]
use glib::translate::*;

impl SvgVersion {
    pub fn as_str(self) -> Option<&'static str> {
        unsafe {
            let res = ffi::cairo_svg_version_to_string(self.into());
            res.as_ref()
                .and_then(|cstr| CStr::from_ptr(cstr as _).to_str().ok())
        }
    }
}

#[derive(Debug)]
pub struct SvgSurface {
    inner: Surface,
}

impl TryFrom<Surface> for SvgSurface {
    type Error = Surface;

    fn try_from(surface: Surface) -> Result<SvgSurface, Surface> {
        if surface.get_type() == SurfaceType::Svg {
            Ok(SvgSurface { inner: surface })
        } else {
            Err(surface)
        }
    }
}

impl SvgSurface {
    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<SvgSurface, Status> {
        let surface = Surface::from_raw_full(ptr)?;
        Self::try_from(surface).map_err(|_| Status::SurfaceTypeMismatch)
    }

    pub fn new<P: AsRef<Path>>(width: f64, height: f64, path: P) -> Result<SvgSurface, Status> {
        let path = path.as_ref().to_string_lossy().into_owned();
        let path = CString::new(path).unwrap();

        unsafe {
            Ok(Self {
                inner: Surface::from_raw_full(ffi::cairo_svg_surface_create(
                    path.as_ptr(),
                    width,
                    height,
                ))?,
            })
        }
    }

    for_stream_constructors!(cairo_svg_surface_create_for_stream);

    pub fn get_versions() -> impl Iterator<Item = SvgVersion> {
        let vers_slice = unsafe {
            let mut vers_ptr = ptr::null_mut();
            let mut num_vers = mem::MaybeUninit::uninit();
            ffi::cairo_svg_get_versions(&mut vers_ptr, num_vers.as_mut_ptr());

            std::slice::from_raw_parts(vers_ptr, num_vers.assume_init() as _)
        };

        vers_slice.iter().map(|v| SvgVersion::from(*v))
    }

    pub fn restrict(&self, version: SvgVersion) {
        unsafe {
            ffi::cairo_svg_surface_restrict_to_version(self.inner.to_raw_none(), version.into());
        }
    }

    #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))]
    pub fn set_document_unit(&mut self, unit: SvgUnit) {
        unsafe {
            ffi::cairo_svg_surface_set_document_unit(self.inner.to_raw_none(), unit.into());
        }
    }

    #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))]
    pub fn get_document_unit(&self) -> SvgUnit {
        unsafe {
            SvgUnit::from(ffi::cairo_svg_surface_get_document_unit(
                self.inner.to_raw_none(),
            ))
        }
    }
}

impl Deref for SvgSurface {
    type Target = Surface;

    fn deref(&self) -> &Surface {
        &self.inner
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for SvgSurface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        let stash = self.inner.to_glib_none();
        Stash(stash.0, stash.1)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for SvgSurface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> SvgSurface {
        Self::try_from(from_glib_none::<_, Surface>(ptr)).unwrap()
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for SvgSurface {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> SvgSurface {
        Self::try_from(from_glib_borrow::<_, Surface>(ptr)).unwrap()
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for SvgSurface {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> SvgSurface {
        Self::from_raw_full(ptr).unwrap()
    }
}

impl fmt::Display for SvgSurface {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SvgSurface")
    }
}

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

    fn draw(surface: &Surface) {
        let cr = Context::new(surface);

        cr.set_line_width(25.0);

        cr.set_source_rgba(1.0, 0.0, 0.0, 0.5);
        cr.line_to(0., 0.);
        cr.line_to(100., 100.);
        cr.stroke();

        cr.set_source_rgba(0.0, 0.0, 1.0, 0.5);
        cr.line_to(0., 100.);
        cr.line_to(100., 0.);
        cr.stroke();
    }

    fn draw_in_buffer() -> Vec<u8> {
        let buffer: Vec<u8> = vec![];

        let surface = SvgSurface::for_stream(100., 100., buffer).unwrap();
        draw(&surface);
        *surface.finish_output_stream().unwrap().downcast().unwrap()
    }

    fn assert_len_close_enough(len_a: usize, len_b: usize) {
        // It seems cairo randomizies some element IDs which might make one svg slightly
        // larger than the other. Here we make sure the difference is within ~10%.
        let len_diff = (len_a as isize - len_b as isize).abs() as usize;
        assert!(len_diff < len_b / 10);
    }

    #[test]
    fn versions() {
        assert!(SvgSurface::get_versions().any(|v| v == SvgVersion::_1_1));
    }

    #[test]
    fn version_string() {
        let ver_str = SvgVersion::_1_1.as_str().unwrap();
        assert_eq!(ver_str, "SVG 1.1");
    }

    #[test]
    #[cfg(unix)]
    fn file() {
        let surface = SvgSurface::new(100., 100., "/dev/null").unwrap();
        draw(&surface);
        surface.finish();
    }

    #[test]
    fn writer() {
        let file = tempfile().expect("tempfile failed");
        let surface = SvgSurface::for_stream(100., 100., file).unwrap();

        draw(&surface);
        let stream = surface.finish_output_stream().unwrap();
        let file = stream.downcast::<std::fs::File>().unwrap();

        let buffer = draw_in_buffer();
        let file_size = file.metadata().unwrap().len();

        assert_len_close_enough(file_size as usize, buffer.len());
    }

    #[test]
    fn ref_writer() {
        let mut file = tempfile().expect("tempfile failed");
        let surface = unsafe { SvgSurface::for_raw_stream(100., 100., &mut file).unwrap() };

        draw(&surface);
        surface.finish_output_stream().unwrap();
    }

    #[test]
    fn buffer() {
        let buffer = draw_in_buffer();

        let header = b"<?xml";
        assert_eq!(&buffer[..header.len()], header);
    }

    #[test]
    fn custom_writer() {
        use std::fs;

        struct CustomWriter(usize, fs::File);

        impl io::Write for CustomWriter {
            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                self.1.write(buf)?;

                self.0 += buf.len();
                Ok(buf.len())
            }

            fn flush(&mut self) -> io::Result<()> {
                Ok(())
            }
        }

        let file = tempfile().expect("tempfile failed");
        let custom_writer = CustomWriter(0, file);

        let surface = SvgSurface::for_stream(100., 100., custom_writer).unwrap();
        draw(&surface);
        let stream = surface.finish_output_stream().unwrap();
        let custom_writer = stream.downcast::<CustomWriter>().unwrap();

        let buffer = draw_in_buffer();

        assert_len_close_enough(custom_writer.0, buffer.len());
    }

    fn with_panicky_stream() -> SvgSurface {
        struct PanicWriter;

        impl io::Write for PanicWriter {
            fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
                panic!("panic in writer");
            }
            fn flush(&mut self) -> io::Result<()> {
                Ok(())
            }
        }

        let surface = SvgSurface::for_stream(20., 20., PanicWriter).unwrap();
        surface.finish();
        surface
    }

    #[test]
    #[should_panic]
    fn finish_stream_propagates_panic() {
        let _ = with_panicky_stream().finish_output_stream();
    }
}
<|endoftext|>


<commit_before>// Copyright 2017-2021 int08h LLC
//
// 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 crate::error::Error;

/// An unsigned 32-bit value (key) that maps to a byte-string (value).
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)]
pub enum Tag {
    // Enforcement of the "tags in strictly increasing order" rule is done using the
    // little-endian encoding of the ASCII tag value; e.g. 'SIG\x00' is 0x00474953 and
    // 'NONC' is 0x434e4f4e.
    //
    // Tags are written here in ascending order
    SIG,
    NONC,
    DELE,
    PATH,
    RADI,
    PUBK,
    MIDP,
    SREP,
    MINT,
    ROOT,
    CERT,
    MAXT,
    INDX,
    PAD,
}

impl Tag {
    /// Translates a tag into its on-the-wire representation
    pub fn wire_value(self) -> &'static [u8] {
        match self {
            Tag::CERT => b"CERT",
            Tag::DELE => b"DELE",
            Tag::INDX => b"INDX",
            Tag::MAXT => b"MAXT",
            Tag::MIDP => b"MIDP",
            Tag::MINT => b"MINT",
            Tag::NONC => b"NONC",
            Tag::PAD => b"PAD\xff",
            Tag::PATH => b"PATH",
            Tag::PUBK => b"PUBK",
            Tag::RADI => b"RADI",
            Tag::ROOT => b"ROOT",
            Tag::SIG => b"SIG\x00",
            Tag::SREP => b"SREP",
        }
    }

    /// Return the `Tag` corresponding to the on-the-wire representation in `bytes` or an
    /// `Error::InvalidTag` if `bytes` do not correspond to a valid tag.
    pub fn from_wire(bytes: &[u8]) -> Result<Self, Error> {
        match bytes {
            b"CERT" => Ok(Tag::CERT),
            b"DELE" => Ok(Tag::DELE),
            b"INDX" => Ok(Tag::INDX),
            b"MAXT" => Ok(Tag::MAXT),
            b"MIDP" => Ok(Tag::MIDP),
            b"MINT" => Ok(Tag::MINT),
            b"NONC" => Ok(Tag::NONC),
            b"PAD\xff" => Ok(Tag::PAD),
            b"PATH" => Ok(Tag::PATH),
            b"PUBK" => Ok(Tag::PUBK),
            b"RADI" => Ok(Tag::RADI),
            b"ROOT" => Ok(Tag::ROOT),
            b"SIG\x00" => Ok(Tag::SIG),
            b"SREP" => Ok(Tag::SREP),
            _ => Err(Error::InvalidTag(Box::from(bytes))),
        }
    }
}
<commit_msg>Extract raw tag bytes to constants<commit_after>// Copyright 2017-2021 int08h LLC
//
// 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 crate::error::Error;

/// An unsigned 32-bit value (key) that maps to a byte-string (value).
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)]
pub enum Tag {
    // Enforcement of the "tags in strictly increasing order" rule is done using the
    // little-endian encoding of the ASCII tag value; e.g. 'SIG\x00' is 0x00474953 and
    // 'NONC' is 0x434e4f4e.
    //
    // Tags are written here in ascending order
    SIG,
    NONC,
    DELE,
    PATH,
    RADI,
    PUBK,
    MIDP,
    SREP,
    MINT,
    ROOT,
    CERT,
    MAXT,
    INDX,
    PAD,
}

impl Tag {
    const BYTES_CERT: &'static [u8] = b"CERT";
    const BYTES_DELE: &'static [u8] = b"DELE";
    const BYTES_INDX: &'static [u8] = b"INDX";
    const BYTES_MAXT: &'static [u8] = b"MAXT";
    const BYTES_MIDP: &'static [u8] = b"MIDP";
    const BYTES_MINT: &'static [u8] = b"MINT";
    const BYTES_NONC: &'static [u8] = b"NONC";
    const BYTES_PAD: &'static [u8] = b"PAD\xff";
    const BYTES_PATH: &'static [u8] = b"PATH";
    const BYTES_PUBK: &'static [u8] = b"PUBK";
    const BYTES_RADI: &'static [u8] = b"RADI";
    const BYTES_ROOT: &'static [u8] = b"ROOT";
    const BYTES_SIG: &'static [u8] = b"SIG\x00";
    const BYTES_SREP: &'static [u8] = b"SREP";

    /// Translates a tag into its on-the-wire representation
    pub fn wire_value(self) -> &'static [u8] {
        match self {
            Tag::CERT => Tag::BYTES_CERT,
            Tag::DELE => Tag::BYTES_DELE,
            Tag::INDX => Tag::BYTES_INDX,
            Tag::MAXT => Tag::BYTES_MAXT,
            Tag::MIDP => Tag::BYTES_MIDP,
            Tag::MINT => Tag::BYTES_MINT,
            Tag::NONC => Tag::BYTES_NONC,
            Tag::PAD => Tag::BYTES_PAD,
            Tag::PATH => Tag::BYTES_PATH,
            Tag::PUBK => Tag::BYTES_PUBK,
            Tag::RADI => Tag::BYTES_RADI,
            Tag::ROOT => Tag::BYTES_ROOT,
            Tag::SIG => Tag::BYTES_SIG,
            Tag::SREP => Tag::BYTES_SREP,
        }
    }

    /// Return the `Tag` corresponding to the on-the-wire representation in `bytes` or an
    /// `Error::InvalidTag` if `bytes` do not correspond to a valid tag.
    pub fn from_wire(bytes: &[u8]) -> Result<Self, Error> {
        match bytes {
            Tag::BYTES_CERT => Ok(Tag::CERT),
            Tag::BYTES_DELE => Ok(Tag::DELE),
            Tag::BYTES_INDX => Ok(Tag::INDX),
            Tag::BYTES_MAXT => Ok(Tag::MAXT),
            Tag::BYTES_MIDP => Ok(Tag::MIDP),
            Tag::BYTES_MINT => Ok(Tag::MINT),
            Tag::BYTES_NONC => Ok(Tag::NONC),
            Tag::BYTES_PAD => Ok(Tag::PAD),
            Tag::BYTES_PATH => Ok(Tag::PATH),
            Tag::BYTES_PUBK => Ok(Tag::PUBK),
            Tag::BYTES_RADI => Ok(Tag::RADI),
            Tag::BYTES_ROOT => Ok(Tag::ROOT),
            Tag::BYTES_SIG => Ok(Tag::SIG),
            Tag::BYTES_SREP => Ok(Tag::SREP),
            _ => Err(Error::InvalidTag(Box::from(bytes))),
        }
    }
}
<|endoftext|>


<commit_before>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use views::tilemap::{TerrainTile, TerrainSpriteSheet, TILES_PCS_W, TILES_PCS_H, get_tiles, viewport_move};
use views::background::{Background};
use data::{load_character};
use sdl2::mixer::{Chunk};
use std::path::Path;

mod tilemap;
mod background;

const PLAYER_SPEED: f64 = 170.0;
const ZOOM_SPEED: f32 = 0.01;
const CHARACTER_W: f64 = 56.0;
const CHARACTER_H: f64 = 43.0;

pub const SCREEN_WIDTH: f64 = 1280.0;
pub const SCREEN_HEIGHT: f64 = 720.0;

const FIRE_SPRITE_START_INDEX: usize = 211;

#[derive(Clone, Copy)]
enum CharacterFrame {
  Right = 0,
  UpRight = 1,
  Up = 2,
  UpLeft = 3,
  Left = 4,
  DownLeft = 5,
  Down = 6,
  DownRight = 7,
}

struct Character {
  rect: Rectangle,
  sprites: Vec<Sprite>,
  current: CharacterFrame,
  heading: CharacterFrame,
  move_anim_index: u32,
  fire_anim_index: u32
}

pub struct GameView {
  player: Character,
  tiles: Vec<TerrainTile>,
  spritesheet: Vec<Sprite>,
  background: Background,
  pistol: Chunk,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let spritesheet = Sprite::load(&mut game.renderer, "assets/character.png").unwrap();
    let pistol_audio = Chunk::from_file(Path::new("assets/audio/pistol.ogg")).unwrap();
    let character_datapoints = load_character();
    let mut sprites = Vec::with_capacity(512);

    for x in 0..(FIRE_SPRITE_START_INDEX - 1) {
      sprites.push(spritesheet.region(character_datapoints[x]).unwrap());
    }

    for x in FIRE_SPRITE_START_INDEX..255 {
      sprites.push(spritesheet.region(character_datapoints[x]).unwrap());
    }

    GameView {
      player: Character {
        rect: Rectangle {
          x: SCREEN_WIDTH * 0.5,
          y: SCREEN_HEIGHT * 0.4,
          w: CHARACTER_W,
          h: CHARACTER_H,
        },
        sprites: sprites.clone(),
        current: CharacterFrame::Down,
        heading: CharacterFrame::Down,
        move_anim_index: 0,
        fire_anim_index: 0
      },

      tiles: get_tiles(),

      spritesheet: TerrainSpriteSheet::new(&game),

      pistol: pistol_audio,

      background: Background {
        pos: 0.0,
        sprite: Sprite::load(&mut game.renderer, "assets/background.png").unwrap(),
      }
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    self.player.rect.x += dx * 1.5;
    self.player.rect.y += dy * 0.75;

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0,
      h: game.output_size().1,
    };

    let curr_rect = game.renderer.viewport();
    let rect = viewport_move(&game, curr_rect);
    game.renderer.set_viewport(rect.to_sdl());

    self.background.render(&mut game.renderer);

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { CharacterFrame::Up }
    else if dx > 0.0 && dy < 0.0   { CharacterFrame::UpRight }
    else if dx < 0.0 && dy < 0.0   { CharacterFrame::UpLeft }
    else if dx == 0.0 && dy == 0.0 { self.player.heading }
    else if dx > 0.0 && dy == 0.0  { CharacterFrame::Right }
    else if dx < 0.0 && dy == 0.0  { CharacterFrame::Left }
    else if dx == 0.0 && dy > 0.0  { CharacterFrame::Down }
    else if dx > 0.0 && dy > 0.0   { CharacterFrame::DownRight }
    else if dx < 0.0 && dy > 0.0   { CharacterFrame::DownLeft }
    else { unreachable!() };

    self.player.heading = self.player.current;
    for x in 0..TILES_PCS_W {
      for y in 0..TILES_PCS_H {
        let index = x * TILES_PCS_H + y;
        game.renderer.copy_sprite(&self.spritesheet[self.tiles[index].current as usize], self.tiles[index].rect);
      }
    }
    match game.events.mouse_click {
      Some(_) => {
        let index = 211 + self.player.current as usize * 5 + self.player.fire_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.fire_anim_index =
          if dx == 0.0 && dy == 0.0 { 0u32 } else if self.player.fire_anim_index < 4u32 { self.player.fire_anim_index + 1u32 } else { 0u32 };
        if self.player.fire_anim_index == 0 {
          game.play_sound(&self.pistol);
        }
      },
      None => {
        let index = self.player.current as usize * 28 + self.player.move_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.move_anim_index =
          if dx == 0.0 && dy == 0.0 { 0u32 } else if self.player.move_anim_index < 13u32 { self.player.move_anim_index + 1u32 } else { 0u32 };
      },
    };

    let scale = game.renderer.scale();
    if game.events.zoom_in == true && scale.0 <= 2.0 && scale.1 <= 2.0 {
      let _ = game.renderer.set_scale(scale.0 + ZOOM_SPEED, scale.1 + ZOOM_SPEED);
    } else if game.events.zoom_out == true && scale.0 > 0.85 && scale.1 > 0.85 {
      let _ = game.renderer.set_scale(scale.0 - ZOOM_SPEED, scale.1 - ZOOM_SPEED);
    }

    ViewAction::None
  }
}
<commit_msg>Rename variable<commit_after>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use views::tilemap::{TerrainTile, TerrainSpriteSheet, TILES_PCS_W, TILES_PCS_H, get_tiles, viewport_move};
use views::background::{Background};
use data::{load_character};
use sdl2::mixer::{Chunk};
use std::path::Path;

mod tilemap;
mod background;

const PLAYER_SPEED: f64 = 170.0;
const ZOOM_SPEED: f32 = 0.01;
const CHARACTER_W: f64 = 56.0;
const CHARACTER_H: f64 = 43.0;

pub const SCREEN_WIDTH: f64 = 1280.0;
pub const SCREEN_HEIGHT: f64 = 720.0;

const FIRE_SPRITE_START_INDEX: usize = 211;

#[derive(Clone, Copy)]
enum CharacterFrame {
  Right = 0,
  UpRight = 1,
  Up = 2,
  UpLeft = 3,
  Left = 4,
  DownLeft = 5,
  Down = 6,
  DownRight = 7,
}

struct Character {
  rect: Rectangle,
  sprites: Vec<Sprite>,
  current: CharacterFrame,
  heading: CharacterFrame,
  move_anim_index: u32,
  fire_anim_index: u32
}

pub struct GameView {
  player: Character,
  tiles: Vec<TerrainTile>,
  sprite_sheet: Vec<Sprite>,
  background: Background,
  pistol: Chunk,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let spritesheet = Sprite::load(&mut game.renderer, "assets/character.png").unwrap();
    let pistol_audio = Chunk::from_file(Path::new("assets/audio/pistol.ogg")).unwrap();
    let character_datapoints = load_character();
    let mut sprites = Vec::with_capacity(512);

    for x in 0..(FIRE_SPRITE_START_INDEX - 1) {
      sprites.push(spritesheet.region(character_datapoints[x]).unwrap());
    }

    for x in FIRE_SPRITE_START_INDEX..255 {
      sprites.push(spritesheet.region(character_datapoints[x]).unwrap());
    }

    GameView {
      player: Character {
        rect: Rectangle {
          x: SCREEN_WIDTH * 0.5,
          y: SCREEN_HEIGHT * 0.4,
          w: CHARACTER_W,
          h: CHARACTER_H,
        },
        sprites: sprites.clone(),
        current: CharacterFrame::Down,
        heading: CharacterFrame::Down,
        move_anim_index: 0,
        fire_anim_index: 0
      },

      tiles: get_tiles(),

      sprite_sheet: TerrainSpriteSheet::new(&game),

      pistol: pistol_audio,

      background: Background {
        pos: 0.0,
        sprite: Sprite::load(&mut game.renderer, "assets/background.png").unwrap(),
      }
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    self.player.rect.x += dx * 1.5;
    self.player.rect.y += dy * 0.75;

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0,
      h: game.output_size().1,
    };

    let curr_rect = game.renderer.viewport();
    let rect = viewport_move(&game, curr_rect);
    game.renderer.set_viewport(rect.to_sdl());

    self.background.render(&mut game.renderer);

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { CharacterFrame::Up }
    else if dx > 0.0 && dy < 0.0   { CharacterFrame::UpRight }
    else if dx < 0.0 && dy < 0.0   { CharacterFrame::UpLeft }
    else if dx == 0.0 && dy == 0.0 { self.player.heading }
    else if dx > 0.0 && dy == 0.0  { CharacterFrame::Right }
    else if dx < 0.0 && dy == 0.0  { CharacterFrame::Left }
    else if dx == 0.0 && dy > 0.0  { CharacterFrame::Down }
    else if dx > 0.0 && dy > 0.0   { CharacterFrame::DownRight }
    else if dx < 0.0 && dy > 0.0   { CharacterFrame::DownLeft }
    else { unreachable!() };

    self.player.heading = self.player.current;
    for x in 0..TILES_PCS_W {
      for y in 0..TILES_PCS_H {
        let index = x * TILES_PCS_H + y;
        game.renderer.copy_sprite(&self.sprite_sheet[self.tiles[index].current as usize], self.tiles[index].rect);
      }
    }
    match game.events.mouse_click {
      Some(_) => {
        let index = 211 + self.player.current as usize * 5 + self.player.fire_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.fire_anim_index =
          if dx == 0.0 && dy == 0.0 { 0u32 } else if self.player.fire_anim_index < 4u32 { self.player.fire_anim_index + 1u32 } else { 0u32 };
        if self.player.fire_anim_index == 0 {
          game.play_sound(&self.pistol);
        }
      },
      None => {
        let index = self.player.current as usize * 28 + self.player.move_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.move_anim_index =
          if dx == 0.0 && dy == 0.0 { 0u32 } else if self.player.move_anim_index < 13u32 { self.player.move_anim_index + 1u32 } else { 0u32 };
      },
    };

    let scale = game.renderer.scale();
    if game.events.zoom_in == true && scale.0 <= 2.0 && scale.1 <= 2.0 {
      let _ = game.renderer.set_scale(scale.0 + ZOOM_SPEED, scale.1 + ZOOM_SPEED);
    } else if game.events.zoom_out == true && scale.0 > 0.85 && scale.1 > 0.85 {
      let _ = game.renderer.set_scale(scale.0 - ZOOM_SPEED, scale.1 - ZOOM_SPEED);
    }

    ViewAction::None
  }
}
<|endoftext|>


<commit_before>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use sdl2::pixels::Color;

const PLAYER_SPEED: f64 = 150.0;
const CHARACTER_W: f64 = 43.0;
const CHARACTER_H: f64 = 39.0;

#[derive(Clone, Copy)]
enum CharacterFrame {
  UpNorm   = 0,
  UpFast   = 1,
  UpSlow   = 2,
  MidFast  = 3,
  MidSlow  = 4,
  DownNorm = 5,
  DownFast = 6,
  DownSlow = 7
}

struct Character {
  rect: Rectangle,
  sprites: Vec<Sprite>,
  current: CharacterFrame,
}

pub struct GameView {
  player: Character,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let spritesheet = Sprite::load(&mut game.renderer, "assets/warrior.png").unwrap();
    let mut sprites = Vec::with_capacity(9);

    for x in 0..8 {
      sprites.push(spritesheet.region(Rectangle {
        w: CHARACTER_W,
        h: CHARACTER_H,
        x: CHARACTER_W * x as f64,
        y: 0 as f64,
      }).unwrap());
    }
    GameView {
      player: Character {
        rect: Rectangle {
          x: 64.0,
          y: 64.0,
          w: CHARACTER_W,
          h: CHARACTER_H,
        },
        sprites: sprites,
        current: CharacterFrame::DownNorm,
      }
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    self.player.rect.x += dx;
    self.player.rect.y += dy;

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0 * 0.70,
      h: game.output_size().1,
    };

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { CharacterFrame::UpNorm }
    else if dx > 0.0 && dy < 0.0   { CharacterFrame::UpFast }
    else if dx < 0.0 && dy < 0.0   { CharacterFrame::UpSlow }
    else if dx == 0.0 && dy == 0.0 { CharacterFrame::DownNorm }
    else if dx > 0.0 && dy == 0.0  { CharacterFrame::MidFast }
    else if dx < 0.0 && dy == 0.0  { CharacterFrame::MidSlow }
    else if dx == 0.0 && dy > 0.0  { CharacterFrame::DownNorm }
    else if dx > 0.0 && dy > 0.0   { CharacterFrame::DownFast }
    else if dx < 0.0 && dy > 0.0   { CharacterFrame::DownSlow }
    else { unreachable!() };

    game.renderer.set_draw_color(Color::RGB(0, 0, 0));
    game.renderer.clear();

    game.renderer.set_draw_color(Color::RGB(170, 170, 170));
    game.renderer.fill_rect(self.player.rect.to_sdl().unwrap());

    game.renderer.copy_sprite(
      &self.player.sprites[self.player.current as usize],
      self.player.rect);

    ViewAction::None
  }
}
<commit_msg>Draw terrain tile<commit_after>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use sdl2::pixels::Color;

const PLAYER_SPEED: f64 = 150.0;
const CHARACTER_W: f64 = 56.0;
const CHARACTER_H: f64 = 43.0;

const TERRAIN_W: f64 = 102.0;
const TERRAIN_H: f64 = 67.0;

#[derive(Clone, Copy)]
enum CharacterFrame {
  Down = 0,
  DownLeft = 1,
  Left = 2,
  UpLeft = 3,
  Up = 4,
  UpRight = 5,
  Right = 6,
  DownRight = 7
}

#[derive(Clone, Copy)]
enum TerrainFrame {
  Sand = 0,
  Grass = 1,
  Water = 2,
  Wood = 3,
}

struct Character {
  rect: Rectangle,
  sprites: Vec<Sprite>,
  current: CharacterFrame,
}

struct TerrainTile {
  rect: Rectangle,
  terrain_sprites: Vec<Sprite>,
  current: TerrainFrame
}

pub struct GameView {
  player: Character,
  tiles: TerrainTile,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let spritesheet = Sprite::load(&mut game.renderer, "assets/warrior.png").unwrap();
    let terrain_spritesheet = Sprite::load(&mut game.renderer, "assets/terrain.png").unwrap();
    let mut sprites = Vec::with_capacity(9);
    let mut terrain_sprites = Vec::with_capacity(4);

    for x in 0..3 {
      terrain_sprites.push(terrain_spritesheet.region(Rectangle {
        w: TERRAIN_W,
        h: TERRAIN_H,
        x: TERRAIN_W * x as f64,
        y: 0.0 as f64,
      }).unwrap());
    }

    for x in 0..8 {
      sprites.push(spritesheet.region(Rectangle {
        w: CHARACTER_W,
        h: CHARACTER_H,
        x: CHARACTER_W * x as f64,
        y: 0.0 as f64,
      }).unwrap());
    }
    GameView {
      player: Character {
        rect: Rectangle {
          x: 64.0,
          y: 64.0,
          w: CHARACTER_W,
          h: CHARACTER_H,
        },
        sprites: sprites,
        current: CharacterFrame::Down,
      },
      tiles: TerrainTile {
        rect: Rectangle {
          x: 102.0,
          y: 64.0,
          w: TERRAIN_W,
          h: TERRAIN_H,
        },
        terrain_sprites: terrain_sprites,
        current: TerrainFrame::Grass,
      },
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    self.player.rect.x += dx;
    self.player.rect.y += dy;

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0 * 0.70,
      h: game.output_size().1,
    };

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { CharacterFrame::Up }
    else if dx > 0.0 && dy < 0.0   { CharacterFrame::DownLeft }
    else if dx < 0.0 && dy < 0.0   { CharacterFrame::Left }
    else if dx == 0.0 && dy == 0.0 { CharacterFrame::Down }
    else if dx > 0.0 && dy == 0.0  { CharacterFrame::UpLeft }
    else if dx < 0.0 && dy == 0.0  { CharacterFrame::UpRight }
    else if dx == 0.0 && dy > 0.0  { CharacterFrame::Down }
    else if dx > 0.0 && dy > 0.0   { CharacterFrame::Right }
    else if dx < 0.0 && dy > 0.0   { CharacterFrame::DownRight }
    else { unreachable!() };

    game.renderer.set_draw_color(Color::RGB(120, 120, 120));
    game.renderer.clear();

    game.renderer.set_draw_color(Color::RGB(170, 170, 170));
    game.renderer.fill_rect(self.player.rect.to_sdl().unwrap());

    game.renderer.copy_sprite(&self.player.sprites[self.player.current as usize], self.player.rect);

    game.renderer.set_draw_color(Color::RGBA(120, 120, 120, 1));
    game.renderer.fill_rect(self.tiles.rect.to_sdl().unwrap());

    game.renderer.copy_sprite(&self.tiles.terrain_sprites[self.tiles.current as usize], self.tiles.rect);

    ViewAction::None
  }
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::{File, OpenOptions};
use std::process::Command;
use std::process::Stdio;
use std::path::Path;
use std::os::unix::prelude::AsRawFd;
use libc::{c_ushort, c_int, c_ulong};
use std::str;
use std::os::unix::prelude::FromRawFd;
use std::cmp::min;

pub struct TTY {
    file: File,
    dimensions: (usize, usize),
    original_state: String
}

pub trait IO {
    fn write(&mut self, line: &str);
    fn read(&mut self) -> Option<String>;
    fn last(&self) -> &str;
    fn lines(&self) -> Vec<String>;
    fn dimensions(&self) -> (usize, usize);
    fn reset(&self);
}

impl IO for TTY {
    fn write(&mut self, line: &str) {

        let it = self.trim(line);
        match self.file.write(it.as_bytes()) {
            Ok(k) => { k },
            Err(e) => { panic!(e.to_string()) }
        };
    }

    fn read(&mut self) -> Option<String> {
        let mut buffer = [0] ;
        let res = match self.file.read(&mut buffer) {
            Ok(c) if  c > 0 => {
                str::from_utf8(&buffer).ok().map(|x| x.to_string())
            },
            _ => None,
        };
        res
    }

    fn last(&self) -> &str {
        "fail"
    }

    fn lines(&self) -> Vec<String> {
        let mut lines: Vec<String> = Vec::new();
        lines.push("fail".to_string());
        lines
    }

    fn dimensions(&self) -> (usize, usize) {
        self.dimensions
    }

    fn reset(&self) {
        TTY::stty(&self.file, &[self.original_state.as_ref()]);
    }
}

impl TTY {
    pub fn new() -> TTY {
        let path = Path::new("/dev/tty");
        let file = OpenOptions::new().read(true).write(true).append(true).open(&path).unwrap();
        let dimension = TTY::get_window_size(&file);
        let orig_state = TTY::previous_state(&file);

        TTY::no_echo_no_escaping(&file);

        TTY {
            original_state: orig_state,
            dimensions: dimension,
            file: file,
        }
    }

    fn trim(&self, line: &str) -> String {
        let actual = min(line.len(), self.dimensions.0);
        line[..actual].into()
    }

    fn get_window_size(file: &File) -> (usize, usize) {
        extern {
            fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
        }
        #[cfg(any(target_os = "macos", target_os = "freebsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(file.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            (size.cols as usize, size.rows as usize)
        } else {
            panic!("Could not read winsize from /dev/tty")
        }
    }

    fn stty(file: &File, args: &[&str]) -> Option<String> {
        unsafe {
            let raw_fd = file.as_raw_fd();
            let stdin =  Stdio::from_raw_fd(raw_fd);
            match Command::new("stty").args(args).stdin(stdin).output() {
                Err(k) => { panic!("something got fucked when reading stty") }
                Ok(output) => { String::from_utf8(output.stdout).ok() }
            }
        }
    }

    fn no_echo_no_escaping(file: &File) {
        TTY::stty(file, &["-echo", "-icanon"]);
    }

    fn previous_state(file: &File) -> String {
        TTY::stty(file, &["-g"]).unwrap_or("".to_string())
    }
}
<commit_msg>Use heatseekers hack to copy the fd to /dev/tty to stdin<commit_after>use std::io::prelude::*;
use std::fs::{File, OpenOptions};
use std::process::Command;
use std::process::Stdio;
use std::path::Path;
use std::os::unix::prelude::AsRawFd;
use libc::{c_ushort, c_int, c_ulong};
use std::str;
use std::os::unix::prelude::FromRawFd;
use std::cmp::min;

pub struct TTY {
    file: File,
    dimensions: (usize, usize),
    original_state: String
}

pub trait IO {
    fn write(&mut self, line: &str);
    fn read(&mut self) -> Option<String>;
    fn last(&self) -> &str;
    fn lines(&self) -> Vec<String>;
    fn dimensions(&self) -> (usize, usize);
    fn reset(&self);
}

impl IO for TTY {
    fn write(&mut self, line: &str) {

        let it = self.trim(line);
        match self.file.write(it.as_bytes()) {
            Ok(k) => { k },
            Err(e) => { panic!(e.to_string()) }
        };
    }

    fn read(&mut self) -> Option<String> {
        let mut buffer = [0] ;
        let res = match self.file.read(&mut buffer) {
            Ok(c) if  c > 0 => {
                str::from_utf8(&buffer).ok().map(|x| x.to_string())
            },
            _ => None,
        };
        res
    }

    fn last(&self) -> &str {
        "fail"
    }

    fn lines(&self) -> Vec<String> {
        let mut lines: Vec<String> = Vec::new();
        lines.push("fail".to_string());
        lines
    }

    fn dimensions(&self) -> (usize, usize) {
        self.dimensions
    }

    fn reset(&self) {
        TTY::stty(&self.file, &[self.original_state.as_ref()]);
    }
}

impl TTY {
    pub fn new() -> TTY {
        let path = Path::new("/dev/tty");
        let file = OpenOptions::new().read(true).write(true).append(true).open(&path).unwrap();
        let dimension = TTY::get_window_size(&file);
        let orig_state = TTY::previous_state(&file);

        TTY::no_echo_no_escaping(&file);

        TTY {
            original_state: orig_state,
            dimensions: dimension,
            file: file,
        }
    }

    fn trim(&self, line: &str) -> String {
        let actual = min(line.len(), self.dimensions.0);
        line[..actual].into()
    }

    fn get_window_size(file: &File) -> (usize, usize) {
        extern {
            fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
        }
        #[cfg(any(target_os = "macos", target_os = "freebsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(file.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            (size.cols as usize, size.rows as usize)
        } else {
            panic!("Could not read winsize from /dev/tty")
        }
    }

    fn stty(file: &File, args: &[&str]) -> Option<String> {
        extern { fn dup2(src: c_int, dst: c_int) -> c_int;  }
        unsafe {
            // This is a hack until a replacement for InheritFd from old_io is available.
            let raw_fd = file.as_raw_fd();
            dup2(raw_fd, 0);
            match Command::new("stty").args(args).stdin(Stdio::inherit()).output() {
                Err(k) => { panic!("something got fucked when reading stty") }
                Ok(output) => { String::from_utf8(output.stdout).ok() }
            }
        }
    }

    fn no_echo_no_escaping(file: &File) {
        TTY::stty(file, &["-echo", "-icanon"]);
    }

    fn previous_state(file: &File) -> String {
        TTY::stty(file, &["-g"]).unwrap_or("".to_string())
    }
}
<|endoftext|>


<commit_before>//! Histogram of oriented gradients and helpers for visualizing them.
//! http://lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pdf

use image::{
	GenericImage,
	Luma
};
use gradients::{
	horizontal_sobel,
	vertical_sobel
};
use multiarray::{
	Array3d,
	View3d
};
use math::l2_norm;
use std::f32;

/// Parameters for HoG descriptors.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct HogOptions {
	/// Number of gradient orientation bins.
	pub orientations: usize,
	/// Whether gradients in opposite directions are treated as equal.
    pub signed: bool,
	/// Width and height of cell in pixels.
	pub cell_side: usize,
	/// Width and height of block in cells.
	pub block_side: usize,
	/// Offset of the start of one block from the next in cells.
	pub block_stride: usize
	// TODO: choice of normalisation - for now we just scale to unit L2 norm
}

impl HogOptions {
    pub fn new(orientations: usize, signed: bool, cell_side: usize,
        block_side: usize, block_stride: usize) -> HogOptions {
        HogOptions {
            orientations: orientations,
            signed: signed,
            cell_side: cell_side,
            block_side: block_side,
            block_stride: block_stride}
    }
}

/// HoG options plus values calculated from these options and the desired
/// image dimensions. Validation must occur when instances of this struct
/// are created - functions receiving a spec will assume that it is valid.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct HogSpec {
	/// Original options.
	options: HogOptions,
	/// Number of non-overlapping cells required to cover the image's width.
	cells_wide: usize,
	/// Number of non-overlapping cells required to cover the image height.
	cells_high: usize,
	/// Number of (possibly overlapping) blocks required to cover the image's width.
	blocks_wide: usize,
	/// Number of (possibly overlapping) blocks required to cover the image's height.
	blocks_high: usize
}

impl HogSpec {
	/// Returns None if image dimensions aren't compatible with the provided options.
	fn from_options(width: u32, height: u32, options: HogOptions) -> Option<HogSpec> {
		let w = width as usize;
		let h = height as usize;
		let divides_evenly = |n, q| n % q == 0;

		if !divides_evenly(w, options.cell_side) ||
		   !divides_evenly(h, options.cell_side) ||
		   !divides_evenly(w - options.block_side, options.block_stride) ||
		   !divides_evenly(h - options.block_side, options.block_stride) {
            return None;
	   	}

		let cells_wide = w / options.cell_side;
		let cells_high = h / options.cell_side;
		Some(HogSpec {
			options: options,
			cells_wide: cells_wide,
			cells_high: cells_high,
			blocks_wide: num_blocks(cells_wide, options.block_side, options.block_stride),
			blocks_high: num_blocks(cells_high, options.block_side, options.block_stride)
		})
	}

	/// The size in floats of the descriptor for a single block.
	fn block_descriptor_length(&self) -> usize {
		self.options.orientations * self.options.block_side.pow(2)
	}

	/// The total size in floats of the HoG descriptor with these dimensions.
	fn descriptor_length(&self) -> usize {
		self.blocks_wide * self.blocks_high * self.block_descriptor_length()
	}

	/// Dimensions of a grid of cell histograms, viewed as a 3d array.
	/// Innermost dimension is orientation bin, horizontal cell location,
	/// then vertical cell location.
	fn cell_grid_lengths(&self) -> [usize; 3] {
		[self.options.orientations, self.cells_wide, self.cells_high]
	}

	/// Dimensions of a grid of block descriptors, viewed as a 3d array.
	/// Innermost dimension is block descriptor position, then horizontal block location,
	/// then vertical block location.
	fn block_grid_lengths(&self) -> [usize; 3] {
		[self.block_descriptor_length(), self.blocks_wide, self.blocks_high]
	}

	/// Area of an image cell in pixels.
	fn cell_area(&self) -> usize {
		self.options.cell_side * self.options.cell_side
	}
}

/// Number of blocks required to cover num_cells cells when each block is
/// block_side long and blocks are staggered by block_stride. Assumes that
/// options are compatible (see is_valid_size function).
fn num_blocks(num_cells: usize, block_side: usize, block_stride: usize) -> usize
{
	(num_cells + block_stride - block_side) / block_stride
}

/// Computes the HoG descriptor of an image, or None if the provided
/// options are incompatible with the image size.
// TODO: produce a helpful error message if the options are invalid
// TODO: support color images by taking the channel with maximum gradient at each point
pub fn hog<I>(image: &I, options: HogOptions) -> Option<Vec<f32>>
	where I: GenericImage<Pixel=Luma<u8>> + 'static {
	let hog_spec: HogSpec;
	match HogSpec::from_options(image.width(), image.height(), options) {
		None => return None,
		Some(spec) => hog_spec = spec
	}
	Some(hog_impl(image, hog_spec))
}

/// Computes the HoG descriptor of an image. Assumes that the provided dimensions are valid.
fn hog_impl<I>(image: &I, spec: HogSpec) -> Vec<f32>
	where I: GenericImage<Pixel=Luma<u8>> + 'static {

	let mut grid: Array3d<f32> = cell_histograms(image, spec);
	let mut descriptor = Array3d::new(spec.block_grid_lengths());

	for by in 0..spec.blocks_high {
		for bx in 0..spec.blocks_wide {
			let mut block_view = descriptor.view_mut();
			let mut block_data = block_view.inner_slice_mut(bx, by);
			let mut block = View3d::from_raw(&mut block_data, spec.cell_grid_lengths());

			for iy in 0..spec.options.block_side {
				let cy = by * spec.options.block_stride + iy;

				for ix in 0..spec.options.block_side {
					let cx = bx * spec.options.block_stride + ix;
					let mut slice = block.inner_slice_mut(ix, iy);
					let hist_view = grid.view_mut();
					let hist = hist_view.inner_slice(cx, cy);

					// TODO: do this faster
					for dir in 0..spec.options.orientations {
						slice[dir] = hist[dir];
					}
					let norm = l2_norm(slice);
					for dir in 0..spec.options.orientations {
						slice[dir] /= norm;
					}
				}
			}
		}
	}

	descriptor.data
}

/// Computes orientation histograms for each cell of an image. Assumes that
/// the provided dimensions are valid.
fn cell_histograms<I>(image: &I, spec: HogSpec) -> Array3d<f32>
    where I: GenericImage<Pixel=Luma<u8>> + 'static {

    let (width, height) = image.dimensions();
	let mut grid = Array3d::new(spec.cell_grid_lengths());
	let cell_area = spec.cell_area() as f32;
	let cell_side = spec.options.cell_side as f32;
	let horizontal = horizontal_sobel(image);
	let vertical = vertical_sobel(image);
	let interval = orientation_bin_width(spec.options);

	for y in 0..height {
		let y_inter = Interpolation::from_position(y as f32 / cell_side);

		for x in 0..width {
			let x_inter = Interpolation::from_position(x as f32 / cell_side);

			let h = horizontal.get_pixel(x, y)[0] as f32;
			let v = vertical.get_pixel(x, y)[0] as f32;
			let m = (h.powi(2) + v.powi(2)).sqrt();
			let d = v.atan2(h);

			let o_inter
				= Interpolation::from_position_wrapping(d / interval, spec.options.orientations);

			for iy in 0..2usize {
				for ix in 0..2usize {
					for io in 0..2usize {
						if contains_outer(&grid.view_mut(), ix, iy) {
							let wy = y_inter.weights[iy];
							let wx = x_inter.weights[ix];
							let wo = o_inter.weights[io];
							let py = y_inter.indices[iy];
							let px = x_inter.indices[ix];
							let po = o_inter.indices[io];
							let up = wy * wx * wo * m / cell_area;
							let mut grid_view = grid.view_mut();
							let current = *grid_view.at_mut([po, px, py]);
						 	*grid_view.at_mut([po, px, py]) = current + up;
						}
					}
				}
			}
		}
	}

	grid
}

/// True if the given outer two indices into a view are within bounds.
fn contains_outer<T>(view: &View3d<T>, u: usize, v: usize) -> bool {
	u >= view.lengths[1] || v >= view.lengths[2]
}

/// Width of an orientation histogram bin in radians.
fn orientation_bin_width(options: HogOptions) -> f32 {
	let dir_range = if options.signed {2f32 * f32::consts::PI} else {f32::consts::PI};
	dir_range / (options.orientations as f32)
}

/// Indices and weights for an interpolated value.
struct Interpolation {
	indices: [usize; 2],
	weights: [f32; 2]
}

impl Interpolation {
	/// Interpolates between two indices, without wrapping.
	fn from_position(pos: f32) -> Interpolation {
		let fraction = pos - pos.floor();
		Interpolation {
			indices: [pos as usize, pos as usize + 1],
			weights: [1f32 - fraction, fraction]
		}
	}

	/// Interpolates between two indices, wrapping the right index.
	/// Assumes that the left index is within bounds.
	fn from_position_wrapping(pos: f32, length: usize) -> Interpolation {
		let mut right = (pos as usize) + 1;
		if right >= length {
			right = 0;
		}
		let fraction = pos - pos.floor();
		Interpolation {
			indices: [pos as usize, right],
			weights: [1f32 - fraction, fraction]
		}
	}
}

#[cfg(test)]
mod test {

    use super::{
        HogOptions,
        HogSpec,
        num_blocks
    };

    #[test]
    fn test_num_blocks() {
        // -----
        // ***
        //   ***
        assert_eq!(num_blocks(5, 3, 2), 2);
        // -----
        // *****
        assert_eq!(num_blocks(5, 5, 2), 1);
        // ----
        // **
        //   **
        assert_eq!(num_blocks(4, 2, 2), 2);
        // ---
        // *
        //  *
        //   *
        assert_eq!(num_blocks(3, 1, 1), 3);
    }

    #[test]
    fn test_hog_spec() {
        assert_eq!(
			HogSpec::from_options(40, 40, HogOptions::new(8, true, 5, 2, 1))
				.unwrap().descriptor_length(), 1568);
        assert_eq!(
			HogSpec::from_options(40, 40, HogOptions::new(9, true, 4, 2, 1))
				.unwrap().descriptor_length(), 2916);
        assert_eq!(
			HogSpec::from_options(40, 40, HogOptions::new(8, true, 4, 2, 1))
				.unwrap().descriptor_length(), 2592);
    }
}
<commit_msg>Fix comment<commit_after>//! Histogram of oriented gradients and helpers for visualizing them.
//! http://lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pdf

use image::{
	GenericImage,
	Luma
};
use gradients::{
	horizontal_sobel,
	vertical_sobel
};
use multiarray::{
	Array3d,
	View3d
};
use math::l2_norm;
use std::f32;

/// Parameters for HoG descriptors.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct HogOptions {
	/// Number of gradient orientation bins.
	pub orientations: usize,
	/// Whether gradients in opposite directions are treated as equal.
    pub signed: bool,
	/// Width and height of cell in pixels.
	pub cell_side: usize,
	/// Width and height of block in cells.
	pub block_side: usize,
	/// Offset of the start of one block from the next in cells.
	pub block_stride: usize
	// TODO: choice of normalisation - for now we just scale to unit L2 norm
}

impl HogOptions {
    pub fn new(orientations: usize, signed: bool, cell_side: usize,
        block_side: usize, block_stride: usize) -> HogOptions {
        HogOptions {
            orientations: orientations,
            signed: signed,
            cell_side: cell_side,
            block_side: block_side,
            block_stride: block_stride}
    }
}

/// HoG options plus values calculated from these options and the desired
/// image dimensions. Validation must occur when instances of this struct
/// are created - functions receiving a spec will assume that it is valid.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct HogSpec {
	/// Original options.
	options: HogOptions,
	/// Number of non-overlapping cells required to cover the image's width.
	cells_wide: usize,
	/// Number of non-overlapping cells required to cover the image height.
	cells_high: usize,
	/// Number of (possibly overlapping) blocks required to cover the image's width.
	blocks_wide: usize,
	/// Number of (possibly overlapping) blocks required to cover the image's height.
	blocks_high: usize
}

impl HogSpec {
	/// Returns None if image dimensions aren't compatible with the provided options.
	fn from_options(width: u32, height: u32, options: HogOptions) -> Option<HogSpec> {
		let w = width as usize;
		let h = height as usize;
		let divides_evenly = |n, q| n % q == 0;

		if !divides_evenly(w, options.cell_side) ||
		   !divides_evenly(h, options.cell_side) ||
		   !divides_evenly(w - options.block_side, options.block_stride) ||
		   !divides_evenly(h - options.block_side, options.block_stride) {
            return None;
	   	}

		let cells_wide = w / options.cell_side;
		let cells_high = h / options.cell_side;
		Some(HogSpec {
			options: options,
			cells_wide: cells_wide,
			cells_high: cells_high,
			blocks_wide: num_blocks(cells_wide, options.block_side, options.block_stride),
			blocks_high: num_blocks(cells_high, options.block_side, options.block_stride)
		})
	}

	/// The size in floats of the descriptor for a single block.
	fn block_descriptor_length(&self) -> usize {
		self.options.orientations * self.options.block_side.pow(2)
	}

	/// The total size in floats of the HoG descriptor with these dimensions.
	fn descriptor_length(&self) -> usize {
		self.blocks_wide * self.blocks_high * self.block_descriptor_length()
	}

	/// Dimensions of a grid of cell histograms, viewed as a 3d array.
	/// Innermost dimension is orientation bin, horizontal cell location,
	/// then vertical cell location.
	fn cell_grid_lengths(&self) -> [usize; 3] {
		[self.options.orientations, self.cells_wide, self.cells_high]
	}

	/// Dimensions of a grid of block descriptors, viewed as a 3d array.
	/// Innermost dimension is block descriptor position, then horizontal block location,
	/// then vertical block location.
	fn block_grid_lengths(&self) -> [usize; 3] {
		[self.block_descriptor_length(), self.blocks_wide, self.blocks_high]
	}

	/// Area of an image cell in pixels.
	fn cell_area(&self) -> usize {
		self.options.cell_side * self.options.cell_side
	}
}

/// Number of blocks required to cover num_cells cells when each block is
/// block_side long and blocks are staggered by block_stride. Assumes that
/// options are compatible.
fn num_blocks(num_cells: usize, block_side: usize, block_stride: usize) -> usize
{
	(num_cells + block_stride - block_side) / block_stride
}

/// Computes the HoG descriptor of an image, or None if the provided
/// options are incompatible with the image size.
// TODO: produce a helpful error message if the options are invalid
// TODO: support color images by taking the channel with maximum gradient at each point
pub fn hog<I>(image: &I, options: HogOptions) -> Option<Vec<f32>>
	where I: GenericImage<Pixel=Luma<u8>> + 'static {
	let hog_spec: HogSpec;
	match HogSpec::from_options(image.width(), image.height(), options) {
		None => return None,
		Some(spec) => hog_spec = spec
	}
	Some(hog_impl(image, hog_spec))
}

/// Computes the HoG descriptor of an image. Assumes that the provided dimensions are valid.
fn hog_impl<I>(image: &I, spec: HogSpec) -> Vec<f32>
	where I: GenericImage<Pixel=Luma<u8>> + 'static {

	let mut grid: Array3d<f32> = cell_histograms(image, spec);
	let mut descriptor = Array3d::new(spec.block_grid_lengths());

	for by in 0..spec.blocks_high {
		for bx in 0..spec.blocks_wide {
			let mut block_view = descriptor.view_mut();
			let mut block_data = block_view.inner_slice_mut(bx, by);
			let mut block = View3d::from_raw(&mut block_data, spec.cell_grid_lengths());

			for iy in 0..spec.options.block_side {
				let cy = by * spec.options.block_stride + iy;

				for ix in 0..spec.options.block_side {
					let cx = bx * spec.options.block_stride + ix;
					let mut slice = block.inner_slice_mut(ix, iy);
					let hist_view = grid.view_mut();
					let hist = hist_view.inner_slice(cx, cy);

					// TODO: do this faster
					for dir in 0..spec.options.orientations {
						slice[dir] = hist[dir];
					}
					let norm = l2_norm(slice);
					for dir in 0..spec.options.orientations {
						slice[dir] /= norm;
					}
				}
			}
		}
	}

	descriptor.data
}

/// Computes orientation histograms for each cell of an image. Assumes that
/// the provided dimensions are valid.
fn cell_histograms<I>(image: &I, spec: HogSpec) -> Array3d<f32>
    where I: GenericImage<Pixel=Luma<u8>> + 'static {

    let (width, height) = image.dimensions();
	let mut grid = Array3d::new(spec.cell_grid_lengths());
	let cell_area = spec.cell_area() as f32;
	let cell_side = spec.options.cell_side as f32;
	let horizontal = horizontal_sobel(image);
	let vertical = vertical_sobel(image);
	let interval = orientation_bin_width(spec.options);

	for y in 0..height {
		let y_inter = Interpolation::from_position(y as f32 / cell_side);

		for x in 0..width {
			let x_inter = Interpolation::from_position(x as f32 / cell_side);

			let h = horizontal.get_pixel(x, y)[0] as f32;
			let v = vertical.get_pixel(x, y)[0] as f32;
			let m = (h.powi(2) + v.powi(2)).sqrt();
			let d = v.atan2(h);

			let o_inter
				= Interpolation::from_position_wrapping(d / interval, spec.options.orientations);

			for iy in 0..2usize {
				for ix in 0..2usize {
					for io in 0..2usize {
						if contains_outer(&grid.view_mut(), ix, iy) {
							let wy = y_inter.weights[iy];
							let wx = x_inter.weights[ix];
							let wo = o_inter.weights[io];
							let py = y_inter.indices[iy];
							let px = x_inter.indices[ix];
							let po = o_inter.indices[io];
							let up = wy * wx * wo * m / cell_area;
							let mut grid_view = grid.view_mut();
							let current = *grid_view.at_mut([po, px, py]);
						 	*grid_view.at_mut([po, px, py]) = current + up;
						}
					}
				}
			}
		}
	}

	grid
}

/// True if the given outer two indices into a view are within bounds.
fn contains_outer<T>(view: &View3d<T>, u: usize, v: usize) -> bool {
	u >= view.lengths[1] || v >= view.lengths[2]
}

/// Width of an orientation histogram bin in radians.
fn orientation_bin_width(options: HogOptions) -> f32 {
	let dir_range = if options.signed {2f32 * f32::consts::PI} else {f32::consts::PI};
	dir_range / (options.orientations as f32)
}

/// Indices and weights for an interpolated value.
struct Interpolation {
	indices: [usize; 2],
	weights: [f32; 2]
}

impl Interpolation {
	/// Interpolates between two indices, without wrapping.
	fn from_position(pos: f32) -> Interpolation {
		let fraction = pos - pos.floor();
		Interpolation {
			indices: [pos as usize, pos as usize + 1],
			weights: [1f32 - fraction, fraction]
		}
	}

	/// Interpolates between two indices, wrapping the right index.
	/// Assumes that the left index is within bounds.
	fn from_position_wrapping(pos: f32, length: usize) -> Interpolation {
		let mut right = (pos as usize) + 1;
		if right >= length {
			right = 0;
		}
		let fraction = pos - pos.floor();
		Interpolation {
			indices: [pos as usize, right],
			weights: [1f32 - fraction, fraction]
		}
	}
}

#[cfg(test)]
mod test {

    use super::{
        HogOptions,
        HogSpec,
        num_blocks
    };

    #[test]
    fn test_num_blocks() {
        // -----
        // ***
        //   ***
        assert_eq!(num_blocks(5, 3, 2), 2);
        // -----
        // *****
        assert_eq!(num_blocks(5, 5, 2), 1);
        // ----
        // **
        //   **
        assert_eq!(num_blocks(4, 2, 2), 2);
        // ---
        // *
        //  *
        //   *
        assert_eq!(num_blocks(3, 1, 1), 3);
    }

    #[test]
    fn test_hog_spec() {
        assert_eq!(
			HogSpec::from_options(40, 40, HogOptions::new(8, true, 5, 2, 1))
				.unwrap().descriptor_length(), 1568);
        assert_eq!(
			HogSpec::from_options(40, 40, HogOptions::new(9, true, 4, 2, 1))
				.unwrap().descriptor_length(), 2916);
        assert_eq!(
			HogSpec::from_options(40, 40, HogOptions::new(8, true, 4, 2, 1))
				.unwrap().descriptor_length(), 2592);
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::fmt;
use std::error;
use std::vec::Vec;
use byteorder;
use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};

pub struct XdrReader {
	reader : io::Cursor<Vec<u8>>
}

pub struct XdrWriter {
	writer : io::Cursor<Vec<u8>>
}

impl XdrWriter {
	pub fn new() -> XdrWriter {
		let v : Vec<u8>= Vec::new();
		XdrWriter{ writer: io::Cursor::new(v)}
	}

	pub fn get_buffer(self) -> Vec<u8> {
		self.writer.into_inner()
	}

}
pub trait XdrPrimitive {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>;
	fn write_to_xdr(x: &mut XdrWriter, v: Self);
}

impl XdrPrimitive for u32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u32, Error>{
		match x.reader.read_u32::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u16, Error>{
		match x.reader.read_u32::<BigEndian>() {
			Ok(v) => Ok(v as u16),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for u8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u8, Error>{
		match x.reader.read_u32::<BigEndian>() {
			Ok(v) => Ok(v as u8),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for i8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i8, Error>{
		match x.reader.read_i32::<BigEndian>() {
			Ok(v) => Ok(v as i8),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i16, Error>{
		match x.reader.read_i32::<BigEndian>() {
			Ok(v) => Ok(v as i16),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i32, Error>{
		match x.reader.read_i32::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for i64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i64, Error>{
		match x.reader.read_i64::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i64::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u64, Error>{
		match x.reader.read_u64::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u64::<BigEndian>(v).unwrap();
	}

}

impl XdrPrimitive for f32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f32, Error>{
		match x.reader.read_f32::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f32::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for f64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f64, Error>{
		match x.reader.read_f64::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f64::<BigEndian>(v).unwrap();
	}
}
<commit_msg>Implement error:Error trait for Error enum<commit_after>use std::io;
use std::fmt;
use std::error;
use std::vec::Vec;
use byteorder;
use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};

#[derive(Debug)]
pub enum Error {
	Io(byteorder::Error),
	InvalidValue,
	InvalidType
}

impl From<byteorder::Error> for Error {
	fn from(err: byteorder::Error) -> Error { Error::Io(err) }
}

impl From<Error> for byteorder::Error {
	fn from(err: Error) -> byteorder::Error {
		match err {
			Error::Io(err) => err,
			Error::InvalidValue => byteorder::Error::Io(io::Error::new(io::ErrorKind::Other, "Invalid value")),
			Error::InvalidType => byteorder::Error::Io(io::Error::new(io::ErrorKind::Other, "Invalid type")),
		}
	}
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
	        match *self {
				Error::InvalidValue => write!(f, "Invalid value."),
				Error::InvalidType => write!(f, "Invalid type."),
				Error::Io(ref err) => err.fmt(f),
			}
	}
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidValue => "Invalid value.",
            Error::InvalidType => "Invalid type.",
            Error::Io(ref err) => error::Error::description(err),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::InvalidValue => None,
            Error::InvalidType => None,
            Error::Io(ref err) => err.cause(),
        }
    }
}

pub struct XdrReader {
	reader : io::Cursor<Vec<u8>>
}

pub struct XdrWriter {
	writer : io::Cursor<Vec<u8>>
}

impl XdrWriter {
	pub fn new() -> XdrWriter {
		let v : Vec<u8>= Vec::new();
		XdrWriter{ writer: io::Cursor::new(v)}
	}

	pub fn get_buffer(self) -> Vec<u8> {
		self.writer.into_inner()
	}

}
pub trait XdrPrimitive {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>;
	fn write_to_xdr(x: &mut XdrWriter, v: Self);
}

impl XdrPrimitive for u32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u32, Error>{
		match x.reader.read_u32::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u16, Error>{
		match x.reader.read_u32::<BigEndian>() {
			Ok(v) => Ok(v as u16),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for u8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u8, Error>{
		match x.reader.read_u32::<BigEndian>() {
			Ok(v) => Ok(v as u8),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for i8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i8, Error>{
		match x.reader.read_i32::<BigEndian>() {
			Ok(v) => Ok(v as i8),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i16, Error>{
		match x.reader.read_i32::<BigEndian>() {
			Ok(v) => Ok(v as i16),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i32, Error>{
		match x.reader.read_i32::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for i64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i64, Error>{
		match x.reader.read_i64::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i64::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u64, Error>{
		match x.reader.read_u64::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u64::<BigEndian>(v).unwrap();
	}

}

impl XdrPrimitive for f32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f32, Error>{
		match x.reader.read_f32::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f32::<BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for f64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f64, Error>{
		match x.reader.read_f64::<BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(v)
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f64::<BigEndian>(v).unwrap();
	}
}
<|endoftext|>