<commit_before>use snake;
use game_state;
use player;

pub static NUMBER_OF_LAYERS: uint = 4;
pub static WATER_COLOR: [f32, ..4] = [0.0, 0.0, 0.45, 1.0];
pub static WATER_FRICTION: f64 = 0.2;
pub static RADIUS: f64 = 0.1;
pub static SURFACE_Y: f64 = 10.0;
pub static INITIAL_GAME_STATE: game_state::GameState = game_state::Play;
pub static INITIAL_CAMERA_POS: [f64, ..2] = [0.0, 0.0];
pub static CAMERA_FOLLOW_PERCENTAGE: f64 = 0.4;

pub static YOU_WIN_TEXT: &'static str = "you win";
pub static YOU_WIN_TEXT_COLOR: [f32, ..4] = GREEN;
pub static YOU_WIN_POS: [f64, ..2] = [-0.42, 0.1];

pub static YOU_LOOSE_TEXT: &'static str = "you loose";
pub static YOU_LOOSE_TEXT_COLOR: [f32, ..4] = RED;
pub static YOU_LOOSE_POS: [f64, ..2] = [-0.55, 0.1];

pub static AIR_BOTTLE_RADIUS: f64 = 0.1;
pub static AIR_BOTTLE_TEST_COLOR: [f32, ..4] = WHITE;
pub static AIR_BOTTLE_TEXT_COLOR: [f32, ..4] = BLACK;
pub static AIR_BOTTLE_FILL_UP: f64 = 0.5;
pub static AIR_BOTTLE_POS: &'static [f64] = &[
    0.5, 0.3,
    0.3, 0.8,
    0.6, 1.4,
];

pub static PLAYER_LOOSE_AIR_SPEED: f64 = 0.1;
pub static PLAYER_COLOR: [f32, ..4] = YELLOW;
pub static PLAYER_BITTEN_COLOR: [f32, ..4] = RED;
pub static PLAYER_INITIAL_BLOOD: f64 = 1.0;
pub static PLAYER_INITIAL_AIR: f64 = 1.0;
pub static PLAYER_RADIUS: f64 = 0.1;
pub static PLAYER_ACCELERATION_RIGHT: f64 = 0.1;
pub static PLAYER_ACCELERATION_LEFT: f64 = 0.1;
pub static PLAYER_ACCELERATION_UP: f64 = 0.1;
pub static PLAYER_ACCELERATION_DOWN: f64 = 0.1;
pub static PLAYER_INITIAL_TWEEN_FACTOR: f64 = 0.0;
pub static PLAYER_TWEEN_SPEED: f64 = 1.0;
pub static PLAYER_BITTEN_FADE_OUT_SECONDS: f64 = 2.0;
pub static PLAYER_INITIAL_STATE: player::PlayerState = player::Normal;

pub struct SnakeSettings {
    pub acceleration_left: f64,
    pub acceleration_right: f64,
    pub acceleration_up: f64,
    pub acceleration_down: f64,
    pub initial_state: snake::SnakeState,
    pub bite_damage: f64,
    pub sensor_distance: f64,
    pub attack_distance: f64,
    pub wait_seconds_before_initial_attack: f64,
    pub wait_seconds_before_repeat_attack: f64,
    pub test_color: [f32, ..4],
    pub radius: f64,
}

pub static SNAKE_TAIL_COLOR: [f32, ..4] = [0.8, 0.6, 0.6, 0.4];

pub static SNAKE_TAIL_DISTANCE: f64 = 0.01;
pub static SNAKE_OVERALL_ACCELERATION: f64 = 2.0;
pub static SNAKE_SETTINGS: SnakeSettings = SnakeSettings {
    acceleration_left: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_right: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_up: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_down: 0.1 * SNAKE_OVERALL_ACCELERATION,
    initial_state: snake::Ignorant,
    bite_damage: 0.1,
    sensor_distance: 2.0,
    attack_distance: 0.5 * PLAYER_RADIUS,
    wait_seconds_before_initial_attack: 1.0,
    wait_seconds_before_repeat_attack: 1.0,
    test_color: BLACK,
    radius: 0.03,
};

pub static SNAKE_SPEED_RIGHT: f64 = 0.1;
pub static SNAKE_SPEED_LEFT: f64 = 0.1;
pub static SNAKE_SPEED_UP: f64 = 0.1;
pub static SNAKE_SPEED_DOWN: f64 = 0.1;

// snake 1.
pub static SNAKE_1_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_1_POS: [f64, ..2] = [-0.8, 0.8];
pub static SNAKE_1_ADD: bool = true;

// snake 2.
pub static SNAKE_2_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_2_POS: [f64, ..2] = [0.8, 0.8];
pub static SNAKE_2_ADD: bool = true;

pub static BAR_RECTANGLE: [f64, ..4] = [0.3, -0.06, 1.5, 0.05];
pub static BAR_MARGIN: f64 = 0.01;
pub static BAR_TEXT_COLOR: [f32, ..4] = WHITE;

pub static AIR_BAR_INITIAL_VALUE: f64 = 0.5;
pub static AIR_BAR_POS: [f64, ..2] = [-0.9, 0.95];
pub static AIR_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static AIR_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_BLUE;
pub static AIR_BAR_BAR_COLOR: [f32, ..4] = LIGHT_BLUE;

pub static BLOOD_BAR_INITIAL_VALUE: f64 = 0.5;
pub static BLOOD_BAR_POS: [f64, ..2] = [-0.9, 0.85];
pub static BLOOD_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static BLOOD_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_RED;
pub static BLOOD_BAR_BAR_COLOR: [f32, ..4] = LIGHT_RED;

pub static BAR_BACKGROUND_HEIGHT: f64 = 0.275;
pub static BAR_BACKGROUND_COLOR: [f32, ..4] = [0.1, 0.1, 0.1, 0.9];
pub static BAR_BACKGROUND_COLOR_2: [f32, ..4] = [0.4, 0.4, 0.4, 0.8];
pub static BAR_BACKGROUND_MARGIN: f64 = 0.01;
pub static BAR_BACKGROUND_MARGIN_2: f64 = 0.02;

pub static BLACK: [f32, ..4] = [0.0, 0.0, 0.0, 1.0];
pub static WHITE: [f32, ..4] = [1.0, 1.0, 1.0, 1.0];
pub static GRAY: [f32, ..4] = [0.5, 0.5, 0.5, 1.0];
pub static DARK_GRAY: [f32, ..4] = [0.2, 0.2, 0.2, 1.0];

pub static RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];
pub static DARK_RED: [f32, ..4] = [0.5, 0.0, 0.0, 1.0];
pub static LIGHT_RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];

pub static GREEN: [f32, ..4] = [0.0, 1.0, 0.0, 1.0];

pub static BLUE: [f32, ..4] = [0.0, 0.0, 1.0, 1.0];
pub static DARK_BLUE: [f32, ..4] = [0.0, 0.0, 0.5, 1.0];
pub static LIGHT_BLUE: [f32, ..4] = [0.5, 0.5, 1.0, 1.0];

pub static YELLOW: [f32, ..4] = [0.0, 1.0, 1.0, 1.0];

pub static ORIGIN: [f64, ..2] = [0.0, 0.0];

<commit_msg>Added air bottles all the way up<commit_after>use snake;
use game_state;
use player;

pub static NUMBER_OF_LAYERS: uint = 4;
pub static WATER_COLOR: [f32, ..4] = [0.0, 0.0, 0.45, 1.0];
pub static WATER_FRICTION: f64 = 0.2;
pub static RADIUS: f64 = 0.1;
pub static SURFACE_Y: f64 = 10.0;
pub static INITIAL_GAME_STATE: game_state::GameState = game_state::Play;
pub static INITIAL_CAMERA_POS: [f64, ..2] = [0.0, 0.0];
pub static CAMERA_FOLLOW_PERCENTAGE: f64 = 0.4;

pub static YOU_WIN_TEXT: &'static str = "you win";
pub static YOU_WIN_TEXT_COLOR: [f32, ..4] = GREEN;
pub static YOU_WIN_POS: [f64, ..2] = [-0.42, 0.1];

pub static YOU_LOOSE_TEXT: &'static str = "you loose";
pub static YOU_LOOSE_TEXT_COLOR: [f32, ..4] = RED;
pub static YOU_LOOSE_POS: [f64, ..2] = [-0.55, 0.1];

pub static AIR_BOTTLE_RADIUS: f64 = 0.1;
pub static AIR_BOTTLE_TEST_COLOR: [f32, ..4] = WHITE;
pub static AIR_BOTTLE_TEXT_COLOR: [f32, ..4] = BLACK;
pub static AIR_BOTTLE_FILL_UP: f64 = 0.5;
pub static AIR_BOTTLE_POS: &'static [f64] = &[
    0.5, 0.3,
    0.3, 0.8,
    0.6, 1.4,
    0.3, 2.2,
    0.1, 3.0,
    0.3, 4.0,
    0.3, 5.0,
    0.6, 5.5,
    0.6, 6.1,
    0.7, 6.8,
    0.5, 8.0,
    0.2, 8.2,
    0.6, 8.8,
];

pub static PLAYER_LOOSE_AIR_SPEED: f64 = 0.1;
pub static PLAYER_COLOR: [f32, ..4] = YELLOW;
pub static PLAYER_BITTEN_COLOR: [f32, ..4] = RED;
pub static PLAYER_INITIAL_BLOOD: f64 = 1.0;
pub static PLAYER_INITIAL_AIR: f64 = 1.0;
pub static PLAYER_RADIUS: f64 = 0.1;
pub static PLAYER_ACCELERATION_RIGHT: f64 = 0.1;
pub static PLAYER_ACCELERATION_LEFT: f64 = 0.1;
pub static PLAYER_ACCELERATION_UP: f64 = 0.1;
pub static PLAYER_ACCELERATION_DOWN: f64 = 0.1;
pub static PLAYER_INITIAL_TWEEN_FACTOR: f64 = 0.0;
pub static PLAYER_TWEEN_SPEED: f64 = 1.0;
pub static PLAYER_BITTEN_FADE_OUT_SECONDS: f64 = 2.0;
pub static PLAYER_INITIAL_STATE: player::PlayerState = player::Normal;

pub struct SnakeSettings {
    pub acceleration_left: f64,
    pub acceleration_right: f64,
    pub acceleration_up: f64,
    pub acceleration_down: f64,
    pub initial_state: snake::SnakeState,
    pub bite_damage: f64,
    pub sensor_distance: f64,
    pub attack_distance: f64,
    pub wait_seconds_before_initial_attack: f64,
    pub wait_seconds_before_repeat_attack: f64,
    pub test_color: [f32, ..4],
    pub radius: f64,
}

pub static SNAKE_TAIL_COLOR: [f32, ..4] = [0.8, 0.6, 0.6, 0.4];

pub static SNAKE_TAIL_DISTANCE: f64 = 0.01;
pub static SNAKE_OVERALL_ACCELERATION: f64 = 2.0;
pub static SNAKE_SETTINGS: SnakeSettings = SnakeSettings {
    acceleration_left: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_right: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_up: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_down: 0.1 * SNAKE_OVERALL_ACCELERATION,
    initial_state: snake::Ignorant,
    bite_damage: 0.1,
    sensor_distance: 2.0,
    attack_distance: 0.5 * PLAYER_RADIUS,
    wait_seconds_before_initial_attack: 1.0,
    wait_seconds_before_repeat_attack: 1.0,
    test_color: BLACK,
    radius: 0.03,
};

pub static SNAKE_SPEED_RIGHT: f64 = 0.1;
pub static SNAKE_SPEED_LEFT: f64 = 0.1;
pub static SNAKE_SPEED_UP: f64 = 0.1;
pub static SNAKE_SPEED_DOWN: f64 = 0.1;

// snake 1.
pub static SNAKE_1_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_1_POS: [f64, ..2] = [-0.8, 0.8];
pub static SNAKE_1_ADD: bool = true;

// snake 2.
pub static SNAKE_2_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_2_POS: [f64, ..2] = [0.8, 0.8];
pub static SNAKE_2_ADD: bool = true;

pub static BAR_RECTANGLE: [f64, ..4] = [0.3, -0.06, 1.5, 0.05];
pub static BAR_MARGIN: f64 = 0.01;
pub static BAR_TEXT_COLOR: [f32, ..4] = WHITE;

pub static AIR_BAR_INITIAL_VALUE: f64 = 0.5;
pub static AIR_BAR_POS: [f64, ..2] = [-0.9, 0.95];
pub static AIR_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static AIR_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_BLUE;
pub static AIR_BAR_BAR_COLOR: [f32, ..4] = LIGHT_BLUE;

pub static BLOOD_BAR_INITIAL_VALUE: f64 = 0.5;
pub static BLOOD_BAR_POS: [f64, ..2] = [-0.9, 0.85];
pub static BLOOD_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static BLOOD_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_RED;
pub static BLOOD_BAR_BAR_COLOR: [f32, ..4] = LIGHT_RED;

pub static BAR_BACKGROUND_HEIGHT: f64 = 0.275;
pub static BAR_BACKGROUND_COLOR: [f32, ..4] = [0.1, 0.1, 0.1, 0.9];
pub static BAR_BACKGROUND_COLOR_2: [f32, ..4] = [0.4, 0.4, 0.4, 0.8];
pub static BAR_BACKGROUND_MARGIN: f64 = 0.01;
pub static BAR_BACKGROUND_MARGIN_2: f64 = 0.02;

pub static BLACK: [f32, ..4] = [0.0, 0.0, 0.0, 1.0];
pub static WHITE: [f32, ..4] = [1.0, 1.0, 1.0, 1.0];
pub static GRAY: [f32, ..4] = [0.5, 0.5, 0.5, 1.0];
pub static DARK_GRAY: [f32, ..4] = [0.2, 0.2, 0.2, 1.0];

pub static RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];
pub static DARK_RED: [f32, ..4] = [0.5, 0.0, 0.0, 1.0];
pub static LIGHT_RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];

pub static GREEN: [f32, ..4] = [0.0, 1.0, 0.0, 1.0];

pub static BLUE: [f32, ..4] = [0.0, 0.0, 1.0, 1.0];
pub static DARK_BLUE: [f32, ..4] = [0.0, 0.0, 0.5, 1.0];
pub static LIGHT_BLUE: [f32, ..4] = [0.5, 0.5, 1.0, 1.0];

pub static YELLOW: [f32, ..4] = [0.0, 1.0, 1.0, 1.0];

pub static ORIGIN: [f64, ..2] = [0.0, 0.0];

<|endoftext|>


<commit_before>
use Settings = piston::game::Settings;
use Game = piston::game::Game;
use GameWindow = piston::game_window::GameWindow;
use piston::gl::Gl;
use graphics;
use graphics::*;
use rand::random;

fn load_vertices() -> Vec<f32> {
    vec!(
         0.0,  0.5,
        -0.5, -0.5,
         0.5, -0.5,
    )
}

fn load_colors() -> Vec<f32> {
    vec!(
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
    )
}

pub struct SnakeApp {
    vertices: Vec<f32>,
    colors: Vec<f32>,
    settings: Settings,
    game_window: GameWindow,
}

impl Game for SnakeApp {
    fn get_game_window<'a>(&'a self) -> &'a GameWindow { &self.game_window }
    fn get_settings<'a>(&'a self) -> &'a Settings { &self.settings }
    fn render(&self, c: &graphics::Context, gl: &mut Gl) {
        // Render triangle.
        // self.shader.unwrap().render(self.vertices.as_slice(), self.colors.as_slice());

        c.rgba(1.0, 0.0, 0.0, 1.0).square(0.0, 0.0, 0.5).fill(gl);
        c.rgba(0.0, 0.0, 1.0, 0.5).square(0.2, 0.0, 0.5).trans(-0.5, 0.0).rot_deg(-5.0).rot_deg(10.0 * random()).fill(gl);
    }
    fn update(&mut self) {
    }
    fn load(&mut self) {
    }
}

impl SnakeApp {
    pub fn new() -> SnakeApp {
        let exit_on_esc = true;
        let background_color = [1.0, 1.0, 1.0, 1.0];
        SnakeApp {
            game_window: GameWindow::window("Snake", 512, 512),
            vertices: load_vertices(),
            colors: load_colors(),
            settings: Settings::new(exit_on_esc, background_color),
        }
    }
}

<commit_msg>Simple triangle polygon test<commit_after>
use Settings = piston::game::Settings;
use Game = piston::game::Game;
use GameWindow = piston::game_window::GameWindow;
use piston::gl::Gl;
use graphics;
use graphics::*;
use rand::random;

fn load_vertices() -> Vec<f32> {
    vec!(
         0.0,  0.5,
        -0.5, -0.5,
         0.5, -0.5,
    )
}

fn load_colors() -> Vec<f32> {
    vec!(
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
    )
}

pub struct SnakeApp {
    vertices: Vec<f32>,
    colors: Vec<f32>,
    settings: Settings,
    game_window: GameWindow,
}

impl Game for SnakeApp {
    fn get_game_window<'a>(&'a self) -> &'a GameWindow { &self.game_window }
    fn get_settings<'a>(&'a self) -> &'a Settings { &self.settings }
    fn render(&self, c: &graphics::Context, gl: &mut Gl) {
        let polygon = [
            0.0, 0.0,
            0.5, 0.0,
            0.5, 0.5
        ];
        c.polygon(polygon.as_slice()).rgba(0.0, 0.0, 1.0, 1.0).fill(gl);
    }
    fn update(&mut self) {
    }
    fn load(&mut self) {
    }
}

impl SnakeApp {
    pub fn new() -> SnakeApp {
        let exit_on_esc = true;
        let background_color = [1.0, 1.0, 1.0, 1.0];
        SnakeApp {
            game_window: GameWindow::window("Snake", 512, 512),
            vertices: load_vertices(),
            colors: load_colors(),
            settings: Settings::new(exit_on_esc, background_color),
        }
    }
}

<|endoftext|>


<commit_before>extern crate termion;
extern crate rand;

use std::io::{self, Read, Write};
use std::collections::{HashSet, HashMap};

use termion::{clear, cursor, style, color};
use termion::raw::IntoRawMode;
use termion::input::TermRead;
use termion::event::Key;
use rand::random;

struct Game<R, W: Write> {
    score: u32,
    grid: [u32; 16usize],
    bgs: HashMap<u32, color::Bg<color::Rgb>>,
    stdin: R,
    stdout: W,
}

fn init<R: Read, W: Write>(stdin: R, mut stdout: W) {
    write!(stdout, "{}", clear::All).unwrap();

    let mut bgs: HashMap<u32, color::Bg<color::Rgb>> = HashMap::with_capacity(17);
    // Ref: http://www.rapidtables.com/web/color/RGB_Color.htm
    // should cover all..
    let colors = [
        color::Bg(color::Rgb(224, 224, 224)), color::Bg(color::Rgb(255, 229, 204)),
        color::Bg(color::Rgb(255, 153, 153)), color::Bg(color::Rgb(204, 255, 209)),
        color::Bg(color::Rgb(204, 255, 255)), color::Bg(color::Rgb(204, 229, 255)),
        color::Bg(color::Rgb(229, 255, 204)), color::Bg(color::Rgb(229, 204, 255)),
        color::Bg(color::Rgb(255, 153, 255)), color::Bg(color::Rgb(255, 204, 229)),
        color::Bg(color::Rgb(255, 255, 51)), color::Bg(color::Rgb(255, 178, 102)),
        color::Bg(color::Rgb(178, 255, 102)), color::Bg(color::Rgb(102, 255, 178)),
        color::Bg(color::Rgb(102, 178, 255)), color::Bg(color::Rgb(102, 102, 255)),
        color::Bg(color::Rgb(255, 0, 0)),
    ];
    bgs.insert(0u32, colors[0]);
    for i in 1..17 {
        bgs.insert(1u32<<i as u32, colors[i]);
    }

    let mut game = Game {
        score: 0,
        grid: [0u32; 16usize],
        bgs: bgs,
        stdin: stdin.keys(),
        stdout: stdout,
    };

    game.reset();
    game.start();
}

impl <R, W: Write> Drop for Game<R, W> {
    fn drop(&mut self) {
        write!(self.stdout, "{}{}{}", clear::All, style::Reset,
               cursor::Goto(1, 1)).unwrap();
    }
}

macro_rules! continue_if_game_over {
    ($game_over:ident) => (
        if $game_over {
            continue;
        }
    )
}

impl <R: Iterator<Item=Result<Key, std::io::Error>>, W: Write> Game<R, W> {
    fn pos(&self, x: i32, y: i32) -> usize {
        (x * 4 + y) as usize
    }

    fn get_by_pos(&self, x: i32, y: i32) -> u32 {
        self.grid[self.pos(x, y)]
    }

    fn set_by_pos(&mut self, x: i32, y: i32, val: u32) {
        self.grid[self.pos(x, y)] = val;
    }

    fn start(&mut self) {
        let mut game_over = false;
        let mut moved = false;
        loop {
            let b = self.stdin.next().unwrap().unwrap();
            match b {
                Key::Up | Key::Char('w') | Key::Char('k') => {
                    continue_if_game_over!(game_over);
                    moved = self.up();
                },
                Key::Right | Key::Char('d') | Key::Char('l') => {
                    continue_if_game_over!(game_over);
                    moved = self.right();
                },
                Key::Down | Key::Char('s') | Key::Char('j') => {
                    continue_if_game_over!(game_over);
                    moved = self.down();
                },
                Key::Left | Key::Char('a') | Key::Char('h') => {
                    continue_if_game_over!(game_over);
                    moved = self.left();
                },
                Key::Char('r') => {
                    self.restart();
                    return;
                },
                Key::Esc | Key::Char('q') => return,
                _ => continue,
            }

            if !moved || game_over {
                continue;
            }
            let full_filled = self.fill_up();
            self.draw();
            if full_filled && self.game_over() {
                game_over = true;
                self.pop_menu();
            }
        }
    }

    fn reset(&mut self) {
        for i in 0..16usize {
            self.grid[i] = 0u32;
        }
        self.score = 0u32;
        let idx1 = random::<u8>() as usize % 16usize;
        self.grid[idx1] = 2u32;
        loop {
            let idx2 = random::<u8>() as usize % 16usize;
            if idx1 != idx2 {
                self.grid[idx2] = 2u32;
                break;
            }
        }
        self.draw();
    }

    fn restart(&mut self) {
        self.reset();
        self.start();
    }

    fn fill_up(&mut self) -> bool {
        let mut holes = HashSet::new();
        for i in 0..16usize {
            if self.grid[i] == 0u32 {
                holes.insert(i);
            }
        }

        let len = holes.len();
        if len == 0 {
            return true;
        }
        for idx in holes.iter() {  // use HashSet's random feature directly..
            // self.grid[*idx] = 1u32 << (*idx as u32 % 2u32 + 1u32);  // whatever...
            let mut threhold = 222u8;
            if len <= 4 {
                threhold = 127u8;
            }
            if random::<u8>() <= threhold {
                self.grid[*idx] = 2u32;
            } else {
                self.grid[*idx] = 4u32;
            }
            break;
        }

        (len-1) == 0
    }

    fn game_over(&mut self) -> bool {
        for x in 0..4 {
            for y in 1..4 {
                if self.get_by_pos(x, y-1) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        for y in 0..4 {
            for x in 1..4 {
                if self.get_by_pos(x-1, y) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        return true;
    }

    fn bg(&self, n: u32) -> color::Bg<color::Rgb> {
        match self.bgs.get(&n) {
            Some(bg) => *bg,
            None => color::Bg(color::Rgb(255, 255, 255)),
        }
    }

    fn draw(&mut self) {
        write!(self.stdout, "{}", cursor::Goto(1, 1)).unwrap();

        // header
        let header_bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        let score = self.score.to_string();
        write!(self.stdout, " {}{} SCORE: {}{:space$}{}\n\r", header_bg, style::Bold, score,
               " ", style::Reset, space=23-score.len()).unwrap();
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        write!(self.stdout, " {:32}\n\r", " ").unwrap();

        // body
        for x in 0..4i32 {
            let mut up_line: String = String::new();
            let mut mid_line: String = String::new();
            let mut down_line: String = String::new();
            for y in 0..4i32 {
                let val = self.get_by_pos(x, y);
                let sval = val.to_string();
                let bg = self.bg(val);

                up_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                if val == 0u32 {
                    mid_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                } else {
                    let pad = (7 - sval.len()) / 2;
                    mid_line.push_str(
                        format!(" {}{:lpad$}{}{}{}{}{:rpad$}{}", bg, " ", color::Fg(color::Black),
                                sval, style::Reset, bg, " ", style::Reset,
                                lpad=pad, rpad=7-sval.len()-pad).as_str());
                }
                down_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
            }
            up_line.push_str("\n\r");
            mid_line.push_str("\n\r");
            down_line.push_str("\n\r");
            self.stdout.write(up_line.as_bytes()).unwrap();
            self.stdout.write(mid_line.as_bytes()).unwrap();
            self.stdout.write(down_line.as_bytes()).unwrap();
            self.stdout.write(format!("{:32}\n\r", " ").as_bytes()).unwrap();
        }
        self.stdout.flush().unwrap();
    }

    fn pop_menu(&mut self) {
        let bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, "{}{}{}  GAME OVER!   {}\n\r",
               cursor::Goto(10, 4), bg, style::Bold, style::Reset).unwrap();
        write!(self.stdout, "{}{}  restart: r   {}\n\r",
               cursor::Goto(10, 5), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}  quit: ESC|q  {}\n\r",
               cursor::Goto(10, 6), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}               {}\n\r",
               cursor::Goto(10, 7), bg, style::Reset).unwrap();
    }

    fn merge<Fget, Fset>(&mut self, xs: [i32; 4], ys: [i32; 4],
                         mut fget: Fget, mut fset: Fset) -> bool
        where Fget: FnMut(&Game<R, W>, i32, i32) -> u32, Fset: FnMut(&mut Game<R, W>, i32, i32, u32) {
        let mut moved = false;  // Fuck: https://github.com/rust-lang/rust/issues/28570

        for x in xs.iter() {
            let mut prev_non_zero_y = ys[0];
            let first_y = ys[0];
            for y in (&ys[1..4]).iter() {
                let mut val = fget(self, *x, *y);
                if val != 0u32 {
                    if prev_non_zero_y < *y && fget(self, *x, prev_non_zero_y) == val {
                        let score = val * 2;
                        self.score += score;
                        fset(self, *x, prev_non_zero_y, score);
                        val = 0u32;
                        fset(self, *x, *y, val);
                        prev_non_zero_y = *y + 1;
                        moved = true;
                    } else {
                        prev_non_zero_y = *y;
                    }
                }

                let mut k = *y;
                while k > first_y {
                    let prev = fget(self, *x, k-1);
                    if prev != 0u32 || val == 0u32 {
                        break;
                    }
                    moved = true;
                    fset(self, *x, k-1, val);
                    fset(self, *x, k, 0u32);
                    k -= 1;
                    prev_non_zero_y = k;
                }
            }
        }

        moved
    }

    fn up(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(y, x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(y, x, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }

    fn right(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-x, -y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-x, -y, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn down(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-y, -x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-y, -x, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn left(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(x, y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(x, y, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }
}

fn main() {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let stdout = stdout.into_raw_mode().unwrap();
    let stdin = io::stdin();
    let stdin = stdin.lock();
    let stderr = io::stderr();
    let mut stderr = stderr.lock();

    let termsize = termion::terminal_size().ok();
    let (termwidth, termheight) = termsize.map(|(w, h)| (w-2, h-2)).unwrap();
    if termwidth < 32 || termheight < 20 {
        write!(stderr, "{}{}terminal size must satisfy with (width >= 32, height >= 20){}",
            style::Bold, color::Fg(color::Red), style::Reset).unwrap();
    } else {
        init(stdin, stdout);
    }
}
<commit_msg>remove unused member<commit_after>extern crate termion;
extern crate rand;

use std::io::{self, Read, Write};
use std::collections::{HashSet, HashMap};

use termion::{clear, cursor, style, color};
use termion::raw::IntoRawMode;
use termion::input::TermRead;
use termion::event::Key;
use rand::random;

struct Game<R, W: Write> {
    score: u32,
    grid: [u32; 16usize],
    bgs: HashMap<u32, color::Bg<color::Rgb>>,
    stdin: R,
    stdout: W,
}

fn init<R: Read, W: Write>(stdin: R, mut stdout: W) {
    write!(stdout, "{}", clear::All).unwrap();

    let mut bgs: HashMap<u32, color::Bg<color::Rgb>> = HashMap::with_capacity(17);
    // Ref: http://www.rapidtables.com/web/color/RGB_Color.htm
    // should cover all..
    let colors = [
        color::Bg(color::Rgb(224, 224, 224)), color::Bg(color::Rgb(255, 229, 204)),
        color::Bg(color::Rgb(255, 153, 153)), color::Bg(color::Rgb(204, 255, 209)),
        color::Bg(color::Rgb(204, 255, 255)), color::Bg(color::Rgb(204, 229, 255)),
        color::Bg(color::Rgb(229, 255, 204)), color::Bg(color::Rgb(229, 204, 255)),
        color::Bg(color::Rgb(255, 153, 255)), color::Bg(color::Rgb(255, 204, 229)),
        color::Bg(color::Rgb(255, 255, 51)), color::Bg(color::Rgb(255, 178, 102)),
        color::Bg(color::Rgb(178, 255, 102)), color::Bg(color::Rgb(102, 255, 178)),
        color::Bg(color::Rgb(102, 178, 255)), color::Bg(color::Rgb(102, 102, 255)),
        color::Bg(color::Rgb(255, 0, 0)),
    ];
    bgs.insert(0u32, colors[0]);
    for i in 1..17 {
        bgs.insert(1u32<<i as u32, colors[i]);
    }

    let mut game = Game {
        score: 0,
        grid: [0u32; 16usize],
        bgs: bgs,
        stdin: stdin.keys(),
        stdout: stdout,
    };

    game.reset();
    game.start();
}

impl <R, W: Write> Drop for Game<R, W> {
    fn drop(&mut self) {
        write!(self.stdout, "{}{}{}", clear::All, style::Reset,
               cursor::Goto(1, 1)).unwrap();
    }
}

macro_rules! continue_if_game_over {
    ($game_over:ident) => (
        if $game_over {
            continue;
        }
    )
}

impl <R: Iterator<Item=Result<Key, std::io::Error>>, W: Write> Game<R, W> {
    fn pos(&self, x: i32, y: i32) -> usize {
        (x * 4 + y) as usize
    }

    fn get_by_pos(&self, x: i32, y: i32) -> u32 {
        self.grid[self.pos(x, y)]
    }

    fn set_by_pos(&mut self, x: i32, y: i32, val: u32) {
        self.grid[self.pos(x, y)] = val;
    }

    fn start(&mut self) {
        let mut game_over = false;

        loop {
            let mut moved = false;
            let b = self.stdin.next().unwrap().unwrap();

            match b {
                Key::Up | Key::Char('w') | Key::Char('k') => {
                    continue_if_game_over!(game_over);
                    moved = self.up();
                },
                Key::Right | Key::Char('d') | Key::Char('l') => {
                    continue_if_game_over!(game_over);
                    moved = self.right();
                },
                Key::Down | Key::Char('s') | Key::Char('j') => {
                    continue_if_game_over!(game_over);
                    moved = self.down();
                },
                Key::Left | Key::Char('a') | Key::Char('h') => {
                    continue_if_game_over!(game_over);
                    moved = self.left();
                },
                Key::Char('r') => {
                    self.restart();
                    return;
                },
                Key::Esc | Key::Char('q') => return,
                _ => continue,
            }

            if !moved || game_over {
                continue;
            }
            let full_filled = self.fill_up();
            self.draw();
            if full_filled && self.game_over() {
                game_over = true;
                self.pop_menu();
            }
        }
    }

    fn reset(&mut self) {
        for i in 0..16usize {
            self.grid[i] = 0u32;
        }
        self.score = 0u32;
        let idx1 = random::<u8>() as usize % 16usize;
        self.grid[idx1] = 2u32;
        loop {
            let idx2 = random::<u8>() as usize % 16usize;
            if idx1 != idx2 {
                self.grid[idx2] = 2u32;
                break;
            }
        }
        self.draw();
    }

    fn restart(&mut self) {
        self.reset();
        self.start();
    }

    fn fill_up(&mut self) -> bool {
        let mut holes = HashSet::new();
        for i in 0..16usize {
            if self.grid[i] == 0u32 {
                holes.insert(i);
            }
        }

        let len = holes.len();
        if len == 0 {
            return true;
        }
        for idx in holes.iter() {  // use HashSet's random feature directly..
            // self.grid[*idx] = 1u32 << (*idx as u32 % 2u32 + 1u32);  // whatever...
            let mut threhold = 222u8;
            if len <= 4 {
                threhold = 127u8;
            }
            if random::<u8>() <= threhold {
                self.grid[*idx] = 2u32;
            } else {
                self.grid[*idx] = 4u32;
            }
            break;
        }

        (len-1) == 0
    }

    fn game_over(&mut self) -> bool {
        for x in 0..4 {
            for y in 1..4 {
                if self.get_by_pos(x, y-1) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        for y in 0..4 {
            for x in 1..4 {
                if self.get_by_pos(x-1, y) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        return true;
    }

    fn bg(&self, n: u32) -> color::Bg<color::Rgb> {
        match self.bgs.get(&n) {
            Some(bg) => *bg,
            None => color::Bg(color::Rgb(255, 255, 255)),
        }
    }

    fn draw(&mut self) {
        write!(self.stdout, "{}", cursor::Goto(1, 1)).unwrap();

        // header
        let header_bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        let score = self.score.to_string();
        write!(self.stdout, " {}{} SCORE: {}{:space$}{}\n\r", header_bg, style::Bold, score,
               " ", style::Reset, space=23-score.len()).unwrap();
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        write!(self.stdout, " {:32}\n\r", " ").unwrap();

        // body
        for x in 0..4i32 {
            let mut up_line: String = String::new();
            let mut mid_line: String = String::new();
            let mut down_line: String = String::new();
            for y in 0..4i32 {
                let val = self.get_by_pos(x, y);
                let sval = val.to_string();
                let bg = self.bg(val);

                up_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                if val == 0u32 {
                    mid_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                } else {
                    let pad = (7 - sval.len()) / 2;
                    mid_line.push_str(
                        format!(" {}{:lpad$}{}{}{}{}{:rpad$}{}", bg, " ", color::Fg(color::Black),
                                sval, style::Reset, bg, " ", style::Reset,
                                lpad=pad, rpad=7-sval.len()-pad).as_str());
                }
                down_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
            }
            up_line.push_str("\n\r");
            mid_line.push_str("\n\r");
            down_line.push_str("\n\r");
            self.stdout.write(up_line.as_bytes()).unwrap();
            self.stdout.write(mid_line.as_bytes()).unwrap();
            self.stdout.write(down_line.as_bytes()).unwrap();
            self.stdout.write(format!("{:32}\n\r", " ").as_bytes()).unwrap();
        }
        self.stdout.flush().unwrap();
    }

    fn pop_menu(&mut self) {
        let bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, "{}{}{}  GAME OVER!   {}\n\r",
               cursor::Goto(10, 4), bg, style::Bold, style::Reset).unwrap();
        write!(self.stdout, "{}{}  restart: r   {}\n\r",
               cursor::Goto(10, 5), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}  quit: ESC|q  {}\n\r",
               cursor::Goto(10, 6), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}               {}\n\r",
               cursor::Goto(10, 7), bg, style::Reset).unwrap();
    }

    fn merge<Fget, Fset>(&mut self, xs: [i32; 4], ys: [i32; 4],
                         mut fget: Fget, mut fset: Fset) -> bool
        where Fget: FnMut(&Game<R, W>, i32, i32) -> u32, Fset: FnMut(&mut Game<R, W>, i32, i32, u32) {
        let mut moved = false;  // Fuck: https://github.com/rust-lang/rust/issues/28570

        for x in xs.iter() {
            let mut prev_non_zero_y = ys[0];
            let first_y = ys[0];
            for y in (&ys[1..4]).iter() {
                let mut val = fget(self, *x, *y);
                if val != 0u32 {
                    if prev_non_zero_y < *y && fget(self, *x, prev_non_zero_y) == val {
                        let score = val * 2;
                        self.score += score;
                        fset(self, *x, prev_non_zero_y, score);
                        val = 0u32;
                        fset(self, *x, *y, val);
                        prev_non_zero_y = *y + 1;
                        moved = true;
                    } else {
                        prev_non_zero_y = *y;
                    }
                }

                let mut k = *y;
                while k > first_y {
                    let prev = fget(self, *x, k-1);
                    if prev != 0u32 || val == 0u32 {
                        break;
                    }
                    moved = true;
                    fset(self, *x, k-1, val);
                    fset(self, *x, k, 0u32);
                    k -= 1;
                    prev_non_zero_y = k;
                }
            }
        }

        moved
    }

    fn up(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(y, x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(y, x, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }

    fn right(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-x, -y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-x, -y, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn down(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-y, -x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-y, -x, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn left(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(x, y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(x, y, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }
}

fn main() {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let stdout = stdout.into_raw_mode().unwrap();
    let stdin = io::stdin();
    let stdin = stdin.lock();
    let stderr = io::stderr();
    let mut stderr = stderr.lock();

    let termsize = termion::terminal_size().ok();
    let (termwidth, termheight) = termsize.map(|(w, h)| (w-2, h-2)).unwrap();
    if termwidth < 32 || termheight < 20 {
        write!(stderr, "{}{}terminal size must satisfy with (width >= 32, height >= 20){}",
            style::Bold, color::Fg(color::Red), style::Reset).unwrap();
    } else {
        init(stdin, stdout);
    }
}
<|endoftext|>


<commit_before>use {Errno, Error, Result, NixPath};
use fcntl::OFlag;
use libc::{self, c_void, size_t, off_t, mode_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;

pub use self::consts::*;

bitflags!{
    flags ProtFlags : libc::c_int {
        const PROT_NONE      = libc::PROT_NONE,
        const PROT_READ      = libc::PROT_READ,
        const PROT_WRITE     = libc::PROT_WRITE,
        const PROT_EXEC      = libc::PROT_EXEC,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        const PROT_GROWSDOWN = libc::PROT_GROWSDOWN,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        const PROT_GROWSUP   = libc::PROT_GROWSUP,
    }
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod consts {
    use libc::{self, c_int};

    bitflags!{
        flags MapFlags: c_int {
            const MAP_FILE       = libc::MAP_FILE,
            const MAP_SHARED     = libc::MAP_SHARED,
            const MAP_PRIVATE    = libc::MAP_PRIVATE,
            const MAP_FIXED      = libc::MAP_FIXED,
            const MAP_ANON       = libc::MAP_ANON,
            const MAP_ANONYMOUS  = libc::MAP_ANON,
            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
            const MAP_32BIT      = libc::MAP_32BIT,
            const MAP_GROWSDOWN  = libc::MAP_GROWSDOWN,
            const MAP_DENYWRITE  = libc::MAP_DENYWRITE,
            const MAP_EXECUTABLE = libc::MAP_EXECUTABLE,
            const MAP_LOCKED     = libc::MAP_LOCKED,
            const MAP_NORESERVE  = libc::MAP_NORESERVE,
            const MAP_POPULATE   = libc::MAP_POPULATE,
            const MAP_NONBLOCK   = libc::MAP_NONBLOCK,
            const MAP_STACK      = libc::MAP_STACK,
            const MAP_HUGETLB    = libc::MAP_HUGETLB,
        }
    }

    pub type MmapAdvise = c_int;

    pub const MADV_NORMAL     : MmapAdvise  = 0; /* No further special treatment.  */
    pub const MADV_RANDOM     : MmapAdvise  = 1; /* Expect random page references.  */
    pub const MADV_SEQUENTIAL : MmapAdvise  = 2; /* Expect sequential page references.  */
    pub const MADV_WILLNEED   : MmapAdvise  = 3; /* Will need these pages.  */
    pub const MADV_DONTNEED   : MmapAdvise  = 4; /* Don't need these pages.  */
    pub const MADV_REMOVE     : MmapAdvise  = 9; /* Remove these pages and resources.  */
    pub const MADV_DONTFORK   : MmapAdvise  = 10; /* Do not inherit across fork.  */
    pub const MADV_DOFORK     : MmapAdvise  = 11; /* Do inherit across fork.  */
    pub const MADV_MERGEABLE  : MmapAdvise  = 12; /* KSM may merge identical pages.  */
    pub const MADV_UNMERGEABLE: MmapAdvise  = 13; /* KSM may not merge identical pages.  */
    pub const MADV_HUGEPAGE   : MmapAdvise  = 14; /* Worth backing with hugepages.  */
    pub const MADV_NOHUGEPAGE : MmapAdvise  = 15; /* Not worth backing with hugepages.  */
    pub const MADV_DONTDUMP   : MmapAdvise  = 16; /* Explicity exclude from the core dump, overrides the coredump filter bits.  */
    pub const MADV_DODUMP     : MmapAdvise  = 17; /* Clear the MADV_DONTDUMP flag.  */
    pub const MADV_HWPOISON   : MmapAdvise  = 100; /* Poison a page for testing.  */

    bitflags!{
        flags MsFlags: c_int {
            const MS_ASYNC      = libc::MS_ASYNC,
            const MS_INVALIDATE = libc::MS_INVALIDATE,
            const MS_SYNC       = libc::MS_SYNC,
        }
    }

    pub const MAP_FAILED: isize               = -1;
}

#[cfg(any(target_os = "macos",
          target_os = "ios"))]
mod consts {
    use libc::{self, c_int};

    bitflags!{
        flags MapFlags: c_int {
            const MAP_FILE    = libc::MAP_FILE,
            const MAP_SHARED  = libc::MAP_SHARED,
            const MAP_PRIVATE = libc::MAP_PRIVATE,
            const MAP_FIXED   = libc::MAP_FIXED,
            const MAP_ANON    = libc::MAP_ANON,
            const MAP_NOCACHE = libc::MAP_NOCACHE,
            const MAP_JIT     = libc::MAP_JIT,
        }
    }

    pub type MmapAdvise = c_int;

    pub const MADV_NORMAL     : MmapAdvise      = 0; /* No further special treatment.  */
    pub const MADV_RANDOM     : MmapAdvise      = 1; /* Expect random page references.  */
    pub const MADV_SEQUENTIAL : MmapAdvise      = 2; /* Expect sequential page references.  */
    pub const MADV_WILLNEED   : MmapAdvise      = 3; /* Will need these pages.  */
    pub const MADV_DONTNEED   : MmapAdvise      = 4; /* Don't need these pages.  */
    pub const MADV_FREE       : MmapAdvise      = 5; /* pages unneeded, discard contents */
    pub const MADV_ZERO_WIRED_PAGES: MmapAdvise = 6; /* zero the wired pages that have not been unwired before the entry is deleted */
    pub const MADV_FREE_REUSABLE : MmapAdvise   = 7; /* pages can be reused (by anyone) */
    pub const MADV_FREE_REUSE : MmapAdvise      = 8; /* caller wants to reuse those pages */
    pub const MADV_CAN_REUSE : MmapAdvise       = 9;

    bitflags!{
        flags MsFlags: c_int {
            const MS_ASYNC      = libc::MS_ASYNC, /* [MF|SIO] return immediately */
            const MS_INVALIDATE = libc::MS_INVALIDATE, /* [MF|SIO] invalidate all cached data */
            const MS_KILLPAGES  = libc::MS_KILLPAGES, /* invalidate pages, leave mapped */
            const MS_DEACTIVATE = libc::MS_DEACTIVATE, /* deactivate pages, leave mapped */
            const MS_SYNC       = libc::MS_SYNC, /* [MF|SIO] msync synchronously */
        }
    }

    pub const MAP_FAILED: isize               = -1;
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
mod consts {
    use libc::c_int;

    bitflags!{
        flags MapFlags: c_int {
            const MAP_FILE         = libc::MAP_FILE,
            const MAP_SHARED       = libc::MAP_SHARED,
            const MAP_PRIVATE      = libc::MAP_PRIVATE,
            const MAP_FIXED        = libc::MAP_FIXED,
            const MAP_RENAME       = libc::MAP_RENAME,
            const MAP_NORESERVE    = libc::MAP_NORESERVE,
            const MAP_HASSEMAPHORE = libc::MAP_HASSEMAPHORE,
            const MAP_STACK        = libc::MAP_STACK,
            #[cfg(target_os = "netbsd")]
            const MAP_WIRED        = libc::MAP_WIRED,
            const MAP_NOSYNC       = libc::MAP_NOSYNC,
            const MAP_ANON         = libc::MAP_ANON,
        }
    }

    pub type MmapAdvise = c_int;

    pub const MADV_NORMAL     : MmapAdvise      = 0; /* No further special treatment.  */
    pub const MADV_RANDOM     : MmapAdvise      = 1; /* Expect random page references.  */
    pub const MADV_SEQUENTIAL : MmapAdvise      = 2; /* Expect sequential page references.  */
    pub const MADV_WILLNEED   : MmapAdvise      = 3; /* Will need these pages.  */
    pub const MADV_DONTNEED   : MmapAdvise      = 4; /* Don't need these pages.  */
    pub const MADV_FREE       : MmapAdvise      = 5; /* pages unneeded, discard contents */
    pub const MADV_NOSYNC     : MmapAdvise      = 6; /* try to avoid flushes to physical media*/
    pub const MADV_AUTOSYNC   : MmapAdvise      = 7; /* refert to default flushing strategy */
    pub const MADV_NOCORE     : MmapAdvise      = 8; /* do not include these pages in a core file */
    pub const MADV_CORE       : MmapAdvise      = 9; /* revert to including pages in a core file */
    #[cfg(not(target_os = "dragonfly"))]
    pub const MADV_PROTECT    : MmapAdvise      = 10; /* protect process from pageout kill */
    #[cfg(target_os = "dragonfly")]
    pub const MADV_INVAL      : MmapAdvise      = 10; /* virt page tables have changed, inval pmap */
    #[cfg(target_os = "dragonfly")]
    pub const MADV_SETMAP     : MmapAdvise      = 11; /* set page table directory page for map */

    bitflags!{
        flags MsFlags: c_int {
            const MS_ASYNC      = libc::MS_ASYNC, /* [MF|SIO] return immediately */
            const MS_INVALIDATE = libc::MS_INVALIDATE, /* [MF|SIO] invalidate all cached data */
            #[cfg(not(target_os = "dragonfly"))]
            const MS_KILLPAGES  = 0x0004, /* invalidate pages, leave mapped */
            #[cfg(not(target_os = "dragonfly"))]
            const MS_DEACTIVATE = 0x0004, /* deactivate pages, leave mapped */
            const MS_SYNC       = libc::MS_SYNC, /* [MF|SIO] msync synchronously */
        }
    }

    pub const MAP_FAILED: isize                 = -1;
}

mod ffi {
    use libc::{c_void, size_t, c_int, c_char, mode_t};

    pub use libc::{mmap, munmap};

    #[allow(improper_ctypes)]
    extern {
        pub fn shm_open(name: *const c_char, oflag: c_int, mode: mode_t) -> c_int;
        pub fn shm_unlink(name: *const c_char) -> c_int;
        pub fn mlock(addr: *const c_void, len: size_t) -> c_int;
        pub fn munlock(addr: *const c_void, len: size_t) -> c_int;
        pub fn madvise (addr: *const c_void, len: size_t, advice: c_int) -> c_int;
        pub fn msync (addr: *const c_void, len: size_t, flags: c_int) -> c_int;
    }
}

pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(ffi::mlock(addr, length)).map(drop)
}

pub fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(unsafe { ffi::munlock(addr, length) }).map(drop)
}

/// Calls to mmap are inherently unsafe, so they must be made in an unsafe block. Typically
/// a higher-level abstraction will hide the unsafe interactions with the mmap'd region.
pub fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
    let ret = unsafe { ffi::mmap(addr, length, prot.bits(), flags.bits(), fd, offset) };

    if ret as isize == MAP_FAILED  {
        Err(Error::Sys(Errno::last()))
    } else {
        Ok(ret)
    }
}

pub fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
    Errno::result(unsafe { ffi::munmap(addr, len) }).map(drop)
}

pub fn madvise(addr: *const c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
    Errno::result(unsafe { ffi::madvise(addr, length, advise) }).map(drop)
}

pub fn msync(addr: *const c_void, length: size_t, flags: MsFlags) -> Result<()> {
    Errno::result(unsafe { ffi::msync(addr, length, flags.bits()) }).map(drop)
}

pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
    let ret = try!(name.with_nix_path(|cstr| {
        unsafe {
            ffi::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as mode_t)
        }
    }));

    Errno::result(ret)
}

pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
    let ret = try!(name.with_nix_path(|cstr| {
        unsafe { ffi::shm_unlink(cstr.as_ptr()) }
    }));

    Errno::result(ret).map(drop)
}
<commit_msg>Use libc<commit_after>use {Errno, Error, Result, NixPath};
use fcntl::OFlag;
use libc::{self, c_void, size_t, off_t, mode_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;

pub use self::consts::*;

bitflags!{
    flags ProtFlags : libc::c_int {
        const PROT_NONE      = libc::PROT_NONE,
        const PROT_READ      = libc::PROT_READ,
        const PROT_WRITE     = libc::PROT_WRITE,
        const PROT_EXEC      = libc::PROT_EXEC,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        const PROT_GROWSDOWN = libc::PROT_GROWSDOWN,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        const PROT_GROWSUP   = libc::PROT_GROWSUP,
    }
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod consts {
    use libc::{self, c_int};

    bitflags!{
        flags MapFlags: c_int {
            const MAP_FILE       = libc::MAP_FILE,
            const MAP_SHARED     = libc::MAP_SHARED,
            const MAP_PRIVATE    = libc::MAP_PRIVATE,
            const MAP_FIXED      = libc::MAP_FIXED,
            const MAP_ANON       = libc::MAP_ANON,
            const MAP_ANONYMOUS  = libc::MAP_ANON,
            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
            const MAP_32BIT      = libc::MAP_32BIT,
            const MAP_GROWSDOWN  = libc::MAP_GROWSDOWN,
            const MAP_DENYWRITE  = libc::MAP_DENYWRITE,
            const MAP_EXECUTABLE = libc::MAP_EXECUTABLE,
            const MAP_LOCKED     = libc::MAP_LOCKED,
            const MAP_NORESERVE  = libc::MAP_NORESERVE,
            const MAP_POPULATE   = libc::MAP_POPULATE,
            const MAP_NONBLOCK   = libc::MAP_NONBLOCK,
            const MAP_STACK      = libc::MAP_STACK,
            const MAP_HUGETLB    = libc::MAP_HUGETLB,
        }
    }

    pub type MmapAdvise = c_int;

    pub const MADV_NORMAL     : MmapAdvise  = 0; /* No further special treatment.  */
    pub const MADV_RANDOM     : MmapAdvise  = 1; /* Expect random page references.  */
    pub const MADV_SEQUENTIAL : MmapAdvise  = 2; /* Expect sequential page references.  */
    pub const MADV_WILLNEED   : MmapAdvise  = 3; /* Will need these pages.  */
    pub const MADV_DONTNEED   : MmapAdvise  = 4; /* Don't need these pages.  */
    pub const MADV_REMOVE     : MmapAdvise  = 9; /* Remove these pages and resources.  */
    pub const MADV_DONTFORK   : MmapAdvise  = 10; /* Do not inherit across fork.  */
    pub const MADV_DOFORK     : MmapAdvise  = 11; /* Do inherit across fork.  */
    pub const MADV_MERGEABLE  : MmapAdvise  = 12; /* KSM may merge identical pages.  */
    pub const MADV_UNMERGEABLE: MmapAdvise  = 13; /* KSM may not merge identical pages.  */
    pub const MADV_HUGEPAGE   : MmapAdvise  = 14; /* Worth backing with hugepages.  */
    pub const MADV_NOHUGEPAGE : MmapAdvise  = 15; /* Not worth backing with hugepages.  */
    pub const MADV_DONTDUMP   : MmapAdvise  = 16; /* Explicity exclude from the core dump, overrides the coredump filter bits.  */
    pub const MADV_DODUMP     : MmapAdvise  = 17; /* Clear the MADV_DONTDUMP flag.  */
    pub const MADV_HWPOISON   : MmapAdvise  = 100; /* Poison a page for testing.  */

    bitflags!{
        flags MsFlags: c_int {
            const MS_ASYNC      = libc::MS_ASYNC,
            const MS_INVALIDATE = libc::MS_INVALIDATE,
            const MS_SYNC       = libc::MS_SYNC,
        }
    }

    pub const MAP_FAILED: isize               = -1;
}

#[cfg(any(target_os = "macos",
          target_os = "ios"))]
mod consts {
    use libc::{self, c_int};

    bitflags!{
        flags MapFlags: c_int {
            const MAP_FILE    = libc::MAP_FILE,
            const MAP_SHARED  = libc::MAP_SHARED,
            const MAP_PRIVATE = libc::MAP_PRIVATE,
            const MAP_FIXED   = libc::MAP_FIXED,
            const MAP_ANON    = libc::MAP_ANON,
            const MAP_NOCACHE = libc::MAP_NOCACHE,
            const MAP_JIT     = libc::MAP_JIT,
        }
    }

    pub type MmapAdvise = c_int;

    pub const MADV_NORMAL     : MmapAdvise      = 0; /* No further special treatment.  */
    pub const MADV_RANDOM     : MmapAdvise      = 1; /* Expect random page references.  */
    pub const MADV_SEQUENTIAL : MmapAdvise      = 2; /* Expect sequential page references.  */
    pub const MADV_WILLNEED   : MmapAdvise      = 3; /* Will need these pages.  */
    pub const MADV_DONTNEED   : MmapAdvise      = 4; /* Don't need these pages.  */
    pub const MADV_FREE       : MmapAdvise      = 5; /* pages unneeded, discard contents */
    pub const MADV_ZERO_WIRED_PAGES: MmapAdvise = 6; /* zero the wired pages that have not been unwired before the entry is deleted */
    pub const MADV_FREE_REUSABLE : MmapAdvise   = 7; /* pages can be reused (by anyone) */
    pub const MADV_FREE_REUSE : MmapAdvise      = 8; /* caller wants to reuse those pages */
    pub const MADV_CAN_REUSE : MmapAdvise       = 9;

    bitflags!{
        flags MsFlags: c_int {
            const MS_ASYNC      = libc::MS_ASYNC, /* [MF|SIO] return immediately */
            const MS_INVALIDATE = libc::MS_INVALIDATE, /* [MF|SIO] invalidate all cached data */
            const MS_KILLPAGES  = libc::MS_KILLPAGES, /* invalidate pages, leave mapped */
            const MS_DEACTIVATE = libc::MS_DEACTIVATE, /* deactivate pages, leave mapped */
            const MS_SYNC       = libc::MS_SYNC, /* [MF|SIO] msync synchronously */
        }
    }

    pub const MAP_FAILED: isize               = -1;
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
mod consts {
    use libc::{self, c_int};

    bitflags!{
        flags MapFlags: c_int {
            const MAP_FILE         = libc::MAP_FILE,
            const MAP_SHARED       = libc::MAP_SHARED,
            const MAP_PRIVATE      = libc::MAP_PRIVATE,
            const MAP_FIXED        = libc::MAP_FIXED,
            const MAP_RENAME       = libc::MAP_RENAME,
            const MAP_NORESERVE    = libc::MAP_NORESERVE,
            const MAP_HASSEMAPHORE = libc::MAP_HASSEMAPHORE,
            const MAP_STACK        = libc::MAP_STACK,
            #[cfg(target_os = "netbsd")]
            const MAP_WIRED        = libc::MAP_WIRED,
            const MAP_NOSYNC       = libc::MAP_NOSYNC,
            const MAP_ANON         = libc::MAP_ANON,
        }
    }

    pub type MmapAdvise = c_int;

    pub const MADV_NORMAL     : MmapAdvise      = 0; /* No further special treatment.  */
    pub const MADV_RANDOM     : MmapAdvise      = 1; /* Expect random page references.  */
    pub const MADV_SEQUENTIAL : MmapAdvise      = 2; /* Expect sequential page references.  */
    pub const MADV_WILLNEED   : MmapAdvise      = 3; /* Will need these pages.  */
    pub const MADV_DONTNEED   : MmapAdvise      = 4; /* Don't need these pages.  */
    pub const MADV_FREE       : MmapAdvise      = 5; /* pages unneeded, discard contents */
    pub const MADV_NOSYNC     : MmapAdvise      = 6; /* try to avoid flushes to physical media*/
    pub const MADV_AUTOSYNC   : MmapAdvise      = 7; /* refert to default flushing strategy */
    pub const MADV_NOCORE     : MmapAdvise      = 8; /* do not include these pages in a core file */
    pub const MADV_CORE       : MmapAdvise      = 9; /* revert to including pages in a core file */
    #[cfg(not(target_os = "dragonfly"))]
    pub const MADV_PROTECT    : MmapAdvise      = 10; /* protect process from pageout kill */
    #[cfg(target_os = "dragonfly")]
    pub const MADV_INVAL      : MmapAdvise      = 10; /* virt page tables have changed, inval pmap */
    #[cfg(target_os = "dragonfly")]
    pub const MADV_SETMAP     : MmapAdvise      = 11; /* set page table directory page for map */

    bitflags!{
        flags MsFlags: c_int {
            const MS_ASYNC      = libc::MS_ASYNC, /* [MF|SIO] return immediately */
            const MS_INVALIDATE = libc::MS_INVALIDATE, /* [MF|SIO] invalidate all cached data */
            #[cfg(not(target_os = "dragonfly"))]
            const MS_KILLPAGES  = 0x0004, /* invalidate pages, leave mapped */
            #[cfg(not(target_os = "dragonfly"))]
            const MS_DEACTIVATE = 0x0004, /* deactivate pages, leave mapped */
            const MS_SYNC       = libc::MS_SYNC, /* [MF|SIO] msync synchronously */
        }
    }

    pub const MAP_FAILED: isize                 = -1;
}

mod ffi {
    use libc::{c_void, size_t, c_int, c_char, mode_t};

    pub use libc::{mmap, munmap};

    #[allow(improper_ctypes)]
    extern {
        pub fn shm_open(name: *const c_char, oflag: c_int, mode: mode_t) -> c_int;
        pub fn shm_unlink(name: *const c_char) -> c_int;
        pub fn mlock(addr: *const c_void, len: size_t) -> c_int;
        pub fn munlock(addr: *const c_void, len: size_t) -> c_int;
        pub fn madvise (addr: *const c_void, len: size_t, advice: c_int) -> c_int;
        pub fn msync (addr: *const c_void, len: size_t, flags: c_int) -> c_int;
    }
}

pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(ffi::mlock(addr, length)).map(drop)
}

pub fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(unsafe { ffi::munlock(addr, length) }).map(drop)
}

/// Calls to mmap are inherently unsafe, so they must be made in an unsafe block. Typically
/// a higher-level abstraction will hide the unsafe interactions with the mmap'd region.
pub fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
    let ret = unsafe { ffi::mmap(addr, length, prot.bits(), flags.bits(), fd, offset) };

    if ret as isize == MAP_FAILED  {
        Err(Error::Sys(Errno::last()))
    } else {
        Ok(ret)
    }
}

pub fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
    Errno::result(unsafe { ffi::munmap(addr, len) }).map(drop)
}

pub fn madvise(addr: *const c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
    Errno::result(unsafe { ffi::madvise(addr, length, advise) }).map(drop)
}

pub fn msync(addr: *const c_void, length: size_t, flags: MsFlags) -> Result<()> {
    Errno::result(unsafe { ffi::msync(addr, length, flags.bits()) }).map(drop)
}

pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
    let ret = try!(name.with_nix_path(|cstr| {
        unsafe {
            ffi::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as mode_t)
        }
    }));

    Errno::result(ret)
}

pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
    let ret = try!(name.with_nix_path(|cstr| {
        unsafe { ffi::shm_unlink(cstr.as_ptr()) }
    }));

    Errno::result(ret).map(drop)
}
<|endoftext|>


<commit_before>use cgmath::prelude::*;
use cgmath::{Matrix4, Point2, Point3, Vector3};

use rand;

use crate::aabb::{self, AABB};
use crate::color::Color;
use crate::index_ptr::IndexPtr;
use crate::intersect::{Interaction, Intersect, Ray};
use crate::material::Material;
use crate::util::ConvArr;
use crate::vertex::Vertex;
use crate::Float;

#[derive(Default)]
pub struct TriangleBuilder {
    vertices: Vec<IndexPtr<Vertex>>,
}

impl TriangleBuilder {
    pub fn new() -> Self {
        Self {
            vertices: Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: IndexPtr<Vertex>) {
        self.vertices.push(vertex);
    }

    pub fn build(self, ng: [f32; 3], material: IndexPtr<Material>) -> Result<Triangle, String> {
        if self.vertices.len() != 3 {
            Err("Triangle doesn't have 3 vertices!".to_string())
        } else {
            Ok(Triangle::new(
                self.vertices[0].clone(),
                self.vertices[1].clone(),
                self.vertices[2].clone(),
                Vector3::from_arr(ng),
                material,
            ))
        }
    }
}

/// Tracable triangle
#[derive(Clone, Debug)]
pub struct Triangle {
    v1: IndexPtr<Vertex>,
    v2: IndexPtr<Vertex>,
    v3: IndexPtr<Vertex>,
    /// Geometric normal
    ng: Vector3<Float>,
    to_barycentric: Matrix4<Float>,
    pub material: IndexPtr<Material>,
}

impl Triangle {
    fn new(
        v1: IndexPtr<Vertex>,
        v2: IndexPtr<Vertex>,
        v3: IndexPtr<Vertex>,
        ng: Vector3<Float>,
        material: IndexPtr<Material>,
    ) -> Self {
        let p1 = v1.p;
        let p2 = v2.p;
        let p3 = v3.p;
        let z = (p2 - p1).cross(p3 - p1).normalize();
        let from_barycentric = Matrix4::from_cols(
            (p2 - p1).extend(0.0),
            (p3 - p1).extend(0.0),
            z.extend(0.0),
            p1.to_homogeneous(),
        );
        let to_barycentric = from_barycentric
            .invert()
            .expect("Non invertible barycentric tranform");
        Self {
            v1,
            v2,
            v3,
            ng,
            to_barycentric,
            material,
        }
    }

    /// Get the barycentric position, normal and texture coordinates
    #[allow(clippy::many_single_char_names)]
    pub fn bary_pnt(&self, u: Float, v: Float) -> (Point3<Float>, Vector3<Float>, Point2<Float>) {
        let v1 = &*self.v1;
        let p1 = v1.p;
        let n1 = v1.n;
        let t1 = v1.t;

        let v2 = &*self.v2;
        let p2 = v2.p;
        let n2 = v2.n;
        let t2 = v2.t;

        let v3 = &*self.v3;
        let p3 = v3.p;
        let n3 = v3.n;
        let t3 = v3.t;

        let b1 = 1.0 - u - v;
        let p = b1 * p1 + u * p2.to_vec() + v * p3.to_vec();
        let n = b1 * n1 + u * n2 + v * n3;
        let t = b1 * t1 + u * t2.to_vec() + v * t3.to_vec();
        (p, n, t)
    }

    pub fn aabb(&self) -> AABB {
        let mut min = self.v1.p;
        min = aabb::min_point(&min, &self.v2.p);
        min = aabb::min_point(&min, &self.v3.p);
        let mut max = self.v1.p;
        max = aabb::max_point(&max, &self.v2.p);
        max = aabb::max_point(&max, &self.v3.p);
        AABB { min, max }
    }

    pub fn le(&self, dir: Vector3<Float>) -> Color {
        if let Some(le) = self.material.emissive {
            if self.ng.dot(dir) > 0.0 {
                return le;
            }
        }
        Color::black()
    }

    pub fn center(&self) -> Point3<Float> {
        Point3::centroid(&[self.v1.p, self.v2.p, self.v3.p])
    }

    pub fn area(&self) -> Float {
        0.5 / self.to_barycentric.determinant().abs()
    }

    pub fn pdf_a(&self) -> Float {
        1.0 / self.area()
    }

    pub fn sample() -> (Float, Float) {
        let r1: Float = rand::random();
        let r2: Float = rand::random();
        let sr1 = r1.sqrt();
        let u = 1.0 - sr1;
        let v = r2 * sr1;
        (u, v)
    }
}

#[derive(Debug)]
pub struct Hit<'a> {
    pub tri: &'a Triangle,
    pub t: Float,
    pub u: Float,
    pub v: Float,
}

impl<'a> Intersect<'a, Hit<'a>> for Triangle {
    fn intersect(&self, ray: &Ray) -> Option<Hit> {
        let bary_o = self.to_barycentric * ray.orig.to_homogeneous();
        let bary_d = self.to_barycentric * ray.dir.extend(0.0);
        let t = -bary_o.z / bary_d.z;
        let u = bary_o.x + t * bary_d.x;
        let v = bary_o.y + t * bary_d.y;
        if u >= 0.0 && v >= 0.0 && u + v <= 1.0 && t > 0.0 && t < ray.length {
            Some(Hit { tri: self, t, u, v })
        } else {
            None
        }
    }
}

impl<'a> Hit<'a> {
    pub fn interaction(self) -> Interaction<'a> {
        let (p, n, t) = self.tri.bary_pnt(self.u, self.v);
        Interaction {
            tri: self.tri,
            p,
            n,
            t,
            mat: &*self.tri.material,
        }
    }
}
<commit_msg>Refactor barycentric computation<commit_after>use cgmath::prelude::*;
use cgmath::{Matrix4, Point2, Point3, Vector3};

use rand;

use crate::aabb::{self, AABB};
use crate::color::Color;
use crate::index_ptr::IndexPtr;
use crate::intersect::{Interaction, Intersect, Ray};
use crate::material::Material;
use crate::util::ConvArr;
use crate::vertex::Vertex;
use crate::Float;

#[derive(Default)]
pub struct TriangleBuilder {
    vertices: Vec<IndexPtr<Vertex>>,
}

impl TriangleBuilder {
    pub fn new() -> Self {
        Self {
            vertices: Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: IndexPtr<Vertex>) {
        self.vertices.push(vertex);
    }

    pub fn build(self, ng: [f32; 3], material: IndexPtr<Material>) -> Result<Triangle, String> {
        if self.vertices.len() != 3 {
            Err("Triangle doesn't have 3 vertices!".to_string())
        } else {
            Ok(Triangle::new(
                self.vertices[0].clone(),
                self.vertices[1].clone(),
                self.vertices[2].clone(),
                Vector3::from_arr(ng),
                material,
            ))
        }
    }
}

/// Tracable triangle
#[derive(Clone, Debug)]
pub struct Triangle {
    v1: IndexPtr<Vertex>,
    v2: IndexPtr<Vertex>,
    v3: IndexPtr<Vertex>,
    /// Geometric normal
    ng: Vector3<Float>,
    to_barycentric: Matrix4<Float>,
    pub material: IndexPtr<Material>,
}

impl Triangle {
    fn new(
        v1: IndexPtr<Vertex>,
        v2: IndexPtr<Vertex>,
        v3: IndexPtr<Vertex>,
        ng: Vector3<Float>,
        material: IndexPtr<Material>,
    ) -> Self {
        let to_barycentric = Self::world_to_barycentric(v1.p, v2.p, v3.p);
        Self {
            v1,
            v2,
            v3,
            ng,
            to_barycentric,
            material,
        }
    }

    /// Compute the conversion from world space to barycentric space
    fn world_to_barycentric(p1: Point3<Float>, p2: Point3<Float>, p3: Point3<Float>) -> Matrix4<Float> {
        // TODO: there should be a way to do this without matrix inversion
        let z = (p2 - p1).cross(p3 - p1).normalize();
        let from_barycentric = Matrix4::from_cols(
            (p2 - p1).extend(0.0),
            (p3 - p1).extend(0.0),
            z.extend(0.0),
            p1.to_homogeneous(),
        );
        from_barycentric
            .invert()
            .expect("Non invertible barycentric tranform")
    }

    /// Get the barycentric position, normal and texture coordinates
    #[allow(clippy::many_single_char_names)]
    pub fn bary_pnt(&self, u: Float, v: Float) -> (Point3<Float>, Vector3<Float>, Point2<Float>) {
        let v1 = &*self.v1;
        let p1 = v1.p;
        let n1 = v1.n;
        let t1 = v1.t;

        let v2 = &*self.v2;
        let p2 = v2.p;
        let n2 = v2.n;
        let t2 = v2.t;

        let v3 = &*self.v3;
        let p3 = v3.p;
        let n3 = v3.n;
        let t3 = v3.t;

        let b1 = 1.0 - u - v;
        let p = b1 * p1 + u * p2.to_vec() + v * p3.to_vec();
        let n = b1 * n1 + u * n2 + v * n3;
        let t = b1 * t1 + u * t2.to_vec() + v * t3.to_vec();
        (p, n, t)
    }

    pub fn aabb(&self) -> AABB {
        let mut min = self.v1.p;
        min = aabb::min_point(&min, &self.v2.p);
        min = aabb::min_point(&min, &self.v3.p);
        let mut max = self.v1.p;
        max = aabb::max_point(&max, &self.v2.p);
        max = aabb::max_point(&max, &self.v3.p);
        AABB { min, max }
    }

    pub fn le(&self, dir: Vector3<Float>) -> Color {
        if let Some(le) = self.material.emissive {
            if self.ng.dot(dir) > 0.0 {
                return le;
            }
        }
        Color::black()
    }

    pub fn center(&self) -> Point3<Float> {
        Point3::centroid(&[self.v1.p, self.v2.p, self.v3.p])
    }

    pub fn area(&self) -> Float {
        0.5 / self.to_barycentric.determinant().abs()
    }

    pub fn pdf_a(&self) -> Float {
        1.0 / self.area()
    }

    pub fn sample() -> (Float, Float) {
        let r1: Float = rand::random();
        let r2: Float = rand::random();
        let sr1 = r1.sqrt();
        let u = 1.0 - sr1;
        let v = r2 * sr1;
        (u, v)
    }
}

#[derive(Debug)]
pub struct Hit<'a> {
    pub tri: &'a Triangle,
    pub t: Float,
    pub u: Float,
    pub v: Float,
}

impl<'a> Intersect<'a, Hit<'a>> for Triangle {
    fn intersect(&self, ray: &Ray) -> Option<Hit> {
        let bary_o = self.to_barycentric * ray.orig.to_homogeneous();
        let bary_d = self.to_barycentric * ray.dir.extend(0.0);
        let t = -bary_o.z / bary_d.z;
        let u = bary_o.x + t * bary_d.x;
        let v = bary_o.y + t * bary_d.y;
        if u >= 0.0 && v >= 0.0 && u + v <= 1.0 && t > 0.0 && t < ray.length {
            Some(Hit { tri: self, t, u, v })
        } else {
            None
        }
    }
}

impl<'a> Hit<'a> {
    pub fn interaction(self) -> Interaction<'a> {
        let (p, n, t) = self.tri.bary_pnt(self.u, self.v);
        Interaction {
            tri: self.tri,
            p,
            n,
            t,
            mat: &*self.tri.material,
        }
    }
}
<|endoftext|>


<commit_before>use utils::is_stop_char;
use std::collections::HashMap;

pub fn get_trigrams_with_positions(text : &str) -> HashMap<String, u32> {
    let counter_hash = count(text);

    // Sort in descending order by number of occurrences and trigrams
    let mut count_vec: Vec<_> = counter_hash.into_iter().map(|(trigram, count)| (count, trigram)).collect();
    count_vec.sort_by(|a, b| b.cmp(a));

    // TODO: extract 600 as LANG_PROFILE_LENGTH * 2
    count_vec.into_iter().take(600).enumerate().map(|(i, (_, trigram))| (trigram, i as u32)).collect()
}

fn count(text : &str) -> HashMap<String, u32> {
    let mut s = text.to_lowercase();
    s.push(' '); // add space to the end

    let mut counter_hash : HashMap<String, u32> = HashMap::new();

    // iterate through the string and count trigrams
    let mut chars_iter = s.chars().map(to_trigram_char);
    let mut c1 = ' ';
    let mut c2 = chars_iter.next().unwrap();
    for cur_char in chars_iter {
        let c3 = cur_char;
        if !(c2 == ' ' && (c1 == ' ' || c3 == ' ')) {
            let mut trigram = String::with_capacity(c1.len_utf8() + c2.len_utf8() + c3.len_utf8());
            trigram.push(c1);
            trigram.push(c2);
            trigram.push(c3);
            let count = counter_hash.entry(trigram).or_insert(0);
            *count += 1;
        }
        c1 = c2;
        c2 = c3;
    }

    counter_hash
}

// Convert punctuations and digits to a space.
#[inline]
fn to_trigram_char(ch : char) -> char {
    if is_stop_char(ch) { ' ' } else { ch }
}

#[cfg(test)]
mod tests {
    use super::to_trigram_char;
    use super::count;
    use super::get_trigrams_with_positions;

    fn assert_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ch);
        }
    }

    fn assert_not_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ' ');
        }
    }

    #[test]
    fn test_to_trigram_char() {
        // valuable chars, that metters
        assert_valuable_trigram_chars(&['a', 'z', 'A', 'Z', 'Ж', 'ß']);

        // punctuations, digits, etc..
        //
        // 0x00 - 0x40
        assert_not_valuable_trigram_chars(&['\t', '\n', ' ', '.', '0', '9', ',', '@']);
        // 0x5B - 0x60
        assert_not_valuable_trigram_chars(&['[', ']', '^', '\\', '`']);
        // 0x7B - 0x7E
        assert_not_valuable_trigram_chars(&['[', '|', '{', '}', '~']);
    }

    fn assert_count(text : &str, pairs : &[(&str, u32)]) {
        let result = count(&text.to_string());
        for &(k, v) in pairs.iter() {
            let &actual_val = result.get(k).unwrap_or(&0);
            assert_eq!(actual_val, v, "trigram '{}' expected to occur {} times, got {}", k, v, actual_val);
        }
        assert_eq!(result.len(), pairs.len());
    }

    #[test]
    fn test_count() {
        assert_count("", &[]);
        assert_count(",", &[]);
        assert_count("a", &[(" a ", 1)]);
        assert_count("-a-", &[(" a ", 1)]);
        assert_count("yes", &[(" ye", 1), ("yes", 1), ("es ", 1)]);
        assert_count("Give - IT...", &[(" gi", 1), ("giv", 1), ("ive", 1), ("ve ", 1), (" it", 1), ("it ", 1)]);
    }

    #[test]
    fn test_get_trigrams_with_positions() {
        let res = get_trigrams_with_positions(&"xaaaaabbbbd".to_string());
        assert_eq!(*res.get("aaa").unwrap(), 0);
        assert_eq!(*res.get("bbb").unwrap(), 1);
    }
}
<commit_msg>Don't make a copy of the text in order to lowercase trigrams<commit_after>use utils::is_stop_char;
use std::collections::HashMap;

pub fn get_trigrams_with_positions(text : &str) -> HashMap<String, u32> {
    let counter_hash = count(text);

    // Sort in descending order by number of occurrences and trigrams
    let mut count_vec: Vec<_> = counter_hash.into_iter().map(|(trigram, count)| (count, trigram)).collect();
    count_vec.sort_by(|a, b| b.cmp(a));

    // TODO: extract 600 as LANG_PROFILE_LENGTH * 2
    count_vec.into_iter().take(600).enumerate().map(|(i, (_, trigram))| (trigram, i as u32)).collect()
}

fn count(text : &str) -> HashMap<String, u32> {
    let mut counter_hash : HashMap<String, u32> = HashMap::new();

    // iterate through the string and count trigrams
    let mut chars_iter = text.chars().map(to_trigram_char).flat_map(char::to_lowercase).chain(Some(' '));
    let mut c1 = ' ';
    // unwrap is safe, because we always chain a space character on the end of the iterator
    let mut c2 = chars_iter.next().unwrap();
    for cur_char in chars_iter {
        let c3 = cur_char;
        if !(c2 == ' ' && (c1 == ' ' || c3 == ' ')) {
            let mut trigram = String::with_capacity(c1.len_utf8() + c2.len_utf8() + c3.len_utf8());
            trigram.push(c1);
            trigram.push(c2);
            trigram.push(c3);
            let count = counter_hash.entry(trigram).or_insert(0);
            *count += 1;
        }
        c1 = c2;
        c2 = c3;
    }

    counter_hash
}

// Convert punctuations and digits to a space.
#[inline]
fn to_trigram_char(ch : char) -> char {
    if is_stop_char(ch) { ' ' } else { ch }
}

#[cfg(test)]
mod tests {
    use super::to_trigram_char;
    use super::count;
    use super::get_trigrams_with_positions;

    fn assert_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ch);
        }
    }

    fn assert_not_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ' ');
        }
    }

    #[test]
    fn test_to_trigram_char() {
        // valuable chars, that metters
        assert_valuable_trigram_chars(&['a', 'z', 'A', 'Z', 'Ж', 'ß']);

        // punctuations, digits, etc..
        //
        // 0x00 - 0x40
        assert_not_valuable_trigram_chars(&['\t', '\n', ' ', '.', '0', '9', ',', '@']);
        // 0x5B - 0x60
        assert_not_valuable_trigram_chars(&['[', ']', '^', '\\', '`']);
        // 0x7B - 0x7E
        assert_not_valuable_trigram_chars(&['[', '|', '{', '}', '~']);
    }

    fn assert_count(text : &str, pairs : &[(&str, u32)]) {
        let result = count(&text.to_string());
        for &(k, v) in pairs.iter() {
            let &actual_val = result.get(k).unwrap_or(&0);
            assert_eq!(actual_val, v, "trigram '{}' expected to occur {} times, got {}", k, v, actual_val);
        }
        assert_eq!(result.len(), pairs.len());
    }

    #[test]
    fn test_count() {
        assert_count("", &[]);
        assert_count(",", &[]);
        assert_count("a", &[(" a ", 1)]);
        assert_count("-a-", &[(" a ", 1)]);
        assert_count("yes", &[(" ye", 1), ("yes", 1), ("es ", 1)]);
        assert_count("Give - IT...", &[(" gi", 1), ("giv", 1), ("ive", 1), ("ve ", 1), (" it", 1), ("it ", 1)]);
    }

    #[test]
    fn test_get_trigrams_with_positions() {
        let res = get_trigrams_with_positions(&"xaaaaabbbbd".to_string());
        assert_eq!(*res.get("aaa").unwrap(), 0);
        assert_eq!(*res.get("bbb").unwrap(), 1);
    }
}
<|endoftext|>


<commit_before>use crate::parse::{ParseBuffer, ParseStream};
use proc_macro2::TokenStream;
use std::iter;

pub fn between<'a>(begin: ParseBuffer<'a>, end: ParseStream<'a>) -> TokenStream {
    let end = end.cursor();
    let mut cursor = begin.cursor();
    let mut tokens = TokenStream::new();
    while cursor != end {
        let (tt, next) = cursor.token_tree().unwrap();
        tokens.extend(iter::once(tt));
        cursor = next;
    }
    tokens
}
<commit_msg>Handle the case where verbatim enters a None group<commit_after>use crate::parse::{ParseBuffer, ParseStream};
use proc_macro2::{Delimiter, TokenStream};
use std::iter;

pub fn between<'a>(begin: ParseBuffer<'a>, end: ParseStream<'a>) -> TokenStream {
    let end = end.cursor();
    let mut cursor = begin.cursor();
    let mut tokens = TokenStream::new();
    while cursor != end {
        let (tt, next) = cursor.token_tree().unwrap();

        if next > end {
            // In some edge cases, a syntax node can actually cross the border
            // of a None-delimited group, due to such groups being transparent
            // to the parser in most cases. In the cases that this can occur,
            // the presence of the group is known to be semantically irrelevant,
            // so we should just ignore the presence of the group. (Issue #1235)
            if let Some((inside, _span, after)) = cursor.group(Delimiter::None) {
                assert!(next == after);
                cursor = inside;
                continue;
            } else {
                panic!("verbatim end must not be inside a delimited group");
            }
        }

        tokens.extend(iter::once(tt));
        cursor = next;
    }
    tokens
}
<|endoftext|>