<commit_before>//============================================================================
//
// A simple Mandelbrot image generator in Rust
//
// Copyright (c) 2014 Gavin Baker <gavinb@antonym.org>
// Published under the MIT license
//
// Packages required:
//
// - OpenGLES
// - GLFW
// - cgmath
//
//============================================================================

#[license = "MIT"];

#[allow(deprecated_owned_vector)];
#[allow(dead_code)];

extern crate native;
extern crate num;

extern crate opengles;
extern crate glfw;
extern crate cgmath;

use opengles::gl2;

use cgmath::vector::Vec3;
use cgmath::aabb::Aabb3;

use std::comm::{Sender, Receiver, channel};
use std::vec_ng::Vec;
use std::io::File;
use std::path::Path;

use num::complex::{Cmplx, Complex64};

use std::num::{sin};

type Vec3f = Vec3<f32>;
type AABB3f = Aabb3<f32>;

//----------------------------------------------------------------------------

static vertex_shader_source: &'static str = "

#version 150

in vec2 position;
in vec3 color;

out vec3 Color;

void main()
{
    Color = color;
    gl_Position = vec4(position, 0.0, 1.0);
}
";

static fragment_shader_source: &'static str = "

#version 150

in vec3 Color;
out vec4 outColor;

void main()
{
    outColor = vec4(Color, 1.0);
}
";

//----------------------------------------------------------------------------

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
    fn call(&self, _: glfw::Error, description: ~str) {
        println!("GLFW Error: {:s}", description);
    }
}

//----------------------------------------------------------------------------

struct WindowController<'a> {
    window: &'a glfw::Window,
    vertices: ~[f32],
    vao: ~[gl2::GLuint],
    vbo: ~[gl2::GLuint],
    vertex_shader: gl2::GLuint,
    fragment_shader: gl2::GLuint,
    shader_program: gl2::GLuint,
    uni_color: gl2::GLint,
    buffer_width: uint,
    buffer_height: uint,
}

impl<'a> WindowController<'a> {
    fn new(window: &'a glfw::Window) -> WindowController<'a> {
        let mut wc = WindowController { window: window,
                                        vao: ~[0],
                                        vbo: ~[0],
                                        vertices: ~[],
                                        vertex_shader: 0,
                                        fragment_shader: 0,
                                        shader_program: 0,
                                        uni_color: 0,
                                        buffer_width: 0,
                                        buffer_height: 0,
        };

        let (w, h) =  wc.window.get_framebuffer_size();
        wc.buffer_width = w as uint;
        wc.buffer_height = h as uint;

        gl2::viewport(0, 0, wc.buffer_width as i32, wc.buffer_height as i32);

        println!("Viewport: {} x {}", wc.buffer_width, wc.buffer_height);

        wc.vertices = ~[
            0.0,  0.5, 1.0, 0.0, 0.0,
            0.5, -0.5, 0.0, 1.0, 0.0,
            -0.5, -0.5, 0.0, 0.0, 1.0,
            ];

        // VAO

        wc.vao = gl2::gen_vertex_arrays(1);
        gl2::bind_vertex_array(wc.vao[0]);

        // VBO

        wc.vbo = gl2::gen_buffers(1);
        gl2::bind_buffer(gl2::ARRAY_BUFFER, wc.vbo[0]);
        gl2::buffer_data(gl2::ARRAY_BUFFER, wc.vertices, gl2::STATIC_DRAW);

        // Vertex Shader

        wc.vertex_shader = gl2::create_shader(gl2::VERTEX_SHADER);

        if wc.vertex_shader == 0 {
            fail!("Create v.shader failed");
        }

        gl2::shader_source(wc.vertex_shader, [vertex_shader_source.to_owned().as_bytes()]);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glShaderSource.v err 0x{:x}", err);
        }

        gl2::compile_shader(wc.vertex_shader);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glCompileShader.f err 0x{:x}", err);
        }

        let status = gl2::get_shader_iv(wc.vertex_shader, gl2::COMPILE_STATUS);

        if status != gl2::TRUE as i32 {
            let log = gl2::get_shader_info_log(wc.vertex_shader);
            fail!("glCompileShader.v err 0x{:x}: {}", status, log);
        }

        // Fragment Shader

        wc.fragment_shader = gl2::create_shader(gl2::FRAGMENT_SHADER);

        if wc.vertex_shader == 0 {
            fail!("Create f.shader failed");
        }

        gl2::shader_source(wc.fragment_shader, [fragment_shader_source.to_owned().as_bytes()]);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glShaderSource.f -> 0x{:x}", err);
        }

        gl2::compile_shader(wc.fragment_shader);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glCompileShader.v err 0x{:x}", err);
        }

        let status = gl2::get_shader_iv(wc.fragment_shader, gl2::COMPILE_STATUS);

        if status != gl2::TRUE as i32 {
            let log = gl2::get_shader_info_log(wc.fragment_shader);
            fail!("glCompileShader.f err 0x{:x}: {}", status, log);
        }

        // Link

        wc.shader_program = gl2::create_program();

        if wc.shader_program == 0 {
            fail!("glCreateProgram failed");
        }

        gl2::attach_shader(wc.shader_program, wc.vertex_shader);
        gl2::attach_shader(wc.shader_program, wc.fragment_shader);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glAttachShader err 0x{:x}", err);
        }

        gl2::link_program(wc.shader_program);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glLinkProgram err 0x{:x}", err);
        }

        let status = gl2::get_program_iv(wc.shader_program, gl2::LINK_STATUS);

        if status != gl2::TRUE as i32 {
            let log = gl2::get_shader_info_log(wc.shader_program);
            fail!("glLinkProgram err {}: {}", status, log);
        }

        gl2::use_program(wc.shader_program);

        let err = gl2::get_error();
        if err != 0 {
            let log = gl2::get_program_info_log(wc.shader_program);
            fail!("glUseProgram error: {}", log);
        }

        // Attributes

        let pos_attrib = gl2::get_attrib_location(wc.shader_program, "position");
        gl2::enable_vertex_attrib_array(pos_attrib as gl2::GLuint);
        gl2::vertex_attrib_pointer_f32(pos_attrib as gl2::GLuint, 2, false, 5*4, 0);

        let col_attrib = gl2::get_attrib_location(wc.shader_program, "color");
        gl2::enable_vertex_attrib_array(col_attrib as gl2::GLuint);
        gl2::vertex_attrib_pointer_f32(col_attrib as gl2::GLuint, 3, false, 5*4, 2*4);

        // Uniforms

        wc.uni_color = gl2::get_uniform_location(wc.shader_program, "triangleColor");

        let err = gl2::get_error();
        if err != 0 {
            fail!("attrib err = {:x}", err);
        }

        wc
    }

    // Rescale pixel coord (x,y) into cspace
    fn scale_coords(&self, x: uint, y: uint) -> (f32, f32) {
        let x0 = -2.5f32;
        let x1 =  1.0f32;
        let y0 = -1.0f32;
        let y1 =  1.0f32;

        let xx = (x as f32) / (self.buffer_width  as f32) * (x1-x0) + x0;
        let yy = (y as f32) / (self.buffer_height as f32) * (y1-y0) + y0;

        (xx, yy)
    }

    fn draw(&self) {

        // Clear

        gl2::clear_color(0.4, 0.4, 0.4, 1.0);
        gl2::clear(gl2::COLOR_BUFFER_BIT);

        // Animate

        let time = glfw::get_time();
        gl2::uniform_3f(self.uni_color, (sin(time * 4.0f32 as f64) as f32 + 1.0f32) / 2.0f32, 0.0f32, 0.0f32);

        // Draw!

        gl2::draw_arrays(gl2::TRIANGLES, 0, 3);

        let err = gl2::get_error();
        if err != 0 {
            println!("draw err = 0x{:x}", err);
        }

        self.window.swap_buffers();
    }

    fn uninit(&mut self) {

        // Cleanup

        gl2::delete_program(self.shader_program);
        gl2::delete_shader(self.fragment_shader);
        gl2::delete_shader(self.vertex_shader);

        gl2::delete_buffers(self.vbo);

        // @todo Missing from bindings
        //gl2::delete_vertex_arrays(vao);
    }

    fn handle_window_event(&self, window: &glfw::Window, (time, event): (f64, glfw::WindowEvent)) {
        match event {

            glfw::CloseEvent => println!("Time: {}, Window close requested.", time),

            glfw::KeyEvent(key, scancode, action, mods) => {
                println!("Time: {}, Key: {}, ScanCode: {}, Action: {}, Modifiers: [{}]", time, key, scancode, action, mods);
                match (key, action) {
                    (glfw::KeyEscape, glfw::Press) => window.set_should_close(true),
                    (glfw::KeyR, glfw::Press) => {
                        // Resize should cause the window to "refresh"
                        let (window_width, window_height) = window.get_size();
                        window.set_size(window_width + 1, window_height);
                        window.set_size(window_width, window_height);
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

//----------------------------------------------------------------------------

struct MandelEngine {
    buffer_width: uint,
    buffer_height: uint,
    buffer: Vec<u8>,// RGBA
}

impl MandelEngine {

    fn new(w: uint, h: uint) -> MandelEngine {
        MandelEngine {
            buffer_width: w,
            buffer_height: h,
            buffer: Vec::with_capacity(w*h), // Monochrome, 8-bit
        }
    }

    // Rescale pixel coord (x,y) into cspace
    fn scale_coords(&self, x: uint, y: uint) -> (f32, f32) {
        let x0 = -2.5f32;
        let x1 =  1.0f32;
        let y0 = -1.0f32;
        let y1 =  1.0f32;

        let xx = (x as f32) / (self.buffer_width  as f32) * (x1-x0) + x0;
        let yy = (y as f32) / (self.buffer_height as f32) * (y1-y0) + y0;

        (xx, yy)
    }

    // Evalute entire region
    fn process(&mut self) {

        let max_iteration = 255;

        println!("+++ process in {} bytes", self.buffer.capacity());

        // Process each pixel
        for py in range(0, self.buffer_height) {
            for px in range(0, self.buffer_width) {

                // Project pixels into Mandelbrot domain
                let (x0, y0) = self.scale_coords(px, py);

                //println!("({},{}) -> ({}, {})", px, py, x0, y0);

                let mut x = 0.0f32;
                let mut y = 0.0f32;
                let mut iteration = 0;

                // Iterate!
                while (x*x + y*y < 4.0) && (iteration < max_iteration) {
                    let x1 = x*x - y*y + x0;
                    y = 2.0*x*y + y0;
                    x = x1;

                    iteration += 1;
                }

                // Colour and plot
                //color = palette[iteration];
                let color = iteration;
                let ofs = px+self.buffer_width*py;
//                *self.buffer.get_mut(ofs) = color;
                self.buffer.push(color);
            }
        }

        // Save
        let filename = "m1.pgm";
        let mut file = File::create(&Path::new(filename));
        file.write(bytes!("P5\n"));
        file.write_str(format!("{} {}\n255\n", self.buffer_width, self.buffer_height));
        file.write(self.buffer.slice(0, self.buffer.capacity()));
    }

    // Evaluate a single point
    fn mandel(&self, z: Complex64) -> uint {
        let maxiter: uint = 80;
        let mut c: Complex64 = z;
        for n in range(0, maxiter) {
            if c.norm() > 2.0 {
                return n;
            }
            c = c*c+z;
        }
        return maxiter;
    }
}

//----------------------------------------------------------------------------

fn start_engine(tx: &Sender<uint>) {
    let mut engine = MandelEngine::new(1920, 960);
    engine.process();
//    tx.send(42);
}

//----------------------------------------------------------------------------

#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, main)
}

fn main() {

    println!("GLFW version: {:s}", glfw::get_version_string());

    glfw::start(proc() {

        glfw::window_hint::context_version_major(3);
        glfw::window_hint::context_version_minor(2);
        glfw::window_hint::opengl_forward_compat(true);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::resizable(false);

        let window = glfw::Window::create(640, 480,
                                          "MandelRust",
                                          glfw::Windowed)
            .expect("Failed to create GLFW window.");

        window.make_context_current();

        let win_ctrl = ~WindowController::new(&window);

        let (tx, rx): (Sender<uint>, Receiver<uint>) = channel();

        native::task::spawn( proc() {
            start_engine(&tx);
        });

//         let indata = rx.recv();
//         println!("indata {}", indata);

        while !window.should_close() {
            glfw::poll_events();
            for event in window.flush_events() {
                win_ctrl.handle_window_event(&window, event);
            }
            win_ctrl.draw();
        }
    });
}
<commit_msg>Add simple colour palette as iteration LUT<commit_after>//============================================================================
//
// A simple Mandelbrot image generator in Rust
//
// Copyright (c) 2014 Gavin Baker <gavinb@antonym.org>
// Published under the MIT license
//
// Packages required:
//
// - OpenGLES
// - GLFW
// - cgmath
//
//============================================================================

#[license = "MIT"];

#[allow(deprecated_owned_vector)];
#[allow(dead_code)];

extern crate native;
extern crate num;

extern crate opengles;
extern crate glfw;
extern crate cgmath;

use opengles::gl2;

use cgmath::vector::Vec3;
use cgmath::aabb::Aabb3;

use std::comm::{Sender, Receiver, channel};
use std::vec_ng::Vec;
use std::io::File;
use std::path::Path;

use num::complex::{Cmplx, Complex64};

use std::num::{sin};

type Vec3f = Vec3<f32>;
type AABB3f = Aabb3<f32>;

//----------------------------------------------------------------------------

static vertex_shader_source: &'static str = "

#version 150

in vec2 position;
in vec3 color;

out vec3 Color;

void main()
{
    Color = color;
    gl_Position = vec4(position, 0.0, 1.0);
}
";

static fragment_shader_source: &'static str = "

#version 150

in vec3 Color;
out vec4 outColor;

void main()
{
    outColor = vec4(Color, 1.0);
}
";

//----------------------------------------------------------------------------

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
    fn call(&self, _: glfw::Error, description: ~str) {
        println!("GLFW Error: {:s}", description);
    }
}

//----------------------------------------------------------------------------

struct WindowController<'a> {
    window: &'a glfw::Window,
    vertices: ~[f32],
    vao: ~[gl2::GLuint],
    vbo: ~[gl2::GLuint],
    vertex_shader: gl2::GLuint,
    fragment_shader: gl2::GLuint,
    shader_program: gl2::GLuint,
    uni_color: gl2::GLint,
    buffer_width: uint,
    buffer_height: uint,
}

impl<'a> WindowController<'a> {
    fn new(window: &'a glfw::Window) -> WindowController<'a> {
        let mut wc = WindowController { window: window,
                                        vao: ~[0],
                                        vbo: ~[0],
                                        vertices: ~[],
                                        vertex_shader: 0,
                                        fragment_shader: 0,
                                        shader_program: 0,
                                        uni_color: 0,
                                        buffer_width: 0,
                                        buffer_height: 0,
        };

        let (w, h) =  wc.window.get_framebuffer_size();
        wc.buffer_width = w as uint;
        wc.buffer_height = h as uint;

        gl2::viewport(0, 0, wc.buffer_width as i32, wc.buffer_height as i32);

        println!("Viewport: {} x {}", wc.buffer_width, wc.buffer_height);

        wc.vertices = ~[
            0.0,  0.5, 1.0, 0.0, 0.0,
            0.5, -0.5, 0.0, 1.0, 0.0,
            -0.5, -0.5, 0.0, 0.0, 1.0,
            ];

        // VAO

        wc.vao = gl2::gen_vertex_arrays(1);
        gl2::bind_vertex_array(wc.vao[0]);

        // VBO

        wc.vbo = gl2::gen_buffers(1);
        gl2::bind_buffer(gl2::ARRAY_BUFFER, wc.vbo[0]);
        gl2::buffer_data(gl2::ARRAY_BUFFER, wc.vertices, gl2::STATIC_DRAW);

        // Vertex Shader

        wc.vertex_shader = gl2::create_shader(gl2::VERTEX_SHADER);

        if wc.vertex_shader == 0 {
            fail!("Create v.shader failed");
        }

        gl2::shader_source(wc.vertex_shader, [vertex_shader_source.to_owned().as_bytes()]);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glShaderSource.v err 0x{:x}", err);
        }

        gl2::compile_shader(wc.vertex_shader);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glCompileShader.f err 0x{:x}", err);
        }

        let status = gl2::get_shader_iv(wc.vertex_shader, gl2::COMPILE_STATUS);

        if status != gl2::TRUE as i32 {
            let log = gl2::get_shader_info_log(wc.vertex_shader);
            fail!("glCompileShader.v err 0x{:x}: {}", status, log);
        }

        // Fragment Shader

        wc.fragment_shader = gl2::create_shader(gl2::FRAGMENT_SHADER);

        if wc.vertex_shader == 0 {
            fail!("Create f.shader failed");
        }

        gl2::shader_source(wc.fragment_shader, [fragment_shader_source.to_owned().as_bytes()]);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glShaderSource.f -> 0x{:x}", err);
        }

        gl2::compile_shader(wc.fragment_shader);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glCompileShader.v err 0x{:x}", err);
        }

        let status = gl2::get_shader_iv(wc.fragment_shader, gl2::COMPILE_STATUS);

        if status != gl2::TRUE as i32 {
            let log = gl2::get_shader_info_log(wc.fragment_shader);
            fail!("glCompileShader.f err 0x{:x}: {}", status, log);
        }

        // Link

        wc.shader_program = gl2::create_program();

        if wc.shader_program == 0 {
            fail!("glCreateProgram failed");
        }

        gl2::attach_shader(wc.shader_program, wc.vertex_shader);
        gl2::attach_shader(wc.shader_program, wc.fragment_shader);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glAttachShader err 0x{:x}", err);
        }

        gl2::link_program(wc.shader_program);

        let err = gl2::get_error();
        if err != 0 {
            fail!("glLinkProgram err 0x{:x}", err);
        }

        let status = gl2::get_program_iv(wc.shader_program, gl2::LINK_STATUS);

        if status != gl2::TRUE as i32 {
            let log = gl2::get_shader_info_log(wc.shader_program);
            fail!("glLinkProgram err {}: {}", status, log);
        }

        gl2::use_program(wc.shader_program);

        let err = gl2::get_error();
        if err != 0 {
            let log = gl2::get_program_info_log(wc.shader_program);
            fail!("glUseProgram error: {}", log);
        }

        // Attributes

        let pos_attrib = gl2::get_attrib_location(wc.shader_program, "position");
        gl2::enable_vertex_attrib_array(pos_attrib as gl2::GLuint);
        gl2::vertex_attrib_pointer_f32(pos_attrib as gl2::GLuint, 2, false, 5*4, 0);

        let col_attrib = gl2::get_attrib_location(wc.shader_program, "color");
        gl2::enable_vertex_attrib_array(col_attrib as gl2::GLuint);
        gl2::vertex_attrib_pointer_f32(col_attrib as gl2::GLuint, 3, false, 5*4, 2*4);

        // Uniforms

        wc.uni_color = gl2::get_uniform_location(wc.shader_program, "triangleColor");

        let err = gl2::get_error();
        if err != 0 {
            fail!("attrib err = {:x}", err);
        }

        wc
    }

    // Rescale pixel coord (x,y) into cspace
    fn scale_coords(&self, x: uint, y: uint) -> (f32, f32) {
        let x0 = -2.5f32;
        let x1 =  1.0f32;
        let y0 = -1.0f32;
        let y1 =  1.0f32;

        let xx = (x as f32) / (self.buffer_width  as f32) * (x1-x0) + x0;
        let yy = (y as f32) / (self.buffer_height as f32) * (y1-y0) + y0;

        (xx, yy)
    }

    fn draw(&self) {

        // Clear

        gl2::clear_color(0.4, 0.4, 0.4, 1.0);
        gl2::clear(gl2::COLOR_BUFFER_BIT);

        // Animate

        let time = glfw::get_time();
        gl2::uniform_3f(self.uni_color, (sin(time * 4.0f32 as f64) as f32 + 1.0f32) / 2.0f32, 0.0f32, 0.0f32);

        // Draw!

        gl2::draw_arrays(gl2::TRIANGLES, 0, 3);

        let err = gl2::get_error();
        if err != 0 {
            println!("draw err = 0x{:x}", err);
        }

        self.window.swap_buffers();
    }

    fn uninit(&mut self) {

        // Cleanup

        gl2::delete_program(self.shader_program);
        gl2::delete_shader(self.fragment_shader);
        gl2::delete_shader(self.vertex_shader);

        gl2::delete_buffers(self.vbo);

        // @todo Missing from bindings
        //gl2::delete_vertex_arrays(vao);
    }

    fn handle_window_event(&self, window: &glfw::Window, (time, event): (f64, glfw::WindowEvent)) {
        match event {

            glfw::CloseEvent => println!("Time: {}, Window close requested.", time),

            glfw::KeyEvent(key, scancode, action, mods) => {
                println!("Time: {}, Key: {}, ScanCode: {}, Action: {}, Modifiers: [{}]", time, key, scancode, action, mods);
                match (key, action) {
                    (glfw::KeyEscape, glfw::Press) => window.set_should_close(true),
                    (glfw::KeyR, glfw::Press) => {
                        // Resize should cause the window to "refresh"
                        let (window_width, window_height) = window.get_size();
                        window.set_size(window_width + 1, window_height);
                        window.set_size(window_width, window_height);
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

//----------------------------------------------------------------------------

type RGB8 = (u8, u8, u8);

struct MandelEngine {
    buffer_width: uint,
    buffer_height: uint,
    buffer: Vec<u8>,// RGBA
    palette: Vec<RGB8>,
}

impl MandelEngine {

    fn new(w: uint, h: uint) -> MandelEngine {

        // Init palette
        let mut p: Vec<RGB8> = Vec::with_capacity(256*3);
        for r in range(0, 256) {
            p.push((r as u8,0,0));
            println!("{}", r);
        }
        for g in range(0, 256) {
            p.push((0,g as u8,0));
        }
        for b in range(0, 255) {
            p.push((0,0,b as u8));
        }

        MandelEngine {
            buffer_width: w,
            buffer_height: h,
            buffer: Vec::with_capacity(w*h*3), // RGB 8-bit
            palette: p
        }
    }

    // Rescale pixel coord (x,y) into cspace
    fn scale_coords(&self, x: uint, y: uint) -> (f32, f32) {
        let x0 = -2.5f32;
        let x1 =  1.0f32;
        let y0 = -1.0f32;
        let y1 =  1.0f32;

        let xx = (x as f32) / (self.buffer_width  as f32) * (x1-x0) + x0;
        let yy = (y as f32) / (self.buffer_height as f32) * (y1-y0) + y0;

        (xx, yy)
    }

    // Evalute entire region
    fn process(&mut self) {

        let max_iteration = 1024;

        println!("+++ process in {} bytes", self.buffer.capacity());

        // Process each pixel
        for py in range(0, self.buffer_height) {
            for px in range(0, self.buffer_width) {

                // Project pixels into Mandelbrot domain
                let (x0, y0) = self.scale_coords(px, py);

                //println!("({},{}) -> ({}, {})", px, py, x0, y0);

                let mut x = 0.0f32;
                let mut y = 0.0f32;
                let mut iteration = 0;

                // Iterate!
                while (x*x + y*y < 4.0) && (iteration < max_iteration) {
                    let x1 = x*x - y*y + x0;
                    y = 2.0*x*y + y0;
                    x = x1;

                    iteration += 1;
                }

                // Colour and plot
                let color = self.palette.get(iteration % 765);
                let (r,g,b) = *color;
                let ofs = px+self.buffer_width*py;

                self.buffer.push(r);
                self.buffer.push(g);
                self.buffer.push(b);
            }
        }

        // Save
        let filename = "m1.pgm";
        let mut file = File::create(&Path::new(filename));
        file.write(bytes!("P6\n"));
        file.write_str(format!("{} {}\n255\n", self.buffer_width, self.buffer_height));
        file.write(self.buffer.slice(0, self.buffer.capacity()));
    }

    // Evaluate a single point
    fn mandel(&self, z: Complex64) -> uint {
        let maxiter: uint = 80;
        let mut c: Complex64 = z;
        for n in range(0, maxiter) {
            if c.norm() > 2.0 {
                return n;
            }
            c = c*c+z;
        }
        return maxiter;
    }
}

//----------------------------------------------------------------------------

fn start_engine(tx: &Sender<uint>) {
    let mut engine = MandelEngine::new(1920, 960);
    engine.process();
//    tx.send(42);
}

//----------------------------------------------------------------------------

#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, main)
}

fn main() {

    println!("GLFW version: {:s}", glfw::get_version_string());

    glfw::start(proc() {

        glfw::window_hint::context_version_major(3);
        glfw::window_hint::context_version_minor(2);
        glfw::window_hint::opengl_forward_compat(true);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::resizable(false);

        let window = glfw::Window::create(640, 480,
                                          "MandelRust",
                                          glfw::Windowed)
            .expect("Failed to create GLFW window.");

        window.make_context_current();

        let win_ctrl = ~WindowController::new(&window);

        let (tx, rx): (Sender<uint>, Receiver<uint>) = channel();

        native::task::spawn( proc() {
            start_engine(&tx);
        });

//         let indata = rx.recv();
//         println!("indata {}", indata);

        while !window.should_close() {
            glfw::poll_events();
            for event in window.flush_events() {
                win_ctrl.handle_window_event(&window, event);
            }
            win_ctrl.draw();
        }
    });
}
<|endoftext|>


<commit_before>use types::{Word, Integral};
use types;
use util::reader::BinaryReader;
use rterror;

use std;
use std::mem;
use num;
use num::bigint;

/// Enum type represents a compacted simplified term format used in BEAM files,
/// must be converted to real term during the module loading phase
#[derive(Debug, PartialEq)]
pub enum CompactTerm {
  Literal(Word),
  Integer(Integral),
  Atom(Word),
  XReg(Word),
  YReg(Word),
  Label(Word),
  Character(Word),
  Float(types::Float),
  List,
  FPReg(Word),
  AllocList,
  ExtLiteral,
}

enum CTETag {
  Literal = 0,
  Integer = 1,
  Atom = 2,
  XReg = 3,
  YReg = 4,
  Label = 5,
  Character = 6,
  Extended = 7,
}

enum CTEExtTag {
  Float = 0b00010111,
  List = 0b00100111,
  FloatReg = 0b00110111,
  AllocList = 0b01000111,
  Literal = 0b01010111,
}

/// Errors created when parsing compact term format. They are delivered to the
/// end caller wrapped in `rterror::Error:CodeLoadingCompactTerm(x)`
#[derive(Debug)]
pub enum CTError {
  BadLiteralTag,
  BadAtomTag,
  BadXRegTag,
  BadYRegTag,
  BadLabelTag,
  BadCharacterTag,
  BadExtendedTag,
  BadFormat,
}

fn module() -> &'static str { "compact_term reader: " }

fn make_err(e: CTError) -> Result<CompactTerm, rterror::Error> {
  Err(rterror::Error::CodeLoadingCompactTerm(e))
}

fn word_to_u32(w: Word) -> u32 {
  assert!(w < std::u32::MAX as usize);
  w as u32
}

pub fn read(r: &mut BinaryReader) -> Result<CompactTerm, rterror::Error> {
  let b = r.read_u8();
  let tag = b & 0b111;
  let err_msg: &'static str = "Failed to parse beam compact term";

  let mut bword = Integral::Word(0);
  if tag < CTETag::Extended as u8 {
    bword = read_word(b, r);
  }

  match tag {
    x if x == CTETag::Literal as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Literal(index))
      }
      return make_err(CTError::BadLiteralTag)
    },
    x if x == CTETag::Atom as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Atom(index))
      }
      return make_err(CTError::BadAtomTag)
    },
    x if x == CTETag::XReg as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::XReg(index))
      }
      return make_err(CTError::BadXRegTag)
    },
    x if x == CTETag::YReg as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::YReg(index))
      }
      return make_err(CTError::BadYRegTag)
    },
    x if x == CTETag::Label as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Label(index))
      }
      return make_err(CTError::BadLabelTag)
    },
    x if x == CTETag::Integer as u8 => {
      return Ok(CompactTerm::Integer(bword))
    },
    x if x == CTETag::Character as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Character(index))
      }
      return make_err(CTError::BadCharacterTag)
    }
    // Extended tag (lower 3 bits = 0b111)
    _ => {
      match b {
        x if x == CTEExtTag::Float as u8 => {
          // floats are always stored as f64
          let fp_bytes = r.read_u64be();
          let fp: f64 = unsafe {
            std::mem::transmute::<u64, f64>(fp_bytes)
          };
          return Ok(CompactTerm::Float(fp as types::Float))
        },
        _ => {
          return make_err(CTError::BadExtendedTag)
        }
      }
    }
  }

  return make_err(CTError::BadFormat)
}

/// Given the first byte, parse an integer encoded after the 3-bit tag,
/// read more bytes from stream if needed.
fn read_word(b: u8, r: &mut BinaryReader) -> Integral {
  if 0 == (b & 0b1000) {
    // Bit 3 is 0 marks that 4 following bits contain the value
    return Integral::Word((b as Word) >> 4);
  }
  // Bit 3 is 1, but...
  if 0 == (b & 0b10000) {
    // Bit 4 is 0, marks that the following 3 bits (most significant) and
    // the following byte (least significant) will contain the 11-bit value
    let r = ((b as Word) & 0b11100000) << 3 | (r.read_u8() as Word);
    return Integral::Word(r);
  } else {
    // Bit 4 is 1 means that bits 5-6-7 contain amount of bytes+2 to store
    // the value
    let mut n_bytes = (((b as Word) & 0b11100000) >> 5) + 2;
    if n_bytes == 9 {
      // bytes=9 means upper 5 bits were set to 1, special case 0b11111xxx
      // which means that following nested tagged value encodes size,
      // followed by the bytes (Size+9)
      let bnext = r.read_u8();
      if let Integral::Word(tmp) = read_word(bnext, r) {
        n_bytes = tmp + 9;
      } else {
        panic!("{}read word encountered a wrong byte length", module())
      }
    }
    // Read the remaining big endian bytes and convert to int
    let long_bytes = r.read_bytes(n_bytes).unwrap();
    let r = num::BigInt::from_bytes_be(bigint::Sign::NoSign,
                                       long_bytes.as_slice());
    Integral::BigInt(r)
  } // if larger than 11 bits
}

//
// Testing section
//
#[cfg(test)]
mod tests {
  use super::*;

  fn try_parse(inp: Vec<u8>, expect: CompactTerm) {
    let mut r = BinaryReader::from_bytes(inp);
    match read(&mut r) {
      Ok(ref e) if e == &expect => {},
      other => {
        println!("Test got {:?}, expected {:?}", other, expect);
        assert!(false)
      }
    }
  }

  #[test]
  fn test_lit() {
    try_parse(vec![0u8], CompactTerm::Literal(0));
  }

  #[test]
  fn test_int() {
    try_parse(vec![0b1u8], CompactTerm::Integer(Integral::Word(0)));
  }

  #[test]
  fn test_float() {
    try_parse(vec![0b00010111u8, 63, 243, 192, 193, 252, 143, 50, 56],
              CompactTerm::Float(1.23456));
  }

  // TODO: test reading longer and very long words with read_word
  // TODO: test extended
}
<commit_msg>Read 11-bit words in CTE fixed and tested<commit_after>use types::{Word, Integral};
use types;
use util::reader::BinaryReader;
use rterror;

use std;
use std::mem;
use num;
use num::bigint;

/// Enum type represents a compacted simplified term format used in BEAM files,
/// must be converted to real term during the module loading phase
#[derive(Debug, PartialEq)]
pub enum CompactTerm {
  Literal(Word),
  Integer(Integral),
  Atom(Word),
  XReg(Word),
  YReg(Word),
  Label(Word),
  Character(Word),
  Float(types::Float),
  List,
  FPReg(Word),
  AllocList,
  ExtLiteral,
}

enum CTETag {
  Literal = 0,
  Integer = 1,
  Atom = 2,
  XReg = 3,
  YReg = 4,
  Label = 5,
  Character = 6,
  Extended = 7,
}

enum CTEExtTag {
  Float = 0b00010111,
  List = 0b00100111,
  FloatReg = 0b00110111,
  AllocList = 0b01000111,
  Literal = 0b01010111,
}

/// Errors created when parsing compact term format. They are delivered to the
/// end caller wrapped in `rterror::Error:CodeLoadingCompactTerm(x)`
#[derive(Debug)]
pub enum CTError {
  BadLiteralTag,
  BadAtomTag,
  BadXRegTag,
  BadYRegTag,
  BadLabelTag,
  BadCharacterTag,
  BadExtendedTag,
  BadFormat,
}

fn module() -> &'static str { "compact_term reader: " }

fn make_err(e: CTError) -> Result<CompactTerm, rterror::Error> {
  Err(rterror::Error::CodeLoadingCompactTerm(e))
}

fn word_to_u32(w: Word) -> u32 {
  assert!(w < std::u32::MAX as usize);
  w as u32
}

pub fn read(r: &mut BinaryReader) -> Result<CompactTerm, rterror::Error> {
  let b = r.read_u8();
  let tag = b & 0b111;
  let err_msg: &'static str = "Failed to parse beam compact term";

  let mut bword = Integral::Word(0);
  if tag < CTETag::Extended as u8 {
    bword = read_word(b, r);
  }

  match tag {
    x if x == CTETag::Literal as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Literal(index))
      }
      return make_err(CTError::BadLiteralTag)
    },
    x if x == CTETag::Atom as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Atom(index))
      }
      return make_err(CTError::BadAtomTag)
    },
    x if x == CTETag::XReg as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::XReg(index))
      }
      return make_err(CTError::BadXRegTag)
    },
    x if x == CTETag::YReg as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::YReg(index))
      }
      return make_err(CTError::BadYRegTag)
    },
    x if x == CTETag::Label as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Label(index))
      }
      return make_err(CTError::BadLabelTag)
    },
    x if x == CTETag::Integer as u8 => {
      return Ok(CompactTerm::Integer(bword))
    },
    x if x == CTETag::Character as u8 => {
      if let Integral::Word(index) = bword {
        return Ok(CompactTerm::Character(index))
      }
      return make_err(CTError::BadCharacterTag)
    }
    // Extended tag (lower 3 bits = 0b111)
    _ => {
      match b {
        x if x == CTEExtTag::Float as u8 => {
          // floats are always stored as f64
          let fp_bytes = r.read_u64be();
          let fp: f64 = unsafe {
            std::mem::transmute::<u64, f64>(fp_bytes)
          };
          return Ok(CompactTerm::Float(fp as types::Float))
        },
        _ => {
          return make_err(CTError::BadExtendedTag)
        }
      }
    }
  }

  return make_err(CTError::BadFormat)
}

/// Given the first byte, parse an integer encoded after the 3-bit tag,
/// read more bytes from stream if needed.
fn read_word(b: u8, r: &mut BinaryReader) -> Integral {
  if 0 == (b & 0b1000) {
    // Bit 3 is 0 marks that 4 following bits contain the value
    return Integral::Word((b as Word) >> 4);
  }
  // Bit 3 is 1, but...
  if 0 == (b & 0b10000) {
    // Bit 4 is 0, marks that the following 3 bits (most significant) and
    // the following byte (least significant) will contain the 11-bit value
    let r = ((b as Word) & 0b11100000) << 3 | (r.read_u8() as Word);
    return Integral::Word(r);
  } else {
    // Bit 4 is 1 means that bits 5-6-7 contain amount of bytes+2 to store
    // the value
    let mut n_bytes = (((b as Word) & 0b11100000) >> 5) + 2;
    if n_bytes == 9 {
      // bytes=9 means upper 5 bits were set to 1, special case 0b11111xxx
      // which means that following nested tagged value encodes size,
      // followed by the bytes (Size+9)
      let bnext = r.read_u8();
      if let Integral::Word(tmp) = read_word(bnext, r) {
        n_bytes = tmp + 9;
      } else {
        panic!("{}read word encountered a wrong byte length", module())
      }
    }
    // Read the remaining big endian bytes and convert to int
    let long_bytes = r.read_bytes(n_bytes).unwrap();
    let r = num::BigInt::from_bytes_be(bigint::Sign::NoSign,
                                       long_bytes.as_slice());
    Integral::BigInt(r)
  } // if larger than 11 bits
}

//
// Testing section
//
#[cfg(test)]
mod tests {
  use super::*;

  fn try_parse(inp: Vec<u8>, expect: CompactTerm) {
    let mut r = BinaryReader::from_bytes(inp);
    match read(&mut r) {
      Ok(ref e) if e == &expect => {},
      other => {
        println!("Test got {:?}, expected {:?}", other, expect);
        assert!(false)
      }
    }
  }

  #[test]
  fn test_lit() {
    try_parse(vec![0u8], CompactTerm::Literal(0));
  }

  #[test]
  fn test_int() {
    try_parse(vec![0b1u8], CompactTerm::Integer(Integral::Word(0)));
  }

  #[test]
  fn test_float() {
    try_parse(vec![0b00010111u8, 63, 243, 192, 193, 252, 143, 50, 56],
              CompactTerm::Float(1.23456));
  }

  // TODO: test extended

  /// Given the vec<u8> input, we read the word encoded in it and compare with
  /// the word or bigint expected
  fn try_read_word(inp: Vec<u8>, expect: Integral) {
    let mut r = BinaryReader::from_bytes(inp);
    let b0 = r.read_u8();
    assert_eq!(read_word(b0, &mut r), expect)
  }

  #[test]
  fn test_read_word_4bit() {
    try_read_word(vec![0b10010000u8], Integral::Word(9));
    try_read_word(vec![0b11110000u8], Integral::Word(15));
  }

  #[test]
  fn test_read_word_11bit() {
    try_read_word(vec![0b1000u8, 255],
                  Integral::Word(255));
    try_read_word(vec![0b10101000u8, 255],
                  Integral::Word(0b101 * 256 + 255));
    try_read_word(vec![0b11101000u8, 0b00001111],
                  Integral::Word(0b111 * 256 + 0b00001111));
  }
}
<|endoftext|>


<commit_before>use super::simplex_vertex::SimplexVertex;
use super::super::common::math::vec_distance;
use super::super::common::Vec2;

pub struct Simplex {
    pub m_v1: SimplexVertex,
    pub m_v2: SimplexVertex,
    pub m_v3: SimplexVertex,
    pub m_count: i32
}

impl Simplex {
    // TODO implement
    // pub fn read_cache()

    // TODO implement
    // pub fn write_cache()

    pub fn get_search_direction(&self) -> Vec2 {
        match self.m_count {
            1 => {
                return -self.m_v1.w;
            },
            2 => {
                let e12 = self.m_v2.w - self.m_v1.w;
                let sgn = Vec2::cross_vec(e12, -self.m_v1.w);
                if sgn > 0.0 {
                    // Origin is left of e12.
                    return Vec2::cross_scalar_vec(1.0, e12);
                } else {
                    // Origin is right of e12.
					return Vec2::cross_vec_scalar(e12, 1.0);
                }
            },
            _ => {
                assert!(false);
                return Vec2::zero();
            }
        }
    }

    pub fn get_closest_point(&self) -> Vec2 {
        match self.m_count {
            0 => {
                assert!(false);
                return Vec2::zero();
            },
            1 => {
                return self.m_v1.w;
            },
            2 => {
                return self.m_v1.a * self.m_v1.w + self.m_v2.a * self.m_v2.w;
            },
            3 => {
                return Vec2::zero();
            },
            _ => {
                assert!(false);
                return Vec2::zero();
            }
        }
    }

    pub fn get_witness_points(&self) -> (Vec2, Vec2) {
        match self.m_count {
            0 => {
                assert!(false);
                return (Vec2::zero(), Vec2::zero());
            },
            1 => {
                return (self.m_v1.w_a, self.m_v1.w_b);
            },
            2 => {
                return (self.m_v1.a * self.m_v1.w_a + self.m_v2.a * self.m_v2.w_a,
                    self.m_v1.a * self.m_v1.w_b + self.m_v2.a * self.m_v2.w_b)
            },
            3 => {
                let p_a = self.m_v1.a * self.m_v1.w_a + self.m_v2.a * self.m_v2.w_a + self.m_v3.a * self.m_v3.w_a;
                return (p_a, p_a)
            },
            _ => {
                assert!(false);
                return (Vec2::zero(), Vec2::zero());
            }
        }
    }

    pub fn get_metric(&self) -> f32 {
        match self.m_count {
            0 => {
                assert!(false);
                return 0.0;
            },
            1 => 0.0,
            2 => {
                return vec_distance(self.m_v1.w, self.m_v2.w);
            },
            3 => {
                return Vec2::cross_vec(self.m_v2.w - self.m_v1.w, self.m_v3.w - self.m_v1.w);
            },
            _ => {
                assert!(false);
                return 0.0;
            }
        }
    }

    // Solve a line segment using barycentric coordinates.
    //
    // p = a1 * w1 + a2 * w2
    // a1 + a2 = 1
    //
    // The vector from the origin to the closest point on the line is
    // perpendicular to the line.
    // e12 = w2 - w1
    // dot(p, e) = 0
    // a1 * dot(w1, e) + a2 * dot(w2, e) = 0
    //
    // 2-by-2 linear system
    // [1      1     ][a1] = [1]
    // [w1.e12 w2.e12][a2] = [0]
    //
    // Define
    // d12_1 =  dot(w2, e12)
    // d12_2 = -dot(w1, e12)
    // d12 = d12_1 + d12_2
    //
    // Solution
    // a1 = d12_1 / d12
    // a2 = d12_2 / d12
    pub fn solve_2(&mut self) {
        let w1 = self.m_v1.w;
        let w2 = self.m_v2.w;
        let e12 = w2 - w1;

        // w1 region
        let d12_2 = - Vec2::dot(w1, e12);
        if d12_2 <= 0.0 {
            // a2 <= 0, so we clamp it to 0
            self.m_v1.a = 1.0;
            self.m_count = 1;
            return;
        }

        // w2 region
        let d12_1 = Vec2::dot(w2, e12);
        if d12_1 <= 0.0 {
            // a1 <= 0, so we clamp it to 0
            self.m_v1.a = 1.0;
            self.m_count = 1;
            self.m_v1 = self.m_v2.clone();
            return;
        }

        let inv_d12 = 1.0 / (d12_1 + d12_2);
        self.m_v1.a = d12_1 * inv_d12;
        self.m_v2.a = d12_2 * inv_d12;
        self.m_count = 2;
    }

    // TODO implement
    pub fn solve_3() {

    }
}
<commit_msg>Adding solve_3 to simplex<commit_after>use super::simplex_vertex::SimplexVertex;
use super::super::common::math::vec_distance;
use super::super::common::Vec2;

pub struct Simplex {
    pub m_v1: SimplexVertex,
    pub m_v2: SimplexVertex,
    pub m_v3: SimplexVertex,
    pub m_count: i32
}

impl Simplex {
    // TODO implement
    // pub fn read_cache()

    // TODO implement
    // pub fn write_cache()

    pub fn get_search_direction(&self) -> Vec2 {
        match self.m_count {
            1 => {
                return -self.m_v1.w;
            },
            2 => {
                let e12 = self.m_v2.w - self.m_v1.w;
                let sgn = Vec2::cross_vec(e12, -self.m_v1.w);
                if sgn > 0.0 {
                    // Origin is left of e12.
                    return Vec2::cross_scalar_vec(1.0, e12);
                } else {
                    // Origin is right of e12.
					return Vec2::cross_vec_scalar(e12, 1.0);
                }
            },
            _ => {
                assert!(false);
                return Vec2::zero();
            }
        }
    }

    pub fn get_closest_point(&self) -> Vec2 {
        match self.m_count {
            0 => {
                assert!(false);
                return Vec2::zero();
            },
            1 => {
                return self.m_v1.w;
            },
            2 => {
                return self.m_v1.a * self.m_v1.w + self.m_v2.a * self.m_v2.w;
            },
            3 => {
                return Vec2::zero();
            },
            _ => {
                assert!(false);
                return Vec2::zero();
            }
        }
    }

    pub fn get_witness_points(&self) -> (Vec2, Vec2) {
        match self.m_count {
            0 => {
                assert!(false);
                return (Vec2::zero(), Vec2::zero());
            },
            1 => {
                return (self.m_v1.w_a, self.m_v1.w_b);
            },
            2 => {
                return (self.m_v1.a * self.m_v1.w_a + self.m_v2.a * self.m_v2.w_a,
                    self.m_v1.a * self.m_v1.w_b + self.m_v2.a * self.m_v2.w_b)
            },
            3 => {
                let p_a = self.m_v1.a * self.m_v1.w_a + self.m_v2.a * self.m_v2.w_a + self.m_v3.a * self.m_v3.w_a;
                return (p_a, p_a)
            },
            _ => {
                assert!(false);
                return (Vec2::zero(), Vec2::zero());
            }
        }
    }

    pub fn get_metric(&self) -> f32 {
        match self.m_count {
            0 => {
                assert!(false);
                return 0.0;
            },
            1 => 0.0,
            2 => {
                return vec_distance(self.m_v1.w, self.m_v2.w);
            },
            3 => {
                return Vec2::cross_vec(self.m_v2.w - self.m_v1.w, self.m_v3.w - self.m_v1.w);
            },
            _ => {
                assert!(false);
                return 0.0;
            }
        }
    }

    // Solve a line segment using barycentric coordinates.
    //
    // p = a1 * w1 + a2 * w2
    // a1 + a2 = 1
    //
    // The vector from the origin to the closest point on the line is
    // perpendicular to the line.
    // e12 = w2 - w1
    // dot(p, e) = 0
    // a1 * dot(w1, e) + a2 * dot(w2, e) = 0
    //
    // 2-by-2 linear system
    // [1      1     ][a1] = [1]
    // [w1.e12 w2.e12][a2] = [0]
    //
    // Define
    // d12_1 =  dot(w2, e12)
    // d12_2 = -dot(w1, e12)
    // d12 = d12_1 + d12_2
    //
    // Solution
    // a1 = d12_1 / d12
    // a2 = d12_2 / d12
    pub fn solve_2(&mut self) {
        let w1 = self.m_v1.w;
        let w2 = self.m_v2.w;
        let e12 = w2 - w1;

        // w1 region
        let d12_2 = - Vec2::dot(w1, e12);
        if d12_2 <= 0.0 {
            // a2 <= 0, so we clamp it to 0
            self.m_v1.a = 1.0;
            self.m_count = 1;
            return;
        }

        // w2 region
        let d12_1 = Vec2::dot(w2, e12);
        if d12_1 <= 0.0 {
            // a1 <= 0, so we clamp it to 0
            self.m_v1.a = 1.0;
            self.m_count = 1;
            self.m_v1 = self.m_v2.clone();
            return;
        }

        let inv_d12 = 1.0 / (d12_1 + d12_2);
        self.m_v1.a = d12_1 * inv_d12;
        self.m_v2.a = d12_2 * inv_d12;
        self.m_count = 2;
    }

    // Possible regions:
    // - points[2]
    // - edge points[0]-points[2]
    // - edge points[1]-points[2]
    // - inside the triangle
    pub fn solve_3(&mut self) {
        let w1 = self.m_v1.w;
        let w2 = self.m_v2.w;
        let w3 = self.m_v3.w;

        // Edge12
    	// [1      1     ][a1] = [1]
    	// [w1.e12 w2.e12][a2] = [0]
    	// a3 = 0
    	let e12 = w2 - w1;
    	let w1e12 = Vec2::dot(w1, e12);
    	let w2e12 = Vec2::dot(w2, e12);
    	let d12_1 = w2e12;
    	let d12_2 = -w1e12;

        // Edge13
    	// [1      1     ][a1] = [1]
    	// [w1.e13 w3.e13][a3] = [0]
    	// a2 = 0
    	let e13 = w3 - w1;
    	let w1e13 = Vec2::dot(w1, e13);
    	let w3e13 = Vec2::dot(w3, e13);
    	let d13_1 = w3e13;
    	let d13_2 = -w1e13;

        // Edge23
    	// [1      1     ][a2] = [1]
    	// [w2.e23 w3.e23][a3] = [0]
    	// a1 = 0
    	let e23 = w3 - w2;
    	let w2e23 = Vec2::dot(w2, e23);
    	let w3e23 = Vec2::dot(w3, e23);
    	let d23_1 = w3e23;
    	let d23_2 = -w2e23;

        // Triangle123
    	let n123 = Vec2::cross_vec(e12, e13);

    	let d123_1 = n123 * Vec2::cross_vec(w2, w3);
    	let d123_2 = n123 * Vec2::cross_vec(w3, w1);
    	let d123_3 = n123 * Vec2::cross_vec(w1, w2);

        // w1 region
    	if d12_2 <= 0.0 && d13_2 <= 0.0 {
    		self.m_v1.a = 1.0;
    		self.m_count = 1;
    		return;
    	}

        // e12
    	if d12_1 > 0.0 && d12_2 > 0.0 && d123_3 <= 0.0 {
    		let inv_d12 = 1.0 / (d12_1 + d12_2);
    		self.m_v1.a = d12_1 * inv_d12;
    		self.m_v2.a = d12_2 * inv_d12;
    		self.m_count = 2;
    		return;
    	}

        // e13
    	if d13_1 > 0.0 && d13_2 > 0.0 && d123_2 <= 0.0 {
    		let inv_d13 = 1.0 / (d13_1 + d13_2);
    		self.m_v1.a = d13_1 * inv_d13;
    		self.m_v3.a = d13_2 * inv_d13;
    		self.m_count = 2;
    		self.m_v2 = self.m_v3.clone();
    		return;
    	}

        // w2 region
    	if d12_1 <= 0.0 && d23_2 <= 0.0 {
    		self.m_v2.a = 1.0;
    		self.m_count = 1;
    		self.m_v1 = self.m_v2.clone();
    		return;
    	}

        // w3 region
    	if d13_1 <= 0.0 && d23_1 <= 0.0 {
    		self.m_v3.a = 1.0;
    		self.m_count = 1;
    		self.m_v1 = self.m_v3.clone();
    		return;
    	}

        // e23
    	if d23_1 > 0.0 && d23_2 > 0.0 && d123_1 <= 0.0 {
    		let inv_d23 = 1.0 / (d23_1 + d23_2);
    		self.m_v2.a = d23_1 * inv_d23;
    		self.m_v3.a = d23_2 * inv_d23;
    		self.m_count = 2;
    		self.m_v1 = self.m_v3.clone();
    		return;
    	}

        // Must be in triangle123
    	let inv_d123 = 1.0 / (d123_1 + d123_2 + d123_3);
    	self.m_v1.a = d123_1 * inv_d123;
    	self.m_v2.a = d123_2 * inv_d123;
    	self.m_v3.a = d123_3 * inv_d123;
    	self.m_count = 3;
    }
}
<|endoftext|>


<commit_before>/// Tests which exercise the overall application, in particular the `MDBook`
/// initialization and build/rendering process.
///
/// This will create an entire book in a temporary directory using some
/// dummy content.

extern crate mdbook;
extern crate tempdir;
extern crate env_logger;

use std::path::Path;
use std::fs::{self, File};
use std::io::{Read, Write};

use tempdir::TempDir;
use mdbook::MDBook;

const SUMMARY_MD: &'static str = "# Summary

[Introduction](intro.md)

- [First Chapter](./first/index.md)
    - [Nested Chapter](./first/nested.md)
- [Second Chapter](./second.md)

[Conclusion](./conclusion.md)
";

const INTRO: &'static str = "# Introduction

Here's some interesting text...";

const FIRST: &'static str = "# First Chapter

more text.";

const NESTED: &'static str = r#"# Nested Chapter

This file has some testable code.

```rust
assert!($TEST_STATUS);
```"#;

const SECOND: &'static str = "# Second Chapter";

const CONCLUSION: &'static str = "# Conclusion";

#[test]
fn build_the_dummy_book() {
    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());

    md.build().unwrap();
}

#[test]
fn mdbook_can_correctly_test_a_passing_book() {
    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());

    assert!(md.test().is_ok());
}

#[test]
fn mdbook_detects_book_with_failing_tests() {
    let temp = create_book(false);
    let mut md: MDBook = MDBook::new(temp.path());

    assert!(md.test().is_err());
}

#[test]
fn by_default_mdbook_generates_rendered_content_in_the_book_directory() {
    let temp = create_book(false);
    let mut md = MDBook::new(temp.path());

    assert!(!temp.path().join("book").exists());
    md.build().unwrap();

    assert!(temp.path().join("book").exists());
    assert!(temp.path().join("book").join("index.html").exists());
}

#[test]
fn make_sure_bottom_level_files_contain_links_to_chapters() {
    let temp = create_book(false);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let dest = temp.path().join("book");
    let links = vec![
        "intro.html",
        "first/index.html",
        "first/nested.html",
        "second.html",
        "conclusion.html",
    ];

    let files_in_bottom_dir = vec!["index.html", "intro.html", "second.html", "conclusion.html"];

    for filename in files_in_bottom_dir {
        assert_contains_strings(dest.join(filename), &links);
    }
}

#[test]
fn check_correct_cross_links_in_nested_dir() {
    let temp = create_book(false);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let first = temp.path().join("book").join("first");
    let links = vec![
        r#"<base href="../">"#,
        "intro.html",
        "first/index.html",
        "first/nested.html",
        "second.html",
        "conclusion.html",
    ];

    let files_in_nested_dir = vec!["index.html", "nested.html"];

    for filename in files_in_nested_dir {
        assert_contains_strings(first.join(filename), &links);
    }
}

#[test]
fn run_mdbook_init() {
    let created_files = vec!["book", "src", "src/SUMMARY.md", "src/chapter_1.md"];

    let temp = TempDir::new("mdbook").unwrap();
    for file in &created_files {
        assert!(!temp.path().join(file).exists());
    }

    let mut md = MDBook::new(temp.path());
    md.init().unwrap();

    for file in &created_files {
        assert!(temp.path().join(file).exists(), "{} doesn't exist", file);
    }
}

#[test]
fn rendered_code_has_playpen_stuff() {
    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let nested = temp.path().join("book/first/nested.html");
    let playpen_class = vec![r#"class="playpen""#];

    assert_contains_strings(nested, &playpen_class);

    let book_js = temp.path().join("book/book.js");
    assert_contains_strings(book_js, &[".playpen"]);
}

#[test]
fn chapter_content_appears_in_rendered_document() {
    let content = vec![
        ("index.html", "Here's some interesting text"),
        ("second.html", "Second Chapter"),
        ("first/nested.html", "testable code"),
        ("first/index.html", "more text"),
        ("conclusion.html", "Conclusion"),
    ];

    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let destination = temp.path().join("book");

    for (filename, text) in content {
        let path = destination.join(filename);
        assert_contains_strings(path, &[text]);
    }
}

/// Create a dummy book in a temporary directory, using the contents of
/// `SUMMARY_MD` as a guide.
///
/// The "Nested Chapter" file contains a code block with a single
/// `assert!($TEST_STATUS)`. If you want to check MDBook's testing
/// functionality, `$TEST_STATUS` can be substitute for either `true` or
/// `false`. This is done using the `passing_test` parameter.
fn create_book(passing_test: bool) -> TempDir {
    let temp = TempDir::new("dummy_book").unwrap();

    let src = temp.path().join("src");
    fs::create_dir_all(&src).unwrap();

    File::create(src.join("SUMMARY.md"))
        .unwrap()
        .write_all(SUMMARY_MD.as_bytes())
        .unwrap();
    File::create(src.join("intro.md"))
        .unwrap()
        .write_all(INTRO.as_bytes())
        .unwrap();

    let first = src.join("first");
    fs::create_dir_all(&first).unwrap();
    File::create(first.join("index.md"))
        .unwrap()
        .write_all(FIRST.as_bytes())
        .unwrap();

    let to_substitute = if passing_test { "true" } else { "false" };
    let nested_text = NESTED.replace("$TEST_STATUS", to_substitute);
    File::create(first.join("nested.md"))
        .unwrap()
        .write_all(nested_text.as_bytes())
        .unwrap();

    File::create(src.join("second.md"))
        .unwrap()
        .write_all(SECOND.as_bytes())
        .unwrap();
    File::create(src.join("conclusion.md"))
        .unwrap()
        .write_all(CONCLUSION.as_bytes())
        .unwrap();

    temp
}

/// Read the contents of the provided file into memory and then iterate through
/// the list of strings asserting that the file contains all of them.
fn assert_contains_strings<P: AsRef<Path>>(filename: P, strings: &[&str]) {
    println!("Checking {}", filename.as_ref().display());
    println!();

    let mut content = String::new();
    File::open(filename)
        .expect("Couldn't open the provided file")
        .read_to_string(&mut content)
        .unwrap();

    println!("{}", content);
    println!();
    println!();

    for s in strings {
        println!("Checking for {:?}", s);
        assert!(content.contains(s));
    }
}
<commit_msg>Updated the book testing to take into account  #340<commit_after>/// Tests which exercise the overall application, in particular the `MDBook`
/// initialization and build/rendering process.
///
/// This will create an entire book in a temporary directory using some
/// dummy content.

extern crate mdbook;
extern crate tempdir;
extern crate env_logger;

use std::path::Path;
use std::fs::{self, File};
use std::io::{Read, Write};

use tempdir::TempDir;
use mdbook::MDBook;

const SUMMARY_MD: &'static str = "# Summary

[Introduction](intro.md)

- [First Chapter](./first/index.md)
    - [Nested Chapter](./first/nested.md)
- [Second Chapter](./second.md)

[Conclusion](./conclusion.md)
";

const INTRO: &'static str = "# Introduction

Here's some interesting text...";

const FIRST: &'static str = "# First Chapter

more text.";

const NESTED: &'static str = r#"# Nested Chapter

This file has some testable code.

```rust
assert!($TEST_STATUS);
```"#;

const SECOND: &'static str = "# Second Chapter";

const CONCLUSION: &'static str = "# Conclusion";

#[test]
fn build_the_dummy_book() {
    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());

    md.build().unwrap();
}

#[test]
fn mdbook_can_correctly_test_a_passing_book() {
    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());

    assert!(md.test(vec![]).is_ok());
}

#[test]
fn mdbook_detects_book_with_failing_tests() {
    let temp = create_book(false);
    let mut md: MDBook = MDBook::new(temp.path());

    assert!(md.test(vec![]).is_err());
}

#[test]
fn by_default_mdbook_generates_rendered_content_in_the_book_directory() {
    let temp = create_book(false);
    let mut md = MDBook::new(temp.path());

    assert!(!temp.path().join("book").exists());
    md.build().unwrap();

    assert!(temp.path().join("book").exists());
    assert!(temp.path().join("book").join("index.html").exists());
}

#[test]
fn make_sure_bottom_level_files_contain_links_to_chapters() {
    let temp = create_book(false);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let dest = temp.path().join("book");
    let links = vec![
        "intro.html",
        "first/index.html",
        "first/nested.html",
        "second.html",
        "conclusion.html",
    ];

    let files_in_bottom_dir = vec!["index.html", "intro.html", "second.html", "conclusion.html"];

    for filename in files_in_bottom_dir {
        assert_contains_strings(dest.join(filename), &links);
    }
}

#[test]
fn check_correct_cross_links_in_nested_dir() {
    let temp = create_book(false);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let first = temp.path().join("book").join("first");
    let links = vec![
        r#"<base href="../">"#,
        "intro.html",
        "first/index.html",
        "first/nested.html",
        "second.html",
        "conclusion.html",
    ];

    let files_in_nested_dir = vec!["index.html", "nested.html"];

    for filename in files_in_nested_dir {
        assert_contains_strings(first.join(filename), &links);
    }
}

#[test]
fn run_mdbook_init() {
    let created_files = vec!["book", "src", "src/SUMMARY.md", "src/chapter_1.md"];

    let temp = TempDir::new("mdbook").unwrap();
    for file in &created_files {
        assert!(!temp.path().join(file).exists());
    }

    let mut md = MDBook::new(temp.path());
    md.init().unwrap();

    for file in &created_files {
        assert!(temp.path().join(file).exists(), "{} doesn't exist", file);
    }
}

#[test]
fn rendered_code_has_playpen_stuff() {
    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let nested = temp.path().join("book/first/nested.html");
    let playpen_class = vec![r#"class="playpen""#];

    assert_contains_strings(nested, &playpen_class);

    let book_js = temp.path().join("book/book.js");
    assert_contains_strings(book_js, &[".playpen"]);
}

#[test]
fn chapter_content_appears_in_rendered_document() {
    let content = vec![
        ("index.html", "Here's some interesting text"),
        ("second.html", "Second Chapter"),
        ("first/nested.html", "testable code"),
        ("first/index.html", "more text"),
        ("conclusion.html", "Conclusion"),
    ];

    let temp = create_book(true);
    let mut md = MDBook::new(temp.path());
    md.build().unwrap();

    let destination = temp.path().join("book");

    for (filename, text) in content {
        let path = destination.join(filename);
        assert_contains_strings(path, &[text]);
    }
}

/// Create a dummy book in a temporary directory, using the contents of
/// `SUMMARY_MD` as a guide.
///
/// The "Nested Chapter" file contains a code block with a single
/// `assert!($TEST_STATUS)`. If you want to check MDBook's testing
/// functionality, `$TEST_STATUS` can be substitute for either `true` or
/// `false`. This is done using the `passing_test` parameter.
fn create_book(passing_test: bool) -> TempDir {
    let temp = TempDir::new("dummy_book").unwrap();

    let src = temp.path().join("src");
    fs::create_dir_all(&src).unwrap();

    File::create(src.join("SUMMARY.md"))
        .unwrap()
        .write_all(SUMMARY_MD.as_bytes())
        .unwrap();
    File::create(src.join("intro.md"))
        .unwrap()
        .write_all(INTRO.as_bytes())
        .unwrap();

    let first = src.join("first");
    fs::create_dir_all(&first).unwrap();
    File::create(first.join("index.md"))
        .unwrap()
        .write_all(FIRST.as_bytes())
        .unwrap();

    let to_substitute = if passing_test { "true" } else { "false" };
    let nested_text = NESTED.replace("$TEST_STATUS", to_substitute);
    File::create(first.join("nested.md"))
        .unwrap()
        .write_all(nested_text.as_bytes())
        .unwrap();

    File::create(src.join("second.md"))
        .unwrap()
        .write_all(SECOND.as_bytes())
        .unwrap();
    File::create(src.join("conclusion.md"))
        .unwrap()
        .write_all(CONCLUSION.as_bytes())
        .unwrap();

    temp
}

/// Read the contents of the provided file into memory and then iterate through
/// the list of strings asserting that the file contains all of them.
fn assert_contains_strings<P: AsRef<Path>>(filename: P, strings: &[&str]) {
    println!("Checking {}", filename.as_ref().display());
    println!();

    let mut content = String::new();
    File::open(filename)
        .expect("Couldn't open the provided file")
        .read_to_string(&mut content)
        .unwrap();

    println!("{}", content);
    println!();
    println!();

    for s in strings {
        println!("Checking for {:?}", s);
        assert!(content.contains(s));
    }
}
<|endoftext|>


<commit_before>use cairo;
use cairo_sys;
use glib;
use glib::translate::*;
use glib_sys;
use libc;
use std::cell::{Cell, RefCell};
use std::ptr;

use aspect_ratio::AspectRatio;
use attributes::Attribute;
use draw::{add_clipping_rect, draw_surface};
use drawing_ctx::{self, RsvgDrawingCtx};
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::{parse, parse_and_validate};
use property_bag::PropertyBag;

pub struct NodeImage {
    aspect: Cell<AspectRatio>,
    x: Cell<RsvgLength>,
    y: Cell<RsvgLength>,
    w: Cell<RsvgLength>,
    h: Cell<RsvgLength>,
    surface: RefCell<Option<cairo::ImageSurface>>,
}

impl NodeImage {
    pub fn new() -> NodeImage {
        NodeImage {
            aspect: Cell::new(AspectRatio::default()),
            x: Cell::new(RsvgLength::default()),
            y: Cell::new(RsvgLength::default()),
            w: Cell::new(RsvgLength::default()),
            h: Cell::new(RsvgLength::default()),
            surface: RefCell::new(None),
        }
    }
}

impl NodeTrait for NodeImage {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical)?),
                Attribute::Width => self.w.set(parse_and_validate(
                    "width",
                    value,
                    LengthDir::Horizontal,
                    RsvgLength::check_nonnegative,
                )?),
                Attribute::Height => self.h.set(parse_and_validate(
                    "height",
                    value,
                    LengthDir::Vertical,
                    RsvgLength::check_nonnegative,
                )?),

                Attribute::PreserveAspectRatio => {
                    self.aspect.set(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::XlinkHref | Attribute::Path => {
                    // "path" is used by some older Adobe Illustrator versions

                    extern "C" {
                        fn rsvg_cairo_surface_new_from_href(
                            handle: *const RsvgHandle,
                            href: *const libc::c_char,
                            error: *mut *mut glib_sys::GError,
                        ) -> *mut cairo_sys::cairo_surface_t;
                    }

                    let mut error = ptr::null_mut();

                    let raw_surface = unsafe {
                        rsvg_cairo_surface_new_from_href(handle, value.to_glib_none().0, &mut error)
                    };
                    if !raw_surface.is_null() {
                        *self.surface.borrow_mut() = Some(unsafe {
                            cairo::ImageSurface::from_raw_full(raw_surface).unwrap()
                        });
                    } else {
                        let _: glib::Error = unsafe { from_glib_full(error) }; // FIXME: we should note that the image couldn't be loaded
                    }
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues,
        draw_ctx: *mut RsvgDrawingCtx,
        clipping: bool,
    ) {
        let values = cascaded.get();

        if let Some(ref surface) = *self.surface.borrow() {
            let x = self.x.get().normalize(values, draw_ctx);
            let y = self.y.get().normalize(values, draw_ctx);
            let w = self.w.get().normalize(values, draw_ctx);
            let h = self.h.get().normalize(values, draw_ctx);

            drawing_ctx::with_discrete_layer(draw_ctx, node, values, clipping, &mut |cr| {
                cr.save();

                let aspect = self.aspect.get();

                if !values.is_overflow() && aspect.is_slice() {
                    add_clipping_rect(draw_ctx, x, y, w, h);
                }

                let (x, y, w, h) = aspect.compute(
                    f64::from(surface.get_width()),
                    f64::from(surface.get_height()),
                    x,
                    y,
                    w,
                    h,
                );

                draw_surface(draw_ctx, values, surface, x, y, w, h, clipping);

                cr.restore();
            });
        }
    }
}
<commit_msg>image: remove unneeded cairo_save/restore<commit_after>use cairo;
use cairo_sys;
use glib;
use glib::translate::*;
use glib_sys;
use libc;
use std::cell::{Cell, RefCell};
use std::ptr;

use aspect_ratio::AspectRatio;
use attributes::Attribute;
use draw::{add_clipping_rect, draw_surface};
use drawing_ctx::{self, RsvgDrawingCtx};
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::{parse, parse_and_validate};
use property_bag::PropertyBag;

pub struct NodeImage {
    aspect: Cell<AspectRatio>,
    x: Cell<RsvgLength>,
    y: Cell<RsvgLength>,
    w: Cell<RsvgLength>,
    h: Cell<RsvgLength>,
    surface: RefCell<Option<cairo::ImageSurface>>,
}

impl NodeImage {
    pub fn new() -> NodeImage {
        NodeImage {
            aspect: Cell::new(AspectRatio::default()),
            x: Cell::new(RsvgLength::default()),
            y: Cell::new(RsvgLength::default()),
            w: Cell::new(RsvgLength::default()),
            h: Cell::new(RsvgLength::default()),
            surface: RefCell::new(None),
        }
    }
}

impl NodeTrait for NodeImage {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical)?),
                Attribute::Width => self.w.set(parse_and_validate(
                    "width",
                    value,
                    LengthDir::Horizontal,
                    RsvgLength::check_nonnegative,
                )?),
                Attribute::Height => self.h.set(parse_and_validate(
                    "height",
                    value,
                    LengthDir::Vertical,
                    RsvgLength::check_nonnegative,
                )?),

                Attribute::PreserveAspectRatio => {
                    self.aspect.set(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::XlinkHref | Attribute::Path => {
                    // "path" is used by some older Adobe Illustrator versions

                    extern "C" {
                        fn rsvg_cairo_surface_new_from_href(
                            handle: *const RsvgHandle,
                            href: *const libc::c_char,
                            error: *mut *mut glib_sys::GError,
                        ) -> *mut cairo_sys::cairo_surface_t;
                    }

                    let mut error = ptr::null_mut();

                    let raw_surface = unsafe {
                        rsvg_cairo_surface_new_from_href(handle, value.to_glib_none().0, &mut error)
                    };
                    if !raw_surface.is_null() {
                        *self.surface.borrow_mut() = Some(unsafe {
                            cairo::ImageSurface::from_raw_full(raw_surface).unwrap()
                        });
                    } else {
                        let _: glib::Error = unsafe { from_glib_full(error) }; // FIXME: we should note that the image couldn't be loaded
                    }
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues,
        draw_ctx: *mut RsvgDrawingCtx,
        clipping: bool,
    ) {
        let values = cascaded.get();

        if let Some(ref surface) = *self.surface.borrow() {
            let x = self.x.get().normalize(values, draw_ctx);
            let y = self.y.get().normalize(values, draw_ctx);
            let w = self.w.get().normalize(values, draw_ctx);
            let h = self.h.get().normalize(values, draw_ctx);

            drawing_ctx::with_discrete_layer(draw_ctx, node, values, clipping, &mut |_cr| {
                let aspect = self.aspect.get();

                if !values.is_overflow() && aspect.is_slice() {
                    add_clipping_rect(draw_ctx, x, y, w, h);
                }

                let (x, y, w, h) = aspect.compute(
                    f64::from(surface.get_width()),
                    f64::from(surface.get_height()),
                    x,
                    y,
                    w,
                    h,
                );

                draw_surface(draw_ctx, values, surface, x, y, w, h, clipping);
            });
        }
    }
}
<|endoftext|>