<commit_before>
use gfx;
use gfx::DeviceHelper;
use graphics::BackEnd;
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;

use Texture;

static VERTEX_SHADER: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
attribute vec2 pos;
attribute vec4 color;
varying vec4 v_Color;
void main() {
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
    GLSL_150: b"
#version 150 core
in vec2 pos;
in vec4 color;
out vec4 v_Color;
void main() {
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
};

static FRAGMENT_SHADER: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
varying vec4 v_Color;
void main() {
    gl_FragColor = v_Color;
}
"
    GLSL_150: b"
#version 150 core
in vec4 v_Color;
out vec4 o_Color;
void main() {
    o_Color = v_Color;
}
"
};

static VERTEX_SHADER_UV: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
attribute vec2 pos;
attribute vec4 color;
attribute vec2 uv;
uniform sampler2D s_texture;
varying vec4 v_Color;
varying vec2 v_UV;
void main() {
    v_UV = uv;
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
    GLSL_150: b"
#version 150 core
in vec2 pos;
in vec4 color;
in vec2 uv;
uniform sampler2D s_texture;
out vec4 v_Color;
out vec2 v_UV;
void main() {
    v_UV = uv;
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
};

static FRAGMENT_SHADER_UV: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
varying vec2 v_UV;
varying vec4 v_Color;
void main()
{
    gl_FragColor = texture2D(s_texture, v_UV) * v_Color;
}
"
    GLSL_150: b"
#version 150 core
out vec4 o_Color;
uniform sampler2D s_texture;
in vec2 v_UV;
in vec4 v_Color;
void main()
{
    o_Color = texture(s_texture, v_UV) * v_Color;
}
"
};

static POS_COMPONENTS: uint = 2;
static COLOR_COMPONENTS: uint = 4;
static UV_COMPONENTS: uint = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

#[vertex_format]
struct PositionFormat { pos: [f32, ..2] }

#[vertex_format]
struct ColorFormat { color: [f32, ..4] }

#[vertex_format]
struct TexCoordsFormat { uv: [f32, ..2] }

#[allow(unused_imports)]
#[shader_param(BatchUV, OwnedBatchUV)]
struct ParamsUV {
    s_texture: gfx::shade::TextureParam,
}

/// The graphics back-end.
pub struct Gfx2d {
    buffer_pos: gfx::BufferHandle<f32>,
    buffer_color: gfx::BufferHandle<f32>,
    buffer_uv: gfx::BufferHandle<f32>,
    batch: gfx::batch::OwnedBatch<(), ()>,
    batch_uv: OwnedBatchUV,
}

impl Gfx2d {
    /// Creates a new Gfx2d object.
    pub fn new<D: gfx::Device<C>,
               C: gfx::CommandBuffer>(device: &mut D) -> Gfx2d {
        let program = device.link_program(
                VERTEX_SHADER.clone(),
                FRAGMENT_SHADER.clone()
            ).unwrap();
        let program_uv = device.link_program(
                VERTEX_SHADER_UV.clone(),
                FRAGMENT_SHADER_UV.clone()
            ).unwrap();

        let buffer_pos = device.create_buffer(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::UsageDynamic);
        let buffer_color = device.create_buffer(
            COLOR_COMPONENTS * BUFFER_SIZE,
            gfx::UsageDynamic);
        let buffer_uv = device.create_buffer(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::UsageDynamic);

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(gfx::VertexFormat::generate(
            None::<PositionFormat>,
            buffer_pos.raw()
        ).into_iter());
        mesh.attributes.extend(gfx::VertexFormat::generate(
            None::<ColorFormat>,
            buffer_color.raw()
        ).into_iter());

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(gfx::VertexFormat::generate(
            None::<TexCoordsFormat>,
            buffer_uv.raw()
        ).into_iter());

        let batch = gfx::batch::OwnedBatch::new(mesh, program, ()).unwrap();

        let sampler = device.create_sampler(
                gfx::tex::SamplerInfo::new(gfx::tex::Trilinear,
                                           gfx::tex::Clamp)
            );

        // Create a dummy texture
        let texture_info = gfx::tex::TextureInfo {
            width: 1,
            height: 1,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
                [0x20u8, 0xA0u8, 0xC0u8, 0x00u8])
            .unwrap();
        let params_uv = ParamsUV {
            s_texture: (texture, Some(sampler))
        };

        let batch_uv = gfx::batch::OwnedBatch::new(
            mesh_uv, program_uv, params_uv).unwrap();

        Gfx2d {
            buffer_pos: buffer_pos,
            buffer_color: buffer_color,
            buffer_uv: buffer_uv,
            batch: batch,
            batch_uv: batch_uv,
        }
    }
}

/// Used for rendering 2D graphics.
pub struct RenderContext<'a, C: 'a + gfx::CommandBuffer> {
    renderer: &'a mut gfx::Renderer<C>,
    frame: &'a gfx::Frame,
    gfx2d: &'a mut Gfx2d,
}

impl<'a, C: gfx::CommandBuffer> RenderContext<'a, C> {
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<C>,
               frame: &'a gfx::Frame,
               gfx2d: &'a mut Gfx2d) -> RenderContext<'a, C> {
        RenderContext {
            renderer: renderer,
            frame: frame,
            gfx2d: gfx2d,
        }
    }
}

impl<'a, C: gfx::CommandBuffer> BackEnd<Texture>
for RenderContext<'a, C> {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        let &RenderContext {
            ref mut renderer,
            frame,
            ..
        } = self;
        renderer.clear(
                gfx::ClearData {
                    color: [r, g, b, a],
                    depth: 0.0,
                    stencil: 0,
                },
                gfx::Color,
                frame
            );
    }

    fn enable_alpha_blend(&mut self) {
        use std::default::Default;
        use gfx::state::{Normal, Inverse, Factor};

        let blend = gfx::state::Blend {
            value: [1.0, 1.0, 1.0, 1.0],
            color: gfx::state::BlendChannel {
                    equation: gfx::state::FuncAdd,
                    source: Factor(Normal, gfx::state::SourceAlpha),
                    destination: Factor(Inverse, gfx::state::SourceAlpha)
                },
            alpha: Default::default()
        };

        self.gfx2d.batch.state.blend = Some(blend);
        self.gfx2d.batch_uv.state.blend = Some(blend);
    }

    fn disable_alpha_blend(&mut self) {
        self.gfx2d.batch.state.blend = None;
        self.gfx2d.batch_uv.state.blend = None;
    }

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

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        let &RenderContext {
            ref mut renderer,
            ref frame,
            gfx2d: &Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_color,
                ref mut batch,
                ..
            },
        } = self;

        assert_eq!(
            vertices.len() * COLOR_COMPONENTS,
            colors.len() * POS_COMPONENTS
        );
        renderer.update_buffer_vec(*buffer_pos, vertices, 0);
        renderer.update_buffer_vec(*buffer_color, colors, 0);

        let n = vertices.len() / POS_COMPONENTS;
        batch.slice = gfx::VertexSlice(gfx::TriangleList, 0, n as u32);
        renderer.draw(batch, *frame);
    }

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

    fn enable_single_texture(&mut self, texture: &Texture) {
        let ParamsUV {
            s_texture: (ref mut s_texture, _)
        } = self.gfx2d.batch_uv.param;
        *s_texture = texture.handle;
    }

    fn disable_single_texture(&mut self) {}

    fn has_texture_alpha(&self, texture: &Texture) -> bool {
        texture.handle.get_info().format.get_components() == Some(gfx::tex::RGBA)
    }

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

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        let &RenderContext {
            ref mut renderer,
            ref frame,
            gfx2d: &Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_color,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        assert_eq!(
            vertices.len() * COLOR_COMPONENTS,
            colors.len() * POS_COMPONENTS
        );
        assert_eq!(
            vertices.len() * UV_COMPONENTS,
            texture_coords.len() * POS_COMPONENTS
        );
        renderer.update_buffer_vec(*buffer_pos, vertices, 0);
        renderer.update_buffer_vec(*buffer_color, colors, 0);
        renderer.update_buffer_vec(*buffer_uv, texture_coords, 0);

        let n = vertices.len() / POS_COMPONENTS;
        batch_uv.slice = gfx::VertexSlice(gfx::TriangleList, 0, n as u32);
        renderer.draw(batch_uv, *frame);
    }
}
<commit_msg>Added render method to simplify setup<commit_after>
use gfx;
use gfx::DeviceHelper;
use graphics::{ Context, BackEnd };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;

use Texture;

static VERTEX_SHADER: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
attribute vec2 pos;
attribute vec4 color;
varying vec4 v_Color;
void main() {
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
    GLSL_150: b"
#version 150 core
in vec2 pos;
in vec4 color;
out vec4 v_Color;
void main() {
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
};

static FRAGMENT_SHADER: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
varying vec4 v_Color;
void main() {
    gl_FragColor = v_Color;
}
"
    GLSL_150: b"
#version 150 core
in vec4 v_Color;
out vec4 o_Color;
void main() {
    o_Color = v_Color;
}
"
};

static VERTEX_SHADER_UV: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
attribute vec2 pos;
attribute vec4 color;
attribute vec2 uv;
uniform sampler2D s_texture;
varying vec4 v_Color;
varying vec2 v_UV;
void main() {
    v_UV = uv;
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
    GLSL_150: b"
#version 150 core
in vec2 pos;
in vec4 color;
in vec2 uv;
uniform sampler2D s_texture;
out vec4 v_Color;
out vec2 v_UV;
void main() {
    v_UV = uv;
    v_Color = color;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
};

static FRAGMENT_SHADER_UV: gfx::ShaderSource = shaders!{
    GLSL_120: b"
#version 120
varying vec2 v_UV;
varying vec4 v_Color;
void main()
{
    gl_FragColor = texture2D(s_texture, v_UV) * v_Color;
}
"
    GLSL_150: b"
#version 150 core
out vec4 o_Color;
uniform sampler2D s_texture;
in vec2 v_UV;
in vec4 v_Color;
void main()
{
    o_Color = texture(s_texture, v_UV) * v_Color;
}
"
};

static POS_COMPONENTS: uint = 2;
static COLOR_COMPONENTS: uint = 4;
static UV_COMPONENTS: uint = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

#[vertex_format]
struct PositionFormat { pos: [f32, ..2] }

#[vertex_format]
struct ColorFormat { color: [f32, ..4] }

#[vertex_format]
struct TexCoordsFormat { uv: [f32, ..2] }

#[allow(unused_imports)]
#[shader_param(BatchUV, OwnedBatchUV)]
struct ParamsUV {
    s_texture: gfx::shade::TextureParam,
}

/// The graphics back-end.
pub struct Gfx2d {
    buffer_pos: gfx::BufferHandle<f32>,
    buffer_color: gfx::BufferHandle<f32>,
    buffer_uv: gfx::BufferHandle<f32>,
    batch: gfx::batch::OwnedBatch<(), ()>,
    batch_uv: OwnedBatchUV,
}

impl Gfx2d {
    /// Creates a new Gfx2d object.
    pub fn new<D: gfx::Device<C>,
               C: gfx::CommandBuffer>(device: &mut D) -> Gfx2d {
        let program = device.link_program(
                VERTEX_SHADER.clone(),
                FRAGMENT_SHADER.clone()
            ).unwrap();
        let program_uv = device.link_program(
                VERTEX_SHADER_UV.clone(),
                FRAGMENT_SHADER_UV.clone()
            ).unwrap();

        let buffer_pos = device.create_buffer(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::UsageDynamic);
        let buffer_color = device.create_buffer(
            COLOR_COMPONENTS * BUFFER_SIZE,
            gfx::UsageDynamic);
        let buffer_uv = device.create_buffer(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::UsageDynamic);

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(gfx::VertexFormat::generate(
            None::<PositionFormat>,
            buffer_pos.raw()
        ).into_iter());
        mesh.attributes.extend(gfx::VertexFormat::generate(
            None::<ColorFormat>,
            buffer_color.raw()
        ).into_iter());

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(gfx::VertexFormat::generate(
            None::<TexCoordsFormat>,
            buffer_uv.raw()
        ).into_iter());

        let batch = gfx::batch::OwnedBatch::new(mesh, program, ()).unwrap();

        let sampler = device.create_sampler(
                gfx::tex::SamplerInfo::new(gfx::tex::Trilinear,
                                           gfx::tex::Clamp)
            );

        // Create a dummy texture
        let texture_info = gfx::tex::TextureInfo {
            width: 1,
            height: 1,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
                [0x20u8, 0xA0u8, 0xC0u8, 0x00u8])
            .unwrap();
        let params_uv = ParamsUV {
            s_texture: (texture, Some(sampler))
        };

        let batch_uv = gfx::batch::OwnedBatch::new(
            mesh_uv, program_uv, params_uv).unwrap();

        Gfx2d {
            buffer_pos: buffer_pos,
            buffer_color: buffer_color,
            buffer_uv: buffer_uv,
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn render<C: gfx::CommandBuffer>(
        &mut self,
        renderer: &mut gfx::Renderer<C>,
        frame: &gfx::Frame,
        f: |c: Context, g: &mut RenderContext<C>|
    ) {
        let ref mut g = RenderContext::new(
            renderer,
            frame,
            self
        );
        let c = Context::abs(
            frame.width as f64,
            frame.height as f64
        );
        f(c, g);
    }
}

/// Used for rendering 2D graphics.
pub struct RenderContext<'a, C: 'a + gfx::CommandBuffer> {
    renderer: &'a mut gfx::Renderer<C>,
    frame: &'a gfx::Frame,
    gfx2d: &'a mut Gfx2d,
}

impl<'a, C: gfx::CommandBuffer> RenderContext<'a, C> {
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<C>,
               frame: &'a gfx::Frame,
               gfx2d: &'a mut Gfx2d) -> RenderContext<'a, C> {
        RenderContext {
            renderer: renderer,
            frame: frame,
            gfx2d: gfx2d,
        }
    }
}

impl<'a, C: gfx::CommandBuffer> BackEnd<Texture>
for RenderContext<'a, C> {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        let &RenderContext {
            ref mut renderer,
            frame,
            ..
        } = self;
        renderer.clear(
                gfx::ClearData {
                    color: [r, g, b, a],
                    depth: 0.0,
                    stencil: 0,
                },
                gfx::Color,
                frame
            );
    }

    fn enable_alpha_blend(&mut self) {
        use std::default::Default;
        use gfx::state::{Normal, Inverse, Factor};

        let blend = gfx::state::Blend {
            value: [1.0, 1.0, 1.0, 1.0],
            color: gfx::state::BlendChannel {
                    equation: gfx::state::FuncAdd,
                    source: Factor(Normal, gfx::state::SourceAlpha),
                    destination: Factor(Inverse, gfx::state::SourceAlpha)
                },
            alpha: Default::default()
        };

        self.gfx2d.batch.state.blend = Some(blend);
        self.gfx2d.batch_uv.state.blend = Some(blend);
    }

    fn disable_alpha_blend(&mut self) {
        self.gfx2d.batch.state.blend = None;
        self.gfx2d.batch_uv.state.blend = None;
    }

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

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        let &RenderContext {
            ref mut renderer,
            ref frame,
            gfx2d: &Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_color,
                ref mut batch,
                ..
            },
        } = self;

        assert_eq!(
            vertices.len() * COLOR_COMPONENTS,
            colors.len() * POS_COMPONENTS
        );
        renderer.update_buffer_vec(*buffer_pos, vertices, 0);
        renderer.update_buffer_vec(*buffer_color, colors, 0);

        let n = vertices.len() / POS_COMPONENTS;
        batch.slice = gfx::VertexSlice(gfx::TriangleList, 0, n as u32);
        renderer.draw(batch, *frame);
    }

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

    fn enable_single_texture(&mut self, texture: &Texture) {
        let ParamsUV {
            s_texture: (ref mut s_texture, _)
        } = self.gfx2d.batch_uv.param;
        *s_texture = texture.handle;
    }

    fn disable_single_texture(&mut self) {}

    fn has_texture_alpha(&self, texture: &Texture) -> bool {
        texture.handle.get_info().format.get_components() == Some(gfx::tex::RGBA)
    }

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

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        let &RenderContext {
            ref mut renderer,
            ref frame,
            gfx2d: &Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_color,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        assert_eq!(
            vertices.len() * COLOR_COMPONENTS,
            colors.len() * POS_COMPONENTS
        );
        assert_eq!(
            vertices.len() * UV_COMPONENTS,
            texture_coords.len() * POS_COMPONENTS
        );
        renderer.update_buffer_vec(*buffer_pos, vertices, 0);
        renderer.update_buffer_vec(*buffer_color, colors, 0);
        renderer.update_buffer_vec(*buffer_uv, texture_coords, 0);

        let n = vertices.len() / POS_COMPONENTS;
        batch_uv.slice = gfx::VertexSlice(gfx::TriangleList, 0, n as u32);
        renderer.draw(batch_uv, *frame);
    }
}
<|endoftext|>


<commit_before>use cairo;
use gdk::{self, WindowExt};
use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt};
use std::cell::RefCell;

use std::rc::Rc;

use color::Color;
use utils::RotateVec;

const LEFT_WIDTH: f64 = 31.;

pub struct Graph {
    colors: Vec<Color>,
    pub data: Vec<RotateVec<f64>>,
    vertical_layout: gtk::Box,
    scroll_layout: gtk::ScrolledWindow,
    horizontal_layout: gtk::Box,
    pub area: DrawingArea,
    max: Option<RefCell<f64>>,
    keep_max: bool,
    display_labels: RefCell<bool>,
    initial_diff: Option<i32>,
    label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    labels_layout_width: i32,
    /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will
    /// be able to go down.
    minimum: Option<f64>,
    // In %, from 0 to whatever
    overhead: Option<f64>,
}

impl Graph {
    /// If `max` is `None`, the graph will expect values between 0 and 1.
    ///
    /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs
    /// won't rescale down. It is not taken into account if `max` is `None`.
    pub fn new(max: Option<f64>, keep_max: bool) -> Graph {
        let g = Graph {
            colors: vec![],
            data: vec![],
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0),
            scroll_layout: gtk::ScrolledWindow::new(
                None::<&gtk::Adjustment>,
                None::<&gtk::Adjustment>,
            ),
            horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0),
            area: DrawingArea::new(),
            max: if let Some(max) = max {
                Some(RefCell::new(max))
            } else {
                None
            },
            keep_max,
            display_labels: RefCell::new(true),
            initial_diff: None,
            label_callbacks: None,
            labels_layout_width: 80,
            minimum: None,
            overhead: None,
        };
        g.scroll_layout.set_min_content_width(g.labels_layout_width);
        g.scroll_layout.add(&g.vertical_layout);
        g.horizontal_layout.pack_start(&g.area, true, true, 0);
        g.horizontal_layout
            .pack_start(&g.scroll_layout, false, true, 10);
        g.horizontal_layout.set_margin_start(5);
        g
    }

    pub fn set_minimum(&mut self, minimum: Option<f64>) {
        self.minimum = minimum;
    }

    pub fn set_overhead(&mut self, overhead: Option<f64>) {
        if let Some(o) = overhead {
            assert!(o >= 0.);
        }
        self.overhead = overhead;
    }

    /// Changes the size of the layout containing labels (the one on the right).
    pub fn set_labels_width(&mut self, labels_layout_width: u32) {
        self.scroll_layout
            .set_min_content_width(labels_layout_width as i32);
        self.labels_layout_width = labels_layout_width as i32;
    }

    pub fn set_label_callbacks(
        &mut self,
        label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    ) {
        self.label_callbacks = label_callbacks;
    }

    pub fn set_display_labels(&self, display_labels: bool) {
        *self.display_labels.borrow_mut() = display_labels;
        if display_labels {
            self.scroll_layout.show_all();
        } else {
            self.scroll_layout.hide();
        }
        self.invalidate();
    }

    pub fn hide(&self) {
        self.horizontal_layout.hide();
    }

    pub fn show_all(&self) {
        self.horizontal_layout.show_all();
        if !*self.display_labels.borrow() {
            self.scroll_layout.hide();
        }
    }

    pub fn attach_to(&self, to: &gtk::Box) {
        to.add(&self.horizontal_layout);
    }

    pub fn push(&mut self, d: RotateVec<f64>, s: &str, override_color: Option<usize>) {
        let c = if let Some(over) = override_color {
            Color::generate(over)
        } else {
            Color::generate(self.data.len() + 11)
        };
        let l = gtk::Label::new(Some(s));
        l.override_color(
            StateFlags::from_bits(0).expect("from_bits failed"),
            Some(&c.to_gdk()),
        );
        self.vertical_layout.add(&l);
        self.colors.push(c);
        self.data.push(d);
    }

    fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) {
        if let Some(ref call) = self.label_callbacks {
            let entries = call(max);
            let font_size = 8.;

            c.set_source_rgb(0., 0., 0.);
            c.set_font_size(font_size);

            c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size);
            c.show_text(entries[0].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.);
            c.show_text(entries[1].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.);
            c.show_text(entries[2].as_str());

            c.move_to(
                font_size - 1.,
                height / 2. + 4. * (entries[3].len() >> 1) as f64,
            );
            c.rotate(-::std::f64::consts::FRAC_PI_2);
            c.show_text(entries[3].as_str());
        }
    }

    pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) {
        let x_start = if self.label_callbacks.is_some() {
            LEFT_WIDTH
        } else {
            0.
        };

        // to limit line "fuzziness"
        #[inline]
        fn rounder(x: f64) -> f64 {
            let fract = x.fract();
            (if fract < 0.5 {
                x.trunc() + 0.5
            } else {
                x.trunc() + 1.5
            })
        }

        c.set_source_rgb(0., 0., 0.);
        c.rectangle(x_start, 0., width, height);
        c.fill();
        c.set_source_rgb(0.5, 0.5, 0.5);
        c.set_line_width(0.5);

        // We always draw 10 lines (12 if we count the borders).
        let x_step = (width - x_start) / 12.;
        let mut current = width - width / 12.;
        if x_step < 0.1 {
            c.stroke();
            return;
        }

        while current > x_start {
            c.move_to(rounder(current), 0.0);
            c.line_to(rounder(current), height);
            current -= x_step;
        }
        let step = height / 10.0;
        current = step - 1.0;
        while current < height - 1. {
            c.move_to(x_start, rounder(current));
            c.line_to(width, rounder(current));
            current += step;
        }
        c.stroke();

        c.set_line_width(1.);

        if let Some(ref self_max) = self.max {
            let mut max = if self.keep_max {
                *self_max.borrow()
            } else {
                1.
            };
            let len = self.data[0].len() - 1;
            for x in 0..len {
                for entry in &self.data {
                    if entry[x] > max {
                        max = entry[x];
                    }
                }
            }
            if let Some(min) = self.minimum {
                if min > max {
                    max = min;
                }
            } else if let Some(over) = self.overhead {
                max = max + max * over / 100.;
            }
            if !self.data.is_empty() && !self.data[0].is_empty() {
                let len = self.data[0].len() - 1;
                let step = (width - 2.0 - x_start) / len as f64;
                current = x_start + 1.0;
                let mut index = len;
                while current > x_start && index > 0 {
                    for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                        c.set_source_rgb(color.r, color.g, color.b);
                        c.move_to(
                            current + step,
                            height - entry[index - 1] / max * (height - 1.0),
                        );
                        c.line_to(current, height - entry[index] / max * (height - 1.0));
                        c.stroke();
                    }
                    current += step;
                    index -= 1;
                }
            }
            if max > *self_max.borrow() || !self.keep_max {
                *self_max.borrow_mut() = max;
            }
            self.draw_labels(c, max, height);
        } else if !self.data.is_empty() && !self.data[0].is_empty() {
            let len = self.data[0].len() - 1;
            let step = (width - 2.0 - x_start) / (len as f64);
            current = x_start + 1.0;
            let mut index = len;
            while current > x_start && index > 0 {
                for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                    c.set_source_rgb(color.r, color.g, color.b);
                    c.move_to(current + step, height - entry[index - 1] * (height - 1.0));
                    c.line_to(current, height - entry[index] * (height - 1.0));
                    c.stroke();
                }
                current += step;
                index -= 1;
            }
            // To be called in last to avoid having to restore state (rotation).
            self.draw_labels(c, 100., height);
        }
    }

    pub fn invalidate(&self) {
        if let Some(t_win) = self.area.get_window() {
            let (x, y) = self
                .area
                .translate_coordinates(&self.area, 0, 0)
                .expect("translate_coordinates failed");
            let rect = gdk::Rectangle {
                x,
                y,
                width: self.area.get_allocated_width(),
                height: self.area.get_allocated_height(),
            };
            t_win.invalidate_rect(Some(&rect), true);
        }
    }

    pub fn send_size_request(&self, width: Option<i32>) {
        let mut width = match width {
            Some(w) => w,
            None => {
                if let Some(parent) = self.area.get_parent() {
                    parent.get_allocation().width
                        - parent.get_margin_start()
                        - parent.get_margin_end()
                } else {
                    eprintln!(
                        "<Graph::send_size_request> A parent is required if no width is \
                               provided..."
                    );
                    return;
                }
            }
        };
        // This condition is to avoid having a graph with a bigger width than the window.
        if let Some(top) = self.area.get_toplevel() {
            let max_width = top.get_allocation().width;
            if width > max_width {
                width = max_width;
            }
        }
        self.area.set_size_request(
            if *self.display_labels.borrow() {
                width
                    - if width >= self.labels_layout_width {
                        self.labels_layout_width
                    } else {
                        width
                    }
            } else {
                width
            },
            200,
        );
    }
}

pub trait Connecter {
    fn connect_to_window_events(&self);
}

impl Connecter for Rc<RefCell<Graph>> {
    fn connect_to_window_events(&self) {
        let s = self.clone();
        if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() {
            // TODO: ugly way to resize drawing area, I should find a better way
            parent.connect_configure_event(move |w, _| {
                let need_diff = s.borrow().initial_diff.is_none();
                if need_diff {
                    let mut s = s.borrow_mut();
                    let parent_width = if let Some(p) = s.area.get_parent() {
                        p.get_allocation().width
                    } else {
                        0
                    };
                    s.initial_diff = Some(w.get_allocation().width - parent_width);
                }
                s.borrow().send_size_request(None);
                false
            });
        } else {
            eprintln!("This method needs to be called *after* it has been put inside a window");
        }
    }
}
<commit_msg>Remove warnings<commit_after>use cairo;
use gdk::{self, WindowExt};
use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt};
use std::cell::RefCell;

use std::rc::Rc;

use color::Color;
use utils::RotateVec;

const LEFT_WIDTH: f64 = 31.;

pub struct Graph {
    colors: Vec<Color>,
    pub data: Vec<RotateVec<f64>>,
    vertical_layout: gtk::Box,
    scroll_layout: gtk::ScrolledWindow,
    horizontal_layout: gtk::Box,
    pub area: DrawingArea,
    max: Option<RefCell<f64>>,
    keep_max: bool,
    display_labels: RefCell<bool>,
    initial_diff: Option<i32>,
    label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    labels_layout_width: i32,
    /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will
    /// be able to go down.
    minimum: Option<f64>,
    // In %, from 0 to whatever
    overhead: Option<f64>,
}

impl Graph {
    /// If `max` is `None`, the graph will expect values between 0 and 1.
    ///
    /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs
    /// won't rescale down. It is not taken into account if `max` is `None`.
    pub fn new(max: Option<f64>, keep_max: bool) -> Graph {
        let g = Graph {
            colors: vec![],
            data: vec![],
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0),
            scroll_layout: gtk::ScrolledWindow::new(
                None::<&gtk::Adjustment>,
                None::<&gtk::Adjustment>,
            ),
            horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0),
            area: DrawingArea::new(),
            max: if let Some(max) = max {
                Some(RefCell::new(max))
            } else {
                None
            },
            keep_max,
            display_labels: RefCell::new(true),
            initial_diff: None,
            label_callbacks: None,
            labels_layout_width: 80,
            minimum: None,
            overhead: None,
        };
        g.scroll_layout.set_min_content_width(g.labels_layout_width);
        g.scroll_layout.add(&g.vertical_layout);
        g.horizontal_layout.pack_start(&g.area, true, true, 0);
        g.horizontal_layout
            .pack_start(&g.scroll_layout, false, true, 10);
        g.horizontal_layout.set_margin_start(5);
        g
    }

    pub fn set_minimum(&mut self, minimum: Option<f64>) {
        self.minimum = minimum;
    }

    pub fn set_overhead(&mut self, overhead: Option<f64>) {
        if let Some(o) = overhead {
            assert!(o >= 0.);
        }
        self.overhead = overhead;
    }

    /// Changes the size of the layout containing labels (the one on the right).
    pub fn set_labels_width(&mut self, labels_layout_width: u32) {
        self.scroll_layout
            .set_min_content_width(labels_layout_width as i32);
        self.labels_layout_width = labels_layout_width as i32;
    }

    pub fn set_label_callbacks(
        &mut self,
        label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    ) {
        self.label_callbacks = label_callbacks;
    }

    pub fn set_display_labels(&self, display_labels: bool) {
        *self.display_labels.borrow_mut() = display_labels;
        if display_labels {
            self.scroll_layout.show_all();
        } else {
            self.scroll_layout.hide();
        }
        self.invalidate();
    }

    pub fn hide(&self) {
        self.horizontal_layout.hide();
    }

    pub fn show_all(&self) {
        self.horizontal_layout.show_all();
        if !*self.display_labels.borrow() {
            self.scroll_layout.hide();
        }
    }

    pub fn attach_to(&self, to: &gtk::Box) {
        to.add(&self.horizontal_layout);
    }

    pub fn push(&mut self, d: RotateVec<f64>, s: &str, override_color: Option<usize>) {
        let c = if let Some(over) = override_color {
            Color::generate(over)
        } else {
            Color::generate(self.data.len() + 11)
        };
        let l = gtk::Label::new(Some(s));
        l.override_color(
            StateFlags::from_bits(0).expect("from_bits failed"),
            Some(&c.to_gdk()),
        );
        self.vertical_layout.add(&l);
        self.colors.push(c);
        self.data.push(d);
    }

    fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) {
        if let Some(ref call) = self.label_callbacks {
            let entries = call(max);
            let font_size = 8.;

            c.set_source_rgb(0., 0., 0.);
            c.set_font_size(font_size);

            c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size);
            c.show_text(entries[0].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.);
            c.show_text(entries[1].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.);
            c.show_text(entries[2].as_str());

            c.move_to(
                font_size - 1.,
                height / 2. + 4. * (entries[3].len() >> 1) as f64,
            );
            c.rotate(-::std::f64::consts::FRAC_PI_2);
            c.show_text(entries[3].as_str());
        }
    }

    pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) {
        let x_start = if self.label_callbacks.is_some() {
            LEFT_WIDTH
        } else {
            0.
        };

        // to limit line "fuzziness"
        #[inline]
        fn rounder(x: f64) -> f64 {
            let fract = x.fract();
            if fract < 0.5 {
                x.trunc() + 0.5
            } else {
                x.trunc() + 1.5
            }
        }

        c.set_source_rgb(0., 0., 0.);
        c.rectangle(x_start, 0., width, height);
        c.fill();
        c.set_source_rgb(0.5, 0.5, 0.5);
        c.set_line_width(0.5);

        // We always draw 10 lines (12 if we count the borders).
        let x_step = (width - x_start) / 12.;
        let mut current = width - width / 12.;
        if x_step < 0.1 {
            c.stroke();
            return;
        }

        while current > x_start {
            c.move_to(rounder(current), 0.0);
            c.line_to(rounder(current), height);
            current -= x_step;
        }
        let step = height / 10.0;
        current = step - 1.0;
        while current < height - 1. {
            c.move_to(x_start, rounder(current));
            c.line_to(width, rounder(current));
            current += step;
        }
        c.stroke();

        c.set_line_width(1.);

        if let Some(ref self_max) = self.max {
            let mut max = if self.keep_max {
                *self_max.borrow()
            } else {
                1.
            };
            let len = self.data[0].len() - 1;
            for x in 0..len {
                for entry in &self.data {
                    if entry[x] > max {
                        max = entry[x];
                    }
                }
            }
            if let Some(min) = self.minimum {
                if min > max {
                    max = min;
                }
            } else if let Some(over) = self.overhead {
                max = max + max * over / 100.;
            }
            if !self.data.is_empty() && !self.data[0].is_empty() {
                let len = self.data[0].len() - 1;
                let step = (width - 2.0 - x_start) / len as f64;
                current = x_start + 1.0;
                let mut index = len;
                while current > x_start && index > 0 {
                    for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                        c.set_source_rgb(color.r, color.g, color.b);
                        c.move_to(
                            current + step,
                            height - entry[index - 1] / max * (height - 1.0),
                        );
                        c.line_to(current, height - entry[index] / max * (height - 1.0));
                        c.stroke();
                    }
                    current += step;
                    index -= 1;
                }
            }
            if max > *self_max.borrow() || !self.keep_max {
                *self_max.borrow_mut() = max;
            }
            self.draw_labels(c, max, height);
        } else if !self.data.is_empty() && !self.data[0].is_empty() {
            let len = self.data[0].len() - 1;
            let step = (width - 2.0 - x_start) / (len as f64);
            current = x_start + 1.0;
            let mut index = len;
            while current > x_start && index > 0 {
                for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                    c.set_source_rgb(color.r, color.g, color.b);
                    c.move_to(current + step, height - entry[index - 1] * (height - 1.0));
                    c.line_to(current, height - entry[index] * (height - 1.0));
                    c.stroke();
                }
                current += step;
                index -= 1;
            }
            // To be called in last to avoid having to restore state (rotation).
            self.draw_labels(c, 100., height);
        }
    }

    pub fn invalidate(&self) {
        if let Some(t_win) = self.area.get_window() {
            let (x, y) = self
                .area
                .translate_coordinates(&self.area, 0, 0)
                .expect("translate_coordinates failed");
            let rect = gdk::Rectangle {
                x,
                y,
                width: self.area.get_allocated_width(),
                height: self.area.get_allocated_height(),
            };
            t_win.invalidate_rect(Some(&rect), true);
        }
    }

    pub fn send_size_request(&self, width: Option<i32>) {
        let mut width = match width {
            Some(w) => w,
            None => {
                if let Some(parent) = self.area.get_parent() {
                    parent.get_allocation().width
                        - parent.get_margin_start()
                        - parent.get_margin_end()
                } else {
                    eprintln!(
                        "<Graph::send_size_request> A parent is required if no width is \
                               provided..."
                    );
                    return;
                }
            }
        };
        // This condition is to avoid having a graph with a bigger width than the window.
        if let Some(top) = self.area.get_toplevel() {
            let max_width = top.get_allocation().width;
            if width > max_width {
                width = max_width;
            }
        }
        self.area.set_size_request(
            if *self.display_labels.borrow() {
                width
                    - if width >= self.labels_layout_width {
                        self.labels_layout_width
                    } else {
                        width
                    }
            } else {
                width
            },
            200,
        );
    }
}

pub trait Connecter {
    fn connect_to_window_events(&self);
}

impl Connecter for Rc<RefCell<Graph>> {
    fn connect_to_window_events(&self) {
        let s = self.clone();
        if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() {
            // TODO: ugly way to resize drawing area, I should find a better way
            parent.connect_configure_event(move |w, _| {
                let need_diff = s.borrow().initial_diff.is_none();
                if need_diff {
                    let mut s = s.borrow_mut();
                    let parent_width = if let Some(p) = s.area.get_parent() {
                        p.get_allocation().width
                    } else {
                        0
                    };
                    s.initial_diff = Some(w.get_allocation().width - parent_width);
                }
                s.borrow().send_size_request(None);
                false
            });
        } else {
            eprintln!("This method needs to be called *after* it has been put inside a window");
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{SeekEnd, IoResult};
use std::num::Bounded;
use std::fmt;

/// The fields in an ID3v1 tag, including the "1.1" track number field
#[deriving(FromPrimitive, Copy)]
#[allow(missing_docs)]
pub enum Fields {
    Title,
    Artist,
    Album,
    Year,
    Comment,
    Track,
    Genre,
}

impl Fields {
    fn length(&self) -> uint {
        LENGTHS[*self as uint] as uint
    }
}

static LENGTHS: &'static [i8]=&[30, 30, 30, 4, 30, -1, 1];

static TAG: &'static [u8] = b"TAG";
pub static TAG_OFFSET: i64 = 128;

static TAGPLUS: &'static [u8] = b"TAG+";
pub static TAGPLUS_OFFSET: i64 = 355;

static XLENGTHS: &'static [i8]=&[60, 60, 60, 30, 6, 6];

/// The fields in an extended ID3v1 tag
#[deriving(FromPrimitive, Copy)]
#[allow(missing_docs)]
pub enum XFields {
    XTitle,
    XArtist,
    XAlbum,
    Speed,
    XGenre,
    Start,
    End,
}

impl XFields {
    fn length(&self) -> uint {
        XLENGTHS[*self as uint] as uint
    }
}

/// A struct representing ID3v1's notion of a four-digit year
#[deriving(Show, Copy)]
pub struct Year
{
    value: u16,
}

impl Year {
    fn value(&self) -> u16 {
        self.value
    }
    fn new(year: u16) -> Option<Year> {
        #![allow(deprecated)]
        let max: Year = Bounded::max_value();
        if year > max.value() {
            None
        } else {
            Some(Year {value: year})
        }
    }
}

impl Bounded for Year {
    #![allow(deprecated)]
    fn min_value() -> Year {
        Year {value: 0}
    }
    fn max_value() -> Year {
        Year {value: 9999}
    }
}

/// A struct representing ID3v1 extended time tags--encoded in the format "mmm:ss", a valid value can be a maximum of 999m99s = 999*60+99 = 60039 seconds
#[deriving(Copy)]
pub struct Time
{
    value: u16,
}

impl Time {
    fn seconds(&self) -> u16 {
        self.value
    }
    fn new(seconds: u16) -> Option<Time> {
        #![allow(deprecated)]
        let max: Time = Bounded::max_value();
        if seconds > max.seconds() {
            None
        } else {
            Some(Time {value: seconds})
        }
    }
}

impl Bounded for Time {
    #![allow(deprecated)]
    fn min_value() -> Time {
        Time {value: 0}
    }
    fn max_value() -> Time {
        Time {value: 60039}
    }
}

impl fmt::Show for Time {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:03}:{:02}", self.seconds()/60, self.seconds()%60)
    }
}

/// A structure containing parsed ID3v1 tag metadata.
#[deriving(Show)]
pub struct Tag {
    /// The full title (ID3v1 + extension if present).
    pub title: Vec<u8>,
    /// The full artist (ID3v1 + extension if present).
    pub artist: Vec<u8>,
    /// The full album (ID3v1 + extension if present).
    pub album: Vec<u8>,
    /// A 4-digit string, if we are lucky
    pub year: Year,
    /// A free-form comment.
    pub comment: Vec<u8>,
    /// Number of the track, 0 if not set. ID3v1.1 data.
    pub track: u8,
    /// The genre mapping is standardized up to 79, some extensions exist.
    /// http://eyed3.nicfit.net/plugins/genres_plugin.html
    pub genre: u8,
    /// 1 (slow), 2, 3, 4 (fast) or 0 (not set). ID3v1 extended data.
    pub speed: u8,
    /// Free-form genre string. ID3v1 extended data.
    pub genre_str: Vec<u8>,
    /// The real start of the track, mmm:ss. ID3v1 extended data.
    pub start_time: Time,
    /// The real end of the track, mmm:ss. ID3v1 extended data.
    pub end_time: Time,
}

fn write_zero_padded<W: Writer>(writer: &mut W, data: &[u8], offset: uint, len: uint) -> IoResult<()> {
    let start = ::std::cmp::min(offset, data.len());
    let actual_len = ::std::cmp::min(offset+len, data.len());
    try!(writer.write(data[start..actual_len]));
    for _ in range(0, len-(actual_len-start)) {
        try!(writer.write_u8(0));
    }
    Ok(())
}

impl Tag {
    /// Creates a new ID3v1 tag with no information.
    pub fn new() -> Tag {
        Tag {
            title: vec![], artist: vec![], album: vec![], year: Year::new(0).unwrap(), comment: vec![], track: 0,
            genre: 0, speed: 0, genre_str: vec![], start_time: Time::new(0).unwrap(), end_time: Time::new(0).unwrap()
        }
    }
    /// Returns whether the tag contains information which would be lost if the extended tag were not written.
    pub fn has_extended_data(&self) -> bool {
        use self::Fields::*;
        self.title.len() > Title.length() ||
        self.artist.len() > Artist.length() ||
        self.album.len() > Album.length() ||
        self.speed > 0 ||
        self.genre_str.len() > 0 ||
        self.start_time.seconds() > 0 ||
        self.end_time.seconds() > 0
    }
    /// Writes the simple ID3 tag (128 bytes) into the given writer.
    /// If write_track_number is true, the comment field will be truncated to 28 bytes and the removed two bytes will be used for a NUL and the track number.
    pub fn write<W: Writer>(&self, writer: &mut W, write_track_number: bool) -> IoResult<()> {
        use self::Fields::*;
        try!(writer.write(TAG));
        try!(write_zero_padded(writer, self.title[], 0, Title.length()));
        try!(write_zero_padded(writer, self.artist[], 0, Artist.length()));
        try!(write_zero_padded(writer, self.album[], 0, Album.length()));
        try!(write!(writer,"{:04}", self.year.value()));
        if write_track_number {
            try!(writer.write(self.comment[..Comment.length()-2]));
            try!(writer.write_u8(0));
            try!(writer.write_u8(self.track));
        } else {
            try!(writer.write(self.comment[..Comment.length()]));
        }
        try!(writer.write_u8(self.genre));
        Ok(())
    }
    /// Writes the extended portion of an ID3v1 tag (227 bytes) into the given buffer. Returns false and does nothing else if the buffer's size is not 227.
    pub fn write_extended<W: Writer>(&self, writer: &mut W) -> IoResult<()> {
        use self::Fields::*;
        use self::XFields::*;
        try!(write_zero_padded(writer, self.title[], Title.length(), XTitle.length()));
        try!(write_zero_padded(writer, self.artist[], Artist.length(), XArtist.length()));
        try!(write_zero_padded(writer, self.album[], Album.length(), XAlbum.length()));
        try!(writer.write_u8(self.speed));
        try!(write_zero_padded(writer, self.genre_str[], 0, XGenre.length()));
        try!(write!(writer,"{}", self.start_time));
        try!(write!(writer,"{}", self.end_time));
        Ok(())
    }
}

/// ID3v1 tag reading helpers.
trait ID3v1Helpers {
    /// Read `n` bytes starting at an offset from the end.
    fn read_from_end(&mut self, n:uint, offset:i64) -> IoResult<Vec<u8>>;

    /// Read a null-terminated ISO-8859-1 string of size at most `n`, at an offset from the end.
    fn read_str(&mut self, n: uint, offset: i64) -> IoResult<String>;
}

impl<R: Reader + Seek> ID3v1Helpers for R {
    #[inline]
    fn read_from_end(&mut self, n: uint, offset:i64) -> IoResult<Vec<u8>> {
        try!(self.seek(-offset, SeekEnd));
        self.read_exact(n)
    }

    #[inline]
    fn read_str(&mut self, n: uint, offset: i64) -> IoResult<String> {
        self.read_from_end(n, offset).map(|vec| extract_nz_88591(vec))
    }
}

/// Checks for the existence of the bytes denoting an ID3v1 metadata block tag.
#[inline]
pub fn probe_tag<R: Reader>(reader: &mut R) -> IoResult<bool> {
    let mut x=&mut [0, ..3];
    let _tag = try!(reader.read_at_least(TAG.len(), x[mut]));
    Ok(TAG == x[])
}

/// Checks for the existence of the bytes denoting an ID3v1 extended metadata tag.
#[inline]
pub fn probe_xtag<R: Reader>(reader: &mut R) -> IoResult<bool> {
    let mut x=&mut [0, ..4];
    let _tag = try!(reader.read_at_least(TAGPLUS.len(), x[mut]));
    Ok(TAGPLUS == x[])
}

fn parse_year(s: &[u8]) -> Year {
    let zero = Year::new(0).unwrap();
    match ::std::str::from_utf8(s) {
        Some(st) => {
            let mn: Option<u16> = ::std::str::from_str(st);
            let n = mn.unwrap_or(0);
            Year::new(n).unwrap_or(zero)
        },
        None => zero
    }
}

fn parse_time(s: &[u8]) -> Time {
    enum State {
        Seconds,
        Minutes,
        LeadingWhitespace,
    }

    let zero = Time::new(0).unwrap();

    let mut mult: u64=1;
    let mut seconds: u64=0;
    let mut state=State::Seconds;

    fn is_digit(s: u8) -> bool {
        s >= b'0' && s <= b'9'
    }
    fn value(s: u8) -> u8 {
        s-b'0'
    }
    for &i in s.iter().rev() {
        match state {
            State::Seconds =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=10;
                } else if i == b':' {
                    state=State::Minutes;
                    mult=60;
                } else {
                    return zero;
                },
            State::Minutes =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=60;
                } else if (i as char).is_whitespace() {
                    state=State::LeadingWhitespace;
                },
            State::LeadingWhitespace =>
                if (i as char).is_whitespace() {
                    continue
                } else {
                    return zero;
                },
        }
    }
    if seconds > 65535 {
        zero
    } else {
        Time::new(seconds as u16).unwrap_or(zero)
    }
}

/// Read an ID3v1 tag from a reader
pub fn read<R: Reader>(reader: &mut R) -> IoResult<Tag> {
    use self::Fields::*;
    macro_rules! maybe_read {
        ($prop:expr, $len:expr) => {
            {
                try!(reader.push($len, &mut $prop));
            }
        };
    }

    let mut tag = Tag::new();
    // Try to read ID3v1 metadata.
    let has_tag = try!(probe_tag(reader));
    if has_tag {
        maybe_read!(tag.title, Title.length());
        maybe_read!(tag.artist, Artist.length());
        maybe_read!(tag.album, Album.length());
        let mut year_str=vec![]; maybe_read!(year_str, Year.length());
        tag.year=parse_year(year_str[]);
        maybe_read!(tag.comment, Comment.length()-2);
        let track_guard_byte = try!(reader.read_u8());
        if track_guard_byte == 0 {
            tag.track=try!(reader.read_u8());
        } else {
            tag.comment.push(track_guard_byte);
            tag.comment.push(try!(reader.read_u8()));
        }
        tag.genre=try!(reader.read_u8());
    }

    Ok(tag)
}

/// Read the extended portion of an extended ID3v1 tag from a reader, combining its data with a previously-read ID3v1 tag
pub fn read_xtag<R: Reader>(reader: &mut R, tag: &mut Tag) -> IoResult<()> {
    use self::Fields::*;
    use self::XFields::*;
    macro_rules! maybe_read {
        ($prop:expr, $len:expr) => {
            {
                try!(reader.push($len, &mut $prop));
            }
        };
    }

    // Try to read ID3v1 extended metadata.
    let has_xtag = try!(probe_xtag(reader));
    if has_xtag {
        maybe_read!(tag.title, XTitle.length());
        maybe_read!(tag.artist, XArtist.length());
        maybe_read!(tag.album, XAlbum.length());
        tag.speed = try!(reader.read_byte());
        maybe_read!(tag.genre_str, Genre.length());
        let mut start_str=vec![]; maybe_read!(start_str, Start.length());
        tag.start_time=parse_time(start_str[]);
        let mut end_str=vec![]; maybe_read!(end_str, End.length());
        tag.end_time=parse_time(end_str[]);
    }
    Ok(())
}

/// Read a string from a null-terminated ISO-8859-1 byte vector.
///
/// Read the whole vector if there is no null byte.
///
/// This function cannot fail, because UTF-8 is compatible with ISO-8859-1
/// at the code point level.
#[inline]
fn extract_nz_88591(s: Vec<u8>) -> String {
    // This works because the ISO 8859-1 code points match the unicode code
    // points. So,`c as char` will map correctly from ISO to unicode.
    s.into_iter().take_while(|&c| c!=0).map(|c| c as char).collect()
}

/// Removes trailing zeros from an &[u8]
pub fn truncate_zeros(mut s: &[u8]) -> &[u8] {
    while s.len() > 0 && s[s.len()-1] == 0 {
        s=s[..s.len()-1]
    }
    s
}

#[test]
fn smoke_test() {
    let mut f=::std::io::fs::File::open(&Path::new("test.mp3"));
    f.seek(-TAG_OFFSET, SeekEnd);
    let mut tag=read(&mut f).unwrap();
    println!("{}", tag);
    f.seek(-TAGPLUS_OFFSET, SeekEnd);
    read_xtag(&mut f, &mut tag);
    println!("{}", tag);
}
<commit_msg>improve ID3v1 docs<commit_after>use std::io::{SeekEnd, IoResult};
use std::num::Bounded;
use std::fmt;

/// The fields in an ID3v1 tag, including the "1.1" track number field.
#[deriving(FromPrimitive, Copy)]
#[allow(missing_docs)]
pub enum Fields {
    Title,
    Artist,
    Album,
    Year,
    Comment,
    Track,
    Genre,
}

impl Fields {
    fn length(&self) -> uint {
        LENGTHS[*self as uint] as uint
    }
}

static LENGTHS: &'static [i8]=&[30, 30, 30, 4, 30, -1, 1];

static TAG: &'static [u8] = b"TAG";
/// How far from the end of a file to probe for an ID3 tag signature.
pub static TAG_OFFSET: i64 = 128;

static TAGPLUS: &'static [u8] = b"TAG+";
/// How far from the end of a file to probe for an extended ID3 tag signature.
pub static TAGPLUS_OFFSET: i64 = 355;

static XLENGTHS: &'static [i8]=&[60, 60, 60, 30, 6, 6];

/// The fields in an extended ID3v1 tag.
#[deriving(FromPrimitive, Copy)]
#[allow(missing_docs)]
pub enum XFields {
    XTitle,
    XArtist,
    XAlbum,
    Speed,
    XGenre,
    Start,
    End,
}

impl XFields {
    fn length(&self) -> uint {
        XLENGTHS[*self as uint] as uint
    }
}

/// ID3v1's notion of a four-digit year.
#[deriving(Show, Copy)]
pub struct Year
{
    value: u16,
}

impl Year {
    fn value(&self) -> u16 {
        self.value
    }
    fn new(year: u16) -> Option<Year> {
        #![allow(deprecated)]
        let max: Year = Bounded::max_value();
        if year > max.value() {
            None
        } else {
            Some(Year {value: year})
        }
    }
}

impl Bounded for Year {
    #![allow(deprecated)]
    fn min_value() -> Year {
        Year {value: 0}
    }
    fn max_value() -> Year {
        Year {value: 9999}
    }
}

/// ID3v1 extended time tags--encoded in the format "mmm:ss", a valid value can be a maximum of 999m99s = 999*60+99 = 60039 seconds.
#[deriving(Copy)]
pub struct Time
{
    value: u16,
}

impl Time {
    fn seconds(&self) -> u16 {
        self.value
    }
    fn new(seconds: u16) -> Option<Time> {
        #![allow(deprecated)]
        let max: Time = Bounded::max_value();
        if seconds > max.seconds() {
            None
        } else {
            Some(Time {value: seconds})
        }
    }
}

impl Bounded for Time {
    #![allow(deprecated)]
    fn min_value() -> Time {
        Time {value: 0}
    }
    fn max_value() -> Time {
        Time {value: 60039}
    }
}

impl fmt::Show for Time {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:03}:{:02}", self.seconds()/60, self.seconds()%60)
    }
}

/// Parsed ID3v1 tag metadata.
#[deriving(Show)]
pub struct Tag {
    /// The full title (ID3v1 + extension if present).
    pub title: Vec<u8>,
    /// The full artist (ID3v1 + extension if present).
    pub artist: Vec<u8>,
    /// The full album (ID3v1 + extension if present).
    pub album: Vec<u8>,
    /// A 4-digit string, if we are lucky
    pub year: Year,
    /// A free-form comment.
    pub comment: Vec<u8>,
    /// Number of the track, 0 if not set. ID3v1.1 data.
    pub track: u8,
    /// The genre mapping is standardized up to 79, some extensions exist.
    /// http://eyed3.nicfit.net/plugins/genres_plugin.html
    pub genre: u8,
    /// 1 (slow), 2, 3, 4 (fast) or 0 (not set). ID3v1 extended data.
    pub speed: u8,
    /// Free-form genre string. ID3v1 extended data.
    pub genre_str: Vec<u8>,
    /// The real start of the track, mmm:ss. ID3v1 extended data.
    pub start_time: Time,
    /// The real end of the track, mmm:ss. ID3v1 extended data.
    pub end_time: Time,
}

fn write_zero_padded<W: Writer>(writer: &mut W, data: &[u8], offset: uint, len: uint) -> IoResult<()> {
    let start = ::std::cmp::min(offset, data.len());
    let actual_len = ::std::cmp::min(offset+len, data.len());
    try!(writer.write(data[start..actual_len]));
    for _ in range(0, len-(actual_len-start)) {
        try!(writer.write_u8(0));
    }
    Ok(())
}

impl Tag {
    /// Creates a new ID3v1 tag with no information.
    pub fn new() -> Tag {
        Tag {
            title: vec![], artist: vec![], album: vec![], year: Year::new(0).unwrap(), comment: vec![], track: 0,
            genre: 0, speed: 0, genre_str: vec![], start_time: Time::new(0).unwrap(), end_time: Time::new(0).unwrap()
        }
    }
    /// Returns whether the tag contains information which would be lost if the extended tag were not written.
    pub fn has_extended_data(&self) -> bool {
        use self::Fields::*;
        self.title.len() > Title.length() ||
        self.artist.len() > Artist.length() ||
        self.album.len() > Album.length() ||
        self.speed > 0 ||
        self.genre_str.len() > 0 ||
        self.start_time.seconds() > 0 ||
        self.end_time.seconds() > 0
    }
    /// Writes the simple ID3 tag (128 bytes) into the given writer.
    /// If write_track_number is true, the comment field will be truncated to 28 bytes and the removed two bytes will be used for a NUL and the track number.
    pub fn write<W: Writer>(&self, writer: &mut W, write_track_number: bool) -> IoResult<()> {
        use self::Fields::*;
        try!(writer.write(TAG));
        try!(write_zero_padded(writer, self.title[], 0, Title.length()));
        try!(write_zero_padded(writer, self.artist[], 0, Artist.length()));
        try!(write_zero_padded(writer, self.album[], 0, Album.length()));
        try!(write!(writer,"{:04}", self.year.value()));
        if write_track_number {
            try!(writer.write(self.comment[..Comment.length()-2]));
            try!(writer.write_u8(0));
            try!(writer.write_u8(self.track));
        } else {
            try!(writer.write(self.comment[..Comment.length()]));
        }
        try!(writer.write_u8(self.genre));
        Ok(())
    }
    /// Writes the extended portion of an ID3v1 tag (227 bytes) into the given buffer. Returns false and does nothing else if the buffer's size is not 227.
    pub fn write_extended<W: Writer>(&self, writer: &mut W) -> IoResult<()> {
        use self::Fields::*;
        use self::XFields::*;
        try!(write_zero_padded(writer, self.title[], Title.length(), XTitle.length()));
        try!(write_zero_padded(writer, self.artist[], Artist.length(), XArtist.length()));
        try!(write_zero_padded(writer, self.album[], Album.length(), XAlbum.length()));
        try!(writer.write_u8(self.speed));
        try!(write_zero_padded(writer, self.genre_str[], 0, XGenre.length()));
        try!(write!(writer,"{}", self.start_time));
        try!(write!(writer,"{}", self.end_time));
        Ok(())
    }
}

/// ID3v1 tag reading helpers.
trait ID3v1Helpers {
    /// Read `n` bytes starting at an offset from the end.
    fn read_from_end(&mut self, n:uint, offset:i64) -> IoResult<Vec<u8>>;

    /// Read a null-terminated ISO-8859-1 string of size at most `n`, at an offset from the end.
    fn read_str(&mut self, n: uint, offset: i64) -> IoResult<String>;
}

impl<R: Reader + Seek> ID3v1Helpers for R {
    #[inline]
    fn read_from_end(&mut self, n: uint, offset:i64) -> IoResult<Vec<u8>> {
        try!(self.seek(-offset, SeekEnd));
        self.read_exact(n)
    }

    #[inline]
    fn read_str(&mut self, n: uint, offset: i64) -> IoResult<String> {
        self.read_from_end(n, offset).map(|vec| extract_nz_88591(vec))
    }
}

/// Checks for presence of the signature indicating an ID3v1 tag at the reader's current offset.
/// Consumes 3 bytes from the reader.
#[inline]
pub fn probe_tag<R: Reader>(reader: &mut R) -> IoResult<bool> {
    let mut x=&mut [0, ..3];
    let _tag = try!(reader.read_at_least(TAG.len(), x[mut]));
    Ok(TAG == x[])
}

/// Checks for presence of the signature indicating an ID3v1 extended metadata tag at the reader's current offset.
/// Consumes 4 bytes from the reader.
#[inline]
pub fn probe_xtag<R: Reader>(reader: &mut R) -> IoResult<bool> {
    let mut x=&mut [0, ..4];
    let _tag = try!(reader.read_at_least(TAGPLUS.len(), x[mut]));
    Ok(TAGPLUS == x[])
}

fn parse_year(s: &[u8]) -> Year {
    let zero = Year::new(0).unwrap();
    match ::std::str::from_utf8(s) {
        Some(st) => {
            let mn: Option<u16> = ::std::str::from_str(st);
            let n = mn.unwrap_or(0);
            Year::new(n).unwrap_or(zero)
        },
        None => zero
    }
}

fn parse_time(s: &[u8]) -> Time {
    enum State {
        Seconds,
        Minutes,
        LeadingWhitespace,
    }

    let zero = Time::new(0).unwrap();

    let mut mult: u64=1;
    let mut seconds: u64=0;
    let mut state=State::Seconds;

    fn is_digit(s: u8) -> bool {
        s >= b'0' && s <= b'9'
    }
    fn value(s: u8) -> u8 {
        s-b'0'
    }
    for &i in s.iter().rev() {
        match state {
            State::Seconds =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=10;
                } else if i == b':' {
                    state=State::Minutes;
                    mult=60;
                } else {
                    return zero;
                },
            State::Minutes =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=60;
                } else if (i as char).is_whitespace() {
                    state=State::LeadingWhitespace;
                },
            State::LeadingWhitespace =>
                if (i as char).is_whitespace() {
                    continue
                } else {
                    return zero;
                },
        }
    }
    if seconds > 65535 {
        zero
    } else {
        Time::new(seconds as u16).unwrap_or(zero)
    }
}

/// Read an ID3v1 tag from a reader.
pub fn read<R: Reader>(reader: &mut R) -> IoResult<Tag> {
    use self::Fields::*;
    macro_rules! maybe_read {
        ($prop:expr, $len:expr) => {
            {
                try!(reader.push($len, &mut $prop));
            }
        };
    }

    let mut tag = Tag::new();
    // Try to read ID3v1 metadata.
    let has_tag = try!(probe_tag(reader));
    if has_tag {
        maybe_read!(tag.title, Title.length());
        maybe_read!(tag.artist, Artist.length());
        maybe_read!(tag.album, Album.length());
        let mut year_str=vec![]; maybe_read!(year_str, Year.length());
        tag.year=parse_year(year_str[]);
        maybe_read!(tag.comment, Comment.length()-2);
        let track_guard_byte = try!(reader.read_u8());
        if track_guard_byte == 0 {
            tag.track=try!(reader.read_u8());
        } else {
            tag.comment.push(track_guard_byte);
            tag.comment.push(try!(reader.read_u8()));
        }
        tag.genre=try!(reader.read_u8());
    }

    Ok(tag)
}

/// Read the extended portion of an extended ID3v1 tag from a reader, combining its data with a previously-read ID3v1 tag.
pub fn read_xtag<R: Reader>(reader: &mut R, tag: &mut Tag) -> IoResult<()> {
    use self::Fields::*;
    use self::XFields::*;
    macro_rules! maybe_read {
        ($prop:expr, $len:expr) => {
            {
                try!(reader.push($len, &mut $prop));
            }
        };
    }

    // Try to read ID3v1 extended metadata.
    let has_xtag = try!(probe_xtag(reader));
    if has_xtag {
        maybe_read!(tag.title, XTitle.length());
        maybe_read!(tag.artist, XArtist.length());
        maybe_read!(tag.album, XAlbum.length());
        tag.speed = try!(reader.read_byte());
        maybe_read!(tag.genre_str, Genre.length());
        let mut start_str=vec![]; maybe_read!(start_str, Start.length());
        tag.start_time=parse_time(start_str[]);
        let mut end_str=vec![]; maybe_read!(end_str, End.length());
        tag.end_time=parse_time(end_str[]);
    }
    Ok(())
}

/// Read a string from a null-terminated ISO-8859-1 byte vector.
///
/// Read the whole vector if there is no null byte.
///
/// This function cannot fail, because UTF-8 is compatible with ISO-8859-1
/// at the code point level.
#[inline]
fn extract_nz_88591(s: Vec<u8>) -> String {
    // This works because the ISO 8859-1 code points match the unicode code
    // points. So,`c as char` will map correctly from ISO to unicode.
    s.into_iter().take_while(|&c| c!=0).map(|c| c as char).collect()
}

/// Remove trailing zeros from an &[u8].
pub fn truncate_zeros(mut s: &[u8]) -> &[u8] {
    while s.len() > 0 && s[s.len()-1] == 0 {
        s=s[..s.len()-1]
    }
    s
}

#[test]
fn smoke_test() {
    let mut f=::std::io::fs::File::open(&Path::new("test.mp3"));
    f.seek(-TAG_OFFSET, SeekEnd);
    let mut tag=read(&mut f).unwrap();
    println!("{}", tag);
    f.seek(-TAGPLUS_OFFSET, SeekEnd);
    read_xtag(&mut f, &mut tag);
    println!("{}", tag);
}
<|endoftext|>


<commit_before>
use sdl::event::{Event, Key};

pub struct InputState {
    pub keys_down: Vec<Key>
}

impl InputState {
    pub fn new() -> InputState {
        InputState {
            keys_down: vec![]
        }
    }

    pub fn check_event(&mut self, event: &Event) {
        match *event {
            Event::Key(key, down, _, _) => {
                if down {
                    self.keys_down.push(key);
                    self.keys_down.dedup();
                } else {
                    self.keys_down.retain(|&k| k != key);
                }
            }
            _ => {}
        }
    }

    pub fn has_key(&self, key: Key) -> bool {
        self.keys_down.iter().find(|&k| *k == key).is_some()
    }
}
<commit_msg>Made InputState.keys_down private<commit_after>
use sdl::event::{Event, Key};

pub struct InputState {
    _keys_down: Vec<Key>
}

impl InputState {
    pub fn new() -> InputState {
        InputState {
            _keys_down: vec![]
        }
    }

    pub fn check_event(&mut self, event: &Event) {
        match *event {
            Event::Key(key, down, _, _) => {
                if down {
                    self._keys_down.push(key);
                    self._keys_down.dedup();
                } else {
                    self._keys_down.retain(|&k| k != key);
                }
            }
            _ => {}
        }
    }

    pub fn has_key(&self, key: Key) -> bool {
        self._keys_down.iter().find(|&k| *k == key).is_some()
    }
}
<|endoftext|>


<commit_before>use csv;
use slog::Logger;
use std::fs::create_dir;
use std::io;
use std::error;
use std::fmt;
use std::path::Path;
use ndarray;
use ndarray::{Array3, Zip};
use ndarray_parallel::prelude::*;
use grid;
use config::Config;

#[derive(Debug,Deserialize)]
/// A simple struct to parse data from a plain csv file
struct PlainRecord {
    /// Index in *x*
    i: usize,
    /// Index in *y*
    j: usize,
    /// Index in *z*
    k: usize,
    /// Data at this position
    data: f64,
}

/// Error type for handling file output. Effectively a wapper around multiple error types we encounter.
#[derive(Debug)]
pub enum Error {
    /// From disk issues.
    Io(io::Error),
    /// From `csv`.
    Csv(csv::Error),
    /// From `ndarray`.
    Shape(ndarray::ShapeError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Csv(ref err) => err.fmt(f),
            Error::Shape(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => err.description(),
            Error::Csv(ref err) => err.description(),
            Error::Shape(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Io(ref err) => Some(err),
            Error::Csv(ref err) => Some(err),
            Error::Shape(ref err) => Some(err),
        }
    }
}

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

impl From<csv::Error> for Error {
    fn from(err: csv::Error) -> Error {
        Error::Csv(err)
    }
}

impl From<ndarray::ShapeError> for Error {
    fn from(err: ndarray::ShapeError) -> Error {
        Error::Shape(err)
    }
}

/// Loads potential file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn potential(target_size: [usize; 3],
                 binary: bool,
                 log: &Logger)
                 -> Result<Array3<f64>, Error> {
    let plain_path = "./input/potential.csv";
    let binary_path = "./input/potential.mpk";
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path.to_string())
    } else {
        None
    };
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path.to_string())
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple potential files found in input directory. Chosing 'potential.{}' based on configuration settings.",
              if binary { "mpk" } else { "csv" });
        if binary {
            potential_plain(plain_file, target_size)
        } else {
            potential_binary(binary_file, target_size)
        }
    } else if plain_file.is_some() {
        potential_plain(plain_file, target_size)
    } else {
        potential_binary(binary_file, target_size)
    }
}

/// Loads a potential from a csv file on disk.
fn potential_plain(file: Option<String>, target_size: [usize; 3]) -> Result<Array3<f64>, Error> {
    //No need for anything more here, just call the general parser.
    parse_csv_to_array3(file, target_size)
}

/// Loads a potential from a mpk file on disk.
fn potential_binary(file: Option<String>, target_size: [usize; 3]) -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, for now call plain
    let _none = file;
    potential_plain(Some("./input/potential.csv".to_string()), target_size)
}

/// Loads previously computed wavefunctions from disk.
pub fn load_wavefunctions(config: &Config,
                          log: &Logger,
                          binary: bool,
                          w_store: &mut Vec<Array3<f64>>)
                          -> Result<(), Error> {
    let num = &config.grid.size;
    let init_size: [usize; 3] = [(num.x + 6) as usize,
                                 (num.y + 6) as usize,
                                 (num.z + 6) as usize];
    // Load required wavefunctions. If the current state resides on disk as well, we load that later.
    for wnum in 0..config.wavenum {
        let wfn = wavefunction(wnum, init_size, binary, log);
        match wfn {
            Ok(w) => w_store.push(w),
            Err(err) => return Err(err),
            //TODO: Probably need to make these errors a litte more expressive in thier format sections. For example:
            //    panic!("Cannot load any wavefunction_{}* file from input folder: {}", wnum, err)
        }
        info!(log, "Loaded (previous) wavefunction {} from disk", wnum);
    }
    Ok(())
}

/// Loads wavefunction file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `wnum` - Excited state level of the wavefunction to load.
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn wavefunction(wnum: u8,
                    target_size: [usize; 3],
                    binary: bool,
                    log: &Logger)
                    -> Result<Array3<f64>, Error> {
    let plain_path = format!("./input/wavefunction_{}.csv", wnum);
    let plain_path_partial = format!("./input/wavefunction_{}_partial.csv", wnum);
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path)
    } else if Path::new(&plain_path_partial).exists() {
        Some(plain_path_partial)
    } else {
        None
    };

    let binary_path = format!("./input/wavefunction_{}.mpk", wnum);
    let binary_path_partial = format!("./input/wavefunction_{}_partial.mpk", wnum);
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path)
    } else if Path::new(&binary_path_partial).exists() {
        Some(binary_path_partial)
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple wavefunction_{} files found in input directory. Chosing '{}' version based on configuration settings.",
              wnum,
              if binary { "mpk" } else { "csv" });
        if binary {
            wavefunction_plain(plain_file, wnum, target_size)
        } else {
            wavefunction_binary(binary_file, wnum, target_size)
        }
    } else if plain_file.is_some() {
        wavefunction_plain(plain_file, wnum, target_size)
    } else {
        wavefunction_binary(binary_file, wnum, target_size)
    }
}

/// Loads a wafefunction from a csv file on disk.
fn wavefunction_plain(file: Option<String>,
                      wnum: u8,
                      target_size: [usize; 3])
                      -> Result<Array3<f64>, Error> {
    //No more to add here, just parse the file in the generic parser.
    let _none = wnum;
    parse_csv_to_array3(file, target_size)
}

/// Loads a wafefunction from a mpk file on disk.
fn wavefunction_binary(file: Option<String>,
                       wnum: u8,
                       target_size: [usize; 3])
                       -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, call plain
    //NOTE: This will guarentee a failure from the file name.
    wavefunction_plain(file, wnum, target_size)
}

/// Checks that the folder `input` exists. If not, creates it.
/// This doesn't specifically need to happen for all instances,
/// but we may want to put restart values in there later on.
pub fn check_input_dir() -> Result<(), Error> {
    //std::io::Error
    if !Path::new("./input").exists() {
        create_dir("./input")?;
    }
    Ok(())
}

/// Given a filename, this funtion reads in the data of a csv file and parses
/// the values into a 3D array. There are a few caveats to this as the file
/// may be of a different shape to the requested size in the configuration file.
/// The routine therefore attempts to resample/interpolate the data to fit the required
/// parameters.
///
/// # Arguments
///
/// * `file` - A filename wrapped in an option. This function is called from filename parsers
/// which may not be able to obtain a valid location.
/// * `target_size` - Requsted size of the resultant array. If this size does not match the data
/// pulled from the file, interpolation or resampling will occur.
///
/// # Returns
///
/// * A 3D array loaded with data from the file and resampled/interpolated if required.
/// If something goes wrong in the parsing or file handling, a `csv::Error` is passed.
fn parse_csv_to_array3(file: Option<String>,
                       target_size: [usize; 3])
                       -> Result<Array3<f64>, Error> {
    match file {
        Some(f) => {
            let mut rdr = csv::ReaderBuilder::new().has_headers(false).from_path(f)?;
            let mut max_i = 0;
            let mut max_j = 0;
            let mut max_k = 0;
            let mut data: Vec<f64> = Vec::new();
            for result in rdr.deserialize() {
                let record: PlainRecord = result?;
                if record.i > max_i {
                    max_i = record.i
                };
                if record.j > max_j {
                    max_j = record.j
                };
                if record.k > max_k {
                    max_k = record.k
                };
                data.push(record.data);
            }
            let numx = max_i + 1;
            let numy = max_j + 1;
            let numz = max_k + 1;
            match Array3::<f64>::from_shape_vec((numx, numy, numz), data) {
                Ok(result) => {
                    //result is now a parsed Array3 with the work area inside.
                    //We must fill this into an array with CD boundaries, provided
                    //it is the correct size. If not, we must scale it.
                    let init_size: [usize; 3] = [numx + 6, numy + 6, numz + 6];
                    let mut complete = Array3::<f64>::zeros(target_size);
                    {
                        let mut work = grid::get_mut_work_area(&mut complete);
                        let same: bool = init_size
                            .iter()
                            .zip(target_size.iter())
                            .all(|(a, b)| a == b);
                        let smaller: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a < b);
                        let larger: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a > b);
                        if same {
                            // Input is the same size, copy down.
                            Zip::from(&mut work)
                                .and(result.view())
                                .par_apply(|work, &result| *work = result);
                        } else if smaller {
                            //TODO: Input has lower resolution. Spread it out.
                            panic!("Wavefunction is lower in resolution than requested");
                        } else if larger {
                            //TODO: Input has higer resolution. Sample it.
                            panic!("Wavefunction is higher in resolution than requested");
                        } else {
                            //TODO: Dimensons are all over the shop. Sample and interp
                            panic!("Wavefunction differs in resolution from requested");
                        }
                    }
                    Ok(complete)
                }
                Err(err) => Err(Error::Shape(err)),
            }
        }
        None => Err(Error::Io(io::Error::from(io::ErrorKind::NotFound))),
    }
}
<commit_msg>wnum not needed in binary/plain calls<commit_after>use csv;
use slog::Logger;
use std::fs::create_dir;
use std::io;
use std::error;
use std::fmt;
use std::path::Path;
use ndarray;
use ndarray::{Array3, Zip};
use ndarray_parallel::prelude::*;
use grid;
use config::Config;

#[derive(Debug,Deserialize)]
/// A simple struct to parse data from a plain csv file
struct PlainRecord {
    /// Index in *x*
    i: usize,
    /// Index in *y*
    j: usize,
    /// Index in *z*
    k: usize,
    /// Data at this position
    data: f64,
}

/// Error type for handling file output. Effectively a wapper around multiple error types we encounter.
#[derive(Debug)]
pub enum Error {
    /// From disk issues.
    Io(io::Error),
    /// From `csv`.
    Csv(csv::Error),
    /// From `ndarray`.
    Shape(ndarray::ShapeError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Csv(ref err) => err.fmt(f),
            Error::Shape(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => err.description(),
            Error::Csv(ref err) => err.description(),
            Error::Shape(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Io(ref err) => Some(err),
            Error::Csv(ref err) => Some(err),
            Error::Shape(ref err) => Some(err),
        }
    }
}

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

impl From<csv::Error> for Error {
    fn from(err: csv::Error) -> Error {
        Error::Csv(err)
    }
}

impl From<ndarray::ShapeError> for Error {
    fn from(err: ndarray::ShapeError) -> Error {
        Error::Shape(err)
    }
}

/// Loads potential file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn potential(target_size: [usize; 3],
                 binary: bool,
                 log: &Logger)
                 -> Result<Array3<f64>, Error> {
    let plain_path = "./input/potential.csv";
    let binary_path = "./input/potential.mpk";
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path.to_string())
    } else {
        None
    };
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path.to_string())
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple potential files found in input directory. Chosing 'potential.{}' based on configuration settings.",
              if binary { "mpk" } else { "csv" });
        if binary {
            potential_plain(plain_file, target_size)
        } else {
            potential_binary(binary_file, target_size)
        }
    } else if plain_file.is_some() {
        potential_plain(plain_file, target_size)
    } else {
        potential_binary(binary_file, target_size)
    }
}

/// Loads a potential from a csv file on disk.
fn potential_plain(file: Option<String>, target_size: [usize; 3]) -> Result<Array3<f64>, Error> {
    //No need for anything more here, just call the general parser.
    parse_csv_to_array3(file, target_size)
}

/// Loads a potential from a mpk file on disk.
fn potential_binary(file: Option<String>, target_size: [usize; 3]) -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, for now call plain
    let _none = file;
    potential_plain(Some("./input/potential.csv".to_string()), target_size)
}

/// Loads previously computed wavefunctions from disk.
pub fn load_wavefunctions(config: &Config,
                          log: &Logger,
                          binary: bool,
                          w_store: &mut Vec<Array3<f64>>)
                          -> Result<(), Error> {
    let num = &config.grid.size;
    let init_size: [usize; 3] = [(num.x + 6) as usize,
                                 (num.y + 6) as usize,
                                 (num.z + 6) as usize];
    // Load required wavefunctions. If the current state resides on disk as well, we load that later.
    for wnum in 0..config.wavenum {
        let wfn = wavefunction(wnum, init_size, binary, log);
        match wfn {
            Ok(w) => w_store.push(w),
            Err(err) => return Err(err),
            //TODO: Probably need to make these errors a litte more expressive in thier format sections. For example:
            //    panic!("Cannot load any wavefunction_{}* file from input folder: {}", wnum, err)
        }
        info!(log, "Loaded (previous) wavefunction {} from disk", wnum);
    }
    Ok(())
}

/// Loads wavefunction file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `wnum` - Excited state level of the wavefunction to load.
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn wavefunction(wnum: u8,
                    target_size: [usize; 3],
                    binary: bool,
                    log: &Logger)
                    -> Result<Array3<f64>, Error> {
    let plain_path = format!("./input/wavefunction_{}.csv", wnum);
    let plain_path_partial = format!("./input/wavefunction_{}_partial.csv", wnum);
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path)
    } else if Path::new(&plain_path_partial).exists() {
        Some(plain_path_partial)
    } else {
        None
    };

    let binary_path = format!("./input/wavefunction_{}.mpk", wnum);
    let binary_path_partial = format!("./input/wavefunction_{}_partial.mpk", wnum);
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path)
    } else if Path::new(&binary_path_partial).exists() {
        Some(binary_path_partial)
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple wavefunction_{} files found in input directory. Chosing '{}' version based on configuration settings.",
              wnum,
              if binary { "mpk" } else { "csv" });
        if binary {
            wavefunction_plain(plain_file, target_size)
        } else {
            wavefunction_binary(binary_file, target_size)
        }
    } else if plain_file.is_some() {
        wavefunction_plain(plain_file, target_size)
    } else {
        wavefunction_binary(binary_file, target_size)
    }
}

/// Loads a wafefunction from a csv file on disk.
fn wavefunction_plain(file: Option<String>,
                      target_size: [usize; 3])
                      -> Result<Array3<f64>, Error> {
    //No more to add here, just parse the file in the generic parser.
    parse_csv_to_array3(file, target_size)
}

/// Loads a wafefunction from a mpk file on disk.
fn wavefunction_binary(file: Option<String>,
                       target_size: [usize; 3])
                       -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, call plain
    //NOTE: This will guarentee a failure from the file name.
    wavefunction_plain(file, target_size)
}

/// Checks that the folder `input` exists. If not, creates it.
/// This doesn't specifically need to happen for all instances,
/// but we may want to put restart values in there later on.
pub fn check_input_dir() -> Result<(), Error> {
    //std::io::Error
    if !Path::new("./input").exists() {
        create_dir("./input")?;
    }
    Ok(())
}

/// Given a filename, this funtion reads in the data of a csv file and parses
/// the values into a 3D array. There are a few caveats to this as the file
/// may be of a different shape to the requested size in the configuration file.
/// The routine therefore attempts to resample/interpolate the data to fit the required
/// parameters.
///
/// # Arguments
///
/// * `file` - A filename wrapped in an option. This function is called from filename parsers
/// which may not be able to obtain a valid location.
/// * `target_size` - Requsted size of the resultant array. If this size does not match the data
/// pulled from the file, interpolation or resampling will occur.
///
/// # Returns
///
/// * A 3D array loaded with data from the file and resampled/interpolated if required.
/// If something goes wrong in the parsing or file handling, a `csv::Error` is passed.
fn parse_csv_to_array3(file: Option<String>,
                       target_size: [usize; 3])
                       -> Result<Array3<f64>, Error> {
    match file {
        Some(f) => {
            let mut rdr = csv::ReaderBuilder::new().has_headers(false).from_path(f)?;
            let mut max_i = 0;
            let mut max_j = 0;
            let mut max_k = 0;
            let mut data: Vec<f64> = Vec::new();
            for result in rdr.deserialize() {
                let record: PlainRecord = result?;
                if record.i > max_i {
                    max_i = record.i
                };
                if record.j > max_j {
                    max_j = record.j
                };
                if record.k > max_k {
                    max_k = record.k
                };
                data.push(record.data);
            }
            let numx = max_i + 1;
            let numy = max_j + 1;
            let numz = max_k + 1;
            match Array3::<f64>::from_shape_vec((numx, numy, numz), data) {
                Ok(result) => {
                    //result is now a parsed Array3 with the work area inside.
                    //We must fill this into an array with CD boundaries, provided
                    //it is the correct size. If not, we must scale it.
                    let init_size: [usize; 3] = [numx + 6, numy + 6, numz + 6];
                    let mut complete = Array3::<f64>::zeros(target_size);
                    {
                        let mut work = grid::get_mut_work_area(&mut complete);
                        let same: bool = init_size
                            .iter()
                            .zip(target_size.iter())
                            .all(|(a, b)| a == b);
                        let smaller: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a < b);
                        let larger: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a > b);
                        if same {
                            // Input is the same size, copy down.
                            Zip::from(&mut work)
                                .and(result.view())
                                .par_apply(|work, &result| *work = result);
                        } else if smaller {
                            //TODO: Input has lower resolution. Spread it out.
                            panic!("Wavefunction is lower in resolution than requested");
                        } else if larger {
                            //TODO: Input has higer resolution. Sample it.
                            panic!("Wavefunction is higher in resolution than requested");
                        } else {
                            //TODO: Dimensons are all over the shop. Sample and interp
                            panic!("Wavefunction differs in resolution from requested");
                        }
                    }
                    Ok(complete)
                }
                Err(err) => Err(Error::Shape(err)),
            }
        }
        None => Err(Error::Io(io::Error::from(io::ErrorKind::NotFound))),
    }
}
<|endoftext|>


<commit_before>use super::*;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Crate {
    attrs: Vec<Attribute>,
    items: Vec<Item>,
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use attr::parsing::inner_attr;
    use space::whitespace;
    use item::parsing::item;

    named!(pub krate -> Crate, do_parse!(
        attrs: many0!(inner_attr) >>
        items: many0!(item) >>
        option!(whitespace) >>
        (Crate {
            attrs: attrs,
            items: items,
        })
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use attr::FilterAttrs;
    use quote::{Tokens, ToTokens};

    impl ToTokens for Crate {
        fn to_tokens(&self, tokens: &mut Tokens) {
            for attr in self.attrs.inner() {
                attr.to_tokens(tokens);
            }
            for item in &self.items {
                item.to_tokens(tokens);
            }
        }
    }
}
<commit_msg>Add shebang to Crate struct without parsing it yet<commit_after>use super::*;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Crate {
    shebang: Option<String>,
    attrs: Vec<Attribute>,
    items: Vec<Item>,
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use attr::parsing::inner_attr;
    use space::whitespace;
    use item::parsing::item;

    named!(pub krate -> Crate, do_parse!(
        attrs: many0!(inner_attr) >>
        items: many0!(item) >>
        option!(whitespace) >>
        (Crate {
            shebang: None,
            attrs: attrs,
            items: items,
        })
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use attr::FilterAttrs;
    use quote::{Tokens, ToTokens};

    impl ToTokens for Crate {
        fn to_tokens(&self, tokens: &mut Tokens) {
            for attr in self.attrs.inner() {
                attr.to_tokens(tokens);
            }
            for item in &self.items {
                item.to_tokens(tokens);
            }
        }
    }
}
<|endoftext|>