<commit_before>#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]

use rustc_tools_util::VersionInfo;
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"#;

fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

fn show_version() {
    let version_info = rustc_tools_util::get_version_info!();
    println!("{}", version_info);
}

pub fn main() {
    // Check for version and help flags even when invoked as 'cargo-clippy'
    if env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }

    if env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Err(code) = process(env::args().skip(2)) {
        process::exit(code);
    }
}

struct ClippyCmd {
    unstable_options: bool,
    cargo_subcommand: &'static str,
    args: Vec<String>,
    clippy_args: String,
}

impl ClippyCmd {
    fn new<I>(mut old_args: I) -> Self
    where
        I: Iterator<Item = String>,
    {
        let mut cargo_subcommand = "check";
        let mut unstable_options = false;
        let mut args = vec![];

        for arg in old_args.by_ref() {
            match arg.as_str() {
                "--fix" => {
                    cargo_subcommand = "fix";
                    continue;
                },
                "--" => break,
                // Cover -Zunstable-options and -Z unstable-options
                s if s.ends_with("unstable-options") => unstable_options = true,
                _ => {},
            }

            args.push(arg);
        }

        if cargo_subcommand == "fix" && !unstable_options {
            panic!("Usage of `--fix` requires `-Z unstable-options`");
        }

        // Run the dogfood tests directly on nightly cargo. This is required due
        // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118.
        if env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) {
            args.insert(0, "+nightly".to_string());
        }

        let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();

        ClippyCmd {
            unstable_options,
            cargo_subcommand,
            args,
            clippy_args,
        }
    }

    fn path_env(&self) -> &'static str {
        if self.unstable_options {
            "RUSTC_WORKSPACE_WRAPPER"
        } else {
            "RUSTC_WRAPPER"
        }
    }

    fn path() -> PathBuf {
        let mut path = env::current_exe()
            .expect("current executable path invalid")
            .with_file_name("clippy-driver");

        if cfg!(windows) {
            path.set_extension("exe");
        }

        path
    }

    fn target_dir() -> Option<(&'static str, OsString)> {
        env::var_os("CLIPPY_DOGFOOD")
            .map(|_| {
                env::var_os("CARGO_MANIFEST_DIR").map_or_else(
                    || std::ffi::OsString::from("clippy_dogfood"),
                    |d| {
                        std::path::PathBuf::from(d)
                            .join("target")
                            .join("dogfood")
                            .into_os_string()
                    },
                )
            })
            .map(|p| ("CARGO_TARGET_DIR", p))
    }

    fn into_std_cmd(self) -> Command {
        let mut cmd = Command::new("cargo");

        cmd.env(self.path_env(), Self::path())
            .envs(ClippyCmd::target_dir())
            .env("CLIPPY_ARGS", self.clippy_args)
            .arg(self.cargo_subcommand)
            .args(&self.args);

        cmd
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let cmd = ClippyCmd::new(old_args);

    let mut cmd = cmd.into_std_cmd();

    let exit_status = cmd
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}

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

    #[test]
    #[should_panic]
    fn fix_without_unstable() {
        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
        let _ = ClippyCmd::new(args);
    }

    #[test]
    fn fix_unstable() {
        let args = "cargo clippy --fix -Zunstable-options"
            .split_whitespace()
            .map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!("fix", cmd.cargo_subcommand);
        assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
        assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
    }

    #[test]
    fn check() {
        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!("check", cmd.cargo_subcommand);
        assert_eq!("RUSTC_WRAPPER", cmd.path_env());
    }

    #[test]
    fn check_unstable() {
        let args = "cargo clippy -Zunstable-options"
            .split_whitespace()
            .map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!("check", cmd.cargo_subcommand);
        assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
    }
}
<commit_msg>Make --fix imply --no-deps<commit_after>#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]

use rustc_tools_util::VersionInfo;
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"#;

fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

fn show_version() {
    let version_info = rustc_tools_util::get_version_info!();
    println!("{}", version_info);
}

pub fn main() {
    // Check for version and help flags even when invoked as 'cargo-clippy'
    if env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }

    if env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Err(code) = process(env::args().skip(2)) {
        process::exit(code);
    }
}

struct ClippyCmd {
    unstable_options: bool,
    cargo_subcommand: &'static str,
    args: Vec<String>,
    clippy_args: Vec<String>,
}

impl ClippyCmd {
    fn new<I>(mut old_args: I) -> Self
    where
        I: Iterator<Item = String>,
    {
        let mut cargo_subcommand = "check";
        let mut unstable_options = false;
        let mut args = vec![];

        for arg in old_args.by_ref() {
            match arg.as_str() {
                "--fix" => {
                    cargo_subcommand = "fix";
                    continue;
                },
                "--" => break,
                // Cover -Zunstable-options and -Z unstable-options
                s if s.ends_with("unstable-options") => unstable_options = true,
                _ => {},
            }

            args.push(arg);
        }

        if cargo_subcommand == "fix" && !unstable_options {
            panic!("Usage of `--fix` requires `-Z unstable-options`");
        }

        // Run the dogfood tests directly on nightly cargo. This is required due
        // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118.
        if env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) {
            args.insert(0, "+nightly".to_string());
        }

        let mut clippy_args: Vec<String> = old_args.collect();
        if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
            clippy_args.push("--no-deps".into());
        }

        ClippyCmd {
            unstable_options,
            cargo_subcommand,
            args,
            clippy_args,
        }
    }

    fn path_env(&self) -> &'static str {
        if self.unstable_options {
            "RUSTC_WORKSPACE_WRAPPER"
        } else {
            "RUSTC_WRAPPER"
        }
    }

    fn path() -> PathBuf {
        let mut path = env::current_exe()
            .expect("current executable path invalid")
            .with_file_name("clippy-driver");

        if cfg!(windows) {
            path.set_extension("exe");
        }

        path
    }

    fn target_dir() -> Option<(&'static str, OsString)> {
        env::var_os("CLIPPY_DOGFOOD")
            .map(|_| {
                env::var_os("CARGO_MANIFEST_DIR").map_or_else(
                    || std::ffi::OsString::from("clippy_dogfood"),
                    |d| {
                        std::path::PathBuf::from(d)
                            .join("target")
                            .join("dogfood")
                            .into_os_string()
                    },
                )
            })
            .map(|p| ("CARGO_TARGET_DIR", p))
    }

    fn into_std_cmd(self) -> Command {
        let mut cmd = Command::new("cargo");
        let clippy_args: String = self
            .clippy_args
            .iter()
            .map(|arg| format!("{}__CLIPPY_HACKERY__", arg))
            .collect();

        cmd.env(self.path_env(), Self::path())
            .envs(ClippyCmd::target_dir())
            .env("CLIPPY_ARGS", clippy_args)
            .arg(self.cargo_subcommand)
            .args(&self.args);

        cmd
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let cmd = ClippyCmd::new(old_args);

    let mut cmd = cmd.into_std_cmd();

    let exit_status = cmd
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}

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

    #[test]
    #[should_panic]
    fn fix_without_unstable() {
        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
        let _ = ClippyCmd::new(args);
    }

    #[test]
    fn fix_unstable() {
        let args = "cargo clippy --fix -Zunstable-options"
            .split_whitespace()
            .map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!("fix", cmd.cargo_subcommand);
        assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
        assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
    }

    #[test]
    fn fix_implies_no_deps() {
        let args = "cargo clippy --fix -Zunstable-options"
            .split_whitespace()
            .map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
    }

    #[test]
    fn no_deps_not_duplicated_with_fix() {
        let args = "cargo clippy --fix -Zunstable-options -- --no-deps"
            .split_whitespace()
            .map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
    }

    #[test]
    fn check() {
        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!("check", cmd.cargo_subcommand);
        assert_eq!("RUSTC_WRAPPER", cmd.path_env());
    }

    #[test]
    fn check_unstable() {
        let args = "cargo clippy -Zunstable-options"
            .split_whitespace()
            .map(ToString::to_string);
        let cmd = ClippyCmd::new(args);
        assert_eq!("check", cmd.cargo_subcommand);
        assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate macro_attr;
#[macro_use] extern crate enum_derive;

use std::io::Write;
use std::env;

mod keybinds;
use keybinds::*;

mod file;
use file::*;

mod terminal;
use terminal::*;

const LINENO_CHARS : i32 = 3;

fn render_file(mut out: &mut Terminal, file: &File) {
    let (screen_width, screen_height) = out.get_size();

    let mut x = 1;
    let mut y = 1;

    for (line_number_maybe, line) in file.wrapped_lines((screen_width - LINENO_CHARS - 1, screen_height)) {
        if let Some(line_number) = line_number_maybe {
            out.goto((x, y));
            out.set_color_fg(Color::Grey);
            write!(out, "{:1$}",
                   line_number + 1, LINENO_CHARS as usize).unwrap();
            out.set_color_fg(Color::Reset);
        }
        x += LINENO_CHARS + 1;
        out.goto((x, y));
        write!(out, "{}", line).unwrap();
        x = 1;
        y += 1;
    }
}

fn render_footer(mut out: &mut Terminal, keys: &KeybindTable) {
    let (screen_width, screen_height) = out.get_size();

    let mut x = 1;
    let mut y = screen_height - 1;
    for (key, action) in keys.entries() {
        out.goto((x, y));
        out.set_color_fg(Color::Black);
        out.set_color_bg(Color::White);
        write!(out, "{}", key).unwrap();
        out.set_color_fg(Color::Reset);
        out.set_color_bg(Color::Reset);
        write!(out, " {}", action).unwrap();
        x += 16;
        if x > screen_width {
            x = 1;
            y -= 1;
        }
    }
}

fn render_status(mut out: &mut Terminal, file: &File) {
    let (width, height) = out.get_size();
    let file_size = (width - LINENO_CHARS - 1, height - 3);
    let x = 1;
    let y = height;
    out.goto((x, y));
    write!(out, "{}", file.debug(file_size)).unwrap();
}

fn main() {
    let keys = KeybindTable::default();
    let mut term = Terminal::default();
    term.clear();
    term.flush().unwrap();
    render_footer(&mut term, &keys);
    let args: Vec<String> = env::args().collect();
    let filename = args.get(1).cloned().unwrap_or(String::from("README.md"));
    let mut file = File::open(&filename);
    render_file(&mut term, &file);
    render_status(&mut term, &file);
    let (screen_w, screen_h) = term.get_size();
    let file_size = (screen_w - LINENO_CHARS - 1, screen_h - 3);
    term.goto((file.cursor(file_size).x + LINENO_CHARS + 1, file.cursor(file_size).y));
    term.flush().unwrap();
    for evt in term.keys() {
        let (screen_w, screen_h) = term.get_size();
        let file_size = (screen_w - LINENO_CHARS - 1, screen_h - 3);
        match evt {
            Event::Key(Key::Ctrl(k)) => {
                match keys.lookup(Key::Ctrl(k)) {
                    Some(Command::Quit) => break,
                    _ => ()
                }
            },
            Event::Key(Key::Left) => file.move_cursor_left(file_size),
            Event::Key(Key::Right) => file.move_cursor_right(file_size),
            Event::Key(Key::Up) => file.move_cursor_up(file_size),
            Event::Key(Key::Down) => file.move_cursor_down(file_size),
            Event::Key(Key::Char(c)) => {
                file.insert(term.get_size(), c);
                render_file(&mut term, &file);
            },
            _ => {}
        }
        render_status(&mut term, &file);
        term.goto((file.cursor(file_size).x + LINENO_CHARS + 1, file.cursor(file_size).y));
        term.flush().unwrap();
    }
}
<commit_msg>Rerender everything if line numbers get stale<commit_after>#[macro_use] extern crate macro_attr;
#[macro_use] extern crate enum_derive;

use std::io::Write;
use std::env;

mod keybinds;
use keybinds::*;

mod file;
use file::*;

mod terminal;
use terminal::*;

const LINENO_CHARS : i32 = 3;

fn render_file(mut out: &mut Terminal, file: &File) {
    let (screen_width, screen_height) = out.get_size();

    let mut x = 1;
    let mut y = 1;

    for (line_number_maybe, line) in file.wrapped_lines((screen_width - LINENO_CHARS - 1, screen_height)) {
        if let Some(line_number) = line_number_maybe {
            out.goto((x, y));
            out.set_color_fg(Color::Grey);
            write!(out, "{:1$}",
                   line_number + 1, LINENO_CHARS as usize).unwrap();
            out.set_color_fg(Color::Reset);
        }
        x += LINENO_CHARS + 1;
        out.goto((x, y));
        write!(out, "{}", line).unwrap();
        x = 1;
        y += 1;
    }
}

fn render_footer(mut out: &mut Terminal, keys: &KeybindTable) {
    let (screen_width, screen_height) = out.get_size();

    let mut x = 1;
    let mut y = screen_height - 1;
    for (key, action) in keys.entries() {
        out.goto((x, y));
        out.set_color_fg(Color::Black);
        out.set_color_bg(Color::White);
        write!(out, "{}", key).unwrap();
        out.set_color_fg(Color::Reset);
        out.set_color_bg(Color::Reset);
        write!(out, " {}", action).unwrap();
        x += 16;
        if x > screen_width {
            x = 1;
            y -= 1;
        }
    }
}

fn render_status(mut out: &mut Terminal, file: &File) {
    let (width, height) = out.get_size();
    let file_size = (width - LINENO_CHARS - 1, height - 3);
    let x = 1;
    let y = height;
    out.goto((x, y));
    write!(out, "{}", file.debug(file_size)).unwrap();
}

fn main() {
    let keys = KeybindTable::default();
    let mut term = Terminal::default();
    term.clear();
    term.flush().unwrap();
    render_footer(&mut term, &keys);
    let args: Vec<String> = env::args().collect();
    let filename = args.get(1).cloned().unwrap_or(String::from("README.md"));
    let mut file = File::open(&filename);
    render_file(&mut term, &file);
    render_status(&mut term, &file);
    let (screen_w, screen_h) = term.get_size();
    let file_size = (screen_w - LINENO_CHARS - 1, screen_h - 3);
    term.goto((file.cursor(file_size).x + LINENO_CHARS + 1, file.cursor(file_size).y));
    term.flush().unwrap();
    let mut file_lines = file.lines.len();
    let mut file_wrapped_lines = file.wrapped_lines(file_size).len();
    let mut file_dirty = false;
    let mut screen_dirty = false;
    for evt in term.keys() {
        let (screen_w, screen_h) = term.get_size();
        let file_size = (screen_w - LINENO_CHARS - 1, screen_h - 3);
        match evt {
            Event::Key(Key::Ctrl(k)) => {
                match keys.lookup(Key::Ctrl(k)) {
                    Some(Command::Quit) => break,
                    _ => ()
                }
            },
            Event::Key(Key::Left) => file.move_cursor_left(file_size),
            Event::Key(Key::Right) => file.move_cursor_right(file_size),
            Event::Key(Key::Up) => file.move_cursor_up(file_size),
            Event::Key(Key::Down) => file.move_cursor_down(file_size),
            Event::Key(Key::Char(c)) => {
                file.insert(term.get_size(), c);
                file_dirty = true;
            },
            _ => {}
        }
        if file_dirty {
            let new_file_lines = file.lines.len();
            let new_file_wrapped_lines = file.wrapped_lines(file_size).len();
            if new_file_lines != file_lines {
                screen_dirty = true;
                file_lines = new_file_lines;
            }
            if new_file_wrapped_lines != file_wrapped_lines {
                screen_dirty = true;
                file_wrapped_lines = new_file_wrapped_lines;
            }
        }
        if screen_dirty {
            term.clear();
            render_footer(&mut term, &keys);
            screen_dirty = false;
        }
        if file_dirty {
            render_file(&mut term, &file);
            file_dirty = false;
        }
        render_status(&mut term, &file);
        term.goto((file.cursor(file_size).x + LINENO_CHARS + 1, file.cursor(file_size).y));
        term.flush().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate open_gl_bindings;
extern crate sdl2;

use open_gl_bindings::gl;

use sdl2::event::Event;

use std::ffi::CStr;
use std::str;

fn find_sdl_gl_driver() -> Option<u32> {
    for (index, item) in sdl2::render::drivers().enumerate() {
        if item.name == "opengl" {
            return Some(index as u32);
        }
    }
    None
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let gl_attr = video_subsystem.gl_attr();
    gl_attr.set_stencil_size(1);
    gl_attr.set_context_major_version(2);
    gl_attr.set_context_minor_version(1);

    let canvas = video_subsystem
        .window("Window", 800, 600)
        .opengl()
        .build()
        .unwrap()
        .into_canvas()
        .index(find_sdl_gl_driver().unwrap())
        .build()
        .unwrap();

    let ctx = gl::Gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
    canvas.window().gl_set_context_to_current().unwrap();

    let char_ptr = unsafe { ctx.GetString(gl::VERSION) };
    let c_str: &CStr = unsafe { CStr::from_ptr(std::mem::transmute(char_ptr)) };
    let buf: &[u8] = c_str.to_bytes();
    let str_slice: &str = str::from_utf8(buf).unwrap();
    let version: String = str_slice.to_owned();

    println!("OpenGL version: {}", version);

    let window = canvas.window();

    let (width, height) = window.drawable_size();

    unsafe {
        ctx.Viewport(0, 0, width as _, height as _);

        ctx.MatrixMode(gl::PROJECTION);
        ctx.LoadIdentity();

        ctx.MatrixMode(gl::MODELVIEW);
        ctx.LoadIdentity();

        ctx.ClearColor(0.0, 0.0, 0.0, 1.0);
        ctx.Enable(gl::BLEND);
        ctx.BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    let mut world_matrix: [f32; 16] = [
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
    ];

    let verts: Vec<f32> = get_verts();

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F10), .. } => {
                    break 'running;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Left), .. } => {
                    world_matrix[12] -= 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Right), .. } => {
                    world_matrix[12] += 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Down), .. } => {
                    world_matrix[13] -= 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Up), .. } => {
                    world_matrix[13] += 0.1;
                }
                _ => {}
            }
        }

        draw_frame(&ctx, &world_matrix, &verts);

        window.gl_swap_window();

        std::thread::sleep(std::time::Duration::from_millis(8));
    }
}

fn draw_frame(ctx: &gl::Gl, world_matrix: &[f32; 16], verts: &Vec<f32>) {
    unsafe {
        ctx.Clear(
            gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT,
        );

        ctx.Clear(gl::STENCIL_BUFFER_BIT);

        ctx.EnableClientState(gl::VERTEX_ARRAY);
        ctx.VertexPointer(2, gl::FLOAT, 0, std::mem::transmute(verts.as_ptr()));

        ctx.MatrixMode(gl::MODELVIEW);
        ctx.PushMatrix();

        ctx.MultMatrixf(world_matrix.as_ptr());

        let cnt = (verts.len() / 2) as _;

        ctx.Enable(gl::STENCIL_TEST);

        ctx.ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
        ctx.StencilOp(gl::INVERT, gl::INVERT, gl::INVERT);
        ctx.StencilFunc(gl::ALWAYS, 0x1, 0x1);
        ctx.Color4f(1.0, 1.0, 1.0, 1.0);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, cnt);

        ctx.ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
        ctx.Color4f(32.0 / 255.0, 32.0 / 255.0, 63.0 / 255.0, 1.0);

        ctx.StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
        ctx.StencilFunc(gl::EQUAL, 1, 1);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, cnt);
        ctx.Disable(gl::STENCIL_TEST);

        //outline
        ctx.Color4f(128.0 / 255.0, 128.0 / 255.0, 32.0 / 255.0, 1.0);
        ctx.DrawArrays(gl::LINE_STRIP, 0, cnt);

        ctx.PopMatrix();
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn get_verts() -> Vec<f32> {
    vec![
        -0.012640, 0.255336,
        0.152259, 0.386185,
        0.223982, 0.275978,
        0.191749, 0.169082,
        0.396864, 0.121742,
        0.355419, -0.003047,
        0.251747, -0.044495,
        0.342622, -0.234376,
        0.219218, -0.279777,
        0.122174, -0.224565,
        0.030379, -0.414003,
        -0.082058, -0.345830,
        -0.099398, -0.235534,
        -0.304740, -0.281878,
        -0.321543, -0.151465,
        -0.246122, -0.069141,
        -0.410383, 0.062507,
        -0.318899, 0.156955,
        -0.207511, 0.149317,
        -0.207000, 0.359823,
        -0.076118, 0.347186,
        -0.012640, 0.255336,
    ]
}
<commit_msg>recreating what we already had using VBOs and shaders<commit_after>extern crate open_gl_bindings;
extern crate sdl2;

use open_gl_bindings::gl;

use sdl2::event::Event;

use std::ffi::CStr;
use std::ffi::CString;
use std::str;

fn find_sdl_gl_driver() -> Option<u32> {
    for (index, item) in sdl2::render::drivers().enumerate() {
        if item.name == "opengl" {
            return Some(index as u32);
        }
    }
    None
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let gl_attr = video_subsystem.gl_attr();
    gl_attr.set_stencil_size(1);
    gl_attr.set_context_major_version(2);
    gl_attr.set_context_minor_version(1);

    let canvas = video_subsystem
        .window("Window", 800, 600)
        .opengl()
        .build()
        .unwrap()
        .into_canvas()
        .index(find_sdl_gl_driver().unwrap())
        .build()
        .unwrap();

    let ctx = gl::Gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
    canvas.window().gl_set_context_to_current().unwrap();

    let char_ptr = unsafe { ctx.GetString(gl::VERSION) };
    let c_str: &CStr = unsafe { CStr::from_ptr(std::mem::transmute(char_ptr)) };
    let buf: &[u8] = c_str.to_bytes();
    let str_slice: &str = str::from_utf8(buf).unwrap();
    let version: String = str_slice.to_owned();

    println!("OpenGL version: {}", version);

    let window = canvas.window();

    let (width, height) = window.drawable_size();

    unsafe {
        ctx.Viewport(0, 0, width as _, height as _);

        ctx.MatrixMode(gl::PROJECTION);
        ctx.LoadIdentity();

        ctx.MatrixMode(gl::MODELVIEW);
        ctx.LoadIdentity();

        ctx.ClearColor(0.0, 0.0, 0.0, 1.0);
        ctx.Enable(gl::BLEND);
        ctx.BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

    }

    let vs = compile_shader(&ctx, VS_SRC, gl::VERTEX_SHADER);

    let fs = compile_shader(&ctx, FS_SRC, gl::FRAGMENT_SHADER);

    let program = link_program(&ctx, vs, fs);

    let mut world_matrix: [f32; 16] = [
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
    ];

    let verts: Vec<f32> = get_verts();

    let vertex_buffer = unsafe {
        let mut buffer = 0;

        ctx.GenBuffers(1, &mut buffer as _);
        ctx.BindBuffer(gl::ARRAY_BUFFER, buffer);
        ctx.BufferData(
            gl::ARRAY_BUFFER,
            (verts.len() * std::mem::size_of::<f32>()) as _,
            std::mem::transmute(verts.as_ptr()),
            gl::DYNAMIC_DRAW,
        );

        buffer
    };

    let indices: Vec<gl::types::GLushort> =
        (0..verts.len()).map(|x| x as gl::types::GLushort).collect();

    let index_buffer = unsafe {
        let mut buffer = 0;

        ctx.GenBuffers(1, &mut buffer as _);
        ctx.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, buffer);
        ctx.BufferData(
            gl::ELEMENT_ARRAY_BUFFER,
            (indices.len() * std::mem::size_of::<gl::types::GLushort>()) as _,
            std::mem::transmute(indices.as_ptr()),
            gl::DYNAMIC_DRAW,
        );

        buffer
    };
    unsafe {
        ctx.UseProgram(program);

        let pos_attr = ctx.GetAttribLocation(program, CString::new("position").unwrap().as_ptr());

        ctx.BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
        ctx.EnableVertexAttribArray(pos_attr as _);
        ctx.VertexAttribPointer(
            pos_attr as _,
            2,
            gl::FLOAT,
            gl::FALSE as _,
            0,
            std::ptr::null(),
        )

    }
    let world_attr =
        unsafe { ctx.GetUniformLocation(program, CString::new("world").unwrap().as_ptr()) };

    let colour_uniform =
        unsafe { ctx.GetUniformLocation(program, CString::new("colour").unwrap().as_ptr()) };

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F10), .. } => {
                    break 'running;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Left), .. } => {
                    world_matrix[12] -= 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Right), .. } => {
                    world_matrix[12] += 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Down), .. } => {
                    world_matrix[13] -= 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Up), .. } => {
                    world_matrix[13] += 0.1;
                }
                _ => {}
            }
        }

        unsafe {
            ctx.UniformMatrix4fv(world_attr as _, 1, gl::FALSE, world_matrix.as_ptr() as _);
        }

        draw_frame(&ctx, (verts.len() / 2) as _, colour_uniform);

        if cfg!(debug_assertions) {
            let mut err;
            while {
                err = unsafe { ctx.GetError() };
                err != gl::NO_ERROR
            }
            {
                println!("OpenGL error: {}", err);
            }
            if err != gl::NO_ERROR {
                panic!();
            }

        }

        window.gl_swap_window();

        std::thread::sleep(std::time::Duration::from_millis(8));
    }
}

fn draw_frame(ctx: &gl::Gl, vert_count: gl::types::GLsizei, colour_uniform: gl::types::GLint) {
    unsafe {
        ctx.Clear(
            gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT,
        );

        ctx.Clear(gl::STENCIL_BUFFER_BIT);

        ctx.Enable(gl::STENCIL_TEST);
        ctx.ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
        ctx.StencilOp(gl::INVERT, gl::INVERT, gl::INVERT);
        ctx.StencilFunc(gl::ALWAYS, 0x1, 0x1);

        ctx.Uniform4f(colour_uniform, 1.0, 1.0, 1.0, 1.0);

        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, vert_count);

        ctx.ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
        ctx.Uniform4f(
            colour_uniform,
            32.0 / 255.0,
            32.0 / 255.0,
            63.0 / 255.0,
            1.0,
        );

        ctx.StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
        ctx.StencilFunc(gl::EQUAL, 1, 1);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, vert_count);
        ctx.Disable(gl::STENCIL_TEST);

        //outline
        ctx.Uniform4f(
            colour_uniform,
            128.0 / 255.0,
            128.0 / 255.0,
            32.0 / 255.0,
            1.0,
        );
        ctx.DrawArrays(gl::LINE_STRIP, 0, vert_count);
    }
}
static VS_SRC: &'static str = "#version 120\n\
    attribute vec2 position;\n\
    uniform mat4 world;\n\
    void main() {\n\
    gl_Position = world * vec4(position, 0.0, 1.0);\n\
    }";

static FS_SRC: &'static str = "#version 120\n\
    uniform vec4 colour;\n\
    void main() {\n\
       gl_FragColor = colour;\n\
    }";

//shader helper functions based on https://gist.github.com/simias/c140d1479ada4d6218c0
fn compile_shader(ctx: &gl::Gl, src: &str, shader_type: gl::types::GLenum) -> gl::types::GLuint {
    let shader;
    unsafe {
        shader = ctx.CreateShader(shader_type);
        // Attempt to compile the shader
        let c_str = CString::new(src.as_bytes()).unwrap();
        ctx.ShaderSource(shader, 1, &c_str.as_ptr(), std::ptr::null());
        ctx.CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as gl::types::GLint;
        ctx.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as gl::types::GLint) {
            let mut buffer = [0u8; 512];
            let mut length: i32 = 0;
            ctx.GetShaderInfoLog(
                shader,
                buffer.len() as i32,
                &mut length,
                buffer.as_mut_ptr() as *mut i8,
            );
            panic!(
                "Compiler log (length: {}):\n{}",
                length,
                std::str::from_utf8(
                    std::ffi::CStr::from_ptr(std::mem::transmute(&buffer)).to_bytes(),
                ).unwrap()
            );
        }
    }
    shader
}

fn link_program(ctx: &gl::Gl, vs: gl::types::GLuint, fs: gl::types::GLuint) -> gl::types::GLuint {
    unsafe {
        let program = ctx.CreateProgram();
        ctx.AttachShader(program, vs);
        ctx.AttachShader(program, fs);
        ctx.LinkProgram(program);
        // Get the link status
        let mut status = gl::FALSE as gl::types::GLint;
        ctx.GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as gl::types::GLint) {
            let mut buffer = [0u8; 512];
            let mut length: i32 = 0;
            ctx.GetProgramInfoLog(
                program,
                buffer.len() as i32,
                &mut length,
                buffer.as_mut_ptr() as *mut i8,
            );
            panic!(
                "Compiler log (length: {}):\n{}",
                length,
                std::str::from_utf8(
                    std::ffi::CStr::from_ptr(std::mem::transmute(&buffer)).to_bytes(),
                ).unwrap()
            );
        }
        program
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn get_verts() -> Vec<f32> {
    vec![
        -0.012640, 0.255336,
        0.152259, 0.386185,
        0.223982, 0.275978,
        0.191749, 0.169082,
        0.396864, 0.121742,
        0.355419, -0.003047,
        0.251747, -0.044495,
        0.342622, -0.234376,
        0.219218, -0.279777,
        0.122174, -0.224565,
        0.030379, -0.414003,
        -0.082058, -0.345830,
        -0.099398, -0.235534,
        -0.304740, -0.281878,
        -0.321543, -0.151465,
        -0.246122, -0.069141,
        -0.410383, 0.062507,
        -0.318899, 0.156955,
        -0.207511, 0.149317,
        -0.207000, 0.359823,
        -0.076118, 0.347186,
        -0.012640, 0.255336,
    ]
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate piston_window;
extern crate opengl_graphics;
extern crate find_folder;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use piston_window::{PistonWindow};
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::{GlyphCache};
use graphics::*;
use std::path::{Path};

const WINDOW_SIZE: f64 = 400.0;
const CENTER: [f64; 2] = [WINDOW_SIZE/2.0, WINDOW_SIZE/2.0];
const MARGIN: f64 = 50.0;

const STARPLOT_SIZE: f64 = 20.0;
const STARPLOT_POS_X: f64 = 190.0;
const STARPLOT_POS_Y: f64 = 190.0;

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK:   [f32; 4] = [0.0, 0.0, 0.0, 1.0];

#[derive(Clone)]
/// Starplot defines the position, size and the different dimensions of
/// the visualization
pub struct Starplot {
    pub size: f64,
    pub x: f64,
    pub y: f64,
    pub dimensions: Vec<Dim>
}

////////////////////////////////////////////////////////////////////////////////
// Inherent methods
////////////////////////////////////////////////////////////////////////////////

impl Starplot {

    /// Creates a new Starplot variable
    pub fn new() -> Starplot {
        Starplot { size: STARPLOT_SIZE,
                   x: 0.0,
                   y: 0.0,
                   dimensions: Vec::new() }
    }

    /// Initializes a new Starplot with a defined position and size
    pub fn init(size: f64, x: f64, y: f64) -> Starplot {
        Starplot { size: size,
                   x: x,
                   y: y,
                   dimensions: Vec::new() }
    }

    /// Adds a dimension to the Starplot with the defined configuration variables
    pub fn add_dim(&mut self, val: f64, range: [f64; 2], label: &'static str) {
        // if value is out of range: panic message
        if val < range[0] || val > range[1] {
            panic!("value {val} out of range [{range_x}, {range_y}]",
                   val = val,
                   range_x = range[0],
                   range_y = range[1]);
        }

        let val = (val - range[0])/(range[1] - range[0]);
        self.dimensions.push(Dim {val: val, range: range, label: label, i_point: [0.0, 0.0], f_point: [0.0, 0.0] });
    }
}

#[derive(Clone)]
/// Dim defines a dimension (axis) of an Starplot
pub struct Dim {
    val: f64,
    range: [f64; 2],
    label: &'static str,
    i_point: [f64; 2],
    f_point: [f64; 2]
}

/// App defines the global application variables for the visualization an Starplot
pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    star: Starplot  // Starplot
}

////////////////////////////////////////////////////////////////////////////////
// Inherent methods
////////////////////////////////////////////////////////////////////////////////

impl App {

    /// Creates a new App instance
    pub fn new(gl: GlGraphics) -> App {
        App {gl: gl, star: Starplot::new() }
    }

    /// Defines the Starplot configuration for the visualization
    fn def_star(&mut self, star: Starplot) {
        self.star = star;
    }

    /// Get the contours of the Starplot dimensions
    fn get_contour(dim_vec: &Vec<Dim>) -> Vec<[f64; 4]> {
        let mut out: Vec<[f64; 4]> = Vec::new();

        for i in 0..dim_vec.len() {
            // if last dimension: connect with the first
            if (i + 1) == dim_vec.len() {
                out.push([ dim_vec[i].f_point[0], dim_vec[i].f_point[1],
                           dim_vec[0].f_point[0], dim_vec[0].f_point[1] ]);

                break;
            }
            // connect each dimension
            out.push([ dim_vec[i].f_point[0], dim_vec[i].f_point[1],
                       dim_vec[i+1].f_point[0], dim_vec[i+1].f_point[1] ]);
        }

        out
    }

    // Get end point of dimension depending on it's value and angle
    fn get_end_point(initial: &[f64; 2], margin: &f64, angle: &f64, val: &f64) -> [f64; 2] {
        [ (initial[0] - margin)*val*angle.cos() + initial[0],
          -(initial[1] - margin)*val*angle.sin() + initial[1] ]
    }

    // Get angle in radiants for each dimension
    fn get_angle(degree_div: &f64, div: f64) -> f64 {
        let degree: f64 = degree_div*(div + 1.0);
        (std::f64::consts::PI)*degree/180f64
    }

    // Render the Starplot
    fn render(&mut self, args: &RenderArgs, font_path: &Path) {
        // define the position and size of the ellipse using a square
        let square = rectangle::square(self.star.x, self.star.y, self.star.size);

        let mut star: Starplot = self.star.clone();

        // get the degree divison for the number of dimensions
        let degree_div: f64 = 360.0/(star.dimensions.len() as f64);

        // get for each dimension it's final point (initial point is the center of the ellipse)
        for (i, dim) in star.dimensions.iter_mut().enumerate() {
            let angle: f64 = App::get_angle(&degree_div, i as f64);

            dim.i_point = [ CENTER[0], CENTER[1] ];
            dim.f_point = App::get_end_point(&[ CENTER[0], CENTER[1] ], &MARGIN, &angle, &dim.val);
        }

        // get the contour connections between dimensions
        let contours: Vec<[f64; 4]> = App::get_contour(&star.dimensions);

        let mut glyph = GlyphCache::new(font_path).unwrap();

        self.gl.draw(args.viewport(), |c, gl| {
            // clear the window
            clear(WHITE, gl);

            // draw ellipse
            ellipse(BLACK, square, c.transform, gl);

            text::Text::new_color(BLACK, 32).draw("hello world!",
                                                      &mut glyph,
                                                      &c.draw_state,
                                                      c.transform,
                                                      gl);

            // draw dimensions
            for dim in star.dimensions.iter() {
                Line::new(BLACK, 1.0).draw([dim.i_point[0], dim.i_point[1], dim.f_point[0], dim.f_point[1]], &c.draw_state, c.transform, gl);
            }
            // draw contours
            for contour in contours.iter() {
                Line::new(BLACK, 1.0).draw(*contour, &c.draw_state, c.transform, gl);
            }
        });
    }

    /*fn update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.rotation += 2.0 * args.dt;
    }*/
}

fn main() {
    // Change this to OpenGL::V2_1 if not working.
    let opengl = OpenGL::V3_2;

    // Create a Piston window
    let mut window: PistonWindow = WindowSettings::new(
            "starplot",
            [WINDOW_SIZE as u32, WINDOW_SIZE as u32]
        )
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    // Get the font for the text representation
    let assets = find_folder::Search::ParentsThenKids(3, 3)
        .for_folder("assets").unwrap();
    let ref font = assets.join("FiraSans-Regular.ttf");

    // Generate an Starplot
    let mut starplot = Starplot::init(STARPLOT_SIZE,
                                      STARPLOT_POS_X,
                                      STARPLOT_POS_Y);

    // Add dimensions to the Starplot
    starplot.add_dim(0.2, [0.0, 1.0], "first");
    starplot.add_dim(0.8, [0.0, 1.0], "second");
    starplot.add_dim(50.0, [0.0, 200.0], "third");
    starplot.add_dim(0.4, [0.0, 1.0], "forth");
    starplot.add_dim(0.7, [0.0, 1.0], "fifth");
    starplot.add_dim(0.6, [0.0, 1.0], "sixth");
    starplot.add_dim(0.5, [0.0, 1.0], "seventh");

    // Add Starplot to the Application
    let mut app = App::new(GlGraphics::new(opengl));
    app.def_star(starplot);

    let mut events = window.events();
    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.render(&r, &font.as_path()); // render the Application
        }

        /*if let Some(_) = e.update_args() {
        }*/
    }
}<commit_msg>labels working<commit_after>extern crate piston;
extern crate graphics;
extern crate piston_window;
extern crate opengl_graphics;
extern crate find_folder;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use piston_window::{PistonWindow};
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::{GlyphCache};
use graphics::*;
use std::path::{Path};

const WINDOW_SIZE: f64 = 400.0;
const CENTER: [f64; 2] = [WINDOW_SIZE/2.0, WINDOW_SIZE/2.0];
const MARGIN: f64 = 50.0;

const STARPLOT_SIZE: f64 = 20.0;
const STARPLOT_POS_X: f64 = 190.0;
const STARPLOT_POS_Y: f64 = 190.0;

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK:   [f32; 4] = [0.0, 0.0, 0.0, 1.0];

#[derive(Clone)]
/// Starplot defines the position, size and the different dimensions of
/// the visualization
pub struct Starplot {
    pub size: f64,
    pub x: f64,
    pub y: f64,
    pub dimensions: Vec<Dim>
}

////////////////////////////////////////////////////////////////////////////////
// Inherent methods
////////////////////////////////////////////////////////////////////////////////

impl Starplot {

    /// Creates a new Starplot variable
    pub fn new() -> Starplot {
        Starplot { size: STARPLOT_SIZE,
                   x: 0.0,
                   y: 0.0,
                   dimensions: Vec::new() }
    }

    /// Initializes a new Starplot with a defined position and size
    pub fn init(size: f64, x: f64, y: f64) -> Starplot {
        Starplot { size: size,
                   x: x,
                   y: y,
                   dimensions: Vec::new() }
    }

    /// Adds a dimension to the Starplot with the defined configuration variables
    pub fn add_dim(&mut self, val: f64, range: [f64; 2], label: &'static str) {
        // if value is out of range: panic message
        if val < range[0] || val > range[1] {
            panic!("value {val} out of range [{range_x}, {range_y}]",
                   val = val,
                   range_x = range[0],
                   range_y = range[1]);
        }

        let val = (val - range[0])/(range[1] - range[0]);
        self.dimensions.push(Dim {val: val, range: range, label: label, i_point: [0.0, 0.0], f_point: [0.0, 0.0] });
    }
}

#[derive(Clone)]
/// Dim defines a dimension (axis) of an Starplot
pub struct Dim {
    val: f64,
    range: [f64; 2],
    label: &'static str,
    i_point: [f64; 2],
    f_point: [f64; 2]
}

/// App defines the global application variables for the visualization an Starplot
pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    star: Starplot  // Starplot
}

////////////////////////////////////////////////////////////////////////////////
// Inherent methods
////////////////////////////////////////////////////////////////////////////////

impl App {

    /// Creates a new App instance
    pub fn new(gl: GlGraphics) -> App {
        App {gl: gl, star: Starplot::new() }
    }

    /// Defines the Starplot configuration for the visualization
    fn def_star(&mut self, star: Starplot) {
        self.star = star;
    }

    /// Get the contours of the Starplot dimensions
    fn get_contour(dim_vec: &Vec<Dim>) -> Vec<[f64; 4]> {
        let mut out: Vec<[f64; 4]> = Vec::new();

        for i in 0..dim_vec.len() {
            // if last dimension: connect with the first
            if (i + 1) == dim_vec.len() {
                out.push([ dim_vec[i].f_point[0], dim_vec[i].f_point[1],
                           dim_vec[0].f_point[0], dim_vec[0].f_point[1] ]);

                break;
            }
            // connect each dimension
            out.push([ dim_vec[i].f_point[0], dim_vec[i].f_point[1],
                       dim_vec[i+1].f_point[0], dim_vec[i+1].f_point[1] ]);
        }

        out
    }

    // Get end point of dimension depending on it's value and angle
    fn get_end_point(initial: &[f64; 2], margin: &f64, angle: &f64, val: &f64) -> [f64; 2] {
        [ (initial[0] - margin)*val*angle.cos() + initial[0],
          -(initial[1] - margin)*val*angle.sin() + initial[1] ]
    }

    // Get angle in radiants for each dimension
    fn get_angle(degree_div: &f64, div: f64) -> f64 {
        let degree: f64 = degree_div*(div + 1.0);
        (std::f64::consts::PI)*degree/180f64
    }

    // Render the Starplot
    fn render(&mut self, args: &RenderArgs, font_path: &Path) {
        // define the position and size of the ellipse using a square
        let square = rectangle::square(self.star.x, self.star.y, self.star.size);

        let mut star: Starplot = self.star.clone();

        // get the degree divison for the number of dimensions
        let degree_div: f64 = 360.0/(star.dimensions.len() as f64);

        // get for each dimension it's final point (initial point is the center of the ellipse)
        for (i, dim) in star.dimensions.iter_mut().enumerate() {
            let angle: f64 = App::get_angle(&degree_div, i as f64);

            dim.i_point = [ CENTER[0], CENTER[1] ];
            dim.f_point = App::get_end_point(&[ CENTER[0], CENTER[1] ], &MARGIN, &angle, &dim.val);
        }

        // get the contour connections between dimensions
        let contours: Vec<[f64; 4]> = App::get_contour(&star.dimensions);

        let mut glyph = GlyphCache::new(font_path).unwrap();

        self.gl.draw(args.viewport(), |c, gl| {
            // clear the window
            clear(WHITE, gl);

            // draw ellipse
            ellipse(BLACK, square, c.transform, gl);

            // draw dimensions and labels
            for dim in star.dimensions.iter() {
                Line::new(BLACK, 1.0).draw([dim.i_point[0], dim.i_point[1], dim.f_point[0], dim.f_point[1]], &c.draw_state, c.transform, gl);

                let transform = c.transform.trans(dim.f_point[0], dim.f_point[1]);
                text::Text::new_color(BLACK, 10).draw(dim.label, &mut glyph, &c.draw_state, transform, gl);
            }
            // draw contours
            for contour in contours.iter() {
                Line::new(BLACK, 1.0).draw(*contour, &c.draw_state, c.transform, gl);
            }
        });
    }

    /*fn update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.rotation += 2.0 * args.dt;
    }*/
}

fn main() {
    // Change this to OpenGL::V2_1 if not working.
    let opengl = OpenGL::V3_2;

    // Create a Piston window
    let mut window: PistonWindow = WindowSettings::new(
            "starplot",
            [WINDOW_SIZE as u32, WINDOW_SIZE as u32]
        )
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    // Get the font for the text representation
    let assets = find_folder::Search::ParentsThenKids(3, 3)
        .for_folder("assets").unwrap();
    let ref font = assets.join("Inconsolata-Regular.ttf");

    // Generate an Starplot
    let mut starplot = Starplot::init(STARPLOT_SIZE,
                                      STARPLOT_POS_X,
                                      STARPLOT_POS_Y);

    // Add dimensions to the Starplot
    starplot.add_dim(0.2, [0.0, 1.0], "first");
    starplot.add_dim(0.8, [0.0, 1.0], "second");
    starplot.add_dim(50.0, [0.0, 200.0], "third");
    starplot.add_dim(0.4, [0.0, 1.0], "forth");
    starplot.add_dim(0.7, [0.0, 1.0], "fifth");
    starplot.add_dim(0.6, [0.0, 1.0], "sixth");
    starplot.add_dim(0.5, [0.0, 1.0], "seventh");

    // Add Starplot to the Application
    let mut app = App::new(GlGraphics::new(opengl));
    app.def_star(starplot);

    let mut events = window.events();
    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.render(&r, &font.as_path()); // render the Application
        }

        /*if let Some(_) = e.update_args() {
        }*/
    }
}<|endoftext|>


<commit_before>extern crate num;
extern crate portaudio;
extern crate rtlsdr;

use portaudio as pa;
use rtlsdr::{RTLSDRDevice, RTLSDRError};
use num::{Num, Zero};
use num::complex::Complex;
use std::env;
use std::f32::consts::PI;
use std::error::Error;
use pa::error::Error as PAError;
use std::thread;
use std::sync::mpsc::channel;

// Make sure that the buffer size is radix-2, otherwise the read_sync function
// will fail with an error code of -8. I want the buffer sizes between the SDR
// and the audio sizes to match, but since we get two samples per read the
// buffer size needs to be double.
const SDR_BUF_SIZE: usize = 524288;

// The buffer size for the audio sink.
const AUDIO_BUF_SIZE: usize = 262144;

// Most other sample rates fail, but this one works for my particular device.
// I will investigate exactly what's happening here and generate a list of
// possible sample rates (if there are others besides this one).
const SDR_SAMPLE_RATE: u32 = 2400000;

// Here is the sample rate of the output waveform we'll try to use.
const AUDIO_SAMPLE_RATE: f32 = 48000.0;

// The number of channels being used for audio.
const CHANNELS: i32 = 2;

fn main() {
    match run() {
        Ok(()) => (),
        Err(e) => println!("ERROR: {}", e),
    }
}

struct RTLSDR {
    rtlsdr: RTLSDRDevice,
}

unsafe impl Send for RTLSDR {}

fn run() -> Result<(), Box<Error>> {
    let sdr_index: i32 = env::args()
        .nth(1)
        .expect("Please specify the SDR index.")
        .parse::<i32>()?;
    let fm_freq_mhz: f32 = env::args()
        .nth(2)
        .expect("Please specify a center frequency in MHz.")
        .parse::<f32>()?;
    let bandwidth: u32 = env::args()
        .nth(3)
        .expect("Please specify the bandwidth.")
        .parse::<u32>()?;

    // Set up the threading channels for communication between our two threads.
    let (dongle_tx, dongle_rx) = channel();
    let (demod_tx, demod_rx) = channel();

    // This thread only collects data off of the SDR as quickly as possible and
    // sends it out.
    thread::spawn(move || {
        // Initialize the SDR based on the user's input parameters.
        let fm_freq: u32 = (fm_freq_mhz * 1e6) as u32;
        let mut sdr =
            RTLSDR { rtlsdr: init_sdr(sdr_index, fm_freq, bandwidth).unwrap() };

        loop {
            let bytes = sdr.rtlsdr.read_sync(SDR_BUF_SIZE).unwrap();
            let iq_vec = read_samples(bytes).unwrap();
            dongle_tx.send(iq_vec).unwrap();
        }
    });

    // This thread does all of the signal processing required to demodulate
    // the signal and get it to a nice audio stream.
    thread::spawn(move || {
        // Decimating down to 100k samples seems to work the best. I don't
        // really quite get why that is, so I'll need to do more research.
        let dec_rate_filt = 6;
        let dec_rate_audio = 4;

        let taps_filt =
            windowed_sinc(bandwidth as f32, SDR_SAMPLE_RATE as f32, 128);
        let taps_audio = windowed_sinc(
            bandwidth as f32 / dec_rate_filt as f32,
            SDR_SAMPLE_RATE as f32 / dec_rate_filt as f32,
            128,
        );

        let mut prev = Complex::zero();
        loop {
            // First, low_pass and decimate the raw IQ signal.
            let mut iq_vec = dongle_rx.recv().unwrap();
            iq_vec = filter(&iq_vec, &taps_filt);
            iq_vec = decimate(iq_vec, dec_rate_filt);

            // Next, demodulate the signal and filter the signal again. The
            // second filter seems to help with the noise after demodulation.
            let res = demod_fm(iq_vec, prev);
            let mut demod_iq = filter_real(&res.0, &taps_audio);
            demod_iq = decimate(demod_iq, dec_rate_audio);
            prev = res.1;
            demod_tx.send(demod_iq).unwrap();
        }
    });

    // Initialize the PortAudio class. We use a blocking stream since we're
    // doing processing on the signal and can't keep up with non-blocking
    // timing requirements.
    let audio = try!(pa::PortAudio::new());
    let settings = try!(audio.default_output_stream_settings(
        CHANNELS,
        AUDIO_SAMPLE_RATE as f64,
        AUDIO_BUF_SIZE as u32,
    ));
    let mut stream = try!(audio.open_blocking_stream(settings));
    try!(stream.start());

    loop {
        let demod_iq = demod_rx.recv().unwrap();

        let out_frames = match stream.write_available() {
            Ok(available) => {
                match available {
                    pa::StreamAvailable::Frames(frames) => frames as u32,
                    pa::StreamAvailable::InputOverflowed => {
                        return Err(Box::new(PAError::InputOverflowed))
                    }
                    pa::StreamAvailable::OutputUnderflowed => {
                        return Err(Box::new(PAError::OutputUnderflowed))
                    }
                }
            }
            Err(e) => return Err(Box::new(e)),
        };

        let buffer_frames = (demod_iq.len() / CHANNELS as usize) as u32;
        let write_frames = std::cmp::min(buffer_frames, out_frames);
        let n_write_samples = write_frames as usize * CHANNELS as usize;

        stream.write(
            write_frames,
            |output| for ix in 0..n_write_samples {
                output[ix] = demod_iq[ix];
            },
        )?;
    }
}

fn init_sdr(
    sdr_index: i32,
    fm_freq: u32,
    bandwidth: u32,
) -> Result<RTLSDRDevice, RTLSDRError> {
    let mut sdr = try!(rtlsdr::open(sdr_index));
    try!(sdr.set_center_freq(fm_freq));
    try!(sdr.set_sample_rate(SDR_SAMPLE_RATE));
    try!(sdr.set_tuner_bandwidth(bandwidth));
    try!(sdr.set_agc_mode(true));
    try!(sdr.reset_buffer());
    Ok(sdr)
}

fn decimate<T: Copy>(signal: Vec<T>, dec_rate: usize) -> Vec<T> {
    let mut ix = 0;
    let new_size = (signal.len() / dec_rate + 1) as usize;
    let mut signal_dec = Vec::<T>::with_capacity(new_size);
    while ix < signal.len() {
        signal_dec.push(signal[ix]);
        ix += dec_rate;
    }
    signal_dec
}

fn read_samples(bytes: Vec<u8>) -> Option<Vec<Complex<f32>>> {
    // First, check that we've been given an even number of bytes. Not sure
    // what to do if we don't get I and Q.
    let bytes_len = bytes.len();
    if bytes_len % 2 != 0 {
        println!("Received an odd number of samples.");
        return None;

    }
    let mut iq_vec: Vec<Complex<f32>> = Vec::with_capacity(bytes_len / 2);

    // Write the values to the complex value and normalize from [0, 255] to
    // [-1, 1].
    for iq in bytes.chunks(2) {
        let iq_cmplx = Complex::new(
            (iq[0] as f32 - 127.0) / 127.0,
            (iq[1] as f32 - 127.0) / 127.0,
        );
        iq_vec.push(iq_cmplx);
    }
    Some(iq_vec)
}

fn demod_fm(
    iq: Vec<Complex<f32>>,
    prev: Complex<f32>,
) -> (Vec<f32>, Complex<f32>) {

    let mut p = prev.clone();
    let mut demod_queue: Vec<f32> = Vec::with_capacity(iq.len());

    let gain = SDR_SAMPLE_RATE as f32 / (2.0 * PI * 75e3);

    for samp in iq.iter() {
        let conj = p.conj() * samp;
        demod_queue.push(conj.arg() * gain);
        p = *samp;
    }
    (demod_queue, p)
}

///
/// Filters a Complex vector.
///
fn filter<T: Copy + Num + Zero>(
    samples: &Vec<Complex<T>>,
    taps: &Vec<T>,
) -> Vec<Complex<T>> {
    // We'll lose taps.len() - 1 samples from the filtering.
    let mut filt_samps: Vec<Complex<T>> =
        Vec::with_capacity(samples.len() - taps.len() + 1);

    for window in samples.windows(taps.len()) {
        let iter = window.iter().zip(taps.iter());
        let filt_samp = iter.fold(Complex::zero(), |acc, (x, y)| acc + *x * *y);
        filt_samps.push(filt_samp);
    }
    filt_samps
}

///
/// Filter a real vector.
///
fn filter_real<T: Copy + Num + Zero>(
    samples: &Vec<T>,
    taps: &Vec<T>,
) -> Vec<T> {
    // We'll lose taps.len() - 1 samples from the filtering.
    let mut filt_samps: Vec<T> =
        Vec::with_capacity(samples.len() - taps.len() + 1);
    for window in samples.windows(taps.len()) {
        let iter = window.iter().zip(taps.iter());
        let filt_samp = iter.fold(T::zero(), |acc, (x, y)| acc + *x * *y);
        filt_samps.push(filt_samp);
    }
    filt_samps
}

///
/// Generates taps for a Hamming window.
///
fn hamming(ntaps: usize) -> Vec<f32> {
    let m: f32 = ntaps as f32 - 1.0;
    let mut taps: Vec<f32> = Vec::with_capacity(ntaps);
    for ix in 0..ntaps {
        taps.push(0.54 - 0.46 * (2.0 * PI * ix as f32 / m).cos());
    }
    taps
}

///
/// Generates filter taps for a sinc filter with a Hamming window.
///
fn windowed_sinc(cutoff_freq: f32, sample_rate: f32, ntaps: usize) -> Vec<f32> {
    let wc: f32 = 2.0 * PI * cutoff_freq / sample_rate;
    let mut taps: Vec<f32> = Vec::with_capacity(ntaps);
    let win_taps: Vec<f32> = hamming(ntaps);
    let ntaps_i: i32 = ntaps as i32;
    let m: i32 = (ntaps_i - 1) / 2;

    for ix in -m..m + 1 {
        let ix_f: f32 = ix as f32;
        if ix == 0 {
            let hi = wc / PI * win_taps[(ix + m) as usize];
            taps.push(hi);
        } else {
            let hi = (ix_f * wc).sin() / (ix_f * PI) *
                win_taps[(ix + m) as usize];
            taps.push(hi);
        }
    }
    taps
}
<commit_msg>Making the window function an input parameter into windowed_sinc<commit_after>extern crate num;
extern crate portaudio;
extern crate rtlsdr;

use portaudio as pa;
use rtlsdr::{RTLSDRDevice, RTLSDRError};
use num::{Num, Zero};
use num::complex::Complex;
use std::env;
use std::f32::consts::PI;
use std::error::Error;
use pa::error::Error as PAError;
use std::thread;
use std::sync::mpsc::channel;

// Make sure that the buffer size is radix-2, otherwise the read_sync function
// will fail with an error code of -8. I want the buffer sizes between the SDR
// and the audio sizes to match, but since we get two samples per read the
// buffer size needs to be double.
const SDR_BUF_SIZE: usize = 524288;

// The buffer size for the audio sink.
const AUDIO_BUF_SIZE: usize = 262144;

// Most other sample rates fail, but this one works for my particular device.
// I will investigate exactly what's happening here and generate a list of
// possible sample rates (if there are others besides this one).
const SDR_SAMPLE_RATE: u32 = 2400000;

// Here is the sample rate of the output waveform we'll try to use.
const AUDIO_SAMPLE_RATE: f32 = 48000.0;

// The number of channels being used for audio.
const CHANNELS: i32 = 2;

fn main() {
    match run() {
        Ok(()) => (),
        Err(e) => println!("ERROR: {}", e),
    }
}

struct RTLSDR {
    rtlsdr: RTLSDRDevice,
}

unsafe impl Send for RTLSDR {}

fn run() -> Result<(), Box<Error>> {
    let sdr_index: i32 = env::args()
        .nth(1)
        .expect("Please specify the SDR index.")
        .parse::<i32>()?;
    let fm_freq_mhz: f32 = env::args()
        .nth(2)
        .expect("Please specify a center frequency in MHz.")
        .parse::<f32>()?;
    let bandwidth: u32 = env::args()
        .nth(3)
        .expect("Please specify the bandwidth.")
        .parse::<u32>()?;

    // Set up the threading channels for communication between our two threads.
    let (dongle_tx, dongle_rx) = channel();
    let (demod_tx, demod_rx) = channel();

    // This thread only collects data off of the SDR as quickly as possible and
    // sends it out.
    thread::spawn(move || {
        // Initialize the SDR based on the user's input parameters.
        let fm_freq: u32 = (fm_freq_mhz * 1e6) as u32;
        let mut sdr =
            RTLSDR { rtlsdr: init_sdr(sdr_index, fm_freq, bandwidth).unwrap() };

        loop {
            let bytes = sdr.rtlsdr.read_sync(SDR_BUF_SIZE).unwrap();
            let iq_vec = read_samples(bytes).unwrap();
            dongle_tx.send(iq_vec).unwrap();
        }
    });

    // This thread does all of the signal processing required to demodulate
    // the signal and get it to a nice audio stream.
    thread::spawn(move || {
        // Decimating down to 100k samples seems to work the best. I don't
        // really quite get why that is, so I'll need to do more research.
        let dec_rate_filt = 6;
        let dec_rate_audio = 4;

        let taps_filt = windowed_sinc(
            bandwidth as f32,
            SDR_SAMPLE_RATE as f32,
            128,
            &hamming,
        );
        let taps_audio = windowed_sinc(
            bandwidth as f32 / dec_rate_filt as f32,
            SDR_SAMPLE_RATE as f32 / dec_rate_filt as f32,
            128,
            &hamming,
        );

        let mut prev = Complex::zero();
        loop {
            // First, low_pass and decimate the raw IQ signal.
            let mut iq_vec = dongle_rx.recv().unwrap();
            iq_vec = filter(&iq_vec, &taps_filt);
            iq_vec = decimate(iq_vec, dec_rate_filt);

            // Next, demodulate the signal and filter the signal again. The
            // second filter seems to help with the noise after demodulation.
            let res = demod_fm(iq_vec, prev);
            let mut demod_iq = filter_real(&res.0, &taps_audio);
            demod_iq = decimate(demod_iq, dec_rate_audio);
            prev = res.1;
            demod_tx.send(demod_iq).unwrap();
        }
    });

    // Initialize the PortAudio class. We use a blocking stream since we're
    // doing processing on the signal and can't keep up with non-blocking
    // timing requirements.
    let audio = try!(pa::PortAudio::new());
    let settings = try!(audio.default_output_stream_settings(
        CHANNELS,
        AUDIO_SAMPLE_RATE as f64,
        AUDIO_BUF_SIZE as u32,
    ));
    let mut stream = try!(audio.open_blocking_stream(settings));
    try!(stream.start());

    loop {
        let demod_iq = demod_rx.recv().unwrap();

        let out_frames = match stream.write_available() {
            Ok(available) => {
                match available {
                    pa::StreamAvailable::Frames(frames) => frames as u32,
                    pa::StreamAvailable::InputOverflowed => {
                        return Err(Box::new(PAError::InputOverflowed))
                    }
                    pa::StreamAvailable::OutputUnderflowed => {
                        return Err(Box::new(PAError::OutputUnderflowed))
                    }
                }
            }
            Err(e) => return Err(Box::new(e)),
        };

        let buffer_frames = (demod_iq.len() / CHANNELS as usize) as u32;
        let write_frames = std::cmp::min(buffer_frames, out_frames);
        let n_write_samples = write_frames as usize * CHANNELS as usize;

        stream.write(
            write_frames,
            |output| for ix in 0..n_write_samples {
                output[ix] = demod_iq[ix];
            },
        )?;
    }
}

fn init_sdr(
    sdr_index: i32,
    fm_freq: u32,
    bandwidth: u32,
) -> Result<RTLSDRDevice, RTLSDRError> {
    let mut sdr = try!(rtlsdr::open(sdr_index));
    try!(sdr.set_center_freq(fm_freq));
    try!(sdr.set_sample_rate(SDR_SAMPLE_RATE));
    try!(sdr.set_tuner_bandwidth(bandwidth));
    try!(sdr.set_agc_mode(true));
    try!(sdr.reset_buffer());
    Ok(sdr)
}

fn decimate<T: Copy>(signal: Vec<T>, dec_rate: usize) -> Vec<T> {
    let mut ix = 0;
    let new_size = (signal.len() / dec_rate + 1) as usize;
    let mut signal_dec = Vec::<T>::with_capacity(new_size);
    while ix < signal.len() {
        signal_dec.push(signal[ix]);
        ix += dec_rate;
    }
    signal_dec
}

fn read_samples(bytes: Vec<u8>) -> Option<Vec<Complex<f32>>> {
    // First, check that we've been given an even number of bytes. Not sure
    // what to do if we don't get I and Q.
    let bytes_len = bytes.len();
    if bytes_len % 2 != 0 {
        println!("Received an odd number of samples.");
        return None;

    }
    let mut iq_vec: Vec<Complex<f32>> = Vec::with_capacity(bytes_len / 2);

    // Write the values to the complex value and normalize from [0, 255] to
    // [-1, 1].
    for iq in bytes.chunks(2) {
        let iq_cmplx = Complex::new(
            (iq[0] as f32 - 127.0) / 127.0,
            (iq[1] as f32 - 127.0) / 127.0,
        );
        iq_vec.push(iq_cmplx);
    }
    Some(iq_vec)
}

fn demod_fm(
    iq: Vec<Complex<f32>>,
    prev: Complex<f32>,
) -> (Vec<f32>, Complex<f32>) {

    let mut p = prev.clone();
    let mut demod_queue: Vec<f32> = Vec::with_capacity(iq.len());

    let gain = SDR_SAMPLE_RATE as f32 / (2.0 * PI * 75e3);

    for samp in iq.iter() {
        let conj = p.conj() * samp;
        demod_queue.push(conj.arg() * gain);
        p = *samp;
    }
    (demod_queue, p)
}

///
/// Filters a Complex vector.
///
fn filter<T: Copy + Num + Zero>(
    samples: &Vec<Complex<T>>,
    taps: &Vec<T>,
) -> Vec<Complex<T>> {
    // We'll lose taps.len() - 1 samples from the filtering.
    let mut filt_samps: Vec<Complex<T>> =
        Vec::with_capacity(samples.len() - taps.len() + 1);

    for window in samples.windows(taps.len()) {
        let iter = window.iter().zip(taps.iter());
        let filt_samp = iter.fold(Complex::zero(), |acc, (x, y)| acc + *x * *y);
        filt_samps.push(filt_samp);
    }
    filt_samps
}

///
/// Filter a real vector.
///
fn filter_real<T: Copy + Num + Zero>(
    samples: &Vec<T>,
    taps: &Vec<T>,
) -> Vec<T> {
    // We'll lose taps.len() - 1 samples from the filtering.
    let mut filt_samps: Vec<T> =
        Vec::with_capacity(samples.len() - taps.len() + 1);
    for window in samples.windows(taps.len()) {
        let iter = window.iter().zip(taps.iter());
        let filt_samp = iter.fold(T::zero(), |acc, (x, y)| acc + *x * *y);
        filt_samps.push(filt_samp);
    }
    filt_samps
}

///
/// Generates taps for a Hamming window.
///
fn hamming(ntaps: usize) -> Vec<f32> {
    let m: f32 = ntaps as f32 - 1.0;
    let mut taps: Vec<f32> = Vec::with_capacity(ntaps);
    for ix in 0..ntaps {
        taps.push(0.54 - 0.46 * (2.0 * PI * ix as f32 / m).cos());
    }
    taps
}

///
/// Generates filter taps for a sinc filter with a Hamming window.
///
fn windowed_sinc(
    cutoff_freq: f32,
    sample_rate: f32,
    ntaps: usize,
    window: &Fn(usize) -> Vec<f32>,
) -> Vec<f32> {
    let wc: f32 = 2.0 * PI * cutoff_freq / sample_rate;
    let mut taps: Vec<f32> = Vec::with_capacity(ntaps);
    let win_taps: Vec<f32> = window(ntaps);
    let ntaps_i: i32 = ntaps as i32;
    let m: i32 = (ntaps_i - 1) / 2;

    for ix in -m..m + 1 {
        let ix_f: f32 = ix as f32;
        if ix == 0 {
            let hi = wc / PI * win_taps[(ix + m) as usize];
            taps.push(hi);
        } else {
            let hi = (ix_f * wc).sin() / (ix_f * PI) *
                win_taps[(ix + m) as usize];
            taps.push(hi);
        }
    }
    taps
}
<|endoftext|>


<commit_before>extern crate app_dirs;
#[macro_use]
extern crate clap;
extern crate indicatif;
extern crate num_cpus;
extern crate term_painter;
extern crate threadpool;

extern crate gitlib;
extern crate util;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;

use app_dirs::{AppInfo, AppDataType};
use indicatif::{ProgressBar, ProgressStyle};
use term_painter::Color::{BrightRed, BrightCyan, BrightGreen, BrightMagenta, BrightYellow};
use term_painter::ToStyle;
use threadpool::ThreadPool;

use gitlib::{FileStatus, GitError, GitRepo};
use util::{GitRepositories, Manifest};

mod cli;

const THREAD_SIGNAL: &str = "Could not signal main thread";

#[derive(Debug, Clone)]
enum RunOption {
    Checkout(String),
    Manifest(ManifestOption),
    Reset,
    Status,
}

#[derive(Debug, Clone)]
enum ManifestOption {
    Clean,
    Preview,
    Update,
}

fn main() {
    let working_dir = env::current_dir().expect("Could not get working directory");
    let matches = cli::build_cli().get_matches();

    let option = match matches.subcommand_name() {
        Some(cli::CMD_CHECKOUT) => {
            let branch_match = matches.subcommand_matches(cli::CMD_CHECKOUT).unwrap();
            let branch = value_t!(branch_match, cli::BRANCH, String).unwrap();
            RunOption::Checkout(branch)
        }
        Some(cli::CMD_MANIFEST) => {
            let matches = matches.subcommand_matches(cli::CMD_MANIFEST).unwrap();

            match matches.subcommand_name() {
                Some(cli::CMD_CLEAN) => RunOption::Manifest(ManifestOption::Clean),
                Some(cli::CMD_UPDATE) => RunOption::Manifest(ManifestOption::Update),
                _ => RunOption::Manifest(ManifestOption::Preview),
            }
        }
        Some(cli::CMD_COMPLETIONS) => {
            if let Some(ref matches) = matches.subcommand_matches(cli::CMD_COMPLETIONS) {
                let shell = value_t!(matches, cli::SHELL, clap::Shell).unwrap();
                cli::build_cli().gen_completions_to(cli::APP_NAME, shell, &mut std::io::stdout());
            }

            return;
        }
        Some(cli::CMD_RESET) => RunOption::Reset,

        // By default, just show status.
        _ => RunOption::Status,
    };

    process(option, &working_dir);
}

fn process(option: RunOption, path: &Path) {
    let manifest_path = build_manifest_path();
    let mut manifest = Manifest::open(&manifest_path, &path);

    if let RunOption::Manifest(ref m) = option {
        let repos = GitRepositories::new(path);

        match *m {
            ManifestOption::Clean => manifest_clean(&manifest_path),
            ManifestOption::Preview => manifest_preview(repos),
            ManifestOption::Update => manifest_update(repos, &mut manifest),
        }
        return;
    }

    let repos = match manifest.is_empty() {
        true => GitRepositories::new(path),
        false => GitRepositories::from_manifest(&manifest),
    };

    let thread_count = num_cpus::get();
    let pool = ThreadPool::new(thread_count);

    let pb = ProgressBar::new_spinner();

    match option {
        RunOption::Reset => reset(repos, pool),
        RunOption::Status => status(repos, pool),
        _ => panic!("Unhandled run option"),
    }
}

fn build_manifest_path() -> PathBuf {
    const APP_INFO: AppInfo = AppInfo {
        name: "git-plz",
        author: "devnought",
    };

    let root = app_dirs::get_app_root(AppDataType::UserCache, &APP_INFO)
        .expect("Could not locate app settings directory");
    let mut path = PathBuf::from(root);
    path.push("manifest.json");

    path
}

fn manifest_update(repos: GitRepositories, manifest: &mut Manifest) {
    manifest.add_repositories(repos);

    println!("{:#?}", &manifest);
}

fn manifest_preview(repos: GitRepositories) {
    for repo in repos {
        println!("{}", repo.path().to_str().unwrap());
    }
}

fn manifest_clean(manifest_path: &Path) {
    println!("Attempting to delete: {:?}", manifest_path);

    if manifest_path.exists() {
        fs::remove_file(manifest_path).unwrap()
    }
}

fn checkout(repo: &GitRepo, branch: &str) -> Result<(), GitError> {
    repo.checkout(branch)?;

    println!("{}",
             repo.path()
                 .to_str()
                 .expect("Could not unwrap repo path"));
    println!("    {}", BrightCyan.paint(branch));

    Ok(())
}

fn reset(repos: GitRepositories, pool: ThreadPool) {
    let (tx, rx) = channel();
    let mut repo_count = 0;

    for repo in repos {
        let tx = tx.clone();
        repo_count += 1;

        pool.execute(move || {
            match repo.statuses() {
                Ok(s) => {
                    if s.len() == 0 {
                        tx.send(None).expect(THREAD_SIGNAL);
                        return;
                    }
                }
                _ => (),
            }

            match repo.remove_untracked() {
                Err(_) => {
                    tx.send(None).expect(THREAD_SIGNAL);
                    return;
                }
                _ => (),
            }

            let head = match repo.reset() {
                Ok(r) => r,
                _ => {
                    tx.send(None).expect(THREAD_SIGNAL);
                    return;
                }
            };

            let tuple = (repo.path().to_path_buf(), head.name().to_string());
            tx.send(Some(tuple)).expect(THREAD_SIGNAL);
        });
    }

    let mut completed = 0;

    while repo_count > completed {
        completed += 1;

        let tuple = match rx.recv() {
            Ok(h) => h,
            Err(_) => break,
        };

        if let None = tuple {
            continue;
        }

        let (path, head) = tuple.unwrap();

        let branch = BrightCyan.paint(head);
        let l_brace = BrightYellow.paint("[");
        let r_brace = BrightYellow.paint("]");

        println!("  {}{}{}  {}", l_brace, branch, r_brace, path.display());
    }

    assert_eq!(completed, repo_count);
}

fn status(repos: GitRepositories, pool: ThreadPool) {
    let (tx, rx) = channel();
    let mut repo_count = 0;

    for repo in repos {
        let tx = tx.clone();
        repo_count += 1;

        pool.execute(move || {
            let statuses = match repo.statuses() {
                Ok(s) => s,
                Err(_) => {
                    tx.send(None).expect(THREAD_SIGNAL);
                    return;
                }
            };

            if statuses.len() == 0 {
                tx.send(None).expect(THREAD_SIGNAL);
                return;
            }

            let statuses_result = statuses.iter().collect::<Vec<_>>();
            let tuple = (repo.path().to_path_buf(), statuses_result);
            tx.send(Some(tuple)).expect(THREAD_SIGNAL);
        });
    }

    let mut completed = 0;

    while repo_count > completed {
        completed += 1;

        let tuple = match rx.recv() {
            Ok(t) => t,
            Err(_) => break,
        };

        if let None = tuple {
            continue;
        }

        let (path, list) = tuple.unwrap();

        println!("{}", path.display());

        for entry in list {
            let (pre, colour) = match *entry.status() {
                FileStatus::Conflicted => ("       Conflicted", BrightMagenta),
                FileStatus::Current => ("          Current", BrightMagenta),
                FileStatus::Deleted => ("          Deleted", BrightRed),
                FileStatus::Ignored => ("          Ignored", BrightMagenta),
                FileStatus::StagedNew => ("       Staged New", BrightMagenta),
                FileStatus::StagedModified => ("  Staged Modified", BrightMagenta),
                FileStatus::StagedDeleted => ("   Staged Deleted", BrightMagenta),
                FileStatus::StagedRenamed => ("   Staged Renamed", BrightMagenta),
                FileStatus::StagedTypechange => ("Staged Typechange", BrightMagenta),
                FileStatus::Modified => ("         Modified", BrightCyan),
                FileStatus::New => ("              New", BrightGreen),
                FileStatus::Renamed => ("          Renamed", BrightCyan),
                FileStatus::Typechange => ("       Typechange", BrightCyan),
                FileStatus::Unknown => ("          Unknown", BrightMagenta),
            };

            println!("  {} {}", colour.paint(pre), entry.path().display());
        }
    }

    assert_eq!(completed, repo_count);
}
<commit_msg>Split out result printing, removed repo count nonsense<commit_after>extern crate app_dirs;
#[macro_use]
extern crate clap;
extern crate indicatif;
extern crate num_cpus;
extern crate term_painter;
extern crate threadpool;

extern crate gitlib;
extern crate util;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::mpsc::{channel, Receiver};

use app_dirs::{AppInfo, AppDataType};
use indicatif::{ProgressBar, ProgressStyle};
use term_painter::Color::{BrightRed, BrightCyan, BrightGreen, BrightMagenta, BrightYellow};
use term_painter::ToStyle;
use threadpool::ThreadPool;

use gitlib::{FileStatus, GitError, GitRepo, GitStatusEntry};
use util::{GitRepositories, Manifest};

mod cli;

const THREAD_SIGNAL: &str = "Could not signal main thread";

#[derive(Debug, Clone)]
enum RunOption {
    Checkout(String),
    Manifest(ManifestOption),
    Reset,
    Status,
}

#[derive(Debug, Clone)]
enum ManifestOption {
    Clean,
    Preview,
    Update,
}

fn main() {
    let working_dir = env::current_dir().expect("Could not get working directory");
    let matches = cli::build_cli().get_matches();

    let option = match matches.subcommand_name() {
        Some(cli::CMD_CHECKOUT) => {
            let branch_match = matches.subcommand_matches(cli::CMD_CHECKOUT).unwrap();
            let branch = value_t!(branch_match, cli::BRANCH, String).unwrap();
            RunOption::Checkout(branch)
        }
        Some(cli::CMD_MANIFEST) => {
            let matches = matches.subcommand_matches(cli::CMD_MANIFEST).unwrap();

            match matches.subcommand_name() {
                Some(cli::CMD_CLEAN) => RunOption::Manifest(ManifestOption::Clean),
                Some(cli::CMD_UPDATE) => RunOption::Manifest(ManifestOption::Update),
                _ => RunOption::Manifest(ManifestOption::Preview),
            }
        }
        Some(cli::CMD_COMPLETIONS) => {
            if let Some(ref matches) = matches.subcommand_matches(cli::CMD_COMPLETIONS) {
                let shell = value_t!(matches, cli::SHELL, clap::Shell).unwrap();
                cli::build_cli().gen_completions_to(cli::APP_NAME, shell, &mut std::io::stdout());
            }

            return;
        }
        Some(cli::CMD_RESET) => RunOption::Reset,

        // By default, just show status.
        _ => RunOption::Status,
    };

    process(option, &working_dir);
}

fn process(option: RunOption, path: &Path) {
    let manifest_path = build_manifest_path();
    let mut manifest = Manifest::open(&manifest_path, &path);

    if let RunOption::Manifest(ref m) = option {
        let repos = GitRepositories::new(path);

        match *m {
            ManifestOption::Clean => manifest_clean(&manifest_path),
            ManifestOption::Preview => manifest_preview(repos),
            ManifestOption::Update => manifest_update(repos, &mut manifest),
        }

        return;
    }

    let repos = match manifest.is_empty() {
        true => GitRepositories::new(path),
        false => GitRepositories::from_manifest(&manifest),
    };

    let thread_count = num_cpus::get();
    let pool = ThreadPool::new(thread_count);
    let pb = ProgressBar::new_spinner();

    match option {
        RunOption::Reset => {
            let rx = reset(repos, pool);

            while let Ok((path, head)) = rx.recv() {
                let branch = BrightCyan.paint(head);
                let l_brace = BrightYellow.paint("[");
                let r_brace = BrightYellow.paint("]");

                println!("  {}{}{}  {}", l_brace, branch, r_brace, path.display());
            }
        }
        RunOption::Status => {
            let rx = status(repos, pool);

            while let Ok((path, list)) = rx.recv() {
                println!("{}", path.display());

                for entry in list {
                    let (pre, colour) = match *entry.status() {
                        FileStatus::Conflicted => ("       Conflicted", BrightMagenta),
                        FileStatus::Current => ("          Current", BrightMagenta),
                        FileStatus::Deleted => ("          Deleted", BrightRed),
                        FileStatus::Ignored => ("          Ignored", BrightMagenta),
                        FileStatus::StagedNew => ("       Staged New", BrightMagenta),
                        FileStatus::StagedModified => ("  Staged Modified", BrightMagenta),
                        FileStatus::StagedDeleted => ("   Staged Deleted", BrightMagenta),
                        FileStatus::StagedRenamed => ("   Staged Renamed", BrightMagenta),
                        FileStatus::StagedTypechange => ("Staged Typechange", BrightMagenta),
                        FileStatus::Modified => ("         Modified", BrightCyan),
                        FileStatus::New => ("              New", BrightGreen),
                        FileStatus::Renamed => ("          Renamed", BrightCyan),
                        FileStatus::Typechange => ("       Typechange", BrightCyan),
                        FileStatus::Unknown => ("          Unknown", BrightMagenta),
                    };

                    println!("  {} {}", colour.paint(pre), entry.path().display());
                }
            }
        }
        _ => panic!("Unhandled run option"),
    }
}

fn build_manifest_path() -> PathBuf {
    const APP_INFO: AppInfo = AppInfo {
        name: "git-plz",
        author: "devnought",
    };

    let root = app_dirs::get_app_root(AppDataType::UserCache, &APP_INFO)
        .expect("Could not locate app settings directory");
    let mut path = PathBuf::from(root);
    path.push("manifest.json");

    path
}

fn manifest_update(repos: GitRepositories, manifest: &mut Manifest) {
    manifest.add_repositories(repos);

    println!("{:#?}", &manifest);
}

fn manifest_preview(repos: GitRepositories) {
    for repo in repos {
        println!("{}", repo.path().to_str().unwrap());
    }
}

fn manifest_clean(manifest_path: &Path) {
    println!("Attempting to delete: {:?}", manifest_path);

    if manifest_path.exists() {
        fs::remove_file(manifest_path).unwrap()
    }
}

fn checkout(repo: &GitRepo, branch: &str) -> Result<(), GitError> {
    repo.checkout(branch)?;

    println!("{}",
             repo.path()
                 .to_str()
                 .expect("Could not unwrap repo path"));
    println!("    {}", BrightCyan.paint(branch));

    Ok(())
}

fn reset(repos: GitRepositories, pool: ThreadPool) -> Receiver<(PathBuf, String)> {
    let (tx, rx) = channel();

    for repo in repos {
        let tx = tx.clone();

        pool.execute(move || {
            match repo.statuses() {
                Ok(s) => {
                    if s.len() == 0 {
                        return;
                    }
                }
                _ => (),
            }

            match repo.remove_untracked() {
                Err(_) => return,
                _ => (),
            }

            let head = match repo.reset() {
                Ok(r) => r,
                _ => return,
            };

            let tuple = (repo.path().to_path_buf(), head.name().to_string());
            tx.send(tuple).expect(THREAD_SIGNAL);
        });
    }

    rx
}

fn status(repos: GitRepositories, pool: ThreadPool) -> Receiver<(PathBuf, Vec<GitStatusEntry>)> {
    let (tx, rx) = channel();

    for repo in repos {
        let tx = tx.clone();

        pool.execute(move || {
            let statuses = match repo.statuses() {
                Ok(s) => s,
                Err(_) => return,
            };

            if statuses.len() == 0 {
                return;
            }

            let statuses_result = statuses.iter().collect::<Vec<_>>();
            let tuple = (repo.path().to_path_buf(), statuses_result);
            tx.send(tuple).expect(THREAD_SIGNAL);
        });
    }

    rx
}
<|endoftext|>