<commit_before>//! Methods for converting shapes into triangles.

use std;
use {
    Image,
    Line,
    Matrix2d,
    Rectangle,
};
use interpolation::{lerp};
use vecmath::{
    multiply,
    orient,
    translate,
};

#[inline(always)]
fn tx(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

#[inline(always)]
fn ty(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygons: &[&[f64]],
    tween_factor: f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * std::f64::consts::PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round border line.
#[inline(always)]
pub fn with_round_border_line_tri_list_xy_f32_rgba_f32(
    resolution_cap: uint,
    m: &Matrix2d,
    line: &Line,
    round_border_radius: &f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let radius = *round_border_radius;
    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
    let (dx, dy) = (x2 - x1, y2 - y1);
    let w = (dx * dx + dy * dy).sqrt();
    let m = multiply(m, &translate(x1, y1));
    let m = multiply(&m, &orient(dx, dy));
    let n = resolution_cap * 2 + 2;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(&m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_cap => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                Some([w + angle.cos() * radius, angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                Some([angle.cos() * radius, angle.sin() * radius])
            },
        }
    }, color, f);
    /*
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = round_border[0];
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
    */
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    round_radius: &f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = *round_radius;
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: || -> Option<[f64, ..2]>,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: &[f64],
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
pub fn rect_tri_list_xy_f32(m: &Matrix2d, rect: &Rectangle) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
pub fn rect_tri_list_rgba_f32(color: [f32, ..4]) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
pub fn rect_tri_list_uv_f32(image: &Image) -> [f32, ..12] {
    let x1 = image.source_rect[0] as f32 / image.texture_width as f32;
    let y1 = image.source_rect[1] as f32 / image.texture_height as f32;
    let x2 = (image.source_rect[0] + image.source_rect[2]) as f32 / image.texture_width as f32;
    let y2 = (image.source_rect[1] + image.source_rect[3]) as f32 / image.texture_height as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}
<commit_msg>Added `with_round_border_line_tri_list_xy_f32_rgba_f32` to `triangulation`<commit_after>//! Methods for converting shapes into triangles.

use std;
use {
    Image,
    Line,
    Matrix2d,
    Rectangle,
};
use interpolation::{lerp};
use vecmath::{
    multiply,
    orient,
    translate,
};

#[inline(always)]
fn tx(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

#[inline(always)]
fn ty(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygons: &[&[f64]],
    tween_factor: f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * std::f64::consts::PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round border line.
#[inline(always)]
pub fn with_round_border_line_tri_list_xy_f32_rgba_f32(
    resolution_cap: uint,
    m: &Matrix2d,
    line: &Line,
    round_border_radius: &f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let radius = *round_border_radius;
    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
    let (dx, dy) = (x2 - x1, y2 - y1);
    let w = (dx * dx + dy * dy).sqrt();
    let m = multiply(m, &translate(x1, y1));
    let m = multiply(&m, &orient(dx, dy));
    let n = resolution_cap * 2;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(&m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_cap => {
                let angle = (j - resolution_cap) as f64 / (resolution_cap - 1) as f64 * std::f64::consts::PI
                    + std::f64::consts::PI;
                let angle = angle + std::f64::consts::FRAC_PI_2;
                Some([w + angle.cos() * radius, angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (resolution_cap - 1) as f64 * std::f64::consts::PI;
                let angle = angle + std::f64::consts::FRAC_PI_2;
                Some([angle.cos() * radius, angle.sin() * radius])
            },
        }
    }, color, f);
    /*
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = round_border[0];
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
    */
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    round_radius: &f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = *round_radius;
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: || -> Option<[f64, ..2]>,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: &[f64],
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
pub fn rect_tri_list_xy_f32(m: &Matrix2d, rect: &Rectangle) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
pub fn rect_tri_list_rgba_f32(color: [f32, ..4]) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
pub fn rect_tri_list_uv_f32(image: &Image) -> [f32, ..12] {
    let x1 = image.source_rect[0] as f32 / image.texture_width as f32;
    let y1 = image.source_rect[1] as f32 / image.texture_height as f32;
    let x2 = (image.source_rect[0] + image.source_rect[2]) as f32 / image.texture_width as f32;
    let y2 = (image.source_rect[1] + image.source_rect[3]) as f32 / image.texture_height as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}
<|endoftext|>


<commit_before>//! Methods for converting shapes into triangles.

use std;
use {
    Image,
    Matrix2d,
    Rectangle,
};
use interpolation::{lerp};

#[inline(always)]
fn tx(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

#[inline(always)]
fn ty(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygons: &[&[f64]],
    tween_factor: f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * std::f64::consts::PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    round_radius: &f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = *round_radius;
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: || -> Option<[f64, ..2]>,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Draw first triangle for testing.
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        // Set the first point in triangle to use the beginning.
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: &[f64],
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
pub fn rect_tri_list_xy_f32(m: &Matrix2d, rect: &Rectangle) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
pub fn rect_tri_list_rgba_f32(color: [f32, ..4]) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
pub fn rect_tri_list_uv_f32(image: &Image) -> [f32, ..12] {
    let x1 = image.source_rect[0] as f32 / image.texture_width as f32;
    let y1 = image.source_rect[1] as f32 / image.texture_height as f32;
    let x2 = (image.source_rect[0] + image.source_rect[2]) as f32 / image.texture_width as f32;
    let y2 = (image.source_rect[1] + image.source_rect[3]) as f32 / image.texture_height as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}

<commit_msg>Fixed comment<commit_after>//! Methods for converting shapes into triangles.

use std;
use {
    Image,
    Matrix2d,
    Rectangle,
};
use interpolation::{lerp};

#[inline(always)]
fn tx(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

#[inline(always)]
fn ty(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygons: &[&[f64]],
    tween_factor: f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * std::f64::consts::PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    round_radius: &f64,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = *round_radius;
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: || -> Option<[f64, ..2]>,
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: &[f64],
    color: [f32, ..4],
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
pub fn rect_tri_list_xy_f32(m: &Matrix2d, rect: &Rectangle) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
pub fn rect_tri_list_rgba_f32(color: [f32, ..4]) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
pub fn rect_tri_list_uv_f32(image: &Image) -> [f32, ..12] {
    let x1 = image.source_rect[0] as f32 / image.texture_width as f32;
    let y1 = image.source_rect[1] as f32 / image.texture_height as f32;
    let x2 = (image.source_rect[0] + image.source_rect[2]) as f32 / image.texture_width as f32;
    let y2 = (image.source_rect[1] + image.source_rect[3]) as f32 / image.texture_height as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}

<|endoftext|>


<commit_before>use collect_slice::CollectSlice;

use bits::{Dibit, Hexbit, HexbitBytes};
use buffer::{Buffer, DibitStorage, VoiceHeaderStorage, VoiceExtraStorage};
use coding::{reed_solomon, golay};
use error::Result;

use voice::header::VoiceHeaderFields;
use voice::control::LinkControlFields;
use voice::consts::HEADER_BYTES;

use error::P25Error::*;

pub struct VoiceHeaderReceiver {
    dibits: Buffer<DibitStorage>,
    hexbits: Buffer<VoiceHeaderStorage>,
}

impl VoiceHeaderReceiver {
    pub fn new() -> VoiceHeaderReceiver {
        VoiceHeaderReceiver {
            dibits: Buffer::new(DibitStorage::new(9)),
            hexbits: Buffer::new(VoiceHeaderStorage::new()),
        }
    }

    pub fn feed(&mut self, dibit: Dibit) -> Option<Result<VoiceHeaderFields>> {
        let buf = match self.dibits.feed(dibit) {
            Some(buf) => *buf as u32,
            None => return None,
        };

        let data = match golay::shortened::decode(buf) {
            Some((data, err)) => data,
            None => return Some(Err(GolayUnrecoverable)),
        };

        let hexbits = match self.hexbits.feed(Hexbit::new(data)) {
            Some(buf) => buf,
            None => return None,
        };

        let data = match reed_solomon::long::decode(hexbits) {
            Some((data, err)) => data,
            None => return Some(Err(ReedSolomonUnrecoverable)),
        };

        let mut bytes = [0; HEADER_BYTES];
        HexbitBytes::new(data.iter().cloned())
            .collect_slice_checked(&mut bytes[..]);

        Some(Ok(VoiceHeaderFields::new(bytes)))
    }
}

pub struct VoiceLCTerminatorReceiver {
    outer: Buffer<DibitStorage>,
    inner: Buffer<VoiceExtraStorage>,
}

impl VoiceLCTerminatorReceiver {
    pub fn new() -> VoiceLCTerminatorReceiver {
        VoiceLCTerminatorReceiver {
            outer: Buffer::new(DibitStorage::new(12)),
            inner: Buffer::new(VoiceExtraStorage::new()),
        }
    }

    pub fn feed(&mut self, dibit: Dibit) -> Option<Result<LinkControlFields>> {
        let buf = match self.outer.feed(dibit) {
            Some(buf) => buf,
            None => return None,
        };

        let data = match golay::extended::decode(*buf as u32) {
            Some((data, err)) => data,
            None => return Some(Err(GolayUnrecoverable)),
        };

        assert!(self.inner.feed(Hexbit::new((data >> 6) as u8)).is_none());

        let hexbits = match self.inner.feed(Hexbit::new((data & 0x3F) as u8)) {
            Some(buf) => buf,
            None => return None,
        };

        let data = match reed_solomon::short::decode(hexbits) {
            Some((data, err)) => data,
            None => return Some(Err(ReedSolomonUnrecoverable)),
        };

        let mut bytes = [0; 9];
        HexbitBytes::new(data.iter().cloned())
            .collect_slice_checked(&mut bytes[..]);

        Some(Ok(LinkControlFields::new(bytes)))
    }
}
<commit_msg>Use LINK_CONTROL_BYTES in message<commit_after>use collect_slice::CollectSlice;

use bits::{Dibit, Hexbit, HexbitBytes};
use buffer::{Buffer, DibitStorage, VoiceHeaderStorage, VoiceExtraStorage};
use coding::{reed_solomon, golay};
use error::Result;

use voice::header::VoiceHeaderFields;
use voice::control::LinkControlFields;
use voice::consts::{HEADER_BYTES, LINK_CONTROL_BYTES};

use error::P25Error::*;

pub struct VoiceHeaderReceiver {
    dibits: Buffer<DibitStorage>,
    hexbits: Buffer<VoiceHeaderStorage>,
}

impl VoiceHeaderReceiver {
    pub fn new() -> VoiceHeaderReceiver {
        VoiceHeaderReceiver {
            dibits: Buffer::new(DibitStorage::new(9)),
            hexbits: Buffer::new(VoiceHeaderStorage::new()),
        }
    }

    pub fn feed(&mut self, dibit: Dibit) -> Option<Result<VoiceHeaderFields>> {
        let buf = match self.dibits.feed(dibit) {
            Some(buf) => *buf as u32,
            None => return None,
        };

        let data = match golay::shortened::decode(buf) {
            Some((data, err)) => data,
            None => return Some(Err(GolayUnrecoverable)),
        };

        let hexbits = match self.hexbits.feed(Hexbit::new(data)) {
            Some(buf) => buf,
            None => return None,
        };

        let data = match reed_solomon::long::decode(hexbits) {
            Some((data, err)) => data,
            None => return Some(Err(ReedSolomonUnrecoverable)),
        };

        let mut bytes = [0; HEADER_BYTES];
        HexbitBytes::new(data.iter().cloned())
            .collect_slice_checked(&mut bytes[..]);

        Some(Ok(VoiceHeaderFields::new(bytes)))
    }
}

pub struct VoiceLCTerminatorReceiver {
    outer: Buffer<DibitStorage>,
    inner: Buffer<VoiceExtraStorage>,
}

impl VoiceLCTerminatorReceiver {
    pub fn new() -> VoiceLCTerminatorReceiver {
        VoiceLCTerminatorReceiver {
            outer: Buffer::new(DibitStorage::new(12)),
            inner: Buffer::new(VoiceExtraStorage::new()),
        }
    }

    pub fn feed(&mut self, dibit: Dibit) -> Option<Result<LinkControlFields>> {
        let buf = match self.outer.feed(dibit) {
            Some(buf) => buf,
            None => return None,
        };

        let data = match golay::extended::decode(*buf as u32) {
            Some((data, err)) => data,
            None => return Some(Err(GolayUnrecoverable)),
        };

        assert!(self.inner.feed(Hexbit::new((data >> 6) as u8)).is_none());

        let hexbits = match self.inner.feed(Hexbit::new((data & 0x3F) as u8)) {
            Some(buf) => buf,
            None => return None,
        };

        let data = match reed_solomon::short::decode(hexbits) {
            Some((data, err)) => data,
            None => return Some(Err(ReedSolomonUnrecoverable)),
        };

        let mut bytes = [0; LINK_CONTROL_BYTES];
        HexbitBytes::new(data.iter().cloned())
            .collect_slice_checked(&mut bytes[..]);

        Some(Ok(LinkControlFields::new(bytes)))
    }
}
<|endoftext|>


<commit_before>//! Thread pool for blocking operations

use crate::loom::sync::{Arc, Condvar, Mutex};
use crate::loom::thread;
use crate::runtime::blocking::schedule::NoopSchedule;
use crate::runtime::blocking::shutdown;
use crate::runtime::builder::ThreadNameFn;
use crate::runtime::context;
use crate::runtime::task::{self, JoinHandle};
use crate::runtime::{Builder, Callback, Handle};

use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::time::Duration;

pub(crate) struct BlockingPool {
    spawner: Spawner,
    shutdown_rx: shutdown::Receiver,
}

#[derive(Clone)]
pub(crate) struct Spawner {
    inner: Arc<Inner>,
}

struct Inner {
    /// State shared between worker threads.
    shared: Mutex<Shared>,

    /// Pool threads wait on this.
    condvar: Condvar,

    /// Spawned threads use this name.
    thread_name: ThreadNameFn,

    /// Spawned thread stack size.
    stack_size: Option<usize>,

    /// Call after a thread starts.
    after_start: Option<Callback>,

    /// Call before a thread stops.
    before_stop: Option<Callback>,

    // Maximum number of threads.
    thread_cap: usize,

    // Customizable wait timeout.
    keep_alive: Duration,
}

struct Shared {
    queue: VecDeque<Task>,
    num_th: usize,
    num_idle: u32,
    num_notify: u32,
    shutdown: bool,
    shutdown_tx: Option<shutdown::Sender>,
    /// Prior to shutdown, we clean up JoinHandles by having each timed-out
    /// thread join on the previous timed-out thread. This is not strictly
    /// necessary but helps avoid Valgrind false positives, see
    /// <https://github.com/tokio-rs/tokio/commit/646fbae76535e397ef79dbcaacb945d4c829f666>
    /// for more information.
    last_exiting_thread: Option<thread::JoinHandle<()>>,
    /// This holds the JoinHandles for all running threads; on shutdown, the thread
    /// calling shutdown handles joining on these.
    worker_threads: HashMap<usize, thread::JoinHandle<()>>,
    /// This is a counter used to iterate worker_threads in a consistent order (for loom's
    /// benefit).
    worker_thread_index: usize,
}

pub(crate) struct Task {
    task: task::UnownedTask<NoopSchedule>,
    mandatory: Mandatory,
}

#[derive(PartialEq, Eq)]
pub(crate) enum Mandatory {
    #[cfg_attr(not(fs), allow(dead_code))]
    Mandatory,
    NonMandatory,
}

impl Task {
    pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
        Task { task, mandatory }
    }

    fn run(self) {
        self.task.run();
    }

    fn shutdown_or_run_if_mandatory(self) {
        match self.mandatory {
            Mandatory::NonMandatory => self.task.shutdown(),
            Mandatory::Mandatory => self.task.run(),
        }
    }
}

const KEEP_ALIVE: Duration = Duration::from_secs(10);

/// Runs the provided function on an executor dedicated to blocking operations.
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
/// in case of runtime shutdown.
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    let rt = context::current();
    rt.spawn_blocking(func)
}

cfg_fs! {
    #[cfg_attr(any(
        all(loom, not(test)), // the function is covered by loom tests
        test
    ), allow(dead_code))]
    /// Runs the provided function on an executor dedicated to blocking
    /// operations. Tasks will be scheduled as mandatory, meaning they are
    /// guaranteed to run unless a shutdown is already taking place. In case a
    /// shutdown is already taking place, `None` will be returned.
    pub(crate) fn spawn_mandatory_blocking<F, R>(func: F) -> Option<JoinHandle<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        let rt = context::current();
        rt.spawn_mandatory_blocking(func)
    }
}

// ===== impl BlockingPool =====

impl BlockingPool {
    pub(crate) fn new(builder: &Builder, thread_cap: usize) -> BlockingPool {
        let (shutdown_tx, shutdown_rx) = shutdown::channel();
        let keep_alive = builder.keep_alive.unwrap_or(KEEP_ALIVE);

        BlockingPool {
            spawner: Spawner {
                inner: Arc::new(Inner {
                    shared: Mutex::new(Shared {
                        queue: VecDeque::new(),
                        num_th: 0,
                        num_idle: 0,
                        num_notify: 0,
                        shutdown: false,
                        shutdown_tx: Some(shutdown_tx),
                        last_exiting_thread: None,
                        worker_threads: HashMap::new(),
                        worker_thread_index: 0,
                    }),
                    condvar: Condvar::new(),
                    thread_name: builder.thread_name.clone(),
                    stack_size: builder.thread_stack_size,
                    after_start: builder.after_start.clone(),
                    before_stop: builder.before_stop.clone(),
                    thread_cap,
                    keep_alive,
                }),
            },
            shutdown_rx,
        }
    }

    pub(crate) fn spawner(&self) -> &Spawner {
        &self.spawner
    }

    pub(crate) fn shutdown(&mut self, timeout: Option<Duration>) {
        let mut shared = self.spawner.inner.shared.lock();

        // The function can be called multiple times. First, by explicitly
        // calling `shutdown` then by the drop handler calling `shutdown`. This
        // prevents shutting down twice.
        if shared.shutdown {
            return;
        }

        shared.shutdown = true;
        shared.shutdown_tx = None;
        self.spawner.inner.condvar.notify_all();

        let last_exited_thread = std::mem::take(&mut shared.last_exiting_thread);
        let workers = std::mem::take(&mut shared.worker_threads);

        drop(shared);

        if self.shutdown_rx.wait(timeout) {
            let _ = last_exited_thread.map(|th| th.join());

            // Loom requires that execution be deterministic, so sort by thread ID before joining.
            // (HashMaps use a randomly-seeded hash function, so the order is nondeterministic)
            let mut workers: Vec<(usize, thread::JoinHandle<()>)> = workers.into_iter().collect();
            workers.sort_by_key(|(id, _)| *id);

            for (_id, handle) in workers.into_iter() {
                let _ = handle.join();
            }
        }
    }
}

impl Drop for BlockingPool {
    fn drop(&mut self) {
        self.shutdown(None);
    }
}

impl fmt::Debug for BlockingPool {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("BlockingPool").finish()
    }
}

// ===== impl Spawner =====

impl Spawner {
    pub(crate) fn spawn(&self, task: Task, rt: &Handle) -> Result<(), ()> {
        let mut shared = self.inner.shared.lock();

        if shared.shutdown {
            // Shutdown the task: it's fine to shutdown this task (even if
            // mandatory) because it was scheduled after the shutdown of the
            // runtime began.
            task.task.shutdown();

            // no need to even push this task; it would never get picked up
            return Err(());
        }

        shared.queue.push_back(task);

        if shared.num_idle == 0 {
            // No threads are able to process the task.

            if shared.num_th == self.inner.thread_cap {
                // At max number of threads
            } else {
                shared.num_th += 1;
                assert!(shared.shutdown_tx.is_some());
                let shutdown_tx = shared.shutdown_tx.clone();

                if let Some(shutdown_tx) = shutdown_tx {
                    let id = shared.worker_thread_index;
                    shared.worker_thread_index += 1;

                    let handle = self.spawn_thread(shutdown_tx, rt, id);

                    shared.worker_threads.insert(id, handle);
                }
            }
        } else {
            // Notify an idle worker thread. The notification counter
            // is used to count the needed amount of notifications
            // exactly. Thread libraries may generate spurious
            // wakeups, this counter is used to keep us in a
            // consistent state.
            shared.num_idle -= 1;
            shared.num_notify += 1;
            self.inner.condvar.notify_one();
        }

        Ok(())
    }

    fn spawn_thread(
        &self,
        shutdown_tx: shutdown::Sender,
        rt: &Handle,
        id: usize,
    ) -> thread::JoinHandle<()> {
        let mut builder = thread::Builder::new().name((self.inner.thread_name)());

        if let Some(stack_size) = self.inner.stack_size {
            builder = builder.stack_size(stack_size);
        }

        let rt = rt.clone();

        builder
            .spawn(move || {
                // Only the reference should be moved into the closure
                let _enter = crate::runtime::context::enter(rt.clone());
                rt.blocking_spawner.inner.run(id);
                drop(shutdown_tx);
            })
            .expect("OS can't spawn a new worker thread")
    }
}

impl Inner {
    fn run(&self, worker_thread_id: usize) {
        if let Some(f) = &self.after_start {
            f()
        }

        let mut shared = self.shared.lock();
        let mut join_on_thread = None;

        'main: loop {
            // BUSY
            while let Some(task) = shared.queue.pop_front() {
                drop(shared);
                task.run();

                shared = self.shared.lock();
            }

            // IDLE
            shared.num_idle += 1;

            while !shared.shutdown {
                let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap();

                shared = lock_result.0;
                let timeout_result = lock_result.1;

                if shared.num_notify != 0 {
                    // We have received a legitimate wakeup,
                    // acknowledge it by decrementing the counter
                    // and transition to the BUSY state.
                    shared.num_notify -= 1;
                    break;
                }

                // Even if the condvar "timed out", if the pool is entering the
                // shutdown phase, we want to perform the cleanup logic.
                if !shared.shutdown && timeout_result.timed_out() {
                    // We'll join the prior timed-out thread's JoinHandle after dropping the lock.
                    // This isn't done when shutting down, because the thread calling shutdown will
                    // handle joining everything.
                    let my_handle = shared.worker_threads.remove(&worker_thread_id);
                    join_on_thread = std::mem::replace(&mut shared.last_exiting_thread, my_handle);

                    break 'main;
                }

                // Spurious wakeup detected, go back to sleep.
            }

            if shared.shutdown {
                // Drain the queue
                while let Some(task) = shared.queue.pop_front() {
                    drop(shared);

                    task.shutdown_or_run_if_mandatory();

                    shared = self.shared.lock();
                }

                // Work was produced, and we "took" it (by decrementing num_notify).
                // This means that num_idle was decremented once for our wakeup.
                // But, since we are exiting, we need to "undo" that, as we'll stay idle.
                shared.num_idle += 1;
                // NOTE: Technically we should also do num_notify++ and notify again,
                // but since we're shutting down anyway, that won't be necessary.
                break;
            }
        }

        // Thread exit
        shared.num_th -= 1;

        // num_idle should now be tracked exactly, panic
        // with a descriptive message if it is not the
        // case.
        shared.num_idle = shared
            .num_idle
            .checked_sub(1)
            .expect("num_idle underflowed on thread exit");

        if shared.shutdown && shared.num_th == 0 {
            self.condvar.notify_one();
        }

        drop(shared);

        if let Some(f) = &self.before_stop {
            f()
        }

        if let Some(handle) = join_on_thread {
            let _ = handle.join();
        }
    }
}

impl fmt::Debug for Spawner {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("blocking::Spawner").finish()
    }
}
<commit_msg>runtime: recover when OS fails to spawn a new thread (#4485)<commit_after>//! Thread pool for blocking operations

use crate::loom::sync::{Arc, Condvar, Mutex};
use crate::loom::thread;
use crate::runtime::blocking::schedule::NoopSchedule;
use crate::runtime::blocking::shutdown;
use crate::runtime::builder::ThreadNameFn;
use crate::runtime::context;
use crate::runtime::task::{self, JoinHandle};
use crate::runtime::{Builder, Callback, Handle};

use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::time::Duration;

pub(crate) struct BlockingPool {
    spawner: Spawner,
    shutdown_rx: shutdown::Receiver,
}

#[derive(Clone)]
pub(crate) struct Spawner {
    inner: Arc<Inner>,
}

struct Inner {
    /// State shared between worker threads.
    shared: Mutex<Shared>,

    /// Pool threads wait on this.
    condvar: Condvar,

    /// Spawned threads use this name.
    thread_name: ThreadNameFn,

    /// Spawned thread stack size.
    stack_size: Option<usize>,

    /// Call after a thread starts.
    after_start: Option<Callback>,

    /// Call before a thread stops.
    before_stop: Option<Callback>,

    // Maximum number of threads.
    thread_cap: usize,

    // Customizable wait timeout.
    keep_alive: Duration,
}

struct Shared {
    queue: VecDeque<Task>,
    num_th: usize,
    num_idle: u32,
    num_notify: u32,
    shutdown: bool,
    shutdown_tx: Option<shutdown::Sender>,
    /// Prior to shutdown, we clean up JoinHandles by having each timed-out
    /// thread join on the previous timed-out thread. This is not strictly
    /// necessary but helps avoid Valgrind false positives, see
    /// <https://github.com/tokio-rs/tokio/commit/646fbae76535e397ef79dbcaacb945d4c829f666>
    /// for more information.
    last_exiting_thread: Option<thread::JoinHandle<()>>,
    /// This holds the JoinHandles for all running threads; on shutdown, the thread
    /// calling shutdown handles joining on these.
    worker_threads: HashMap<usize, thread::JoinHandle<()>>,
    /// This is a counter used to iterate worker_threads in a consistent order (for loom's
    /// benefit).
    worker_thread_index: usize,
}

pub(crate) struct Task {
    task: task::UnownedTask<NoopSchedule>,
    mandatory: Mandatory,
}

#[derive(PartialEq, Eq)]
pub(crate) enum Mandatory {
    #[cfg_attr(not(fs), allow(dead_code))]
    Mandatory,
    NonMandatory,
}

impl Task {
    pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
        Task { task, mandatory }
    }

    fn run(self) {
        self.task.run();
    }

    fn shutdown_or_run_if_mandatory(self) {
        match self.mandatory {
            Mandatory::NonMandatory => self.task.shutdown(),
            Mandatory::Mandatory => self.task.run(),
        }
    }
}

const KEEP_ALIVE: Duration = Duration::from_secs(10);

/// Runs the provided function on an executor dedicated to blocking operations.
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
/// in case of runtime shutdown.
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    let rt = context::current();
    rt.spawn_blocking(func)
}

cfg_fs! {
    #[cfg_attr(any(
        all(loom, not(test)), // the function is covered by loom tests
        test
    ), allow(dead_code))]
    /// Runs the provided function on an executor dedicated to blocking
    /// operations. Tasks will be scheduled as mandatory, meaning they are
    /// guaranteed to run unless a shutdown is already taking place. In case a
    /// shutdown is already taking place, `None` will be returned.
    pub(crate) fn spawn_mandatory_blocking<F, R>(func: F) -> Option<JoinHandle<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        let rt = context::current();
        rt.spawn_mandatory_blocking(func)
    }
}

// ===== impl BlockingPool =====

impl BlockingPool {
    pub(crate) fn new(builder: &Builder, thread_cap: usize) -> BlockingPool {
        let (shutdown_tx, shutdown_rx) = shutdown::channel();
        let keep_alive = builder.keep_alive.unwrap_or(KEEP_ALIVE);

        BlockingPool {
            spawner: Spawner {
                inner: Arc::new(Inner {
                    shared: Mutex::new(Shared {
                        queue: VecDeque::new(),
                        num_th: 0,
                        num_idle: 0,
                        num_notify: 0,
                        shutdown: false,
                        shutdown_tx: Some(shutdown_tx),
                        last_exiting_thread: None,
                        worker_threads: HashMap::new(),
                        worker_thread_index: 0,
                    }),
                    condvar: Condvar::new(),
                    thread_name: builder.thread_name.clone(),
                    stack_size: builder.thread_stack_size,
                    after_start: builder.after_start.clone(),
                    before_stop: builder.before_stop.clone(),
                    thread_cap,
                    keep_alive,
                }),
            },
            shutdown_rx,
        }
    }

    pub(crate) fn spawner(&self) -> &Spawner {
        &self.spawner
    }

    pub(crate) fn shutdown(&mut self, timeout: Option<Duration>) {
        let mut shared = self.spawner.inner.shared.lock();

        // The function can be called multiple times. First, by explicitly
        // calling `shutdown` then by the drop handler calling `shutdown`. This
        // prevents shutting down twice.
        if shared.shutdown {
            return;
        }

        shared.shutdown = true;
        shared.shutdown_tx = None;
        self.spawner.inner.condvar.notify_all();

        let last_exited_thread = std::mem::take(&mut shared.last_exiting_thread);
        let workers = std::mem::take(&mut shared.worker_threads);

        drop(shared);

        if self.shutdown_rx.wait(timeout) {
            let _ = last_exited_thread.map(|th| th.join());

            // Loom requires that execution be deterministic, so sort by thread ID before joining.
            // (HashMaps use a randomly-seeded hash function, so the order is nondeterministic)
            let mut workers: Vec<(usize, thread::JoinHandle<()>)> = workers.into_iter().collect();
            workers.sort_by_key(|(id, _)| *id);

            for (_id, handle) in workers.into_iter() {
                let _ = handle.join();
            }
        }
    }
}

impl Drop for BlockingPool {
    fn drop(&mut self) {
        self.shutdown(None);
    }
}

impl fmt::Debug for BlockingPool {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("BlockingPool").finish()
    }
}

// ===== impl Spawner =====

impl Spawner {
    pub(crate) fn spawn(&self, task: Task, rt: &Handle) -> Result<(), ()> {
        let mut shared = self.inner.shared.lock();

        if shared.shutdown {
            // Shutdown the task: it's fine to shutdown this task (even if
            // mandatory) because it was scheduled after the shutdown of the
            // runtime began.
            task.task.shutdown();

            // no need to even push this task; it would never get picked up
            return Err(());
        }

        shared.queue.push_back(task);

        if shared.num_idle == 0 {
            // No threads are able to process the task.

            if shared.num_th == self.inner.thread_cap {
                // At max number of threads
            } else {
                assert!(shared.shutdown_tx.is_some());
                let shutdown_tx = shared.shutdown_tx.clone();

                if let Some(shutdown_tx) = shutdown_tx {
                    let id = shared.worker_thread_index;

                    match self.spawn_thread(shutdown_tx, rt, id) {
                        Ok(handle) => {
                            shared.num_th += 1;
                            shared.worker_thread_index += 1;
                            shared.worker_threads.insert(id, handle);
                        }
                        Err(ref e) if is_temporary_os_thread_error(e) && shared.num_th > 0 => {
                            // OS temporarily failed to spawn a new thread.
                            // The task will be picked up eventually by a currently
                            // busy thread.
                        }
                        Err(e) => {
                            // The OS refused to spawn the thread and there is no thread
                            // to pick up the task that has just been pushed to the queue.
                            panic!("OS can't spawn worker thread: {}", e)
                        }
                    }
                }
            }
        } else {
            // Notify an idle worker thread. The notification counter
            // is used to count the needed amount of notifications
            // exactly. Thread libraries may generate spurious
            // wakeups, this counter is used to keep us in a
            // consistent state.
            shared.num_idle -= 1;
            shared.num_notify += 1;
            self.inner.condvar.notify_one();
        }

        Ok(())
    }

    fn spawn_thread(
        &self,
        shutdown_tx: shutdown::Sender,
        rt: &Handle,
        id: usize,
    ) -> std::io::Result<thread::JoinHandle<()>> {
        let mut builder = thread::Builder::new().name((self.inner.thread_name)());

        if let Some(stack_size) = self.inner.stack_size {
            builder = builder.stack_size(stack_size);
        }

        let rt = rt.clone();

        builder.spawn(move || {
            // Only the reference should be moved into the closure
            let _enter = crate::runtime::context::enter(rt.clone());
            rt.blocking_spawner.inner.run(id);
            drop(shutdown_tx);
        })
    }
}

// Tells whether the error when spawning a thread is temporary.
#[inline]
fn is_temporary_os_thread_error(error: &std::io::Error) -> bool {
    matches!(error.kind(), std::io::ErrorKind::WouldBlock)
}

impl Inner {
    fn run(&self, worker_thread_id: usize) {
        if let Some(f) = &self.after_start {
            f()
        }

        let mut shared = self.shared.lock();
        let mut join_on_thread = None;

        'main: loop {
            // BUSY
            while let Some(task) = shared.queue.pop_front() {
                drop(shared);
                task.run();

                shared = self.shared.lock();
            }

            // IDLE
            shared.num_idle += 1;

            while !shared.shutdown {
                let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap();

                shared = lock_result.0;
                let timeout_result = lock_result.1;

                if shared.num_notify != 0 {
                    // We have received a legitimate wakeup,
                    // acknowledge it by decrementing the counter
                    // and transition to the BUSY state.
                    shared.num_notify -= 1;
                    break;
                }

                // Even if the condvar "timed out", if the pool is entering the
                // shutdown phase, we want to perform the cleanup logic.
                if !shared.shutdown && timeout_result.timed_out() {
                    // We'll join the prior timed-out thread's JoinHandle after dropping the lock.
                    // This isn't done when shutting down, because the thread calling shutdown will
                    // handle joining everything.
                    let my_handle = shared.worker_threads.remove(&worker_thread_id);
                    join_on_thread = std::mem::replace(&mut shared.last_exiting_thread, my_handle);

                    break 'main;
                }

                // Spurious wakeup detected, go back to sleep.
            }

            if shared.shutdown {
                // Drain the queue
                while let Some(task) = shared.queue.pop_front() {
                    drop(shared);

                    task.shutdown_or_run_if_mandatory();

                    shared = self.shared.lock();
                }

                // Work was produced, and we "took" it (by decrementing num_notify).
                // This means that num_idle was decremented once for our wakeup.
                // But, since we are exiting, we need to "undo" that, as we'll stay idle.
                shared.num_idle += 1;
                // NOTE: Technically we should also do num_notify++ and notify again,
                // but since we're shutting down anyway, that won't be necessary.
                break;
            }
        }

        // Thread exit
        shared.num_th -= 1;

        // num_idle should now be tracked exactly, panic
        // with a descriptive message if it is not the
        // case.
        shared.num_idle = shared
            .num_idle
            .checked_sub(1)
            .expect("num_idle underflowed on thread exit");

        if shared.shutdown && shared.num_th == 0 {
            self.condvar.notify_one();
        }

        drop(shared);

        if let Some(f) = &self.before_stop {
            f()
        }

        if let Some(handle) = join_on_thread {
            let _ = handle.join();
        }
    }
}

impl fmt::Debug for Spawner {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("blocking::Spawner").finish()
    }
}
<|endoftext|>


<commit_before>use crate::park::Unpark;
use crate::time::driver::Inner;
use crate::time::Instant;

use loom::thread;

use std::sync::atomic::Ordering;
use std::sync::Arc;

struct MockUnpark;

impl Unpark for MockUnpark {
    fn unpark(&self) {}
}

#[test]
fn balanced_incr_and_decr() {
    const OPS: usize = 100;

    fn incr(inner: Arc<Inner>) {
        for _ in 0..OPS {
            inner.increment().expect("increment should not have failed");
            thread::yield_now();
        }
    }

    fn decr(inner: Arc<Inner>) {
        let mut ops_performed = 0;
        while ops_performed < OPS {
            if inner.num(Ordering::Relaxed) > 0 {
                ops_performed += 1;
                inner.decrement();
            }
            thread::yield_now();
        }
    }

    loom::model(|| {
        let unpark = Box::new(MockUnpark);
        let instant = Instant::now();

        let inner = Arc::new(Inner::new(instant, unpark));

        let incr_inner = inner.clone();
        let decr_inner = inner.clone();

        let incr_hndle = thread::spawn(move || incr(incr_inner));
        let decr_hndle = thread::spawn(move || decr(decr_inner));

        incr_hndle.join().expect("should never fail");
        decr_hndle.join().expect("should never fail");

        assert_eq!(inner.num(Ordering::SeqCst), 0);
    })
}
<commit_msg>timer: fix loom test (#2346)<commit_after>use crate::park::Unpark;
use crate::time::driver::Inner;
use crate::time::Instant;

use loom::thread;

use std::sync::atomic::Ordering;
use std::sync::Arc;

struct MockUnpark;

impl Unpark for MockUnpark {
    fn unpark(&self) {}
}

#[test]
fn balanced_incr_and_decr() {
    const OPS: usize = 5;

    fn incr(inner: Arc<Inner>) {
        for _ in 0..OPS {
            inner.increment().expect("increment should not have failed");
            thread::yield_now();
        }
    }

    fn decr(inner: Arc<Inner>) {
        let mut ops_performed = 0;
        while ops_performed < OPS {
            if inner.num(Ordering::Relaxed) > 0 {
                ops_performed += 1;
                inner.decrement();
            }
            thread::yield_now();
        }
    }

    loom::model(|| {
        let unpark = Box::new(MockUnpark);
        let instant = Instant::now();

        let inner = Arc::new(Inner::new(instant, unpark));

        let incr_inner = inner.clone();
        let decr_inner = inner.clone();

        let incr_hndle = thread::spawn(move || incr(incr_inner));
        let decr_hndle = thread::spawn(move || decr(decr_inner));

        incr_hndle.join().expect("should never fail");
        decr_hndle.join().expect("should never fail");

        assert_eq!(inner.num(Ordering::SeqCst), 0);
    })
}
<|endoftext|>


<commit_before>extern crate csv;

use super::pokemon_model;
use super::natures;
use super::enums;
use super::stats;
use super::determinant_values;
use super::movedex;
use super::moves;

use std::collections::HashMap;

/// Represents a single Token of a Pokemon with individual values for this token.
#[derive(Debug, Clone)]
pub struct PokemonToken {
    pokedex_id: usize,
    name: String,
    level: u16,
    height: u8,
    weight: u16,
    gender: enums::Gender,
    type_one: enums::Types,
    type_two: enums::Types,
    non_volatile_status: (enums::NonVolatile, u8),
    move_one: Option<(moves::Technique, u8)>,
    move_two: Option<(moves::Technique, u8)>,
    move_three: Option<(moves::Technique, u8)>,
    move_four: Option<(moves::Technique, u8)>,
    nature: natures::Nature,
    dv: determinant_values::Dv,
    base_stats: stats::Stats,
    current_stats: stats::Stats,
    end_of_turn_flags: HashMap<enums::EndOfTurn, u8>,
    description: String,
    mega_evolution: Option<pokemon_model::PokemonModel>,
}

impl PokemonToken {
    /// Provides a Pokemon Token from a given model.
    pub fn from_model(model: pokemon_model::PokemonModel) -> PokemonToken {
        let level = 50;
        let dv = determinant_values::Dv::get_dvs();
        let nature = natures::Nature::get_random_nature();
        let stats = stats::Stats::calculate_stats(model.clone(), dv.clone(), nature.clone(), level);

        PokemonToken {
            pokedex_id: model.get_id(),
            name: model.get_name(),
            level: level,
            height: model.get_height(),
            weight: model.get_weight(),
            gender: enums::get_gender(model.clone().get_gender_rate()),
            type_one: model.get_types().0,
            type_two: model.get_types().1,
            non_volatile_status: (enums::NonVolatile::Undefined, 0),
            move_one: None,
            move_two: None,
            move_three: None,
            move_four: None,
            nature: nature,
            dv: dv,
            base_stats: stats.clone(),
            current_stats: stats,
            end_of_turn_flags: HashMap::new(),
            description: model.get_description(),
            mega_evolution: model.get_mega(),
        }
    }
    pub fn is_asleep(&self) -> bool {
        self.non_volatile_status.0 == enums::NonVolatile::Sleep
    }
    pub fn get_moves(&self, dex: movedex::Movedex) -> movedex::Movedex {
        dex.for_token(self.get_level(), self.pokedex_id)
    }
    pub fn get_id(&self) -> usize {
        self.pokedex_id
    }
    pub fn get_name(&self) -> String {
        self.clone().name
    }
    pub fn get_level(&self) -> u16 {
        self.clone().level
    }
    pub fn get_gender(&self) -> enums::Gender {
        self.clone().gender
    }
    pub fn get_types(&self) -> (enums::Types, enums::Types) {
        (self.clone().type_one, self.clone().type_two)
    }
    pub fn get_nature(&self) -> natures::Nature {
        self.clone().nature
    }
    pub fn get_non_volatile(&self) -> (enums::NonVolatile, u8) {
        self.clone().non_volatile_status
    }
    pub fn get_dv(&self) -> determinant_values::Dv {
        self.clone().dv
    }
    pub fn get_current(&mut self) -> &mut stats::Stats {
        &mut self.current_stats
    }
    pub fn get_base(&self) -> stats::Stats {
        self.base_stats.clone()
    }
    pub fn get_end_of_turn_flags(&self) -> HashMap<enums::EndOfTurn, u8> {
        self.clone().end_of_turn_flags
    }

    pub fn get_description(&self) -> String {
        self.clone().description
    }

    pub fn get_mega(&self) -> Option<PokemonToken> {
        if self.mega_evolution.is_some() {
            return Some(PokemonToken::from_model(self.mega_evolution.clone().unwrap()));
        }
        None
    }

    /// Getter function for move one. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_one(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_one {
            return Some(x.0);
        }
        None
    }
    /// Getter function for move two. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_two(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_two {
            return Some(x.0);
        }
        None
    }
    /// Getter function for move three. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_three(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_three {
            return Some(x.0);
        }
        None
    }
    /// Getter function for move four. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_four(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_four {
            return Some(x.0);
        }
        None
    }

    pub fn set_non_volatile(&mut self, status: enums::NonVolatile) {
        self.non_volatile_status = (status, 0);
    }
    pub fn add_end_flag(&mut self, flag: enums::EndOfTurn) {
        self.end_of_turn_flags.insert(flag, 0);
    }
    pub fn set_moves(&mut self, moves: Vec<moves::Technique>) {
        self.move_one = Some((moves[0].clone(), moves[0].get_power_points().unwrap()));
        if moves.len() >= 1 {
            self.move_two = Some((moves[1].clone(), moves[1].get_power_points().unwrap()));
        }
        if moves.len() >= 2 {
            self.move_three = Some((moves[2].clone(), moves[2].get_power_points().unwrap()));
        }
        if moves.len() >= 3 {
            self.move_four = Some((moves[3].clone(), moves[3].get_power_points().unwrap()));
        }
    }
    pub fn set_type(&mut self, position: u8, change: enums::Types) {
        match position {
            0 => self.type_one = change,
            1 => self.type_two = change,
            _ => unreachable!(),
        }
    }
    pub fn decrement_ap(&mut self) {
        unimplemented!();
    }
}
<commit_msg>getter methods chages in pokemon_token.rs<commit_after>extern crate csv;

use super::pokemon_model;
use super::natures;
use super::enums;
use super::stats;
use super::determinant_values;
use super::movedex;
use super::moves;

use std::collections::HashMap;

/// Represents a single Token of a Pokemon with individual values for this token.
#[derive(Debug, Clone)]
pub struct PokemonToken {
    pokedex_id: usize,
    name: String,
    level: u16,
    height: u8,
    weight: u16,
    gender: enums::Gender,
    type_one: enums::Types,
    type_two: enums::Types,
    non_volatile_status: (enums::NonVolatile, u8),
    move_one: Option<(moves::Technique, u8)>,
    move_two: Option<(moves::Technique, u8)>,
    move_three: Option<(moves::Technique, u8)>,
    move_four: Option<(moves::Technique, u8)>,
    nature: natures::Nature,
    dv: determinant_values::Dv,
    base_stats: stats::Stats,
    current_stats: stats::Stats,
    end_of_turn_flags: HashMap<enums::EndOfTurn, u8>,
    description: String,
    mega_evolution: Option<pokemon_model::PokemonModel>,
}

impl PokemonToken {
    /// Provides a Pokemon Token from a given model.
    pub fn from_model(model: pokemon_model::PokemonModel) -> PokemonToken {
        let level = 50;
        let dv = determinant_values::Dv::get_dvs();
        let nature = natures::Nature::get_random_nature();
        let stats = stats::Stats::calculate_stats(model.clone(), dv.clone(), nature.clone(), level);

        PokemonToken {
            pokedex_id: model.get_id(),
            name: model.get_name(),
            level: level,
            height: model.get_height(),
            weight: model.get_weight(),
            gender: enums::get_gender(model.clone().get_gender_rate()),
            type_one: model.get_types().0,
            type_two: model.get_types().1,
            non_volatile_status: (enums::NonVolatile::Undefined, 0),
            move_one: None,
            move_two: None,
            move_three: None,
            move_four: None,
            nature: nature,
            dv: dv,
            base_stats: stats.clone(),
            current_stats: stats,
            end_of_turn_flags: HashMap::new(),
            description: model.get_description(),
            mega_evolution: model.get_mega(),
        }
    }
    pub fn is_asleep(&self) -> bool {
        self.non_volatile_status.0 == enums::NonVolatile::Sleep
    }
    pub fn get_moves(&self, dex: movedex::Movedex) -> movedex::Movedex {
        dex.for_token(self.get_level(), self.pokedex_id)
    }
    pub fn get_id(&self) -> usize {
        self.pokedex_id
    }
    pub fn get_name(&self) -> String {
        self.name.clone()
    }
    pub fn get_level(&self) -> u16 {
        self.level
    }
    pub fn get_gender(&self) -> enums::Gender {
        self.gender.clone()
    }
    pub fn get_types(&self) -> (enums::Types, enums::Types) {
        (self.type_one.clone(), self.type_two.clone())
    }
    pub fn get_nature(&self) -> natures::Nature {
        self.nature.clone()
    }
    pub fn get_non_volatile(&self) -> (enums::NonVolatile, u8) {
        self.non_volatile_status.clone()
    }
    pub fn get_dv(&self) -> determinant_values::Dv {
        self.dv.clone()
    }
    pub fn get_current(&mut self) -> &mut stats::Stats {
        &mut self.current_stats
    }
    pub fn get_base(&self) -> stats::Stats {
        self.base_stats.clone()
    }
    pub fn get_end_of_turn_flags(&self) -> HashMap<enums::EndOfTurn, u8> {
        self.end_of_turn_flags.clone()
    }

    pub fn get_description(&self) -> String {
        self.description.clone()
    }

    pub fn get_mega(&self) -> Option<PokemonToken> {
        if self.mega_evolution.is_some() {
            return Some(PokemonToken::from_model(self.mega_evolution.clone().unwrap()));
        }
        None
    }

    /// Getter function for move one. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_one(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_one {
            return Some(x.0);
        }
        None
    }
    /// Getter function for move two. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_two(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_two {
            return Some(x.0);
        }
        None
    }
    /// Getter function for move three. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_three(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_three {
            return Some(x.0);
        }
        None
    }
    /// Getter function for move four. If the move is set, the function returns it, if not,
    /// it returns None
    pub fn get_move_four(self) -> Option<moves::Technique> {
        if let Some(x) = self.move_four {
            return Some(x.0);
        }
        None
    }

    pub fn set_non_volatile(&mut self, status: enums::NonVolatile) {
        self.non_volatile_status = (status, 0);
    }
    pub fn add_end_flag(&mut self, flag: enums::EndOfTurn) {
        self.end_of_turn_flags.insert(flag, 0);
    }
    pub fn set_moves(&mut self, moves: Vec<moves::Technique>) {
        self.move_one = Some((moves[0].clone(), moves[0].get_power_points().unwrap()));
        if moves.len() >= 1 {
            self.move_two = Some((moves[1].clone(), moves[1].get_power_points().unwrap()));
        }
        if moves.len() >= 2 {
            self.move_three = Some((moves[2].clone(), moves[2].get_power_points().unwrap()));
        }
        if moves.len() >= 3 {
            self.move_four = Some((moves[3].clone(), moves[3].get_power_points().unwrap()));
        }
    }
    pub fn set_type(&mut self, position: u8, change: enums::Types) {
        match position {
            0 => self.type_one = change,
            1 => self.type_two = change,
            _ => unreachable!(),
        }
    }
    pub fn decrement_ap(&mut self) {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Div};
use std::time;

use euclid::{TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D};

use defs::*;
use math;
use math::*;
use shape::*;

// TODO: Do I need a custom AATransform that manages precision
// somehow?

// TODO: think about how movement will work: this is tricky. When a
// chunk is broken in to pieces, we need to make sure our
// current_shape and target_shape are switched to being ones that are
// in the correct chunk

#[derive(Debug)]
pub struct CameraState {
    pub camera_bounds: TypedRect<f32, DrawSpace>,

    pub current_chunk: TopChunk,
    pub current_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,
    pub current_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub target_chunk: TopChunk, // This should always be the parent chunk of the player chunk
    pub target_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub current_to_target_path: MonotonePath, // This should stay in sync with the above...
}

impl CameraState {
    fn center<T, U>(rect: &TypedRect<T, U>) -> TypedPoint2D<T, U>
    where
        T: Copy + Add<T, Output = T> + Div<T, Output = T> + From<f32>,
    {
        TypedPoint2D::new(
            rect.origin.x + rect.size.width / T::from(2.0),
            rect.origin.y + rect.size.height / T::from(2.0),
        )
    }

    fn intended_target(logical_state: &LogicalState) -> TopChunk {
        logical_state
            .universe
            .parent_of(&logical_state.player_chunk)
    }

    fn target_transform_for(
        chunk: &TopChunk,
        logical_state: &LogicalState,
        bounds: TypedRect<f32, DrawSpace>,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        let chunk_bounds = chunk.bounding_box(&logical_state.universe);

        transform::fit_rect_in(
            &chunk_bounds
                .to_f32()
                .inflate(DRAW_PARENT_MARGIN, DRAW_PARENT_MARGIN),
            &bounds,
        )
    }

    pub fn new(
        resolution: &TypedSize2D<u32, ScreenSpace>,
        logical_state: &LogicalState,
    ) -> CameraState {
        let camera_bounds = TypedRect::new(
            TypedPoint2D::zero(),
            TypedSize2D::new(resolution.width as f32, resolution.height as f32),
        );

        let start_chunk = CameraState::intended_target(&logical_state);
        let transform =
            CameraState::target_transform_for(&start_chunk, logical_state, camera_bounds);

        CameraState {
            camera_bounds: camera_bounds,

            current_chunk: start_chunk.clone(),
            current_transform: transform,
            current_neutral_transform: transform,

            target_chunk: start_chunk.clone(),
            target_neutral_transform: transform,

            current_to_target_path: MonotonePath::Zero,
        }
    }

    pub fn do_zoom(&mut self, logical_state: &LogicalState) -> () {
        let new_target = CameraState::intended_target(logical_state);
        self.target_chunk = new_target.clone();
        self.target_neutral_transform =
            CameraState::target_transform_for(&new_target, logical_state, self.camera_bounds);

        self.current_to_target_path = self.current_to_target_path.up_target();
    }

    pub fn scale_from_neutral(&self) -> f32 {
        transform::scale(
            &self
                .current_transform
                .post_mul(&self.current_neutral_transform.inverse().unwrap()),
        )
    }

    pub fn current_visual_level(&self) -> f32 {
        self.scale_from_neutral().log(ZOOM_SCALE as f32)
    }

    fn target_to_current_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, UniverseSpace> {
        self.current_to_target_path
            .as_delta_from(&logical_state.universe, &self.current_chunk)
            .0
            .to_scale_transform()
    }

    fn true_target_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        self.target_to_current_transform(logical_state)
            .post_mul(&self.target_neutral_transform)
    }

    pub fn update(&mut self, logical_state: &LogicalState, time_delta: time::Duration) -> () {
        // TODO: This is currently busted but somehow I get away with it?

        self.current_transform = transform::lerp(
            &self.current_transform,
            &self.true_target_transform(logical_state),
            1.0 - (-math::time::duration_to_secs(time_delta) * CAMERA_LERP_SPEED).exp(),
        );

        self.normalise(logical_state);
    }

    fn normalise(&mut self, logical_state: &LogicalState) -> () {
        let scale_from_neutral = self.scale_from_neutral();

        // MUST TODO: This will break if the individual chunks get too
        // big, need to scale by the size of the target/current chunk?
        // println!("{:#?}", scale_from_neutral);

        let need_normalisation =
            (scale_from_neutral > CAMERA_UPPER_NORMALISE_TRIGGER && self.current_to_target_path.zdelta() < 0) ||
            (scale_from_neutral < CAMERA_LOWER_NORMALISE_TRIGGER && self.current_to_target_path.zdelta() > 0);

        if need_normalisation
        {
            let (adjustment, new_current_chunk) = self
                .current_to_target_path
                .take(1)
                .as_delta_from(&logical_state.universe, &self.current_chunk);

            self.current_neutral_transform = CameraState::target_transform_for(
                &new_current_chunk,
                logical_state,
                self.camera_bounds,
            );
            self.current_chunk = new_current_chunk;

            self.current_transform = self
                .current_transform
                .pre_mul(&adjustment.invert().to_scale_transform());

            self.current_to_target_path = self.current_to_target_path.drop(1);
        }
    }

    // MUST TODO: This will start to matter when chunks are made
    pub fn recenter(&mut self, logical_state: &LogicalState) -> () {
        // let old_target_chunk = &self.target_chunk;
        // let new_target_chunk = CameraState::intended_target(logical_state);

        // self.target_chunk = new_target_chunk;

        // let old_current_chunk = &self.current_chunk;
        // let new_current_chunk = CameraState::intended_target(logical_state);

        // self.current_chunk = new_current_chunk;

        //unimplemented!();
    }
}
<commit_msg>Mark recenter as unimplemented<commit_after>use std::ops::{Add, Div};
use std::time;

use euclid::{TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D};

use defs::*;
use math;
use math::*;
use shape::*;

// TODO: Do I need a custom AATransform that manages precision
// somehow?

// TODO: think about how movement will work: this is tricky. When a
// chunk is broken in to pieces, we need to make sure our
// current_shape and target_shape are switched to being ones that are
// in the correct chunk

#[derive(Debug)]
pub struct CameraState {
    pub camera_bounds: TypedRect<f32, DrawSpace>,

    pub current_chunk: TopChunk,
    pub current_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,
    pub current_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub target_chunk: TopChunk, // This should always be the parent chunk of the player chunk
    pub target_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub current_to_target_path: MonotonePath, // This should stay in sync with the above...
}

impl CameraState {
    fn center<T, U>(rect: &TypedRect<T, U>) -> TypedPoint2D<T, U>
    where
        T: Copy + Add<T, Output = T> + Div<T, Output = T> + From<f32>,
    {
        TypedPoint2D::new(
            rect.origin.x + rect.size.width / T::from(2.0),
            rect.origin.y + rect.size.height / T::from(2.0),
        )
    }

    fn intended_target(logical_state: &LogicalState) -> TopChunk {
        logical_state
            .universe
            .parent_of(&logical_state.player_chunk)
    }

    fn target_transform_for(
        chunk: &TopChunk,
        logical_state: &LogicalState,
        bounds: TypedRect<f32, DrawSpace>,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        let chunk_bounds = chunk.bounding_box(&logical_state.universe);

        transform::fit_rect_in(
            &chunk_bounds
                .to_f32()
                .inflate(DRAW_PARENT_MARGIN, DRAW_PARENT_MARGIN),
            &bounds,
        )
    }

    pub fn new(
        resolution: &TypedSize2D<u32, ScreenSpace>,
        logical_state: &LogicalState,
    ) -> CameraState {
        let camera_bounds = TypedRect::new(
            TypedPoint2D::zero(),
            TypedSize2D::new(resolution.width as f32, resolution.height as f32),
        );

        let start_chunk = CameraState::intended_target(&logical_state);
        let transform =
            CameraState::target_transform_for(&start_chunk, logical_state, camera_bounds);

        CameraState {
            camera_bounds: camera_bounds,

            current_chunk: start_chunk.clone(),
            current_transform: transform,
            current_neutral_transform: transform,

            target_chunk: start_chunk.clone(),
            target_neutral_transform: transform,

            current_to_target_path: MonotonePath::Zero,
        }
    }

    pub fn do_zoom(&mut self, logical_state: &LogicalState) -> () {
        let new_target = CameraState::intended_target(logical_state);
        self.target_chunk = new_target.clone();
        self.target_neutral_transform =
            CameraState::target_transform_for(&new_target, logical_state, self.camera_bounds);

        self.current_to_target_path = self.current_to_target_path.up_target();
    }

    pub fn scale_from_neutral(&self) -> f32 {
        transform::scale(
            &self
                .current_transform
                .post_mul(&self.current_neutral_transform.inverse().unwrap()),
        )
    }

    pub fn current_visual_level(&self) -> f32 {
        self.scale_from_neutral().log(ZOOM_SCALE as f32)
    }

    fn target_to_current_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, UniverseSpace> {
        self.current_to_target_path
            .as_delta_from(&logical_state.universe, &self.current_chunk)
            .0
            .to_scale_transform()
    }

    fn true_target_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        self.target_to_current_transform(logical_state)
            .post_mul(&self.target_neutral_transform)
    }

    pub fn update(&mut self, logical_state: &LogicalState, time_delta: time::Duration) -> () {
        // TODO: This is currently busted but somehow I get away with it?

        self.current_transform = transform::lerp(
            &self.current_transform,
            &self.true_target_transform(logical_state),
            1.0 - (-math::time::duration_to_secs(time_delta) * CAMERA_LERP_SPEED).exp(),
        );

        self.normalise(logical_state);
    }

    fn normalise(&mut self, logical_state: &LogicalState) -> () {
        let scale_from_neutral = self.scale_from_neutral();

        // MUST TODO: This will break if the individual chunks get too
        // big, need to scale by the size of the target/current chunk?
        // println!("{:#?}", scale_from_neutral);

        let need_normalisation =
            (scale_from_neutral > CAMERA_UPPER_NORMALISE_TRIGGER && self.current_to_target_path.zdelta() < 0) ||
            (scale_from_neutral < CAMERA_LOWER_NORMALISE_TRIGGER && self.current_to_target_path.zdelta() > 0);

        if need_normalisation
        {
            let (adjustment, new_current_chunk) = self
                .current_to_target_path
                .take(1)
                .as_delta_from(&logical_state.universe, &self.current_chunk);

            self.current_neutral_transform = CameraState::target_transform_for(
                &new_current_chunk,
                logical_state,
                self.camera_bounds,
            );
            self.current_chunk = new_current_chunk;

            self.current_transform = self
                .current_transform
                .pre_mul(&adjustment.invert().to_scale_transform());

            self.current_to_target_path = self.current_to_target_path.drop(1);
        }
    }

    // MUST TODO: This will start to matter when chunks are made
    pub fn recenter(&mut self, logical_state: &LogicalState) -> () {
        unimplemented!();

        // let old_target_chunk = &self.target_chunk;
        // let new_target_chunk = CameraState::intended_target(logical_state);

        // self.target_chunk = new_target_chunk;

        // let old_current_chunk = &self.current_chunk;
        // let new_current_chunk = CameraState::intended_target(logical_state);

        // self.current_chunk = new_current_chunk;

        //unimplemented!();
    }
}
<|endoftext|>