<commit_before>use std::ops::{Add};
use util::DijkstraNode;
use util::V2;
use util;
use dir6::Dir6;
use entity::Entity;
use terrain::TerrainType;
use geom::HexGeom;
use world;
use action;
use flags;
use ecs::{ComponentAccess};
use {Light};

/// Unambiguous location in the game world.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Show, RustcEncodable, RustcDecodable)]
pub struct Location {
    pub x: i8,
    pub y: i8,
    // TODO: Add third dimension for multiple persistent levels.
}

impl Location {
    pub fn new(x: i8, y: i8) -> Location { Location { x: x, y: y } }

    /// Return terrain at the location.
    pub fn terrain(&self) -> TerrainType {
        let mut ret = world::with(|w| w.area.terrain(*self));
        // Mobs standing on doors make the doors open.
        if ret == TerrainType::Door && self.has_mobs() {
            ret = TerrainType::OpenDoor;
        }
        // Grass is only occasionally fancy.
        // TODO: Make variant tiles into a generic method.
        if ret == TerrainType::Grass {
            let n = util::noise(self.x as i32 + self.y as i32 * 57);
            if n > 0.85 {
                ret = TerrainType::Grass2;
            }
        }
        ret
    }

    pub fn blocks_sight(&self) -> bool {
        self.terrain().blocks_sight()
    }

    pub fn blocks_walk(&self) -> bool {
        if self.terrain().blocks_walk() { return true; }
        if self.entities().iter().any(|e| e.blocks_walk()) {
            return true;
        }
        false
    }

    pub fn entities(&self) -> Vec<Entity> {
        world::with(|w| w.spatial.entities_at(*self))
    }

    /// Return the most significant entity at the location.
    pub fn main_entity(&self) -> Option<Entity> {
        let e = self.entities();
        // TODO: Actually make the result entity the most significant one (eg.
        // mob if there are both mobs and items).
        if e.len() > 0 { Some(e[0]) } else { None }
    }

    /// Return an item at the location that can be interacted with.
    pub fn top_item(&self) -> Option<Entity> {
        self.entities().iter().find(|e| e.can_be_picked_up()).map(|&x| x)
    }

    pub fn has_entities(&self) -> bool { !self.entities().is_empty() }

    pub fn has_mobs(&self) -> bool {
        self.entities().iter().any(|e| e.is_mob())
    }

    /// Returns the mob at the given location, if any. The assumption is that
    /// there is always a single primary mob in locations that contain
    /// multiple mobs that can be selected as the return value.
    pub fn mob_at(&self) -> Option<Entity> {
        for &e in self.entities().iter() {
            if e.is_mob() {
                return Some(e);
            }
        }
        None
    }

    /// Vector pointing from this location into the other one if the locations
    /// are on the same Euclidean plane.
    pub fn v2_at(&self, other: Location) -> Option<V2<i32>> {
        // Return None for pairs on different floors if multi-floor support is
        // added.
        Some(V2(other.x as i32, other.y as i32) - V2(self.x as i32, self.y as i32))
    }

    /// Hex distance from this location to the other one, if applicable.
    pub fn distance_from(&self, other: Location) -> Option<i32> {
        if let Some(v) = self.v2_at(other) { Some(v.hex_dist()) } else { None }
    }

    pub fn dir6_towards(&self, other: Location) -> Option<Dir6> {
        if let Some(v) = self.v2_at(other) { Some(Dir6::from_v2(v)) } else { None }
    }

    /// Return the status of the location in the player's field of view.
    /// Returns None if the location is unexplored.
    pub fn fov_status(&self) -> Option<::FovStatus> {
        if let Some(p) = action::player() {
            match world::with(|w| {
                if let Some(ref mm) = w.map_memories().get(p) {
                    Ok (if mm.seen.contains(self) {
                        Some(::FovStatus::Seen)
                    } else if mm.remembered.contains(self) {
                        Some(::FovStatus::Remembered)
                    } else {
                        None
                    })
                } else {
                    Err(())
                }
            }) {
                Ok(x) => return x,
                _ => ()
            };
        }
        // Just show everything by default.
        Some(::FovStatus::Seen)
    }

    /// Area name for the location.
    pub fn name(&self) -> String {
        match action::current_depth() {
            0 => "Limbo".to_string(),
            1 => "Overworld".to_string(),
            n => format!("Dungeon {}", n - 1)
        }
    }

    /// Light level for the location.
    pub fn light(&self) -> Light {
        if action::current_depth() == 1 {
            // Topside, full light.
            return Light::new(1.0);
        }
        if self.terrain().is_luminous() {
            return Light::new(1.0);
        }

        if let Some(d) = self.distance_from(flags::camera()) {
            let lum = 1.0 - d as f32 / 10.0;
            return Light::new(if lum >= 0.0 { lum } else { 0.0 });
        }
        return Light::new(1.0);
    }
}

impl Add<V2<i32>> for Location {
    type Output = Location;
    fn add(self, other: V2<i32>) -> Location {
        Location::new(
            (self.x as i32 + other.0) as i8,
            (self.y as i32 + other.1) as i8)
    }
}

/// An abstract type that maps a 2D plane into game world Locations. This can
/// be just a straightforward mapping, or it can involve something exotic like
/// a non-Euclidean space where the lines from the Chart origin are raycast
/// through portals.
pub trait Chart: Add<V2<i32>, Output=Location> {}

impl Chart for Location {}

/// The other half of a Chart, mapping Locations into 2D plane positions, if a
/// mapping exists. It depends on the weirdness of a space how trivial this is
/// to do.
pub trait Unchart {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>>;
}

impl Unchart for Location {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>> {
        Some(V2(loc.x as i32 - self.x as i32, loc.y as i32 - self.y as i32))
    }
}

impl DijkstraNode for Location {
    fn neighbors(&self) -> Vec<Location> {
        Dir6::iter().map(|d| *self + d.to_v2()).collect()
    }
}
<commit_msg>Initial light level underground is below 1.0<commit_after>use std::ops::{Add};
use util::DijkstraNode;
use util::V2;
use util;
use dir6::Dir6;
use entity::Entity;
use terrain::TerrainType;
use geom::HexGeom;
use world;
use action;
use flags;
use ecs::{ComponentAccess};
use {Light};

/// Unambiguous location in the game world.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Show, RustcEncodable, RustcDecodable)]
pub struct Location {
    pub x: i8,
    pub y: i8,
    // TODO: Add third dimension for multiple persistent levels.
}

impl Location {
    pub fn new(x: i8, y: i8) -> Location { Location { x: x, y: y } }

    /// Return terrain at the location.
    pub fn terrain(&self) -> TerrainType {
        let mut ret = world::with(|w| w.area.terrain(*self));
        // Mobs standing on doors make the doors open.
        if ret == TerrainType::Door && self.has_mobs() {
            ret = TerrainType::OpenDoor;
        }
        // Grass is only occasionally fancy.
        // TODO: Make variant tiles into a generic method.
        if ret == TerrainType::Grass {
            let n = util::noise(self.x as i32 + self.y as i32 * 57);
            if n > 0.85 {
                ret = TerrainType::Grass2;
            }
        }
        ret
    }

    pub fn blocks_sight(&self) -> bool {
        self.terrain().blocks_sight()
    }

    pub fn blocks_walk(&self) -> bool {
        if self.terrain().blocks_walk() { return true; }
        if self.entities().iter().any(|e| e.blocks_walk()) {
            return true;
        }
        false
    }

    pub fn entities(&self) -> Vec<Entity> {
        world::with(|w| w.spatial.entities_at(*self))
    }

    /// Return the most significant entity at the location.
    pub fn main_entity(&self) -> Option<Entity> {
        let e = self.entities();
        // TODO: Actually make the result entity the most significant one (eg.
        // mob if there are both mobs and items).
        if e.len() > 0 { Some(e[0]) } else { None }
    }

    /// Return an item at the location that can be interacted with.
    pub fn top_item(&self) -> Option<Entity> {
        self.entities().iter().find(|e| e.can_be_picked_up()).map(|&x| x)
    }

    pub fn has_entities(&self) -> bool { !self.entities().is_empty() }

    pub fn has_mobs(&self) -> bool {
        self.entities().iter().any(|e| e.is_mob())
    }

    /// Returns the mob at the given location, if any. The assumption is that
    /// there is always a single primary mob in locations that contain
    /// multiple mobs that can be selected as the return value.
    pub fn mob_at(&self) -> Option<Entity> {
        for &e in self.entities().iter() {
            if e.is_mob() {
                return Some(e);
            }
        }
        None
    }

    /// Vector pointing from this location into the other one if the locations
    /// are on the same Euclidean plane.
    pub fn v2_at(&self, other: Location) -> Option<V2<i32>> {
        // Return None for pairs on different floors if multi-floor support is
        // added.
        Some(V2(other.x as i32, other.y as i32) - V2(self.x as i32, self.y as i32))
    }

    /// Hex distance from this location to the other one, if applicable.
    pub fn distance_from(&self, other: Location) -> Option<i32> {
        if let Some(v) = self.v2_at(other) { Some(v.hex_dist()) } else { None }
    }

    pub fn dir6_towards(&self, other: Location) -> Option<Dir6> {
        if let Some(v) = self.v2_at(other) { Some(Dir6::from_v2(v)) } else { None }
    }

    /// Return the status of the location in the player's field of view.
    /// Returns None if the location is unexplored.
    pub fn fov_status(&self) -> Option<::FovStatus> {
        if let Some(p) = action::player() {
            match world::with(|w| {
                if let Some(ref mm) = w.map_memories().get(p) {
                    Ok (if mm.seen.contains(self) {
                        Some(::FovStatus::Seen)
                    } else if mm.remembered.contains(self) {
                        Some(::FovStatus::Remembered)
                    } else {
                        None
                    })
                } else {
                    Err(())
                }
            }) {
                Ok(x) => return x,
                _ => ()
            };
        }
        // Just show everything by default.
        Some(::FovStatus::Seen)
    }

    /// Area name for the location.
    pub fn name(&self) -> String {
        match action::current_depth() {
            0 => "Limbo".to_string(),
            1 => "Overworld".to_string(),
            n => format!("Dungeon {}", n - 1)
        }
    }

    /// Light level for the location.
    pub fn light(&self) -> Light {
        if action::current_depth() == 1 {
            // Topside, full light.
            return Light::new(1.0);
        }
        if self.terrain().is_luminous() {
            return Light::new(1.0);
        }

        if let Some(d) = self.distance_from(flags::camera()) {
            let lum = 0.8 - d as f32 / 10.0;
            return Light::new(if lum >= 0.0 { lum } else { 0.0 });
        }
        return Light::new(1.0);
    }
}

impl Add<V2<i32>> for Location {
    type Output = Location;
    fn add(self, other: V2<i32>) -> Location {
        Location::new(
            (self.x as i32 + other.0) as i8,
            (self.y as i32 + other.1) as i8)
    }
}

/// An abstract type that maps a 2D plane into game world Locations. This can
/// be just a straightforward mapping, or it can involve something exotic like
/// a non-Euclidean space where the lines from the Chart origin are raycast
/// through portals.
pub trait Chart: Add<V2<i32>, Output=Location> {}

impl Chart for Location {}

/// The other half of a Chart, mapping Locations into 2D plane positions, if a
/// mapping exists. It depends on the weirdness of a space how trivial this is
/// to do.
pub trait Unchart {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>>;
}

impl Unchart for Location {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>> {
        Some(V2(loc.x as i32 - self.x as i32, loc.y as i32 - self.y as i32))
    }
}

impl DijkstraNode for Location {
    fn neighbors(&self) -> Vec<Location> {
        Dir6::iter().map(|d| *self + d.to_v2()).collect()
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
use clap::{App, Arg, ArgMatches};
extern crate imageflow_core;
use imageflow_core::boring::*;

use imageflow_core::ffi::{Filter, TESTED_FILTER_OPTIONS};
use std::path::{PathBuf, Path};
use std::fs::File;
use std::io::Write;
// TODO
// Disclaim use for jpeg optimization
// Disclaim use for png or gif files
// Focus on scaling
// No metadata support
// Let users adjust quality setting

fn build_app() -> App<'static, 'static> {

    App::new("flow-proto1")
        .version("0.0.1")
        .author("Email us at imageflow@imazen.io")
        .about("Throwaway prototype tool to play with downscaling jpegs via libimageflow. Not for \
                production use. Optimizations disabled on Windows; expect poor perf there. Lots \
                of things are broken, but you should still tell us about them.")
        .arg(Arg::with_name("v")
            .short("v")
            .long("verbose")
            .multiple(true)
            .help("Sets the level of verbosity"))
        .arg(Arg::with_name("input")
            .short("i")
            .long("input")
            .value_name("FILEIN")
            .takes_value(true)
            .required(true)
            .help("path to input file"))
        .arg(Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("FILEOUT")
            .takes_value(true)
            .required(true)
            .help("path to output file"))
        .arg(Arg::with_name("width")
            .short("w")
            .long("width")
            .value_name("WIDTH")
            .takes_value(true)
            .required(true)
            .help("scale to this width or smaller."))
        .arg(Arg::with_name("height")
            .short("h")
            .long("height")
            .value_name("HEIGHT")
            .takes_value(true)
            .required(true)
            .help("scale to this height or smaller."))
        .arg(Arg::with_name("jpeg-quality")
            .long("jpeg-quality")
            .value_name("0..100")
            .takes_value(true)
            .help("Jpeg compression level."))
        .arg(Arg::with_name("benchmark-threads")
            .long("benchmark-threads")
            .value_name("1..16")
            .takes_value(true)
            .help("Number of concurrent threads to use when benchmarking (one operation per \
                   thread)."))
        .arg(Arg::with_name("benchmark-count")
            .long("benchmark-count")
            .value_name("1..1000")
            .takes_value(true)
            .help("How many times to perform the specified operation."))
        .arg(Arg::with_name("sharpen")
            .long("sharpen")
            .value_name("0..100")
            .takes_value(true)
            .help("Percent sharpening to apply."))
        .arg(Arg::with_name("format")
            .long("format")
            .value_name("png | jpg | png24")
            .takes_value(true)
            .possible_values(&["png", "jpeg", "jpg", "png24"])
            .help("Output image format to use. Baseline jpeg and 32-bit PNG supported."))
        .arg(Arg::with_name("constrain")
            .long("constrain")
            .value_name("max | distort")
            .takes_value(true)
            .possible_values(&["max", "distort"])
            .help("Constrain the image within a bounding box (max) or stretch to fit (distort)"))
        .arg(Arg::with_name("down-filter")
            .long("down-filter")
            .value_name("FILTER")
            .takes_value(true)
            .possible_values(TESTED_FILTER_OPTIONS)
            .help("Filter to use when downscaling"))
        .arg(Arg::with_name("up-filter")
            .long("up-filter")
            .value_name("FILTER")
            .possible_values(TESTED_FILTER_OPTIONS)
            .takes_value(true)
            .help("Filter to use when upscaling"))
        .arg(Arg::with_name("incorrectgamma")
            .long("incorrectgamma")
            .help("Enables incorrect gamma handling (for benchmarking comparison purposes)."))
        .arg(Arg::with_name("min_precise_scaling_ratio")
            .long("min_precise_scaling_ratio")
            .short("mpsr")
            .value_name("MINRATIO")
            .takes_value(true)
            .help("Defaults to 2.1. Jpeg-integrated block scaling is permitted down to 2.1x \
                   final size"))
}

struct ParsedResult {
    input_file: PathBuf,
    output_file: PathBuf,
    commands: BoringCommands,
    bench: Option<BenchmarkOptions>,
}

fn parse(matches: ArgMatches) -> Result<ParsedResult, String> {

    let w = matches.value_of("width").and_then(|x| x.parse::<u32>().ok());
    let h = matches.value_of("height").and_then(|x| x.parse::<u32>().ok());

    let sharpen = matches.value_of("sharpen").and_then(|x| x.parse::<f32>().ok()); //.and_then(|x| Some(x / 100f32));

    let q = matches.value_of("jpeg-quality").and_then(|x| x.parse::<i32>().ok());

    let fmt = value_t!(matches, "format", ImageFormat).unwrap_or(ImageFormat::Jpeg);

    let constrain = value_t!(matches, "constrain", ConstraintMode).unwrap_or(ConstraintMode::Max);

    let down_filter = value_t!(matches, "down-filter", Filter).unwrap_or(Filter::Robidoux);
    let up_filter = value_t!(matches, "up-filter", Filter).unwrap_or(Filter::Ginseng);

    // Clap requires these to exist, thus the safe unwrap()
    let in_file = Path::new(matches.value_of("input").unwrap());
    let out_file = Path::new(matches.value_of("output").unwrap());
    let min_precise_scaling_ratio = matches.value_of("min_precise_scaling_ratio")
        .and_then(|x| x.parse::<f32>().ok());

    if w.or(h).is_none() {
        return Err("You must specified at least one of (width, height) ".to_owned());
    }
    if !in_file.is_file() {
        return Err(format!("The specified input file could not be found: {}",
                           in_file.to_str().unwrap()));
    }

    // parent() will return "", an empty path. We don't want to validate the current dir exists, that's going too far.
    let parent_exists = out_file.parent()
        .and_then(|p| match p.to_str().unwrap() {
            "" => None,
            _ => Some(p),
        })
        .map(|p| p.exists());

    // It's OK if there's no parent directory specified.
    // It's only bad if there's one specified and it doesn't exists.
    let warn_about_parent = !parent_exists.unwrap_or(true);
    if warn_about_parent {
        return Err(format!("The parent directory for the output file could not be found: {}",
                           out_file.parent().unwrap().to_str().unwrap()));
    }

    let commands = BoringCommands {
        w: w.unwrap_or(0) as i32,
        h: h.unwrap_or(0) as i32,
        sharpen: sharpen.unwrap_or(0f32) as f32,
        jpeg_quality: q.unwrap_or(90),
        fit: constrain,
        precise_scaling_ratio: min_precise_scaling_ratio.unwrap_or(2.1f32),
        luma_correct: !matches.is_present("incorrectgamma"),
        format: fmt,
        up_filter: up_filter,
        down_filter: down_filter,
    };

    let b_threads = matches.value_of("benchmark-threads").and_then(|x| x.parse::<usize>().ok());

    let b_count = matches.value_of("benchmark-count").and_then(|x| x.parse::<usize>().ok());

    let bench;
    if b_threads.is_some() || b_count.is_some() {
        bench = Some(BenchmarkOptions {
            thread_count: b_threads.unwrap_or(1),
            run_count: b_count.unwrap_or(10),
            input_path: in_file.to_path_buf(),
            commands: commands.clone(),
        });
    } else {
        bench = None;
    }

    Ok(ParsedResult {
        input_file: in_file.to_path_buf(),
        output_file: out_file.to_path_buf(),
        commands: commands,
        bench: bench,
    })

}

fn main() {
    let matches = build_app().get_matches();

    let result = match parse(matches) {
        Ok(c) => {
            match process_image_by_paths(c.input_file, c.output_file, c.commands) {
                Ok(_) if c.bench.is_some() => {
                    match benchmark(c.bench.unwrap()) {
                        Ok(results) => {
                            println!("{}", results.to_json_string());
                            Ok(())
                        }
                        Err(e) => Err(e),
                    }
                }
                Ok(res) => Ok(res),
                Err(e) => Err(e),
            }
        }
        Err(e) => Err(e),
    };

    if result.is_err() {
        println!("Failed: {}", result.err().unwrap());
        std::process::exit(1);
    }

}

#[test]
fn test_correct_parsing() {

    File::create("delete.jpg").expect("File creation should work.");

    let valid_args = vec!["flow-proto1",
                          "-i",
                          "delete.jpg",
                          "-o",
                          "b.jpg",
                          "-w",
                          "20",
                          "-h",
                          "20",
                          "--down-filter",
                          "mitchell"];

    let result = parse(build_app().get_matches_from(valid_args)).expect("To parse correctly");

    assert_eq!(result.commands.down_filter, Filter::Mitchell);

    // std::fs::remove_file("delete.jpg").unwrap();

}

#[test]
fn test_correct_execution() {

    {

        let mut f = File::create("test_input.jpg").unwrap();

        let jpeg_bytes =
            &[0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01,
              0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC2, 0x00, 0x0B, 0x08, 0x00, 0x01, 0x00, 0x01,
              0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA,
              0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10];

        f.write_all(jpeg_bytes).unwrap();
        f.sync_all().unwrap();
        drop(f);
    }

    let valid_args =
        vec!["flow-proto1", "-i", "test_input.jpg", "-o", "b.jpg", "-w", "20", "-h", "20"];

    let parsed_result = parse(build_app().get_matches_from(valid_args))
        .expect("To parse correctly");

    process_image_by_paths(parsed_result.input_file,
                           parsed_result.output_file,
                           parsed_result.commands)
        .unwrap();

    // std::fs::remove_file("delete.jpg").unwrap();

}
<commit_msg>flow-proto1 jpeg default quality should be 100<commit_after>#[macro_use]
extern crate clap;
use clap::{App, Arg, ArgMatches};
extern crate imageflow_core;
use imageflow_core::boring::*;

use imageflow_core::ffi::{Filter, TESTED_FILTER_OPTIONS};
use std::path::{PathBuf, Path};
use std::fs::File;
use std::io::Write;
// TODO
// Disclaim use for jpeg optimization
// Disclaim use for png or gif files
// Focus on scaling
// No metadata support
// Let users adjust quality setting

fn build_app() -> App<'static, 'static> {

    App::new("flow-proto1")
        .version("0.0.1")
        .author("Email us at imageflow@imazen.io")
        .about("Throwaway prototype tool to play with downscaling jpegs via libimageflow. Not for \
                production use. Optimizations disabled on Windows; expect poor perf there. Lots \
                of things are broken, but you should still tell us about them.")
        .arg(Arg::with_name("v")
            .short("v")
            .long("verbose")
            .multiple(true)
            .help("Sets the level of verbosity"))
        .arg(Arg::with_name("input")
            .short("i")
            .long("input")
            .value_name("FILEIN")
            .takes_value(true)
            .required(true)
            .help("path to input file"))
        .arg(Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("FILEOUT")
            .takes_value(true)
            .required(true)
            .help("path to output file"))
        .arg(Arg::with_name("width")
            .short("w")
            .long("width")
            .value_name("WIDTH")
            .takes_value(true)
            .required(true)
            .help("scale to this width or smaller."))
        .arg(Arg::with_name("height")
            .short("h")
            .long("height")
            .value_name("HEIGHT")
            .takes_value(true)
            .required(true)
            .help("scale to this height or smaller."))
        .arg(Arg::with_name("jpeg-quality")
            .long("jpeg-quality")
            .value_name("0..100")
            .takes_value(true)
            .help("Jpeg compression level."))
        .arg(Arg::with_name("benchmark-threads")
            .long("benchmark-threads")
            .value_name("1..16")
            .takes_value(true)
            .help("Number of concurrent threads to use when benchmarking (one operation per \
                   thread)."))
        .arg(Arg::with_name("benchmark-count")
            .long("benchmark-count")
            .value_name("1..1000")
            .takes_value(true)
            .help("How many times to perform the specified operation."))
        .arg(Arg::with_name("sharpen")
            .long("sharpen")
            .value_name("0..100")
            .takes_value(true)
            .help("Percent sharpening to apply."))
        .arg(Arg::with_name("format")
            .long("format")
            .value_name("png | jpg | png24")
            .takes_value(true)
            .possible_values(&["png", "jpeg", "jpg", "png24"])
            .help("Output image format to use. Baseline jpeg and 32-bit PNG supported."))
        .arg(Arg::with_name("constrain")
            .long("constrain")
            .value_name("max | distort")
            .takes_value(true)
            .possible_values(&["max", "distort"])
            .help("Constrain the image within a bounding box (max) or stretch to fit (distort)"))
        .arg(Arg::with_name("down-filter")
            .long("down-filter")
            .value_name("FILTER")
            .takes_value(true)
            .possible_values(TESTED_FILTER_OPTIONS)
            .help("Filter to use when downscaling"))
        .arg(Arg::with_name("up-filter")
            .long("up-filter")
            .value_name("FILTER")
            .possible_values(TESTED_FILTER_OPTIONS)
            .takes_value(true)
            .help("Filter to use when upscaling"))
        .arg(Arg::with_name("incorrectgamma")
            .long("incorrectgamma")
            .help("Enables incorrect gamma handling (for benchmarking comparison purposes)."))
        .arg(Arg::with_name("min_precise_scaling_ratio")
            .long("min_precise_scaling_ratio")
            .short("mpsr")
            .value_name("MINRATIO")
            .takes_value(true)
            .help("Defaults to 2.1. Jpeg-integrated block scaling is permitted down to 2.1x \
                   final size"))
}

struct ParsedResult {
    input_file: PathBuf,
    output_file: PathBuf,
    commands: BoringCommands,
    bench: Option<BenchmarkOptions>,
}

fn parse(matches: ArgMatches) -> Result<ParsedResult, String> {

    let w = matches.value_of("width").and_then(|x| x.parse::<u32>().ok());
    let h = matches.value_of("height").and_then(|x| x.parse::<u32>().ok());

    let sharpen = matches.value_of("sharpen").and_then(|x| x.parse::<f32>().ok()); //.and_then(|x| Some(x / 100f32));

    let q = matches.value_of("jpeg-quality").and_then(|x| x.parse::<i32>().ok());

    let fmt = value_t!(matches, "format", ImageFormat).unwrap_or(ImageFormat::Jpeg);

    let constrain = value_t!(matches, "constrain", ConstraintMode).unwrap_or(ConstraintMode::Max);

    let down_filter = value_t!(matches, "down-filter", Filter).unwrap_or(Filter::Robidoux);
    let up_filter = value_t!(matches, "up-filter", Filter).unwrap_or(Filter::Ginseng);

    // Clap requires these to exist, thus the safe unwrap()
    let in_file = Path::new(matches.value_of("input").unwrap());
    let out_file = Path::new(matches.value_of("output").unwrap());
    let min_precise_scaling_ratio = matches.value_of("min_precise_scaling_ratio")
        .and_then(|x| x.parse::<f32>().ok());

    if w.or(h).is_none() {
        return Err("You must specified at least one of (width, height) ".to_owned());
    }
    if !in_file.is_file() {
        return Err(format!("The specified input file could not be found: {}",
                           in_file.to_str().unwrap()));
    }

    // parent() will return "", an empty path. We don't want to validate the current dir exists, that's going too far.
    let parent_exists = out_file.parent()
        .and_then(|p| match p.to_str().unwrap() {
            "" => None,
            _ => Some(p),
        })
        .map(|p| p.exists());

    // It's OK if there's no parent directory specified.
    // It's only bad if there's one specified and it doesn't exists.
    let warn_about_parent = !parent_exists.unwrap_or(true);
    if warn_about_parent {
        return Err(format!("The parent directory for the output file could not be found: {}",
                           out_file.parent().unwrap().to_str().unwrap()));
    }

    let commands = BoringCommands {
        w: w.unwrap_or(0) as i32,
        h: h.unwrap_or(0) as i32,
        sharpen: sharpen.unwrap_or(0f32) as f32,
        jpeg_quality: q.unwrap_or(100),
        fit: constrain,
        precise_scaling_ratio: min_precise_scaling_ratio.unwrap_or(2.1f32),
        luma_correct: !matches.is_present("incorrectgamma"),
        format: fmt,
        up_filter: up_filter,
        down_filter: down_filter,
    };

    let b_threads = matches.value_of("benchmark-threads").and_then(|x| x.parse::<usize>().ok());

    let b_count = matches.value_of("benchmark-count").and_then(|x| x.parse::<usize>().ok());

    let bench;
    if b_threads.is_some() || b_count.is_some() {
        bench = Some(BenchmarkOptions {
            thread_count: b_threads.unwrap_or(1),
            run_count: b_count.unwrap_or(10),
            input_path: in_file.to_path_buf(),
            commands: commands.clone(),
        });
    } else {
        bench = None;
    }

    Ok(ParsedResult {
        input_file: in_file.to_path_buf(),
        output_file: out_file.to_path_buf(),
        commands: commands,
        bench: bench,
    })

}

fn main() {
    let matches = build_app().get_matches();

    let result = match parse(matches) {
        Ok(c) => {
            match process_image_by_paths(c.input_file, c.output_file, c.commands) {
                Ok(_) if c.bench.is_some() => {
                    match benchmark(c.bench.unwrap()) {
                        Ok(results) => {
                            println!("{}", results.to_json_string());
                            Ok(())
                        }
                        Err(e) => Err(e),
                    }
                }
                Ok(res) => Ok(res),
                Err(e) => Err(e),
            }
        }
        Err(e) => Err(e),
    };

    if result.is_err() {
        println!("Failed: {}", result.err().unwrap());
        std::process::exit(1);
    }

}

#[test]
fn test_correct_parsing() {

    File::create("delete.jpg").expect("File creation should work.");

    let valid_args = vec!["flow-proto1",
                          "-i",
                          "delete.jpg",
                          "-o",
                          "b.jpg",
                          "-w",
                          "20",
                          "-h",
                          "20",
                          "--down-filter",
                          "mitchell"];

    let result = parse(build_app().get_matches_from(valid_args)).expect("To parse correctly");

    assert_eq!(result.commands.down_filter, Filter::Mitchell);

    // std::fs::remove_file("delete.jpg").unwrap();

}

#[test]
fn test_correct_execution() {

    {

        let mut f = File::create("test_input.jpg").unwrap();

        let jpeg_bytes =
            &[0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01,
              0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC2, 0x00, 0x0B, 0x08, 0x00, 0x01, 0x00, 0x01,
              0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA,
              0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10];

        f.write_all(jpeg_bytes).unwrap();
        f.sync_all().unwrap();
        drop(f);
    }

    let valid_args =
        vec!["flow-proto1", "-i", "test_input.jpg", "-o", "b.jpg", "-w", "20", "-h", "20"];

    let parsed_result = parse(build_app().get_matches_from(valid_args))
        .expect("To parse correctly");

    process_image_by_paths(parsed_result.input_file,
                           parsed_result.output_file,
                           parsed_result.commands)
        .unwrap();

    // std::fs::remove_file("delete.jpg").unwrap();

}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2016 Ivo Wetzel

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::{Error, ErrorKind};
use std::collections::HashMap;
use std::net::{SocketAddr, ToSocketAddrs};
use traits::socket::Socket;
use shared::udp_socket::UdpSocket;
use shared::stats::{StatsCollector, Stats};
use super::{Config, Connection, ConnectionID, Handler, tick};

/// Implementation of a multi-client server with handler based event dispatch.
#[derive(Debug)]
pub struct Server {
    closed: bool,
    config: Config,
    local_address: Option<SocketAddr>,
    statistics: StatsCollector
}

impl Server {

    /// Creates a new server with the given configuration.
    pub fn new(config: Config) -> Server {
        Server {
            closed: false,
            config: config,
            local_address: None,
            statistics: StatsCollector::new(config)
        }
    }

    /// Returns the local address that the server is bound to.
    pub fn local_addr(&self) -> Result<SocketAddr, Error> {
        self.local_address.ok_or_else(|| Error::new(ErrorKind::AddrNotAvailable, ""))
    }

    /// Returns statistics (i.e. bandwidth usage) for the last second.
    pub fn stats(&mut self) -> Stats {
        self.statistics.average()
    }

    /// Binds the server to the specified local address by creating a socket
    /// and actively listens for incoming client connections.
    ///
    /// Clients connecting to the server must use a compatible connection
    /// / packet configuration in order to be able to connect.
    ///
    /// The `handler` is a struct that implements the `Handler` trait in order
    /// to handle events from the server and its connections.
    pub fn bind<A: ToSocketAddrs>(
        &mut self, handler: &mut Handler<Server>, addr: A

    ) -> Result<(), Error> {

        let socket = try!(UdpSocket::new(
            addr,
            self.config.packet_max_size
        ));

        self.bind_to_socket(handler, socket)

    }

    /// Binds the server to specified socket and actively listens for incoming
    /// client connections.
    ///
    /// Clients connecting to the server must use a compatible connection
    /// / packet configuration in order to be able to connect.
    ///
    /// The `handler` is a struct that implements the `Handler` trait in order
    /// to handle events from the server and its connections.
    pub fn bind_to_socket<S: Socket>(
        &mut self, handler: &mut Handler<Server>, mut socket: S

    ) -> Result<(), Error> {

        // Store bound socket address
        let local_addr = try!(socket.local_addr());
        self.local_address = Some(local_addr);

        // Reset stats
        self.statistics.reset();

        // List of dropped connections
        let mut dropped: Vec<ConnectionID> = Vec::new();

        // Mappping of connections to their remote sender address
        let mut addresses: HashMap<ConnectionID, SocketAddr> = HashMap::new();

        // Mapping of the actual connection objects
        let mut connections: HashMap<ConnectionID, Connection> = HashMap::new();

        // Invoke handler
        handler.bind(self);

        // Receive and send until we shut down.
        let mut tick_overflow = 0;
        while !self.closed {

            let tick_start = tick::start();
            let tick_delay = 1000000000 / self.config.send_rate;

            // Receive all incoming UDP packets to our local address
            let mut bytes_received = 0;
            while let Ok((addr, packet)) = socket.try_recv() {

                // Try to extract the connection id from the packet
                if let Some(id) = Connection::id_from_packet(&self.config, &packet) {

                    // Retrieve or create a connection for the current
                    // connection id
                    let connection = connections.entry(id).or_insert_with(|| {

                        // Also map the intitial address which is used by
                        // the connection
                        addresses.insert(id, addr);

                        let mut conn = Connection::new(
                            self.config,
                            local_addr,
                            addr,
                            handler.rate_limiter(&self.config)
                        );

                        conn.set_id(id);
                        conn

                    });

                    // Map the current remote address of the connection to
                    // the latest address that sent a packet for the
                    // connection id in question. This is done in order to
                    // work in situations were the remote port of a
                    // connection is switched around by NAT.
                    if addr != connection.peer_addr() {
                        connection.set_peer_addr(addr);
                        addresses.remove(&id);
                        addresses.insert(id, addr);
                    }

                    // Statistics
                    bytes_received += packet.len();

                    // Then feed the packet into the connection object for
                    // parsing
                    connection.receive_packet(
                        packet, tick_delay / 1000000, self, handler
                    );

                }

            }

            self.statistics.set_bytes_received(bytes_received as u32);

            // Invoke handler
            handler.tick_connections(self, &mut connections);

            // Create outgoing packets for all connections
            let mut bytes_sent = 0;
            for (id, conn) in &mut connections {

                // Resolve the last known remote address for this
                // connection and send the data
                let addr = addresses.get(id).unwrap();

                // Then invoke the connection to send a outgoing packet
                bytes_sent += conn.send_packet(&mut socket, addr, self, handler);

                // Collect all lost / closed connections
                if !conn.open() {
                    dropped.push(*id);
                }

            }

            // Update statistics
            self.statistics.set_bytes_sent(bytes_sent);
            self.statistics.tick();

            // Remove any dropped connections and their address mappings
            if !dropped.is_empty() {

                for id in &dropped {
                    connections.remove(id).unwrap().reset();
                    addresses.remove(id);
                }

                dropped.clear();

            }

            tick::end(tick_delay, tick_start, &mut tick_overflow, &self.config);

        }

        // Invoke handler
        handler.shutdown(self);

        // Reset socket address
        self.local_address = None;

        // Reset all connection states
        for (_, conn) in &mut connections {
            conn.reset();
        }

        Ok(())

    }

    /// Shuts down the server, closing all active client connections.
    ///
    /// This exits the tick loop, resets all connections and shuts down the
    /// underlying socket the server was bound to.
    pub fn shutdown(&mut self) -> Result<(), Error> {
        if self.closed {
            Err(Error::new(ErrorKind::NotConnected, ""))

        } else {
            self.closed = true;
            Ok(())
        }
    }

}

<commit_msg>[IMPROVE] Simplfiy removal of dropped connections via drain iterator.<commit_after>// Copyright (c) 2015-2016 Ivo Wetzel

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::{Error, ErrorKind};
use std::collections::HashMap;
use std::net::{SocketAddr, ToSocketAddrs};
use traits::socket::Socket;
use shared::udp_socket::UdpSocket;
use shared::stats::{StatsCollector, Stats};
use super::{Config, Connection, ConnectionID, Handler, tick};

/// Implementation of a multi-client server with handler based event dispatch.
#[derive(Debug)]
pub struct Server {
    closed: bool,
    config: Config,
    local_address: Option<SocketAddr>,
    statistics: StatsCollector
}

impl Server {

    /// Creates a new server with the given configuration.
    pub fn new(config: Config) -> Server {
        Server {
            closed: false,
            config: config,
            local_address: None,
            statistics: StatsCollector::new(config)
        }
    }

    /// Returns the local address that the server is bound to.
    pub fn local_addr(&self) -> Result<SocketAddr, Error> {
        self.local_address.ok_or_else(|| Error::new(ErrorKind::AddrNotAvailable, ""))
    }

    /// Returns statistics (i.e. bandwidth usage) for the last second.
    pub fn stats(&mut self) -> Stats {
        self.statistics.average()
    }

    /// Binds the server to the specified local address by creating a socket
    /// and actively listens for incoming client connections.
    ///
    /// Clients connecting to the server must use a compatible connection
    /// / packet configuration in order to be able to connect.
    ///
    /// The `handler` is a struct that implements the `Handler` trait in order
    /// to handle events from the server and its connections.
    pub fn bind<A: ToSocketAddrs>(
        &mut self, handler: &mut Handler<Server>, addr: A

    ) -> Result<(), Error> {

        let socket = try!(UdpSocket::new(
            addr,
            self.config.packet_max_size
        ));

        self.bind_to_socket(handler, socket)

    }

    /// Binds the server to specified socket and actively listens for incoming
    /// client connections.
    ///
    /// Clients connecting to the server must use a compatible connection
    /// / packet configuration in order to be able to connect.
    ///
    /// The `handler` is a struct that implements the `Handler` trait in order
    /// to handle events from the server and its connections.
    pub fn bind_to_socket<S: Socket>(
        &mut self, handler: &mut Handler<Server>, mut socket: S

    ) -> Result<(), Error> {

        // Store bound socket address
        let local_addr = try!(socket.local_addr());
        self.local_address = Some(local_addr);

        // Reset stats
        self.statistics.reset();

        // List of dropped connections
        let mut dropped: Vec<ConnectionID> = Vec::new();

        // Mappping of connections to their remote sender address
        let mut addresses: HashMap<ConnectionID, SocketAddr> = HashMap::new();

        // Mapping of the actual connection objects
        let mut connections: HashMap<ConnectionID, Connection> = HashMap::new();

        // Invoke handler
        handler.bind(self);

        // Receive and send until we shut down.
        let mut tick_overflow = 0;
        while !self.closed {

            let tick_start = tick::start();
            let tick_delay = 1000000000 / self.config.send_rate;

            // Receive all incoming UDP packets to our local address
            let mut bytes_received = 0;
            while let Ok((addr, packet)) = socket.try_recv() {

                // Try to extract the connection id from the packet
                if let Some(id) = Connection::id_from_packet(&self.config, &packet) {

                    // Retrieve or create a connection for the current
                    // connection id
                    let connection = connections.entry(id).or_insert_with(|| {

                        // Also map the intitial address which is used by
                        // the connection
                        addresses.insert(id, addr);

                        let mut conn = Connection::new(
                            self.config,
                            local_addr,
                            addr,
                            handler.rate_limiter(&self.config)
                        );

                        conn.set_id(id);
                        conn

                    });

                    // Map the current remote address of the connection to
                    // the latest address that sent a packet for the
                    // connection id in question. This is done in order to
                    // work in situations were the remote port of a
                    // connection is switched around by NAT.
                    if addr != connection.peer_addr() {
                        connection.set_peer_addr(addr);
                        addresses.remove(&id);
                        addresses.insert(id, addr);
                    }

                    // Statistics
                    bytes_received += packet.len();

                    // Then feed the packet into the connection object for
                    // parsing
                    connection.receive_packet(
                        packet, tick_delay / 1000000, self, handler
                    );

                }

            }

            self.statistics.set_bytes_received(bytes_received as u32);

            // Invoke handler
            handler.tick_connections(self, &mut connections);

            // Create outgoing packets for all connections
            let mut bytes_sent = 0;
            for (id, conn) in &mut connections {

                // Resolve the last known remote address for this
                // connection and send the data
                let addr = addresses.get(id).unwrap();

                // Then invoke the connection to send a outgoing packet
                bytes_sent += conn.send_packet(&mut socket, addr, self, handler);

                // Collect all lost / closed connections
                if !conn.open() {
                    dropped.push(*id);
                }

            }

            // Update statistics
            self.statistics.set_bytes_sent(bytes_sent);
            self.statistics.tick();

            // Remove any dropped connections and their address mappings
            for id in dropped.drain(..) {
                connections.remove(&id).unwrap().reset();
                addresses.remove(&id);
            }

            tick::end(tick_delay, tick_start, &mut tick_overflow, &self.config);

        }

        // Invoke handler
        handler.shutdown(self);

        // Reset socket address
        self.local_address = None;

        // Reset all connection states
        for (_, conn) in &mut connections {
            conn.reset();
        }

        Ok(())

    }

    /// Shuts down the server, closing all active client connections.
    ///
    /// This exits the tick loop, resets all connections and shuts down the
    /// underlying socket the server was bound to.
    pub fn shutdown(&mut self) -> Result<(), Error> {
        if self.closed {
            Err(Error::new(ErrorKind::NotConnected, ""))

        } else {
            self.closed = true;
            Ok(())
        }
    }

}

<|endoftext|>


<commit_before>use luminance::{FragmentShader, GeometryShader, StageError, ShaderTypeable,
                TessellationControlShader, TessellationEvaluationShader, VertexShader};
use luminance_gl::gl33::{ProgramProxy, Stage};
use std::fs;
use std::io::Read;
use std::path::Path;

pub use luminance::{ProgramError, Uniformable, UniformUpdate};
pub use luminance::shader::program::UniformWarning;
pub use luminance_gl::gl33::{self, Uniform};
pub use luminance_gl::gl33::token::*;

#[derive(Debug)]
pub enum ShaderError {
  StageError(StageError),
  ProgramError(ProgramError)
}

fn compile_stages(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str) -> Result<(Option<(Stage<TessellationControlShader>, Stage<TessellationEvaluationShader>)>, Stage<VertexShader>, Option<Stage<GeometryShader>>, Stage<FragmentShader>), StageError> {
  let tess = match tess_src {
    None => None,
    Some((tcs_src, tes_src)) => {
      let tcs = try!(Stage::new(tcs_src));
      let tes = try!(Stage::new(tes_src));
      Some((tcs, tes))
    }
  };
  let vs = try!(Stage::new(vs_src));
  let gs = match gs_src {
    None => None,
    Some(gs_src) => {
      let gs = try!(Stage::new(gs_src));
      Some(gs)
    }
  };
  let fs = try!(Stage::new(fs_src));

  Ok((tess, vs, gs, fs))
}

pub fn new_program<GetUni, T>(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str, get_uni: &GetUni) -> Result<gl33::Program<T>, ProgramError> where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  let stages = compile_stages(tess_src, vs_src, gs_src, fs_src);

  match stages {
    Ok((tess, vs, gs, fs)) => {
      if let Some((tcs, tes)) = tess {
        if let Some(gs) = gs {
          gl33::Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
        } else {
          gl33::Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
        }
      } else if let Some(gs) = gs {
        gl33::Program::new(None, &vs, Some(&gs), &fs, get_uni)
      } else {
        gl33::Program::new(None, &vs, None, &fs, get_uni)
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

pub fn new_program_from_disk<P, GetUni, T>(tess_path: Option<(P, P)>, vs_path: P, gs_path: Option<P>, fs_path: P, get_uni: &GetUni) -> Result<gl33::Program<T>, ProgramError>
      where P: AsRef<Path>,
            GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  // load vertex and fragment shaders first
  let vs = try!(read_stage(vs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
  let fs = try!(read_stage(fs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));

  match (tess_path, gs_path) {
    (None, None) => { // no tessellation nor geometry
      gl33::Program::new(None, &vs, None, &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), None) => { // tessellation without geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      gl33::Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
    },
    (None, Some(gs_path)) => { // geometry without tessellation
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      gl33::Program::new(None, &vs, Some(&gs), &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), Some(gs_path)) => { // tessellation and geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      gl33::Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
    }
  }
}

pub fn read_stage<P, T>(path: P) -> Result<Stage<T>, StageError> where P: AsRef<Path>, T: ShaderTypeable {
  let path = path.as_ref();

  info!("loading {:?} stage: \x1b[35m{:?}", T::shader_type(), path);

  let fh = fs::File::open(path);

  match fh {
    Err(e) => {
      Err(StageError::CompilationFailed(T::shader_type(), format!("{:?}", e)))
    },
    Ok(mut fh) => {
      let mut stage_src = String::new();
      let _ = fh.read_to_string(&mut stage_src);

      Stage::new(stage_src.chars().as_str())
    }
  }
}

#[cfg(feature = "hot-resource")]
mod hot {
  use super::*;

  use luminance_gl::gl33::ProgramProxy;
  use resource::ResourceManager;
  use std::ops::Deref;
  use std::path::{Path, PathBuf};
  use std::sync::mpsc;

  pub struct Program<T> {
    rx: mpsc::Receiver<()>,
    program: gl33::Program<T>,
    get_uni: Box<Fn(ProgramProxy) -> Result<T, ProgramError>>,
    vs_path: PathBuf,
    fs_path: PathBuf,
    tess_path: Option<(PathBuf, PathBuf)>,
    gs_path: Option<PathBuf>
  }

  impl<T> Program<T> {
    pub fn load<GetUni, P>(manager: &mut ResourceManager, tess_path: Option<(P, P)>, vs_path: P, gs_path: Option<P>, fs_path: P, get_uni: GetUni) -> Result<Self, ProgramError>
        where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> + 'static,
              P: AsRef<Path> {
      let vs_path = vs_path.as_ref();
      let fs_path = fs_path.as_ref();
      let tess_path = tess_path.as_ref().map(|&(ref tcs, ref tes)| (tcs.as_ref(), tes.as_ref()));
      let gs_path = gs_path.as_ref().map(|gs| gs.as_ref());

      let program = try!(new_program_from_disk(tess_path, vs_path, gs_path, fs_path, &get_uni));

      let (sx, rx) = mpsc::channel();
      manager.monitor(vs_path, sx.clone());
      manager.monitor(fs_path, sx.clone());

      if let Some((tcs, tes)) = tess_path {
        manager.monitor(tcs, sx.clone());
        manager.monitor(tes, sx.clone());
      }

      if let Some(gs) = gs_path {
        manager.monitor(gs, sx.clone());
      }

      Ok(Program {
        rx: rx,
        program: program,
        get_uni: Box::new(get_uni),
        vs_path: vs_path.to_path_buf(),
        fs_path: fs_path.to_path_buf(),
        tess_path: tess_path.map(|(tcs, tes)| (tcs.to_path_buf(), tes.to_path_buf())),
        gs_path: gs_path.map(|gs| gs.to_path_buf()),
      })
    }

    fn reload(&mut self) {
      let vs = self.vs_path.as_path();
      let fs = self.fs_path.as_path();
      let tess = self.tess_path.as_ref().map(|&(ref tcs, ref tes)| (tcs.as_path(), tes.as_path()));
      let gs = self.gs_path.as_ref().map(|gs| gs.as_path());

      let program = new_program_from_disk(tess, vs, gs, fs, &self.get_uni.as_ref());

      match program {
        Ok(program) => {
          self.program = program;
          info!("reloaded shader program");
        },
        Err(e) => {
          err!("reloading program has failed: {:?}", e);
        }
      }
    }

    pub fn sync(&mut self) {
      if self.rx.try_recv().is_ok() {
        self.reload();
      }
    }
  }

  impl<'a, T> Deref for Program<T> {
    type Target = gl33::Program<T>;

    fn deref(&self) -> &Self::Target {
      &self.program
    }
  }
}

#[cfg(not(feature = "hot-resource"))]
mod cold {
  use super::*;

  use luminance_gl::gl33::ProgramProxy;
  use resource::ResourceManager;
  use std::ops::Deref;
  use std::path::Path;

  pub struct Program<T>(gl33::Program<T>);

  impl<T> Program<T> {
    pub fn load<GetUni, P>(_: &mut ResourceManager, tess_path: Option<(P, P)>, vs_path: P, gs_path: Option<P>, fs_path: P, get_uni: GetUni) -> Result<Program<T>, ProgramError>
        where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> + 'static,
              P: AsRef<Path> {
      let vs_path = vs_path.as_ref();
      let fs_path = fs_path.as_ref();
      let tess_path = tess_path.as_ref().map(|&(ref tcs, ref tes)| (tcs.as_ref(), tes.as_ref()));
      let gs_path = gs_path.as_ref().map(|gs| gs.as_ref());

      let program = try!(new_program_from_disk(tess_path, vs_path, gs_path, fs_path, &get_uni));

      Ok(Program(program))
    }
  }

  impl<'a, T> Deref for Program<T> {
    type Target = gl33::Program<T>;

    fn deref(&self) -> &Self::Target {
      &self.0
    }
  }
}

#[cfg(feature = "hot-resource")]
pub use self::hot::*;
#[cfg(not(feature = "hot-resource"))]
pub use self::cold::*;

/// A helper function used to make uniforms optionable. If there’s a warning, it’s printed out.
pub fn opt_uni<T>(uni: (Uniform<T>, Option<UniformWarning>)) -> Uniform<T> where T: Uniformable<GL33> {
  if let Some(warning) = uni.1 {
    warn!("{:?}", warning);
  }

  uni.0
}
<commit_msg>Added sync for cold shaders. (#3)<commit_after>use luminance::{FragmentShader, GeometryShader, StageError, ShaderTypeable,
                TessellationControlShader, TessellationEvaluationShader, VertexShader};
use luminance_gl::gl33::{ProgramProxy, Stage};
use std::fs;
use std::io::Read;
use std::path::Path;

pub use luminance::{ProgramError, Uniformable, UniformUpdate};
pub use luminance::shader::program::UniformWarning;
pub use luminance_gl::gl33::{self, Uniform};
pub use luminance_gl::gl33::token::*;

#[derive(Debug)]
pub enum ShaderError {
  StageError(StageError),
  ProgramError(ProgramError)
}

fn compile_stages(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str) -> Result<(Option<(Stage<TessellationControlShader>, Stage<TessellationEvaluationShader>)>, Stage<VertexShader>, Option<Stage<GeometryShader>>, Stage<FragmentShader>), StageError> {
  let tess = match tess_src {
    None => None,
    Some((tcs_src, tes_src)) => {
      let tcs = try!(Stage::new(tcs_src));
      let tes = try!(Stage::new(tes_src));
      Some((tcs, tes))
    }
  };
  let vs = try!(Stage::new(vs_src));
  let gs = match gs_src {
    None => None,
    Some(gs_src) => {
      let gs = try!(Stage::new(gs_src));
      Some(gs)
    }
  };
  let fs = try!(Stage::new(fs_src));

  Ok((tess, vs, gs, fs))
}

pub fn new_program<GetUni, T>(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str, get_uni: &GetUni) -> Result<gl33::Program<T>, ProgramError> where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  let stages = compile_stages(tess_src, vs_src, gs_src, fs_src);

  match stages {
    Ok((tess, vs, gs, fs)) => {
      if let Some((tcs, tes)) = tess {
        if let Some(gs) = gs {
          gl33::Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
        } else {
          gl33::Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
        }
      } else if let Some(gs) = gs {
        gl33::Program::new(None, &vs, Some(&gs), &fs, get_uni)
      } else {
        gl33::Program::new(None, &vs, None, &fs, get_uni)
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

pub fn new_program_from_disk<P, GetUni, T>(tess_path: Option<(P, P)>, vs_path: P, gs_path: Option<P>, fs_path: P, get_uni: &GetUni) -> Result<gl33::Program<T>, ProgramError>
      where P: AsRef<Path>,
            GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  // load vertex and fragment shaders first
  let vs = try!(read_stage(vs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
  let fs = try!(read_stage(fs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));

  match (tess_path, gs_path) {
    (None, None) => { // no tessellation nor geometry
      gl33::Program::new(None, &vs, None, &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), None) => { // tessellation without geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      gl33::Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
    },
    (None, Some(gs_path)) => { // geometry without tessellation
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      gl33::Program::new(None, &vs, Some(&gs), &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), Some(gs_path)) => { // tessellation and geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      gl33::Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
    }
  }
}

pub fn read_stage<P, T>(path: P) -> Result<Stage<T>, StageError> where P: AsRef<Path>, T: ShaderTypeable {
  let path = path.as_ref();

  info!("loading {:?} stage: \x1b[35m{:?}", T::shader_type(), path);

  let fh = fs::File::open(path);

  match fh {
    Err(e) => {
      Err(StageError::CompilationFailed(T::shader_type(), format!("{:?}", e)))
    },
    Ok(mut fh) => {
      let mut stage_src = String::new();
      let _ = fh.read_to_string(&mut stage_src);

      Stage::new(stage_src.chars().as_str())
    }
  }
}

#[cfg(feature = "hot-resource")]
mod hot {
  use super::*;

  use luminance_gl::gl33::ProgramProxy;
  use resource::ResourceManager;
  use std::ops::Deref;
  use std::path::{Path, PathBuf};
  use std::sync::mpsc;

  pub struct Program<T> {
    rx: mpsc::Receiver<()>,
    program: gl33::Program<T>,
    get_uni: Box<Fn(ProgramProxy) -> Result<T, ProgramError>>,
    vs_path: PathBuf,
    fs_path: PathBuf,
    tess_path: Option<(PathBuf, PathBuf)>,
    gs_path: Option<PathBuf>
  }

  impl<T> Program<T> {
    pub fn load<GetUni, P>(manager: &mut ResourceManager, tess_path: Option<(P, P)>, vs_path: P, gs_path: Option<P>, fs_path: P, get_uni: GetUni) -> Result<Self, ProgramError>
        where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> + 'static,
              P: AsRef<Path> {
      let vs_path = vs_path.as_ref();
      let fs_path = fs_path.as_ref();
      let tess_path = tess_path.as_ref().map(|&(ref tcs, ref tes)| (tcs.as_ref(), tes.as_ref()));
      let gs_path = gs_path.as_ref().map(|gs| gs.as_ref());

      let program = try!(new_program_from_disk(tess_path, vs_path, gs_path, fs_path, &get_uni));

      let (sx, rx) = mpsc::channel();
      manager.monitor(vs_path, sx.clone());
      manager.monitor(fs_path, sx.clone());

      if let Some((tcs, tes)) = tess_path {
        manager.monitor(tcs, sx.clone());
        manager.monitor(tes, sx.clone());
      }

      if let Some(gs) = gs_path {
        manager.monitor(gs, sx.clone());
      }

      Ok(Program {
        rx: rx,
        program: program,
        get_uni: Box::new(get_uni),
        vs_path: vs_path.to_path_buf(),
        fs_path: fs_path.to_path_buf(),
        tess_path: tess_path.map(|(tcs, tes)| (tcs.to_path_buf(), tes.to_path_buf())),
        gs_path: gs_path.map(|gs| gs.to_path_buf()),
      })
    }

    fn reload(&mut self) {
      let vs = self.vs_path.as_path();
      let fs = self.fs_path.as_path();
      let tess = self.tess_path.as_ref().map(|&(ref tcs, ref tes)| (tcs.as_path(), tes.as_path()));
      let gs = self.gs_path.as_ref().map(|gs| gs.as_path());

      let program = new_program_from_disk(tess, vs, gs, fs, &self.get_uni.as_ref());

      match program {
        Ok(program) => {
          self.program = program;
          info!("reloaded shader program");
        },
        Err(e) => {
          err!("reloading program has failed: {:?}", e);
        }
      }
    }

    pub fn sync(&mut self) {
      if self.rx.try_recv().is_ok() {
        self.reload();
      }
    }
  }

  impl<'a, T> Deref for Program<T> {
    type Target = gl33::Program<T>;

    fn deref(&self) -> &Self::Target {
      &self.program
    }
  }
}

#[cfg(not(feature = "hot-resource"))]
mod cold {
  use super::*;

  use luminance_gl::gl33::ProgramProxy;
  use resource::ResourceManager;
  use std::ops::Deref;
  use std::path::Path;

  pub struct Program<T>(gl33::Program<T>);

  impl<T> Program<T> {
    pub fn load<GetUni, P>(_: &mut ResourceManager, tess_path: Option<(P, P)>, vs_path: P, gs_path: Option<P>, fs_path: P, get_uni: GetUni) -> Result<Program<T>, ProgramError>
        where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> + 'static,
              P: AsRef<Path> {
      let vs_path = vs_path.as_ref();
      let fs_path = fs_path.as_ref();
      let tess_path = tess_path.as_ref().map(|&(ref tcs, ref tes)| (tcs.as_ref(), tes.as_ref()));
      let gs_path = gs_path.as_ref().map(|gs| gs.as_ref());

      let program = try!(new_program_from_disk(tess_path, vs_path, gs_path, fs_path, &get_uni));

      Ok(Program(program))
    }

    pub fn sync(&mut self) {}
  }

  impl<'a, T> Deref for Program<T> {
    type Target = gl33::Program<T>;

    fn deref(&self) -> &Self::Target {
      &self.0
    }
  }
}

#[cfg(feature = "hot-resource")]
pub use self::hot::*;
#[cfg(not(feature = "hot-resource"))]
pub use self::cold::*;

/// A helper function used to make uniforms optionable. If there’s a warning, it’s printed out.
pub fn opt_uni<T>(uni: (Uniform<T>, Option<UniformWarning>)) -> Uniform<T> where T: Uniformable<GL33> {
  if let Some(warning) = uni.1 {
    warn!("{:?}", warning);
  }

  uni.0
}
<|endoftext|>


<commit_before>use serde::Deserialize;
use serde_json::from_reader;
use std::f32::consts;
use std::fs::File;
use std::ops::{Add, Div, Mul, Sub};
use std::path::Path;

use linear::{BaseFloat, Scale, Unit, UnitQuaternion, Vector2, Vector3, Vector4};
use resource::{Load, LoadError, ResCache};

/// Time used as sampling type in splines.
pub type Time = f32;

/// A spline control point.
///
/// This type associates a value at a given time. It also contains an interpolation object used to
/// determine how to interpolate values on the segment defined by this key and the next one.
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Key<T> {
  /// Time at which the `Key` should be reached.
  pub t: Time,
  /// Actual value.
  pub value: T,
  /// Interpolation mode.
  #[serde(default)]
  pub interpolation: Interpolation
}

impl<T> Key<T> {
  /// Create a new key.
  pub fn new(t: Time, value: T, interpolation: Interpolation) -> Self {
    Key {
      t: t,
      value: value,
      interpolation: interpolation
    }
  }
}

/// Interpolation mode.
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub enum Interpolation {
  /// Hold a `Key` until the time passes the normalized step threshold, in which case the next
  /// key is used.
  ///
  /// *Note: if you set the threshold to `0.5`, the first key will be used until the time is half
  /// between the two keys; the second key will be in used afterwards. If you set it to `1.0`, the
  /// first key will be kept until the next key.*
  #[serde(rename = "step")]
  Step(f32),
  /// Linear interpolation between a key and the next one.
  #[serde(rename = "linear")]
  Linear,
  /// Cosine interpolation between a key and the next one.
  #[serde(rename = "cosine")]
  Cosine,
  /// Catmull-Rom interpolation.
  #[serde(rename = "catmull_rom")]
  CatmullRom
}

impl Default for Interpolation {
  /// `Interpolation::Linear` is the default.
  fn default() -> Self {
    Interpolation::Linear
  }
}

/// Spline curve used to provide interpolation between control points (keys).
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Spline<T> {
  keys: Vec<Key<T>>,
}

impl<T> Spline<T> {
  /// Create a new spline out of keys. The keys don’t have to be sorted because they’re sorted by
  /// this function.
  pub fn from_keys(mut keys: Vec<Key<T>>) -> Self {
    keys.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap());

    Spline {
      keys: keys
    }
  }

  /// Sample a spline at a given time.
  ///
  /// # Return
  ///
  /// `None` if you try to sample a value at a time that has no key associated with. That can also
  /// happen if you try to sample between two keys with a specific interpolation mode that make the
  /// sampling impossible. For instance, `Interpolate::CatmullRom` requires *four* keys. If you’re
  /// near the beginning of the spline or its end, ensure you have enough keys around to make the
  /// sampling.
  pub fn sample(&self, t: Time) -> Option<T> where T: Interpolate {
    let keys = &self.keys;
    let i = search_lower_cp(keys, t);

    let i = match i {
      Some(i) => i,
      None => return None
    };

    let cp0 = &keys[i];

    match cp0.interpolation {
      Interpolation::Step(threshold) => {
        let cp1 = &keys[i+1];
        let nt = normalize_time(t, cp0, cp1);
        Some(if nt < threshold { cp0.value } else { cp1.value })
      },
      Interpolation::Linear => {
        let cp1 = &keys[i+1];
        let nt = normalize_time(t, cp0, cp1);

        Some(Interpolate::lerp(cp0.value, cp1.value, nt))
      },
      Interpolation::Cosine => {
        let cp1 = &keys[i+1];
        let nt = normalize_time(t, cp0, cp1);
        let cos_nt = (1. - f32::cos(nt * consts::PI)) * 0.5;

        Some(Interpolate::lerp(cp0.value, cp1.value, cos_nt))
      },
      Interpolation::CatmullRom => {
        // We need at least four points for Catmull Rom; ensure we have them, otherwise, return
        // None.
        if i == 0 || i >= keys.len() - 2 {
          None
        } else {
          let cp1 = &keys[i+1];
          let cpm0 = &keys[i-1];
          let cpm1 = &keys[i+2];
          let nt = normalize_time(t, cp0, cp1);

          Some(Interpolate::cubic_hermite((cpm0.value, cpm0.t), (cp0.value, cp0.t), (cp1.value, cp1.t), (cpm1.value, cpm1.t), nt))
        }
      }
    }
  }

  /// Sample a spline at a given time with clamping.
  ///
  /// # Return
  ///
  /// If you sample before the first key or after the last one,
  /// return the first key or the last one, respectively.
  ///
  /// # Panic
  ///
  /// This function panics if you have no key.
  pub fn clamped_sample(&self, t: Time) -> T where T: Interpolate {
    let first = self.keys.first().unwrap();
    let last = self.keys.last().unwrap();

    if t <= first.t {
      return first.value;
    } else if t >= last.t {
      return last.value;
    }

    self.sample(t).unwrap()
  }
}

impl<T> Load for Spline<T> where T: SplineDeserializerAdapter {
  type Args = ();

  const TY_STR: &'static str = "splines";

  fn load<P>(path: P, _: &mut ResCache, _: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path> {
    let path = path.as_ref();

    info!("loading spline: {:?}", path);

    let file = File::open(path).map_err(|e| LoadError::FileNotFound(path.to_path_buf(), format!("{:?}", e)))?;
    let keys: Vec<Key<T::Deserialized>> = from_reader(file).map_err(|e| LoadError::ParseFailed(format!("{:?}", e)))?;

    Ok(Spline::from_keys(keys.into_iter().map(|key|
      Key::new(key.t, T::from_deserialized(key.value), key.interpolation)
    ).collect()))
  }
}

/// Spline deserializer adapter used to deserialize splines which keys’ values types don’t directly
/// implement deserialization.
pub trait SplineDeserializerAdapter {
  type Deserialized: Deserialize;

  fn from_deserialized(de: Self::Deserialized) -> Self;
}

impl SplineDeserializerAdapter for f32 {
  type Deserialized = Self;

  fn from_deserialized(de: Self::Deserialized) -> Self {
    de
  }
}

impl<T> SplineDeserializerAdapter for Vector2<T> where T: Clone + Deserialize {
  type Deserialized = [T; 2];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    (&de).into()
  }
}

impl<T> SplineDeserializerAdapter for Vector3<T> where T: Clone + Deserialize {
  type Deserialized = [T; 3];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    (&de).into()
  }
}

impl<T> SplineDeserializerAdapter for Vector4<T> where T: Clone + Deserialize {
  type Deserialized = [T; 4];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    (&de).into()
  }
}

impl<T> SplineDeserializerAdapter for UnitQuaternion<T> where T: BaseFloat + Deserialize {
  type Deserialized = [T; 4];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    Unit::new((&de).into())
  }
}

impl SplineDeserializerAdapter for Scale {
  type Deserialized = [f32; 3];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    Scale::from(&de)
  }
}

/// Iterator over spline keys.
pub struct SplineIterator<'a, T> where T: 'a {
  anim_param: &'a Spline<T>,
  i: usize
}

impl<'a, T> Iterator for SplineIterator<'a, T> {
  type Item = &'a Key<T>;

  fn next(&mut self) -> Option<Self::Item> {
    let r = self.anim_param.keys.get(self.i);

    if let Some(_) = r {
      self.i += 1;
    }

    r
  }
}

impl<'a, T> IntoIterator for &'a Spline<T> {
  type Item = &'a Key<T>;
  type IntoIter = SplineIterator<'a, T>;

  fn into_iter(self) -> Self::IntoIter {
    SplineIterator {
      anim_param: self,
      i: 0
    }
  }
}

/// Keys that can be interpolated between. Implementing this trait is required to perform sampling
/// on splines.
pub trait Interpolate: Copy {
  /// Linear interpolation.
  fn lerp(a: Self, b: Self, t: Time) -> Self;
  /// Cubic hermite interpolation.
  ///
  /// Default to `Self::lerp`.
  fn cubic_hermite(_: (Self, Time), a: (Self, Time), b: (Self, Time), _: (Self, Time), t: Time) -> Self {
    Self::lerp(a.0, b.0, t)
  }
}

impl Interpolate for f32 {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for Vector2<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for Vector3<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for Vector4<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for UnitQuaternion<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    a * UnitQuaternion::new(&(UnitQuaternion::new(&a.quaternion().conjugate()) * b).quaternion().powf(t))
  }
}

// Default implementation of Interpolate::lerp.
pub fn lerp<T>(a: T, b: T, t: Time) -> T where T: Add<Output = T> + Mul<Time, Output = T> {
  a * (1. - t) + b * t
}

// Default implementation of Interpolate::cubic_hermit.
pub fn cubic_hermite<T>(x: (T, Time), a: (T, Time), b: (T, Time), y: (T, Time), t: Time) -> T
    where T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Time, Output = T> + Div<Time, Output = T> {
  // time stuff
  let t2 = t * t;
  let t3 = t2 * t;
  let two_t3 = 2. * t3;
  let three_t2 = 3. * t2;

  // tangents
  let m0 = (b.0 - x.0) / (b.1 - x.1);
	let m1 = (y.0 - a.0) / (y.1 - a.1);

  a.0 * (two_t3 - three_t2 + 1.) + m0 * (t3 - 2. * t2 + t) + b.0 * (-two_t3 + three_t2) + m1 * (t3 - t2)
}

// Normalize a time ([0;1]) given two control points.
pub fn normalize_time<T>(t: Time, cp: &Key<T>, cp1: &Key<T>) -> Time {
  (t - cp.t) / (cp1.t - cp.t)
}

// Find the lower control point corresponding to a given time.
fn search_lower_cp<T>(cps: &[Key<T>], t: Time) -> Option<usize> {
  let mut i = 0;
  let len = cps.len();

  if len < 2 {
    return None;
  }

  loop {
    let cp = &cps[i];
    let cp1 = &cps[i+1];

    if t >= cp1.t {
      if i >= len - 2 {
        return None;
      }

      i += 1;
    } else if t < cp.t {
      if i == 0 {
        return None;
      }

      i -= 1;
    } else {
      break; // found
    }
  }

  Some(i)
}
<commit_msg>impl Interpolate for Scale.<commit_after>use serde::Deserialize;
use serde_json::from_reader;
use std::f32::consts;
use std::fs::File;
use std::ops::{Add, Div, Mul, Sub};
use std::path::Path;

use linear::{BaseFloat, Scale, Unit, UnitQuaternion, Vector2, Vector3, Vector4};
use resource::{Load, LoadError, ResCache};

/// Time used as sampling type in splines.
pub type Time = f32;

/// A spline control point.
///
/// This type associates a value at a given time. It also contains an interpolation object used to
/// determine how to interpolate values on the segment defined by this key and the next one.
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Key<T> {
  /// Time at which the `Key` should be reached.
  pub t: Time,
  /// Actual value.
  pub value: T,
  /// Interpolation mode.
  #[serde(default)]
  pub interpolation: Interpolation
}

impl<T> Key<T> {
  /// Create a new key.
  pub fn new(t: Time, value: T, interpolation: Interpolation) -> Self {
    Key {
      t: t,
      value: value,
      interpolation: interpolation
    }
  }
}

/// Interpolation mode.
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub enum Interpolation {
  /// Hold a `Key` until the time passes the normalized step threshold, in which case the next
  /// key is used.
  ///
  /// *Note: if you set the threshold to `0.5`, the first key will be used until the time is half
  /// between the two keys; the second key will be in used afterwards. If you set it to `1.0`, the
  /// first key will be kept until the next key.*
  #[serde(rename = "step")]
  Step(f32),
  /// Linear interpolation between a key and the next one.
  #[serde(rename = "linear")]
  Linear,
  /// Cosine interpolation between a key and the next one.
  #[serde(rename = "cosine")]
  Cosine,
  /// Catmull-Rom interpolation.
  #[serde(rename = "catmull_rom")]
  CatmullRom
}

impl Default for Interpolation {
  /// `Interpolation::Linear` is the default.
  fn default() -> Self {
    Interpolation::Linear
  }
}

/// Spline curve used to provide interpolation between control points (keys).
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Spline<T> {
  keys: Vec<Key<T>>,
}

impl<T> Spline<T> {
  /// Create a new spline out of keys. The keys don’t have to be sorted because they’re sorted by
  /// this function.
  pub fn from_keys(mut keys: Vec<Key<T>>) -> Self {
    keys.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap());

    Spline {
      keys: keys
    }
  }

  /// Sample a spline at a given time.
  ///
  /// # Return
  ///
  /// `None` if you try to sample a value at a time that has no key associated with. That can also
  /// happen if you try to sample between two keys with a specific interpolation mode that make the
  /// sampling impossible. For instance, `Interpolate::CatmullRom` requires *four* keys. If you’re
  /// near the beginning of the spline or its end, ensure you have enough keys around to make the
  /// sampling.
  pub fn sample(&self, t: Time) -> Option<T> where T: Interpolate {
    let keys = &self.keys;
    let i = search_lower_cp(keys, t);

    let i = match i {
      Some(i) => i,
      None => return None
    };

    let cp0 = &keys[i];

    match cp0.interpolation {
      Interpolation::Step(threshold) => {
        let cp1 = &keys[i+1];
        let nt = normalize_time(t, cp0, cp1);
        Some(if nt < threshold { cp0.value } else { cp1.value })
      },
      Interpolation::Linear => {
        let cp1 = &keys[i+1];
        let nt = normalize_time(t, cp0, cp1);

        Some(Interpolate::lerp(cp0.value, cp1.value, nt))
      },
      Interpolation::Cosine => {
        let cp1 = &keys[i+1];
        let nt = normalize_time(t, cp0, cp1);
        let cos_nt = (1. - f32::cos(nt * consts::PI)) * 0.5;

        Some(Interpolate::lerp(cp0.value, cp1.value, cos_nt))
      },
      Interpolation::CatmullRom => {
        // We need at least four points for Catmull Rom; ensure we have them, otherwise, return
        // None.
        if i == 0 || i >= keys.len() - 2 {
          None
        } else {
          let cp1 = &keys[i+1];
          let cpm0 = &keys[i-1];
          let cpm1 = &keys[i+2];
          let nt = normalize_time(t, cp0, cp1);

          Some(Interpolate::cubic_hermite((cpm0.value, cpm0.t), (cp0.value, cp0.t), (cp1.value, cp1.t), (cpm1.value, cpm1.t), nt))
        }
      }
    }
  }

  /// Sample a spline at a given time with clamping.
  ///
  /// # Return
  ///
  /// If you sample before the first key or after the last one,
  /// return the first key or the last one, respectively.
  ///
  /// # Panic
  ///
  /// This function panics if you have no key.
  pub fn clamped_sample(&self, t: Time) -> T where T: Interpolate {
    let first = self.keys.first().unwrap();
    let last = self.keys.last().unwrap();

    if t <= first.t {
      return first.value;
    } else if t >= last.t {
      return last.value;
    }

    self.sample(t).unwrap()
  }
}

impl<T> Load for Spline<T> where T: SplineDeserializerAdapter {
  type Args = ();

  const TY_STR: &'static str = "splines";

  fn load<P>(path: P, _: &mut ResCache, _: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path> {
    let path = path.as_ref();

    info!("loading spline: {:?}", path);

    let file = File::open(path).map_err(|e| LoadError::FileNotFound(path.to_path_buf(), format!("{:?}", e)))?;
    let keys: Vec<Key<T::Deserialized>> = from_reader(file).map_err(|e| LoadError::ParseFailed(format!("{:?}", e)))?;

    Ok(Spline::from_keys(keys.into_iter().map(|key|
      Key::new(key.t, T::from_deserialized(key.value), key.interpolation)
    ).collect()))
  }
}

/// Spline deserializer adapter used to deserialize splines which keys’ values types don’t directly
/// implement deserialization.
pub trait SplineDeserializerAdapter {
  type Deserialized: Deserialize;

  fn from_deserialized(de: Self::Deserialized) -> Self;
}

impl SplineDeserializerAdapter for f32 {
  type Deserialized = Self;

  fn from_deserialized(de: Self::Deserialized) -> Self {
    de
  }
}

impl<T> SplineDeserializerAdapter for Vector2<T> where T: Clone + Deserialize {
  type Deserialized = [T; 2];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    (&de).into()
  }
}

impl<T> SplineDeserializerAdapter for Vector3<T> where T: Clone + Deserialize {
  type Deserialized = [T; 3];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    (&de).into()
  }
}

impl<T> SplineDeserializerAdapter for Vector4<T> where T: Clone + Deserialize {
  type Deserialized = [T; 4];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    (&de).into()
  }
}

impl<T> SplineDeserializerAdapter for UnitQuaternion<T> where T: BaseFloat + Deserialize {
  type Deserialized = [T; 4];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    Unit::new((&de).into())
  }
}

impl SplineDeserializerAdapter for Scale {
  type Deserialized = [f32; 3];

  fn from_deserialized(de: Self::Deserialized) -> Self {
    Scale::from(&de)
  }
}

/// Iterator over spline keys.
pub struct SplineIterator<'a, T> where T: 'a {
  anim_param: &'a Spline<T>,
  i: usize
}

impl<'a, T> Iterator for SplineIterator<'a, T> {
  type Item = &'a Key<T>;

  fn next(&mut self) -> Option<Self::Item> {
    let r = self.anim_param.keys.get(self.i);

    if let Some(_) = r {
      self.i += 1;
    }

    r
  }
}

impl<'a, T> IntoIterator for &'a Spline<T> {
  type Item = &'a Key<T>;
  type IntoIter = SplineIterator<'a, T>;

  fn into_iter(self) -> Self::IntoIter {
    SplineIterator {
      anim_param: self,
      i: 0
    }
  }
}

/// Keys that can be interpolated between. Implementing this trait is required to perform sampling
/// on splines.
pub trait Interpolate: Copy {
  /// Linear interpolation.
  fn lerp(a: Self, b: Self, t: Time) -> Self;
  /// Cubic hermite interpolation.
  ///
  /// Default to `Self::lerp`.
  fn cubic_hermite(_: (Self, Time), a: (Self, Time), b: (Self, Time), _: (Self, Time), t: Time) -> Self {
    Self::lerp(a.0, b.0, t)
  }
}

impl Interpolate for f32 {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for Vector2<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for Vector3<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for Vector4<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    lerp(a, b, t)
  }

  fn cubic_hermite(x: (Self, Time), a: (Self, Time), b: (Self, Time), y: (Self, Time), t: Time) -> Self {
    cubic_hermite(x, a, b, y, t)
  }
}

impl Interpolate for UnitQuaternion<f32> {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    a * UnitQuaternion::new(&(UnitQuaternion::new(&a.quaternion().conjugate()) * b).quaternion().powf(t))
  }
}

impl Interpolate for Scale {
  fn lerp(a: Self, b: Self, t: Time) -> Self {
    let av = Vector3::new(a.x, a.y, a.z);
    let bv = Vector3::new(b.x, b.y, b.z);
    let r = *lerp(av, bv, t).as_ref();

    Scale::from(&r)
  }
}

// Default implementation of Interpolate::lerp.
pub fn lerp<T>(a: T, b: T, t: Time) -> T where T: Add<Output = T> + Mul<Time, Output = T> {
  a * (1. - t) + b * t
}

// Default implementation of Interpolate::cubic_hermit.
pub fn cubic_hermite<T>(x: (T, Time), a: (T, Time), b: (T, Time), y: (T, Time), t: Time) -> T
    where T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Time, Output = T> + Div<Time, Output = T> {
  // time stuff
  let t2 = t * t;
  let t3 = t2 * t;
  let two_t3 = 2. * t3;
  let three_t2 = 3. * t2;

  // tangents
  let m0 = (b.0 - x.0) / (b.1 - x.1);
	let m1 = (y.0 - a.0) / (y.1 - a.1);

  a.0 * (two_t3 - three_t2 + 1.) + m0 * (t3 - 2. * t2 + t) + b.0 * (-two_t3 + three_t2) + m1 * (t3 - t2)
}

// Normalize a time ([0;1]) given two control points.
pub fn normalize_time<T>(t: Time, cp: &Key<T>, cp1: &Key<T>) -> Time {
  (t - cp.t) / (cp1.t - cp.t)
}

// Find the lower control point corresponding to a given time.
fn search_lower_cp<T>(cps: &[Key<T>], t: Time) -> Option<usize> {
  let mut i = 0;
  let len = cps.len();

  if len < 2 {
    return None;
  }

  loop {
    let cp = &cps[i];
    let cp1 = &cps[i+1];

    if t >= cp1.t {
      if i >= len - 2 {
        return None;
      }

      i += 1;
    } else if t < cp.t {
      if i == 0 {
        return None;
      }

      i -= 1;
    } else {
      break; // found
    }
  }

  Some(i)
}
<|endoftext|>


<commit_before>use super::command::{Command, Query};
use super::command::Command::Status;
use std::collections::HashMap;
use command_query::CommandQuery;
use queryable::Queryable;
use command_line::CommandLine;
use commandable::Commandable;
use request_cancellable::RequestCancellable;

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct StatusCommand {
    command: Command,
    arguments: HashMap<String, String>,
}

impl Default for StatusCommand {
    fn default() -> StatusCommand {
        StatusCommand {
            command: Status,
            arguments: HashMap::new(),
        }
    }
}

impl StatusCommand {
    pub fn new() -> StatusCommand {
        Default::default()
    }

    pub fn build(self) -> (Command, Query) {
        let mut query: Query = vec![];
        for (key, value) in self.arguments.iter() {
            query.push((key.to_owned(), value.to_owned()));
        }
        (self.command, query)
    }
}

impl Queryable for StatusCommand {
    fn to_query(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandQuery::new(command, query);
        command.encode()
    }
}

impl Commandable for StatusCommand {
    fn to_command(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandLine::new(command, query);
        command.encode()
    }
}

request_cancellable!(StatusCommand);

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::HashMap;
    use command::Command::Status;
    use queryable::Queryable;
    use commandable::Commandable;

    #[test]
    fn test_new() {
        let status = StatusCommand::new();
        let expected = StatusCommand {
            command: Status,
            arguments: HashMap::new(),
        };
        assert_eq!(expected, status);
    }

    #[test]
    fn test_build() {
        let actual = StatusCommand::new().build();
        let expected = (Status, vec![]);
        assert_eq!(expected, actual);
    }

    #[test]
    fn test_queryable() {
        let query = StatusCommand::new().to_query();
        let url_encoded = "/d/status?";
        assert_eq!(url_encoded.to_string(), query);
    }

    #[test]
    fn test_commandable() {
        let query = StatusCommand::new().to_command();
        let url_encoded = "status";
        assert_eq!(url_encoded.to_string(), query);
    }
}
<commit_msg>status: Fix typo<commit_after>use super::command::{Command, Query};
use super::command::Command::Status;
use std::collections::HashMap;
use command_query::CommandQuery;
use queryable::Queryable;
use command_line::CommandLine;
use commandable::Commandable;
use request_cancellable::RequestCancellable;

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct StatusCommand {
    command: Command,
    arguments: HashMap<String, String>,
}

impl Default for StatusCommand {
    fn default() -> StatusCommand {
        StatusCommand {
            command: Status,
            arguments: HashMap::new(),
        }
    }
}

impl StatusCommand {
    pub fn new() -> StatusCommand {
        Default::default()
    }

    pub fn build(self) -> (Command, Query) {
        let mut query: Query = vec![];
        for (key, value) in self.arguments.iter() {
            query.push((key.to_owned(), value.to_owned()));
        }
        (self.command, query)
    }
}

impl Queryable for StatusCommand {
    fn to_query(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandQuery::new(command, query);
        command.encode()
    }
}

impl Commandable for StatusCommand {
    fn to_command(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandLine::new(command, query);
        command.encode()
    }
}

request_cancellable!(StatusCommand);

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::HashMap;
    use command::Command::Status;
    use queryable::Queryable;
    use commandable::Commandable;

    #[test]
    fn test_new() {
        let status = StatusCommand::new();
        let expected = StatusCommand {
            command: Status,
            arguments: HashMap::new(),
        };
        assert_eq!(expected, status);
    }

    #[test]
    fn test_build() {
        let actual = StatusCommand::new().build();
        let expected = (Status, vec![]);
        assert_eq!(expected, actual);
    }

    #[test]
    fn test_queryable() {
        let query = StatusCommand::new().to_query();
        let url_encoded = "/d/status?";
        assert_eq!(url_encoded.to_string(), query);
    }

    #[test]
    fn test_commandable() {
        let query = StatusCommand::new().to_command();
        let cli_encoded = "status";
        assert_eq!(cli_encoded.to_string(), query);
    }
}
<|endoftext|>