<commit_before>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: obj/primitive/sphere.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      An arbitrarily subdivided Icosahedron.
*/

use math::{ Vec3f };
use primitive::Vertex_PC;
use primitive::Cube;

#[path = "../../gl/mod.rs"]
mod gl;
#[path = "../../gl/util.rs"]
mod util;
#[macro_escape]
#[path = "../../gl/check.rs"]
mod check;

pub struct Sphere
{
  radius: f32,
  vao: gl::GLuint,
  vbo: gl::GLuint,
  verts: ~[Vertex_PC],

  vox_vao: gl::GLuint,
  vox_vbo: gl::GLuint,
  voxels: ~[Cube],
}

impl Sphere
{
  pub fn new(new_radius: f32, new_subdivides: i32) -> Sphere
  {
    /* Magic numbers place all verts 1.0 from center. */
    let magic_x = 0.525731112119133606f32;
    let magic_z = 0.850650808352039932f32;

    let mut sphere = Sphere
    {
      radius: new_radius,
      vao: 0,
      vbo: 0,
      verts: ~[],

      vox_vao: 0,
      vox_vbo: 0,
      voxels: ~[],
    };

    let root_verts: [Vec3f, ..12] =
    [
      Vec3f::new(-magic_x, 0.0, magic_z),
      Vec3f::new(magic_x, 0.0, magic_z),
      Vec3f::new(-magic_x, 0.0, -magic_z),
      Vec3f::new(magic_x, 0.0, -magic_z),

      Vec3f::new(0.0, magic_z, magic_x),
      Vec3f::new(0.0, magic_z, -magic_x),
      Vec3f::new(0.0, -magic_z, magic_x),
      Vec3f::new(0.0, -magic_z, -magic_x),

      Vec3f::new(magic_z, magic_x, 0.0),
      Vec3f::new(-magic_z, magic_x, 0.0),
      Vec3f::new(magic_z, -magic_x, 0.0),
      Vec3f::new(-magic_z, -magic_x, 0.0),
    ];

    let verts: &[Vertex_PC] =
    &[
      /* http://www.glprogramming.com/red/chapter02.html#name8 */
      Vertex_PC::new(root_verts[0], root_verts[0]), Vertex_PC::new(root_verts[4], root_verts[0]), Vertex_PC::new(root_verts[1], root_verts[0]), Vertex_PC::new(root_verts[0], root_verts[1]), Vertex_PC::new(root_verts[9], root_verts[1]),   Vertex_PC::new(root_verts[4],  root_verts[1]),
      Vertex_PC::new(root_verts[9], root_verts[2]), Vertex_PC::new(root_verts[5], root_verts[2]), Vertex_PC::new(root_verts[4], root_verts[2]), Vertex_PC::new(root_verts[4], root_verts[3]), Vertex_PC::new(root_verts[5], root_verts[3]),   Vertex_PC::new(root_verts[8],  root_verts[3]),
      Vertex_PC::new(root_verts[4], root_verts[4]), Vertex_PC::new(root_verts[8], root_verts[4]), Vertex_PC::new(root_verts[1], root_verts[4]), Vertex_PC::new(root_verts[8], root_verts[5]), Vertex_PC::new(root_verts[10],root_verts[5]),  Vertex_PC::new(root_verts[1],  root_verts[5]),
      Vertex_PC::new(root_verts[8], root_verts[6]), Vertex_PC::new(root_verts[3], root_verts[6]), Vertex_PC::new(root_verts[10],root_verts[6]), Vertex_PC::new(root_verts[5], root_verts[7]), Vertex_PC::new(root_verts[3], root_verts[7]),  Vertex_PC::new(root_verts[8],  root_verts[7]),
      Vertex_PC::new(root_verts[5], root_verts[8]), Vertex_PC::new(root_verts[2], root_verts[8]), Vertex_PC::new(root_verts[3], root_verts[8]), Vertex_PC::new(root_verts[2], root_verts[9]), Vertex_PC::new(root_verts[7], root_verts[9]),   Vertex_PC::new(root_verts[3],  root_verts[9]),
      Vertex_PC::new(root_verts[7], root_verts[1]), Vertex_PC::new(root_verts[10],root_verts[1]), Vertex_PC::new(root_verts[3], root_verts[1]), Vertex_PC::new(root_verts[7], root_verts[2]), Vertex_PC::new(root_verts[6], root_verts[2]),  Vertex_PC::new(root_verts[10], root_verts[2]),
      Vertex_PC::new(root_verts[7], root_verts[3]), Vertex_PC::new(root_verts[11],root_verts[3]), Vertex_PC::new(root_verts[6], root_verts[3]), Vertex_PC::new(root_verts[11],root_verts[4]), Vertex_PC::new(root_verts[0], root_verts[4]), Vertex_PC::new(root_verts[6],   root_verts[4]),
      Vertex_PC::new(root_verts[0], root_verts[5]), Vertex_PC::new(root_verts[1], root_verts[5]), Vertex_PC::new(root_verts[6], root_verts[5]), Vertex_PC::new(root_verts[6], root_verts[6]), Vertex_PC::new(root_verts[1], root_verts[6]),    Vertex_PC::new(root_verts[10], root_verts[6]),
      Vertex_PC::new(root_verts[9], root_verts[7]), Vertex_PC::new(root_verts[0], root_verts[7]), Vertex_PC::new(root_verts[11],root_verts[7]), Vertex_PC::new(root_verts[9], root_verts[8]), Vertex_PC::new(root_verts[11],root_verts[8]), Vertex_PC::new(root_verts[2],  root_verts[8]),
      Vertex_PC::new(root_verts[9], root_verts[9]), Vertex_PC::new(root_verts[2], root_verts[9]), Vertex_PC::new(root_verts[5], root_verts[9]), Vertex_PC::new(root_verts[7], root_verts[0]), Vertex_PC::new(root_verts[2], root_verts[0]),    Vertex_PC::new(root_verts[11], root_verts[0]),
    ];
    for uint::range_step(0, verts.len(), 3) |x|
    { sphere.subdivide(verts[x], verts[x + 1], verts[x + 2], new_subdivides); }
    sphere.voxels = voxelize(sphere.verts);

    sphere.vao = check!(gl::gen_vertex_arrays(1))[0]; /* TODO: Check these. */
    sphere.vbo = check!(gl::gen_buffers(1))[0];
    check!(gl::bind_vertex_array(sphere.vao));
    check!(gl::bind_buffer(gl::ARRAY_BUFFER, sphere.vbo));
    check!(gl::buffer_data(gl::ARRAY_BUFFER, sphere.verts, gl::STATIC_DRAW));

    sphere.vox_vao = check!(gl::gen_vertex_arrays(1))[0]; /* TODO: Check these. */
    sphere.vox_vbo = check!(gl::gen_buffers(1))[0];
    check!(gl::bind_vertex_array(sphere.vox_vao));
    check!(gl::bind_buffer(gl::ARRAY_BUFFER, sphere.vox_vbo));
    check!(gl::buffer_data(gl::ARRAY_BUFFER, sphere.voxels, gl::STATIC_DRAW));

    sphere
  }

  /* Recursive subdivide for a given triangle. */
  priv fn subdivide(&mut self, v1: Vertex_PC, v2: Vertex_PC, v3: Vertex_PC, depth: i32)
  {
    if depth == 0
    {
      self.verts.push(v1);
      self.verts.push(v2);
      self.verts.push(v3);
      return;
    }

    let mut v12 = Vertex_PC::zero(), v23 = Vertex_PC::zero(), v31 = Vertex_PC::zero();
    v12.color = v1.color;
    v23.color = v1.color;
    v31.color = v1.color;

    v12.position.x = v1.position.x + v2.position.x; /* TODO: Lack of clean mutable indexing. */
    v23.position.x = v2.position.x + v3.position.x;
    v31.position.x = v3.position.x + v1.position.x;

    v12.position.y = v1.position.y + v2.position.y;
    v23.position.y = v2.position.y + v3.position.y;
    v31.position.y = v3.position.y + v1.position.y;

    v12.position.z = v1.position.z + v2.position.z;
    v23.position.z = v2.position.z + v3.position.z;
    v31.position.z = v3.position.z + v1.position.z;

    v12.position.normalize();
    v23.position.normalize();
    v31.position.normalize();
    self.subdivide(v1, v12, v31, depth - 1);
    self.subdivide(v2, v23, v12, depth - 1);
    self.subdivide(v3, v31, v23, depth - 1);
    self.subdivide(v12, v23, v31, depth - 1);
  }

  pub fn draw(&self)
  {
    {
      check!(gl::bind_vertex_array(self.vao));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, self.vbo));

      check!(gl::vertex_attrib_pointer_f32(0, 3, false, (sys::size_of::<Vertex_PC>()) as i32, 0));
      check!(gl::vertex_attrib_pointer_f32(1, 3, false, (sys::size_of::<Vertex_PC>()) as i32, sys::size_of::<Vec3f>() as u32));
      check!(gl::enable_vertex_attrib_array(0));
      check!(gl::enable_vertex_attrib_array(1));

      check!(gl::draw_arrays(gl::TRIANGLES, 0, (self.verts.len() as i32)));

      check!(gl::disable_vertex_attrib_array(0));
      check!(gl::disable_vertex_attrib_array(1));
      check!(gl::bind_vertex_array(0));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, 0));
    }

    {
      check!(gl::bind_vertex_array(self.vox_vao));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, self.vox_vbo));

      check!(gl::vertex_attrib_pointer_f32(0, 3, false, (sys::size_of::<Vertex_PC>()) as i32, 0));
      check!(gl::vertex_attrib_pointer_f32(1, 3, false, (sys::size_of::<Vertex_PC>()) as i32, sys::size_of::<Vec3f>() as u32));
      check!(gl::enable_vertex_attrib_array(0));
      check!(gl::enable_vertex_attrib_array(1));

      check!(gl::polygon_mode(gl::FRONT_AND_BACK, gl::LINE));
      check!(gl::draw_arrays(gl::TRIANGLES, 0, (self.voxels.len() as i32 * 36)));
      check!(gl::polygon_mode(gl::FRONT_AND_BACK, gl::FILL));

      check!(gl::disable_vertex_attrib_array(0));
      check!(gl::disable_vertex_attrib_array(1));
      check!(gl::bind_vertex_array(0));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, 0));
    }
  }
}

macro_rules! index
(
  ($arr:ident[$x:expr][$y:expr][$z:expr]) =>
  (
    $arr[($z * resolution * resolution) + (y * resolution) + x]
  )
)
priv fn voxelize(verts: &[Vertex_PC]) -> ~[Cube]
{
  /* Require at least one triangle. */
  assert!(verts.len() >= 3);

  let resolution = 10.0f32;

  /* Bounding box of vert dimensions. */
  let mut min = Vec3f::new(verts[0].position.x, verts[0].position.y, verts[0].position.z);
  let mut max = Vec3f::new(verts[0].position.x, verts[0].position.y, verts[0].position.z);
  for verts.each |curr|
  {
    min.x = cmp::min(min.x, curr.position.x);
    min.y = cmp::min(min.y, curr.position.y);
    min.z = cmp::min(min.z, curr.position.z);

    max.x = cmp::max(max.x, curr.position.x);
    max.y = cmp::max(max.y, curr.position.y);
    max.z = cmp::max(max.z, curr.position.z);
  }
  let center = Vec3f::new(max.x - ((max.x - min.x) / 2.0), max.y - ((max.y - min.y) / 2.0), max.z - ((max.z - min.z) / 2.0));
  io::println(fmt!("Min: %s", min.to_str()));
  io::println(fmt!("Max: %s", max.to_str()));
  io::println(fmt!("Center: %s", center.to_str()));

  /* Calculate, given resolution (how many voxels across), the dimensions of a voxel. */
  let size = cmp::max(max.x - min.x, cmp::max(max.y - min.y, max.z - min.z)) / resolution;
  io::println(fmt!("Size: %?", size));

  /* Create 3D array of voxels. */
  let mid_offset = ((resolution / 2.0) * size);
  io::println(fmt!("Mid Offset: %s", mid_offset.to_str()));
  let mut new_verts: ~[Cube] = vec::with_capacity((f32::pow(resolution + 1.0, 3.0)) as uint);
  for uint::range(0, resolution as uint + 1) |z| /* TODO: Calculate max/min here to find out where this actually lies. */
  { for uint::range(0, resolution as uint + 1) |y|
    { for uint::range(0, resolution as uint + 1) |x|
      {
        let c = Vec3f::new((x as f32 * size) - mid_offset, (y as f32 * size) - mid_offset, (z as f32 * size) - mid_offset) - center;
        let cube = Cube::new(size, c);
        new_verts.push(cube);
      }
    }
  }
  assert!(new_verts.len() == (f32::pow(resolution + 1.0, 3.0)) as uint);

  /* Triangle -> box collision checking to enable voxels. */

  /* Pass back on to sphere for rendering. */
  new_verts
}

<commit_msg> #12 Moved sphere to Triangles and cleaned up<commit_after>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: obj/primitive/sphere.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      An arbitrarily subdivided Icosahedron.
*/

use math::{ Vec3f };
use primitive::Vertex_PC;
use primitive::Triangle;
use primitive::Cube;

#[path = "../../gl/mod.rs"]
mod gl;
#[path = "../../gl/util.rs"]
mod util;
#[macro_escape]
#[path = "../../gl/check.rs"]
mod check;

pub struct Sphere
{
  radius: f32,
  vao: gl::GLuint,
  vbo: gl::GLuint,
  tris: ~[Triangle],

  vox_vao: gl::GLuint,
  vox_vbo: gl::GLuint,
  voxels: ~[Cube],
}

impl Sphere
{
  pub fn new(new_radius: f32, new_subdivides: i32) -> Sphere
  {
    /* Magic numbers place all verts 1.0 from center. */
    let magic_x = 0.525731112119133606f32;
    let magic_z = 0.850650808352039932f32;

    let mut sphere = Sphere
    {
      radius: new_radius,
      vao: 0,
      vbo: 0,
      tris: ~[],

      vox_vao: 0,
      vox_vbo: 0,
      voxels: ~[],
    };

    let root_verts: [Vec3f, ..12] =
    [
      Vec3f::new(-magic_x, 0.0, magic_z),
      Vec3f::new(magic_x, 0.0, magic_z),
      Vec3f::new(-magic_x, 0.0, -magic_z),
      Vec3f::new(magic_x, 0.0, -magic_z),

      Vec3f::new(0.0, magic_z, magic_x),
      Vec3f::new(0.0, magic_z, -magic_x),
      Vec3f::new(0.0, -magic_z, magic_x),
      Vec3f::new(0.0, -magic_z, -magic_x),

      Vec3f::new(magic_z, magic_x, 0.0),
      Vec3f::new(-magic_z, magic_x, 0.0),
      Vec3f::new(magic_z, -magic_x, 0.0),
      Vec3f::new(-magic_z, -magic_x, 0.0),
    ];

    let tris: &[Triangle] =
    &[
      /* http://www.glprogramming.com/red/chapter02.html#name8 */
      Triangle::new(Vertex_PC::new(root_verts[0], root_verts[0]), Vertex_PC::new(root_verts[4], root_verts[0]), Vertex_PC::new(root_verts[1], root_verts[0])),
      Triangle::new(Vertex_PC::new(root_verts[0], root_verts[1]), Vertex_PC::new(root_verts[9], root_verts[1]), Vertex_PC::new(root_verts[4], root_verts[1])),
      Triangle::new(Vertex_PC::new(root_verts[9], root_verts[2]), Vertex_PC::new(root_verts[5], root_verts[2]), Vertex_PC::new(root_verts[4], root_verts[2])),
      Triangle::new(Vertex_PC::new(root_verts[4], root_verts[3]), Vertex_PC::new(root_verts[5], root_verts[3]), Vertex_PC::new(root_verts[8], root_verts[3])),
      Triangle::new(Vertex_PC::new(root_verts[4], root_verts[4]), Vertex_PC::new(root_verts[8], root_verts[4]), Vertex_PC::new(root_verts[1], root_verts[4])),
      Triangle::new(Vertex_PC::new(root_verts[8], root_verts[5]), Vertex_PC::new(root_verts[10],root_verts[5]), Vertex_PC::new(root_verts[1], root_verts[5])),
      Triangle::new(Vertex_PC::new(root_verts[8], root_verts[6]), Vertex_PC::new(root_verts[3], root_verts[6]), Vertex_PC::new(root_verts[10],root_verts[6])),
      Triangle::new(Vertex_PC::new(root_verts[5], root_verts[7]), Vertex_PC::new(root_verts[3], root_verts[7]), Vertex_PC::new(root_verts[8], root_verts[7])),
      Triangle::new(Vertex_PC::new(root_verts[5], root_verts[8]), Vertex_PC::new(root_verts[2], root_verts[8]), Vertex_PC::new(root_verts[3], root_verts[8])),
      Triangle::new(Vertex_PC::new(root_verts[2], root_verts[9]), Vertex_PC::new(root_verts[7], root_verts[9]), Vertex_PC::new(root_verts[3], root_verts[9])),
      Triangle::new(Vertex_PC::new(root_verts[7], root_verts[1]), Vertex_PC::new(root_verts[10],root_verts[1]), Vertex_PC::new(root_verts[3], root_verts[1])),
      Triangle::new(Vertex_PC::new(root_verts[7], root_verts[2]), Vertex_PC::new(root_verts[6], root_verts[2]), Vertex_PC::new(root_verts[10],root_verts[2])),
      Triangle::new(Vertex_PC::new(root_verts[7], root_verts[3]), Vertex_PC::new(root_verts[11],root_verts[3]), Vertex_PC::new(root_verts[6], root_verts[3])),
      Triangle::new(Vertex_PC::new(root_verts[11],root_verts[4]), Vertex_PC::new(root_verts[0], root_verts[4]), Vertex_PC::new(root_verts[6], root_verts[4])),
      Triangle::new(Vertex_PC::new(root_verts[0], root_verts[5]), Vertex_PC::new(root_verts[1], root_verts[5]), Vertex_PC::new(root_verts[6], root_verts[5])),
      Triangle::new(Vertex_PC::new(root_verts[6], root_verts[6]), Vertex_PC::new(root_verts[1], root_verts[6]), Vertex_PC::new(root_verts[10],root_verts[6])),
      Triangle::new(Vertex_PC::new(root_verts[9], root_verts[7]), Vertex_PC::new(root_verts[0], root_verts[7]), Vertex_PC::new(root_verts[11],root_verts[7])),
      Triangle::new(Vertex_PC::new(root_verts[9], root_verts[8]), Vertex_PC::new(root_verts[11],root_verts[8]), Vertex_PC::new(root_verts[2], root_verts[8])),
      Triangle::new(Vertex_PC::new(root_verts[9], root_verts[9]), Vertex_PC::new(root_verts[2], root_verts[9]), Vertex_PC::new(root_verts[5], root_verts[9])),
      Triangle::new(Vertex_PC::new(root_verts[7], root_verts[0]), Vertex_PC::new(root_verts[2], root_verts[0]), Vertex_PC::new(root_verts[11],root_verts[0])),
    ];
    for uint::range(0, tris.len()) |x|
    { sphere.subdivide(tris[x], new_subdivides); }
    sphere.voxels = voxelize(sphere.tris, 1);

    sphere.vao = check!(gl::gen_vertex_arrays(1))[0]; /* TODO: Check these. */
    sphere.vbo = check!(gl::gen_buffers(1))[0];
    check!(gl::bind_vertex_array(sphere.vao));
    check!(gl::bind_buffer(gl::ARRAY_BUFFER, sphere.vbo));
    check!(gl::buffer_data(gl::ARRAY_BUFFER, sphere.tris, gl::STATIC_DRAW));

    sphere.vox_vao = check!(gl::gen_vertex_arrays(1))[0]; /* TODO: Check these. */
    sphere.vox_vbo = check!(gl::gen_buffers(1))[0];
    check!(gl::bind_vertex_array(sphere.vox_vao));
    check!(gl::bind_buffer(gl::ARRAY_BUFFER, sphere.vox_vbo));
    check!(gl::buffer_data(gl::ARRAY_BUFFER, sphere.voxels, gl::STATIC_DRAW));

    sphere
  }

  /* Recursive subdivide for a given triangle. */
  priv fn subdivide(&mut self, tri: Triangle, depth: i32)
  {
    if depth == 0
    {
      self.tris.push(tri);
      return;
    }

    let mut v12 = Vertex_PC::zero(), v23 = Vertex_PC::zero(), v31 = Vertex_PC::zero();
    v12.color = tri.verts[0].color;
    v23.color = tri.verts[0].color;
    v31.color = tri.verts[0].color;

    v12.position.x = tri.verts[0].position.x + tri.verts[1].position.x; /* TODO: Lack of clean mutable indexing. */
    v23.position.x = tri.verts[1].position.x + tri.verts[2].position.x;
    v31.position.x = tri.verts[2].position.x + tri.verts[0].position.x;

    v12.position.y = tri.verts[0].position.y + tri.verts[1].position.y;
    v23.position.y = tri.verts[1].position.y + tri.verts[2].position.y;
    v31.position.y = tri.verts[2].position.y + tri.verts[0].position.y;

    v12.position.z = tri.verts[0].position.z + tri.verts[1].position.z;
    v23.position.z = tri.verts[1].position.z + tri.verts[2].position.z;
    v31.position.z = tri.verts[2].position.z + tri.verts[0].position.z;

    v12.position.normalize();
    v23.position.normalize();
    v31.position.normalize();
    self.subdivide(Triangle::new(tri.verts[0], v12, v31), depth - 1);
    self.subdivide(Triangle::new(tri.verts[1], v23, v12), depth - 1);
    self.subdivide(Triangle::new(tri.verts[2], v31, v23), depth - 1);
    self.subdivide(Triangle::new(v12, v23, v31), depth - 1);
  }

  pub fn draw(&self)
  {
    {
      check!(gl::bind_vertex_array(self.vao));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, self.vbo));

      check!(gl::vertex_attrib_pointer_f32(0, 3, false, (sys::size_of::<Vertex_PC>()) as i32, 0));
      check!(gl::vertex_attrib_pointer_f32(1, 3, false, (sys::size_of::<Vertex_PC>()) as i32, sys::size_of::<Vec3f>() as u32));
      check!(gl::enable_vertex_attrib_array(0));
      check!(gl::enable_vertex_attrib_array(1));

      check!(gl::draw_arrays(gl::TRIANGLES, 0, (self.tris.len() as i32 * 3)));

      check!(gl::disable_vertex_attrib_array(0));
      check!(gl::disable_vertex_attrib_array(1));
      check!(gl::bind_vertex_array(0));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, 0));
    }

    {
      check!(gl::bind_vertex_array(self.vox_vao));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, self.vox_vbo));

      check!(gl::vertex_attrib_pointer_f32(0, 3, false, (sys::size_of::<Vertex_PC>()) as i32, 0));
      check!(gl::vertex_attrib_pointer_f32(1, 3, false, (sys::size_of::<Vertex_PC>()) as i32, sys::size_of::<Vec3f>() as u32));
      check!(gl::enable_vertex_attrib_array(0));
      check!(gl::enable_vertex_attrib_array(1));

      check!(gl::polygon_mode(gl::FRONT_AND_BACK, gl::LINE));
      check!(gl::draw_arrays(gl::TRIANGLES, 0, (self.voxels.len() as i32 * 36)));
      check!(gl::polygon_mode(gl::FRONT_AND_BACK, gl::FILL));

      check!(gl::disable_vertex_attrib_array(0));
      check!(gl::disable_vertex_attrib_array(1));
      check!(gl::bind_vertex_array(0));
      check!(gl::bind_buffer(gl::ARRAY_BUFFER, 0));
    }
  }
}

macro_rules! index
(
  ($arr:ident[$x:expr][$y:expr][$z:expr]) =>
  (
    $arr[($z * resolution * resolution) + (y * resolution) + x]
  )
)
priv fn voxelize(tris: &[Triangle], resolution: i32) -> ~[Cube]
{
  /* Require at least one triangle. */
  assert!(tris.len() >= 3);

  /* Bounding box of vert dimensions. */
  let mut min = Vec3f::new(tris[0].verts[0].position.x, tris[0].verts[0].position.y, tris[0].verts[0].position.z);
  let mut max = Vec3f::new(tris[0].verts[0].position.x, tris[0].verts[0].position.y, tris[0].verts[0].position.z);
  for tris.each |curr|
  {
    for curr.verts.each |vert|
    {
      min.x = cmp::min(min.x, vert.position.x);
      min.y = cmp::min(min.y, vert.position.y);
      min.z = cmp::min(min.z, vert.position.z);

      max.x = cmp::max(max.x, vert.position.x);
      max.y = cmp::max(max.y, vert.position.y);
      max.z = cmp::max(max.z, vert.position.z);
    }
  }
  let center = Vec3f::new(max.x - ((max.x - min.x) / 2.0), max.y - ((max.y - min.y) / 2.0), max.z - ((max.z - min.z) / 2.0));

  /* Calculate, given resolution (how many voxels across), the dimensions of a voxel. */
  let size = cmp::max(max.x - min.x, cmp::max(max.y - min.y, max.z - min.z)) / (resolution as f32);

  /* Create 3D array of voxels. */
  let mid_offset = (((resolution  as f32) / 2.0) * size);
  let mut voxels: ~[Cube] = vec::with_capacity((f32::pow((resolution + 1) as f32, 3.0)) as uint);
  for uint::range(0, resolution as uint + 1) |z|
  { for uint::range(0, resolution as uint + 1) |y|
    { for uint::range(0, resolution as uint + 1) |x|
      {
        let c = Vec3f::new((x as f32 * size) - mid_offset, (y as f32 * size) - mid_offset, (z as f32 * size) - mid_offset) - center;
        let cube = Cube::new(size, c);
        voxels.push(cube);
      }
    }
  }
  assert!(voxels.len() == (f32::pow((resolution + 1) as f32, 3.0)) as uint);

  /* Triangle -> box collision checking to enable voxels. */

  /* Pass back on to sphere for rendering. */
  voxels
}

<|endoftext|>


<commit_before>use seed::prelude::*;
use crate::Msg;
use std::convert::TryInto;

use num_traits::{pow};
use num_bigint::{BigUint,ToBigUint};

mod fermat_utils {
    use seed::prelude::*;
    use crate::Msg;

    pub struct Fermat<'a> {
		pub n: u64,
		pub f: u64,
		pub digits: u64,
		pub prime_factors: &'a str,
        pub download: &'a str,
	}

    pub fn fermats<'a>() -> Vec<Fermat<'a>> {
        vec![
			Fermat {n: 11, f: 0, digits:  617, prime_factors: "P27567 × 974849 × 167988556341760475137 × 3560841906445833920513 × P564", download: "F0"},
            Fermat {n: 10, f: 0, digits:  309, prime_factors: "45592577 × 6487031809 × 4659775785220018543264560743076778192897 × P252", download: "F0"},
            Fermat {n:  9, f: 0, digits:  155, prime_factors: "2424833 × 7455602825647884208337395736200454918783366342657 × P99", download: "F0"},
            Fermat {n:  8, f: 0, digits:   78, prime_factors: "1238926361552897 × P62", download: "F0"},
            Fermat {n:  7, f: 0, digits:   39, prime_factors: "59649589127497217 × 5704689200685129054721", download: "F0"},
            Fermat {n:  6, f: 0, digits:   20, prime_factors: "274177 × 67280421310721", download: "F0"},
            Fermat {n:  5, f: 0, digits:   10, prime_factors: "P116 × 6700417", download: "F0"},
            Fermat {n:  4, f: 0, digits:    5, prime_factors: "P6543", download: "F0"},
            Fermat {n:  3, f: 0, digits:    3, prime_factors: "P55", download: "F0"},
            Fermat {n:  2, f: 0, digits:    2, prime_factors: "P7", download: "F0"},
            Fermat {n:  1, f: 0, digits:    1, prime_factors: "P3", download: "F0"},
            Fermat {n:  0, f: 0, digits:    1, prime_factors: "P2", download: "F0"},
        ]
    }

    pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = format!("data:text/plain,{}",&filecontent);
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let two:usize = 2;

    let mut fermats = fermat_utils::fermats();

    for n in 0..fermats.len() {
        let download_filename:String = format!("F{}.txt",&fermats[n].n.to_string());

		let equation:String = format!("2{}+1",&(two.pow((fermats.len()-1-n).try_into().unwrap())).to_string());
        let nth_fermat:usize = fermats.len()-1-n;//counts from 11 to 0
        let fermat_value:BigUint = pow(two.to_biguint().unwrap(), pow(two, nth_fermat)) + 1.to_biguint().unwrap();

        html.push(
            tr![
                td![fermats[n].n.to_string()],//rank
				td![El::from_html(&equation)],//fermat number as a formula

                td![fermats[n].digits.to_string()],//digits in length
                td![El::from_html(fermats[n].prime_factors)],//prime factors
                td![fermat_utils::save_as_file(download_filename,fermat_value.to_string())],
            ]
        );
    }

    fermats.reverse();

    div![
        h1!["The Fermat Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Id => "tbl"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Fermat"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Prime Factors"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn fermat_test<'a>() {
        let mut fermats:Vec<fermat_utils::Fermat<'a>> = fermat_utils::fermats();
		fermats.reverse();
        assert_eq!(fermats[2].prime_factors, "P7");
        assert_eq!(fermats[11].digits, 617);
    }
}<commit_msg>Removed duplicate save_as_file() and used one in util.rs<commit_after>use seed::prelude::*;
use crate::Msg;
use std::convert::TryInto;

use num_traits::{pow};
use num_bigint::{BigUint,ToBigUint};

mod fermat_utils {
    pub struct Fermat<'a> {
		pub n: u64,
		pub f: u64,
		pub digits: u64,
		pub prime_factors: &'a str,
        pub download: &'a str,
	}

    pub fn fermats<'a>() -> Vec<Fermat<'a>> {
        vec![
			Fermat {n: 11, f: 0, digits:  617, prime_factors: "P27567 × 974849 × 167988556341760475137 × 3560841906445833920513 × P564", download: "F0"},
            Fermat {n: 10, f: 0, digits:  309, prime_factors: "45592577 × 6487031809 × 4659775785220018543264560743076778192897 × P252", download: "F0"},
            Fermat {n:  9, f: 0, digits:  155, prime_factors: "2424833 × 7455602825647884208337395736200454918783366342657 × P99", download: "F0"},
            Fermat {n:  8, f: 0, digits:   78, prime_factors: "1238926361552897 × P62", download: "F0"},
            Fermat {n:  7, f: 0, digits:   39, prime_factors: "59649589127497217 × 5704689200685129054721", download: "F0"},
            Fermat {n:  6, f: 0, digits:   20, prime_factors: "274177 × 67280421310721", download: "F0"},
            Fermat {n:  5, f: 0, digits:   10, prime_factors: "P116 × 6700417", download: "F0"},
            Fermat {n:  4, f: 0, digits:    5, prime_factors: "P6543", download: "F0"},
            Fermat {n:  3, f: 0, digits:    3, prime_factors: "P55", download: "F0"},
            Fermat {n:  2, f: 0, digits:    2, prime_factors: "P7", download: "F0"},
            Fermat {n:  1, f: 0, digits:    1, prime_factors: "P3", download: "F0"},
            Fermat {n:  0, f: 0, digits:    1, prime_factors: "P2", download: "F0"},
        ]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let two:usize = 2;

    let mut fermats = fermat_utils::fermats();

    for n in 0..fermats.len() {
        let download_filename:String = format!("F{}.txt",&fermats[n].n.to_string());

		let equation:String = format!("2{}+1",&(two.pow((fermats.len()-1-n).try_into().unwrap())).to_string());
        let nth_fermat:usize = fermats.len()-1-n;//counts from 11 to 0
        let fermat_value:BigUint = pow(two.to_biguint().unwrap(), pow(two, nth_fermat)) + 1.to_biguint().unwrap();

        html.push(
            tr![
                td![fermats[n].n.to_string()],//rank
				td![El::from_html(&equation)],//fermat number as a formula

                td![fermats[n].digits.to_string()],//digits in length
                td![El::from_html(fermats[n].prime_factors)],//prime factors
                td![crate::utils::save_as_file(download_filename,fermat_value.to_string())],
            ]
        );
    }

    fermats.reverse();

    div![
        h1!["The Fermat Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Id => "tbl"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Fermat"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Prime Factors"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn fermat_test<'a>() {
        let mut fermats:Vec<fermat_utils::Fermat<'a>> = fermat_utils::fermats();
		fermats.reverse();
        assert_eq!(fermats[2].prime_factors, "P7");
        assert_eq!(fermats[11].digits, 617);
    }
}<|endoftext|>


<commit_before>use seed::prelude::*;
use crate::Msg;
use std::convert::TryInto;

mod fermat_utils {
    use seed::prelude::*;
    use crate::Msg;

    pub struct Fermat<'a> {
		pub n: u64,
		pub f: u64,
		pub digits: u64,
		pub prime_factors: &'a str,
        pub download: &'a str,
	}

    pub fn fermats<'a>() -> Vec<Fermat<'a>> {
        vec![
			Fermat {n: 11, f: 0, digits:  617, prime_factors: "P27567 × 974849 × 167988556341760475137 × 3560841906445833920513 × P564", download: "F0"},
            Fermat {n: 10, f: 0, digits:  309, prime_factors: "45592577 × 6487031809 × 4659775785220018543264560743076778192897 × P252", download: "F0"},
            Fermat {n:  9, f: 0, digits:  155, prime_factors: "2424833 × 7455602825647884208337395736200454918783366342657 × P99", download: "F0"},
            Fermat {n:  8, f: 0, digits:   78, prime_factors: "1238926361552897 × P62", download: "F0"},
            Fermat {n:  7, f: 0, digits:   39, prime_factors: "59649589127497217 × 5704689200685129054721", download: "F0"},
            Fermat {n:  6, f: 0, digits:   20, prime_factors: "274177 × 67280421310721", download: "F0"},
            Fermat {n:  5, f: 0, digits:   10, prime_factors: "P116 × 6700417", download: "F0"},
            Fermat {n:  4, f: 0, digits:    5, prime_factors: "P6543", download: "F0"},
            Fermat {n:  3, f: 0, digits:    3, prime_factors: "P55", download: "F0"},
            Fermat {n:  2, f: 0, digits:    2, prime_factors: "P7", download: "F0"},
            Fermat {n:  1, f: 0, digits:    1, prime_factors: "P3", download: "F0"},
            Fermat {n:  0, f: 0, digits:    1, prime_factors: "P2", download: "F0"},
        ]
    }

    pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain,",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let two:usize = 2;

    let mut fermats = fermat_utils::fermats();

    for n in 0..fermats.len() {
        //let download_txt:String = vec!["https://static.bigprimes.net/archive/fermat/F",&fermats[n].n.to_string(),".txt"].into_iter().collect();

		let equation:String = vec!["2",&(two.pow((fermats.len()-1-n).try_into().unwrap())).to_string(),"+1"].into_iter().collect();

        html.push(
            tr![
                td![fermats[n].n.to_string()],//rank
				td![El::from_html(&equation)],//fermat number as a formula

                td![fermats[n].digits.to_string()],//digits in length
                td![El::from_html(fermats[n].prime_factors)],//prime factors
                //a![attrs!{At::Href => download_txt},"TXT"],//downloads
                fermat_utils::save_as_file(String::from("digital.txt"),String::from("sanctum")),
            ]
        );
    }

    fermats.reverse();

    div![
        h1!["The Fermat Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Id => "tbl"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Fermat"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Prime Factors"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn fermat_test<'a>() {
        let mut fermats:Vec<fermat_utils::Fermat<'a>> = fermat_utils::fermats();
		fermats.reverse();
        assert_eq!(fermats[2].prime_factors, "P7");
        assert_eq!(fermats[11].digits, 617);
    }
}<commit_msg>fermat TXT downloads are now procedurally created by rust with correct data<commit_after>use seed::prelude::*;
use crate::Msg;
use std::convert::TryInto;

use num_traits::{pow};
use num_bigint::{BigUint,ToBigUint};

mod fermat_utils {
    use seed::prelude::*;
    use crate::Msg;

    pub struct Fermat<'a> {
		pub n: u64,
		pub f: u64,
		pub digits: u64,
		pub prime_factors: &'a str,
        pub download: &'a str,
	}

    pub fn fermats<'a>() -> Vec<Fermat<'a>> {
        vec![
			Fermat {n: 11, f: 0, digits:  617, prime_factors: "P27567 × 974849 × 167988556341760475137 × 3560841906445833920513 × P564", download: "F0"},
            Fermat {n: 10, f: 0, digits:  309, prime_factors: "45592577 × 6487031809 × 4659775785220018543264560743076778192897 × P252", download: "F0"},
            Fermat {n:  9, f: 0, digits:  155, prime_factors: "2424833 × 7455602825647884208337395736200454918783366342657 × P99", download: "F0"},
            Fermat {n:  8, f: 0, digits:   78, prime_factors: "1238926361552897 × P62", download: "F0"},
            Fermat {n:  7, f: 0, digits:   39, prime_factors: "59649589127497217 × 5704689200685129054721", download: "F0"},
            Fermat {n:  6, f: 0, digits:   20, prime_factors: "274177 × 67280421310721", download: "F0"},
            Fermat {n:  5, f: 0, digits:   10, prime_factors: "P116 × 6700417", download: "F0"},
            Fermat {n:  4, f: 0, digits:    5, prime_factors: "P6543", download: "F0"},
            Fermat {n:  3, f: 0, digits:    3, prime_factors: "P55", download: "F0"},
            Fermat {n:  2, f: 0, digits:    2, prime_factors: "P7", download: "F0"},
            Fermat {n:  1, f: 0, digits:    1, prime_factors: "P3", download: "F0"},
            Fermat {n:  0, f: 0, digits:    1, prime_factors: "P2", download: "F0"},
        ]
    }

    pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain,",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let two:usize = 2;

    let mut fermats = fermat_utils::fermats();

    for n in 0..fermats.len() {
        let download_filename:String = vec!["F",&fermats[n].n.to_string(),".txt"].into_iter().collect();

		let equation:String = vec!["2",&(two.pow((fermats.len()-1-n).try_into().unwrap())).to_string(),"+1"].into_iter().collect();
        let nth_fermat:usize = fermats.len()-1-n;//counts from 11 to 0
        let fermat_value:BigUint = pow(two.to_biguint().unwrap(), pow(two, nth_fermat)) + 1.to_biguint().unwrap();

        html.push(
            tr![
                td![fermats[n].n.to_string()],//rank
				td![El::from_html(&equation)],//fermat number as a formula

                td![fermats[n].digits.to_string()],//digits in length
                td![El::from_html(fermats[n].prime_factors)],//prime factors
                td![fermat_utils::save_as_file(String::from(download_filename),fermat_value.to_string())],
            ]
        );
    }

    fermats.reverse();

    div![
        h1!["The Fermat Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Id => "tbl"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Fermat"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Prime Factors"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn fermat_test<'a>() {
        let mut fermats:Vec<fermat_utils::Fermat<'a>> = fermat_utils::fermats();
		fermats.reverse();
        assert_eq!(fermats[2].prime_factors, "P7");
        assert_eq!(fermats[11].digits, 617);
    }
}<|endoftext|>


<commit_before>use *;

#[test]
fn test_standard() {
    let v = Varlink::from_string(
        "
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
vendor: string,
product: string,
version: string,
url: string,
interfaces: []string
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.varlink.service");
    assert_eq!(
        v.interface.doc,
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\
         "
    );
    assert_eq!(
        v.interface
            .methods
            .get("GetInterfaceDescription".into())
            .unwrap()
            .doc,
        "# Get the description of an interface that is implemented by this service."
    );
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(),
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\n\
         interface org.varlink.service\n\
         \n\
         # Get a list of all the interfaces a service provides and information\n\
         # about the implementation.\n\
         method GetInfo() -> (vendor: string, product: string, version: string, url: string, \
         interfaces: []string)\n\
         \n\
         # Get the description of an interface that is implemented by this service.\n\
         method GetInterfaceDescription(interface: string) -> (description: string)\n\
         \n\
         # The requested interface was not found.\n\
         error InterfaceNotFound (interface: string)\n\
         \n\
         # One of the passed parameters is invalid.\n\
         error InvalidParameter (parameter: string)\n\
         \n\
         # The requested method was not found\n\
         error MethodNotFound (method: string)\n\
         \n\
         # The interface defines the requested method, but the service does not\n\
         # implement it.\n\
         error MethodNotImplemented (method: string)\n\
         "
    );
}

#[test]
fn test_complex() {
    let v = Varlink::from_string(
        "interface org.example.complex
type TypeEnum ( a, b, c )

type TypeFoo (
bool: bool,
int: int,
float: float,
string: string,
enum: ( foo, bar, baz ),
type: TypeEnum,
anon: ( foo: bool, bar: int, baz: ( a: int, b: int) )
)

method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)

error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.example.complex");
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(), "\
        interface org.example.complex\n\n\
        type TypeEnum (a, b, c)\n\n\
        type TypeFoo (bool: bool, int: int, float: float, string: string, enum: (foo, bar, baz), type: \
        TypeEnum, anon: (foo: bool, bar: int, baz: (a: int, b: int)))\n\n\
        method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)\n\n\
        error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)\n"
    );
}

#[test]
fn test_one_method() {
    let v = Varlink::from_string("interface foo.bar\nmethod Foo()->()");
    assert!(v.is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(VInterface("interface foo.bar\nmethod Foo()->(b:)").is_err());
}

#[test]
fn test_domainnames() {
    assert!(Varlink::from_string("interface org.varlink.service\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.0example\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.example-dash\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.-example.leadinghyphen\nmethod F()->()").is_err());
    assert!(
        Varlink::from_string("interface com.example-.danglinghyphen-\nmethod F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface Com.example.uppercase-toplevel\nmethod F()->()").is_err()
    );
    assert!(Varlink::from_string("interface Co9.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1om.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.Example\nmethod F()->()").is_err());
}

#[test]
fn test_type_no_args() {
    assert!(Varlink::from_string("interface foo.bar\n type I ()\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_enum() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: (foo, bar, baz))\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_string() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: string)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_stringmap() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]string)\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_stringmap_set() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]())\nmethod F()->()").is_ok()
    );
}

#[test]
fn test_type_object() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: object)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_int() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: int)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_float() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: float)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:[]bool)\nmethod  F()->()").is_ok());
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:bool[ ])\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[1]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 1 ]bool)\nmethod  F()->()")
            .is_err()
    );
}

#[test]
fn test_format() {
    let v = Varlink::from_string("interface foo.bar\ntype I(b:[]bool)\nmethod  F()->()").unwrap();
    assert_eq!(
        v.interface.to_string(),
        "interface foo.bar\n\ntype I (b: []bool)\n\nmethod F() -> ()\n"
    );
}

#[test]
fn test_duplicate() {
    let e = Varlink::from_string(
        "
interface foo.example
type Device()
type Device()
type T()
type T()
method F() -> ()
method F() -> ()
",
    ).err()
    .unwrap();
    assert_eq!(
        e.to_string(),
        "Interface definition error: '\
Interface `foo.example`: multiple definitions of type `Device`!
Interface `foo.example`: multiple definitions of type `F`!
Interface `foo.example`: multiple definitions of type `T`!'"
    );
}
<commit_msg>varlink_parser: re-add xnn-- test case<commit_after>use *;

#[test]
fn test_standard() {
    let v = Varlink::from_string(
        "
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
vendor: string,
product: string,
version: string,
url: string,
interfaces: []string
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.varlink.service");
    assert_eq!(
        v.interface.doc,
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\
         "
    );
    assert_eq!(
        v.interface
            .methods
            .get("GetInterfaceDescription".into())
            .unwrap()
            .doc,
        "# Get the description of an interface that is implemented by this service."
    );
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(),
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\n\
         interface org.varlink.service\n\
         \n\
         # Get a list of all the interfaces a service provides and information\n\
         # about the implementation.\n\
         method GetInfo() -> (vendor: string, product: string, version: string, url: string, \
         interfaces: []string)\n\
         \n\
         # Get the description of an interface that is implemented by this service.\n\
         method GetInterfaceDescription(interface: string) -> (description: string)\n\
         \n\
         # The requested interface was not found.\n\
         error InterfaceNotFound (interface: string)\n\
         \n\
         # One of the passed parameters is invalid.\n\
         error InvalidParameter (parameter: string)\n\
         \n\
         # The requested method was not found\n\
         error MethodNotFound (method: string)\n\
         \n\
         # The interface defines the requested method, but the service does not\n\
         # implement it.\n\
         error MethodNotImplemented (method: string)\n\
         "
    );
}

#[test]
fn test_complex() {
    let v = Varlink::from_string(
        "interface org.example.complex
type TypeEnum ( a, b, c )

type TypeFoo (
bool: bool,
int: int,
float: float,
string: string,
enum: ( foo, bar, baz ),
type: TypeEnum,
anon: ( foo: bool, bar: int, baz: ( a: int, b: int) )
)

method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)

error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.example.complex");
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(), "\
        interface org.example.complex\n\n\
        type TypeEnum (a, b, c)\n\n\
        type TypeFoo (bool: bool, int: int, float: float, string: string, enum: (foo, bar, baz), type: \
        TypeEnum, anon: (foo: bool, bar: int, baz: (a: int, b: int)))\n\n\
        method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)\n\n\
        error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)\n"
    );
}

#[test]
fn test_one_method() {
    let v = Varlink::from_string("interface foo.bar\nmethod Foo()->()");
    assert!(v.is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(VInterface("interface foo.bar\nmethod Foo()->(b:)").is_err());
}

#[test]
fn test_domainnames() {
    assert!(Varlink::from_string("interface org.varlink.service\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.0example\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.example-dash\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.-example.leadinghyphen\nmethod F()->()").is_err());
    assert!(
        Varlink::from_string("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()").is_ok()
    );
    assert!(
        Varlink::from_string("interface com.example-.danglinghyphen-\nmethod F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface Com.example.uppercase-toplevel\nmethod F()->()").is_err()
    );
    assert!(Varlink::from_string("interface Co9.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1om.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.Example\nmethod F()->()").is_err());
}

#[test]
fn test_type_no_args() {
    assert!(Varlink::from_string("interface foo.bar\n type I ()\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_enum() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: (foo, bar, baz))\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_string() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: string)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_stringmap() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]string)\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_stringmap_set() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]())\nmethod F()->()").is_ok()
    );
}

#[test]
fn test_type_object() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: object)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_int() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: int)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_float() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: float)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:[]bool)\nmethod  F()->()").is_ok());
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:bool[ ])\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[1]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 1 ]bool)\nmethod  F()->()")
            .is_err()
    );
}

#[test]
fn test_format() {
    let v = Varlink::from_string("interface foo.bar\ntype I(b:[]bool)\nmethod  F()->()").unwrap();
    assert_eq!(
        v.interface.to_string(),
        "interface foo.bar\n\ntype I (b: []bool)\n\nmethod F() -> ()\n"
    );
}

#[test]
fn test_duplicate() {
    let e = Varlink::from_string(
        "
interface foo.example
type Device()
type Device()
type T()
type T()
method F() -> ()
method F() -> ()
",
    ).err()
    .unwrap();
    assert_eq!(
        e.to_string(),
        "Interface definition error: '\
Interface `foo.example`: multiple definitions of type `Device`!
Interface `foo.example`: multiple definitions of type `F`!
Interface `foo.example`: multiple definitions of type `T`!'"
    );
}
<|endoftext|>


<commit_before>use crate::ast;
use crate::evaluation::object;
use crate::lexer;
use crate::parser;
use crate::token;

// TODO: Consider this to be a hack.
//
// We have 3 types of untis now.
// 1. Program
// 2. Statement
// 3. Expression
//
// To generalize eval API we would wrap program in a special
// wrapper enum, which knows how to handle every unit.

#[derive(Debug)]
pub enum WrappedNode {
    P(ast::Program),
    S(token::Statements),
    E(token::Expression),
}
pub type WN = WrappedNode; // Just alias to avoid typing :)

// Option number 2.
// Write 3 different functions to work with 3
// different types of values.
//
// It's super bad design decision, cause I'll have to
// use different evalers in my REPL implementation,
// but I don't see the design clearly for now.

// For now we'll stick with design approach with WrappedNode.
// Perhaps use From/Into modification?

// To save some typing in creating basic object
// we will use constants.

const NIL: object::Object = object::Object::Nil(object::Nil {});
const TRUE: object::Object = object::Object::Boolean(object::Boolean { value: true });
const FALSE: object::Object = object::Object::Boolean(object::Boolean { value: false });

pub fn eval(node: WN) -> object::Object {
    match node {
        WN::P(program) => eval_statements(program.statements),
        WN::S(statement) => match statement {
            token::Statements::ExpressionStatement(expr) => eval(WN::E(expr.expression)),
            token::Statements::LetStatement(_) => panic!("don't know how to handle let statement"),
            token::Statements::ReturnStatement(_) => {
                panic!("don't know how to handle return statement")
            }
        },
        WN::E(expression) => match expression {
            token::Expression::IntegerLiteral(il) => {
                object::Object::Integer(object::Integer { value: il.value })
            }
            token::Expression::Identifier(_i) => panic!("don't how to handle identifier"),
            token::Expression::PrefixExpression(pe) => {
                let right = eval(WN::E(pe.right));
                match pe.operator.as_ref() {
                    "!" => match right {
                        TRUE => FALSE,
                        FALSE => TRUE,
                        NIL => TRUE,
                        _ => FALSE,
                    },
                    "-" => match right {
                        object::Object::Integer(int_obj) => {
                            object::Object::Integer(object::Integer {
                                value: -int_obj.value,
                            })
                        }
                        _ => NIL, // terrible semantics
                    },
                    _ => object::Object::Nil(object::Nil {}), // just nil, ok
                }
            }
            token::Expression::InfixExpression(ie) => {
                let left = eval(WN::E(ie.left));
                let right = eval(WN::E(ie.right));
                if let (object::Object::Integer(left_obj), object::Object::Integer(right_obj)) =
                    (left, right)
                {
                    match ie.operator.as_ref() {
                        "+" => object::Object::Integer(object::Integer {
                            value: left_obj.value + right_obj.value,
                        }),
                        "-" => object::Object::Integer(object::Integer {
                            value: left_obj.value - right_obj.value,
                        }),
                        "*" => object::Object::Integer(object::Integer {
                            value: left_obj.value * right_obj.value,
                        }),
                        "/" => object::Object::Integer(object::Integer {
                            value: left_obj.value / right_obj.value,
                        }),
                        _ => panic!(
                            "Unexpected operator `{}`, while evaling expression",
                            ie.operator
                        ),
                    }
                } else {
                    NIL
                }
            }
            token::Expression::Boolean(b) => {
                // TODO: Check possible perf optimization? Needed?
                // Reuse TRUE and FALSE I mean
                object::Object::Boolean(object::Boolean { value: b.value })
            }
            token::Expression::IfExpression(_ie) => panic!("don't how to handle if expression"),
            token::Expression::FunctionLiteral(_fl) => {
                panic!("don't how to handle function literal")
            }
            token::Expression::CallExpression(_ce) => panic!("don't how to handle call expression"),
        },
    }
}

pub fn eval_statements(statements: Vec<token::Statements>) -> object::Object {
    // TODO: not sure we need unwrap here.
    statements
        .into_iter()
        .map(|statement| eval(WN::S(statement)))
        .last()
        .unwrap()
}

#[cfg(test)]
mod tests {
    use crate::evaluation;
    use crate::lexer;
    use crate::parser;
    use std::collections::HashMap;

    #[test]
    fn test_eval_integer_expression() {
        let pairs = vec![("1".to_string(), 1), ("2".to_string(), 2)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_integer_object(evaluated, expected);
        })
    }

    #[test]
    fn test_eval_boolean_expression() {
        let pairs = vec![("true".to_string(), true), ("false".to_string(), false)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_boolean_object(evaluated, expected);
        })
    }

    #[test]
    fn test_bang_operator() {
        let pairs = vec![
            ("!true".to_string(), false),
            ("!false".to_string(), true),
            ("!1".to_string(), false),
            ("!!true".to_string(), true),
            ("!!false".to_string(), false),
            ("!!1".to_string(), true),
        ];

        for (value, expected) in pairs {
            assert_boolean_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_prefix_negate_operator() {
        let pairs = vec![("-1".to_string(), -1), ("--1".to_string(), 1)];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_integer_arithmetic() {
        let pairs = vec![
            ("1 + 1 + 1".to_string(), 3),
            ("2 * 2 * 2".to_string(), 8),
            ("4 / 2 * 2".to_string(), 4),
            ("1 + 3 * 2".to_string(), 7),
        ];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    fn run_eval(source_code: String) -> evaluation::object::Object {
        let lexer = lexer::Lexer::new(source_code);
        let mut parser = parser::Parser::new(lexer);

        let mut lambda_parsers = parser::LambdaParsers {
            prefix_parse_fns: HashMap::new(),
            infix_parse_fns: HashMap::new(),
        };
        lambda_parsers.register_parsers();

        let program = parser.parse_program(&lambda_parsers);

        evaluation::evaluator::eval(evaluation::evaluator::WN::P(program))
    }

    fn assert_integer_object(object: evaluation::object::Object, expected: i32) {
        let integer = match object {
            evaluation::object::Object::Integer(int) => int,
            otherwise => panic!("expected integer, got {:?}", otherwise),
        };

        assert_eq!(integer.value, expected)
    }

    fn assert_boolean_object(object: evaluation::object::Object, expected: bool) {
        let bool = match object {
            evaluation::object::Object::Boolean(b) => b,
            otherwise => panic!("expected boolean, got {:?}", otherwise),
        };

        assert_eq!(bool.value, expected)
    }
}
<commit_msg>evaluator for boolean operations<commit_after>use crate::ast;
use crate::evaluation::object;
use crate::lexer;
use crate::parser;
use crate::token;

// TODO: Consider this to be a hack.
//
// We have 3 types of untis now.
// 1. Program
// 2. Statement
// 3. Expression
//
// To generalize eval API we would wrap program in a special
// wrapper enum, which knows how to handle every unit.

#[derive(Debug)]
pub enum WrappedNode {
    P(ast::Program),
    S(token::Statements),
    E(token::Expression),
}
pub type WN = WrappedNode; // Just alias to avoid typing :)

// Option number 2.
// Write 3 different functions to work with 3
// different types of values.
//
// It's super bad design decision, cause I'll have to
// use different evalers in my REPL implementation,
// but I don't see the design clearly for now.

// For now we'll stick with design approach with WrappedNode.
// Perhaps use From/Into modification?

// To save some typing in creating basic object
// we will use constants.

const NIL: object::Object = object::Object::Nil(object::Nil {});
const TRUE: object::Object = object::Object::Boolean(object::Boolean { value: true });
const FALSE: object::Object = object::Object::Boolean(object::Boolean { value: false });

pub fn eval(node: WN) -> object::Object {
    match node {
        WN::P(program) => eval_statements(program.statements),
        WN::S(statement) => match statement {
            token::Statements::ExpressionStatement(expr) => eval(WN::E(expr.expression)),
            token::Statements::LetStatement(_) => panic!("don't know how to handle let statement"),
            token::Statements::ReturnStatement(_) => {
                panic!("don't know how to handle return statement")
            }
        },
        WN::E(expression) => match expression {
            token::Expression::IntegerLiteral(il) => {
                object::Object::Integer(object::Integer { value: il.value })
            }
            token::Expression::Identifier(_i) => panic!("don't how to handle identifier"),
            token::Expression::PrefixExpression(pe) => {
                let right = eval(WN::E(pe.right));
                match pe.operator.as_ref() {
                    "!" => match right {
                        TRUE => FALSE,
                        FALSE => TRUE,
                        NIL => TRUE,
                        _ => FALSE,
                    },
                    "-" => match right {
                        object::Object::Integer(int_obj) => {
                            object::Object::Integer(object::Integer {
                                value: -int_obj.value,
                            })
                        }
                        _ => NIL, // terrible semantics
                    },
                    _ => object::Object::Nil(object::Nil {}), // just nil, ok
                }
            }
            token::Expression::InfixExpression(ie) => {
                let left = eval(WN::E(ie.left));
                let right = eval(WN::E(ie.right));
                if let (object::Object::Integer(left_obj), object::Object::Integer(right_obj)) =
                    (left, right)
                {
                    match ie.operator.as_ref() {
                        "+" => object::Object::Integer(object::Integer {
                            value: left_obj.value + right_obj.value,
                        }),
                        "-" => object::Object::Integer(object::Integer {
                            value: left_obj.value - right_obj.value,
                        }),
                        "*" => object::Object::Integer(object::Integer {
                            value: left_obj.value * right_obj.value,
                        }),
                        "/" => object::Object::Integer(object::Integer {
                            value: left_obj.value / right_obj.value,
                        }),
                        "<" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value < right_obj.value
                        }),
                        ">" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value > right_obj.value
                        }),
                        "==" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value == right_obj.value
                        }),
                        "!=" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value != right_obj.value
                        }),
                        _ => panic!(
                            "Unexpected operator `{}`, while evaling expression",
                            ie.operator
                        ),
                    }
                } else {
                    NIL
                }
            }
            token::Expression::Boolean(b) => {
                // TODO: Check possible perf optimization? Needed?
                // Reuse TRUE and FALSE I mean
                object::Object::Boolean(object::Boolean { value: b.value })
            }
            token::Expression::IfExpression(_ie) => panic!("don't how to handle if expression"),
            token::Expression::FunctionLiteral(_fl) => {
                panic!("don't how to handle function literal")
            }
            token::Expression::CallExpression(_ce) => panic!("don't how to handle call expression"),
        },
    }
}

pub fn eval_statements(statements: Vec<token::Statements>) -> object::Object {
    // TODO: not sure we need unwrap here.
    statements
        .into_iter()
        .map(|statement| eval(WN::S(statement)))
        .last()
        .unwrap()
}

#[cfg(test)]
mod tests {
    use crate::evaluation;
    use crate::lexer;
    use crate::parser;
    use std::collections::HashMap;

    #[test]
    fn test_eval_integer_expression() {
        let pairs = vec![("1".to_string(), 1), ("2".to_string(), 2)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_integer_object(evaluated, expected);
        })
    }

    #[test]
    fn test_eval_boolean_expression() {
        let pairs = vec![
            ("true".to_string(), true),
            ("false".to_string(), false),
            ("1 < 2".to_string(), true),
            ("1 > 2".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 == 1".to_string(), true),
            ("1 != 1".to_string(), false),
            ("1 == 2".to_string(), false),
            ("1 != 2".to_string(), true),
        ];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_boolean_object(evaluated, expected);
        })
    }

    #[test]
    fn test_bang_operator() {
        let pairs = vec![
            ("!true".to_string(), false),
            ("!false".to_string(), true),
            ("!1".to_string(), false),
            ("!!true".to_string(), true),
            ("!!false".to_string(), false),
            ("!!1".to_string(), true),
        ];

        for (value, expected) in pairs {
            assert_boolean_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_prefix_negate_operator() {
        let pairs = vec![("-1".to_string(), -1), ("--1".to_string(), 1)];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_integer_arithmetic() {
        let pairs = vec![
            ("1 + 1 + 1".to_string(), 3),
            ("2 * 2 * 2".to_string(), 8),
            ("4 / 2 * 2".to_string(), 4),
            ("1 + 3 * 2".to_string(), 7),
        ];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    fn run_eval(source_code: String) -> evaluation::object::Object {
        let lexer = lexer::Lexer::new(source_code);
        let mut parser = parser::Parser::new(lexer);

        let mut lambda_parsers = parser::LambdaParsers {
            prefix_parse_fns: HashMap::new(),
            infix_parse_fns: HashMap::new(),
        };
        lambda_parsers.register_parsers();

        let program = parser.parse_program(&lambda_parsers);

        evaluation::evaluator::eval(evaluation::evaluator::WN::P(program))
    }

    fn assert_integer_object(object: evaluation::object::Object, expected: i32) {
        let integer = match object {
            evaluation::object::Object::Integer(int) => int,
            otherwise => panic!("expected integer, got {:?}", otherwise),
        };

        assert_eq!(integer.value, expected)
    }

    fn assert_boolean_object(object: evaluation::object::Object, expected: bool) {
        let bool = match object {
            evaluation::object::Object::Boolean(b) => b,
            otherwise => panic!("expected boolean, got {:?}", otherwise),
        };

        assert_eq!(bool.value, expected)
    }
}
<|endoftext|>


<commit_before>use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{ElementResult, SetAttributes};
use crate::error::*;
use crate::node::{CascadedValues, Node};
use crate::parsers::{NumberList, Parse, ParseValue};
use crate::properties::ColorInterpolationFilters;
use crate::rect::IRect;
use crate::session::Session;
use crate::surface_utils::{
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;
use crate::xml::Attributes;

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput};
use super::{
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};

/// Color matrix operation types.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum OperationType {
    Matrix,
    Saturate,
    HueRotate,
    LuminanceToAlpha,
}

enum_default!(OperationType, OperationType::Matrix);

/// The `feColorMatrix` filter primitive.
#[derive(Default)]
pub struct FeColorMatrix {
    base: Primitive,
    params: ColorMatrix,
}

/// Resolved `feColorMatrix` primitive for rendering.
#[derive(Clone)]
pub struct ColorMatrix {
    pub in1: Input,
    pub matrix: Matrix5<f64>,
    pub color_interpolation_filters: ColorInterpolationFilters,
}

impl Default for ColorMatrix {
    fn default() -> ColorMatrix {
        ColorMatrix {
            in1: Default::default(),
            color_interpolation_filters: Default::default(),

            // nalgebra's Default for Matrix5 is all zeroes, so we actually need this :(
            matrix: Matrix5::identity(),
        }
    }
}

#[rustfmt::skip]
impl SetAttributes for FeColorMatrix {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        self.params.in1 = self.base.parse_one_input(attrs, session)?;

        // First, determine the operation type.
        let mut operation_type = Default::default();
        for (attr, value) in attrs
            .iter()
            .filter(|(attr, _)| attr.expanded() == expanded_name!("", "type"))
        {
            operation_type = attr.parse(value)?;
        }

        // Now read the matrix correspondingly.
        // LuminanceToAlpha doesn't accept any matrix.
        if operation_type == OperationType::LuminanceToAlpha {
            self.params.matrix = {
                Matrix5::new(
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.2125, 0.7154, 0.0721, 0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 1.0,
                )
            };
        } else {
            for (attr, value) in attrs
                .iter()
                .filter(|(attr, _)| attr.expanded() == expanded_name!("", "values"))
            {
                let new_matrix = match operation_type {
                    OperationType::LuminanceToAlpha => unreachable!(),
                    OperationType::Matrix => {
                        let NumberList::<20, 20>(v) = attr.parse(value)?;
                        let matrix = Matrix4x5::from_row_slice(&v);
                        let mut matrix = matrix.fixed_resize(0.0);
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                    OperationType::Saturate => {
                        let s: f64 = attr.parse(value)?;

                        Matrix5::new(
                            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
                            0.0,               0.0,               0.0,               1.0, 0.0,
                            0.0,               0.0,               0.0,               0.0, 1.0,
                        )
                    }
                    OperationType::HueRotate => {
                        let degrees: f64 = attr.parse(value)?;
                        ColorMatrix::hue_rotate_matrix(degrees.to_radians())
                    }
                };

                self.params.matrix = new_matrix;
            }
        }

        Ok(())
    }
}

impl ColorMatrix {
    pub fn render(
        &self,
        bounds_builder: BoundsBuilder,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterOutput, FilterError> {
        let input_1 = ctx.get_input(
            acquired_nodes,
            draw_ctx,
            &self.in1,
            self.color_interpolation_filters,
        )?;
        let bounds: IRect = bounds_builder
            .add_input(&input_1)
            .compute(ctx)
            .clipped
            .into();

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input_1.surface().surface_type(),
        )?;

        surface.modify(&mut |data, stride| {
            for (x, y, pixel) in Pixels::within(input_1.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;

                let pixel_vec = if alpha == 0.0 {
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
                } else {
                    Vector5::new(
                        f64::from(pixel.r) / 255f64 / alpha,
                        f64::from(pixel.g) / 255f64 / alpha,
                        f64::from(pixel.b) / 255f64 / alpha,
                        alpha,
                        1.0,
                    )
                };
                let mut new_pixel_vec = Vector5::zeros();
                self.matrix.mul_to(&pixel_vec, &mut new_pixel_vec);

                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);

                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;

                let output_pixel = Pixel {
                    r: premultiply(new_pixel_vec[0]),
                    g: premultiply(new_pixel_vec[1]),
                    b: premultiply(new_pixel_vec[2]),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterOutput {
            surface: surface.share()?,
            bounds,
        })
    }

    pub fn hue_rotate_matrix(radians: f64) -> Matrix5<f64> {
        let (sin, cos) = radians.sin_cos();

        let a = Matrix3::new(
            0.213, 0.715, 0.072, 0.213, 0.715, 0.072, 0.213, 0.715, 0.072,
        );

        let b = Matrix3::new(
            0.787, -0.715, -0.072, -0.213, 0.285, -0.072, -0.213, -0.715, 0.928,
        );

        let c = Matrix3::new(
            -0.213, -0.715, 0.928, 0.143, 0.140, -0.283, -0.787, 0.715, 0.072,
        );

        let top_left = a + b * cos + c * sin;

        let mut matrix = top_left.fixed_resize(0.0);
        matrix[(3, 3)] = 1.0;
        matrix[(4, 4)] = 1.0;
        matrix
    }
}

impl FilterEffect for FeColorMatrix {
    fn resolve(
        &self,
        _acquired_nodes: &mut AcquiredNodes<'_>,
        node: &Node,
    ) -> Result<ResolvedPrimitive, FilterResolveError> {
        let cascaded = CascadedValues::new_from_node(node);
        let values = cascaded.get();

        let mut params = self.params.clone();
        params.color_interpolation_filters = values.color_interpolation_filters();

        Ok(ResolvedPrimitive {
            primitive: self.base.clone(),
            params: PrimitiveParams::ColorMatrix(params),
        })
    }
}

impl Parse for OperationType {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "matrix" => OperationType::Matrix,
            "saturate" => OperationType::Saturate,
            "hueRotate" => OperationType::HueRotate,
            "luminanceToAlpha" => OperationType::LuminanceToAlpha,
        )?)
    }
}
<commit_msg>filters/color_matrix.rs: use set_attribute()<commit_after>use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{set_attribute, ElementResult, SetAttributes};
use crate::error::*;
use crate::node::{CascadedValues, Node};
use crate::parsers::{NumberList, Parse, ParseValue};
use crate::properties::ColorInterpolationFilters;
use crate::rect::IRect;
use crate::session::Session;
use crate::surface_utils::{
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;
use crate::xml::Attributes;

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput};
use super::{
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};

/// Color matrix operation types.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum OperationType {
    Matrix,
    Saturate,
    HueRotate,
    LuminanceToAlpha,
}

enum_default!(OperationType, OperationType::Matrix);

/// The `feColorMatrix` filter primitive.
#[derive(Default)]
pub struct FeColorMatrix {
    base: Primitive,
    params: ColorMatrix,
}

/// Resolved `feColorMatrix` primitive for rendering.
#[derive(Clone)]
pub struct ColorMatrix {
    pub in1: Input,
    pub matrix: Matrix5<f64>,
    pub color_interpolation_filters: ColorInterpolationFilters,
}

impl Default for ColorMatrix {
    fn default() -> ColorMatrix {
        ColorMatrix {
            in1: Default::default(),
            color_interpolation_filters: Default::default(),

            // nalgebra's Default for Matrix5 is all zeroes, so we actually need this :(
            matrix: Matrix5::identity(),
        }
    }
}

#[rustfmt::skip]
impl SetAttributes for FeColorMatrix {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        self.params.in1 = self.base.parse_one_input(attrs, session)?;

        // First, determine the operation type.
        let mut operation_type = Default::default();
        for (attr, value) in attrs
            .iter()
            .filter(|(attr, _)| attr.expanded() == expanded_name!("", "type"))
        {
            set_attribute(&mut operation_type, attr.parse(value), session);
        }

        // Now read the matrix correspondingly.
        // LuminanceToAlpha doesn't accept any matrix.
        if operation_type == OperationType::LuminanceToAlpha {
            self.params.matrix = {
                Matrix5::new(
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.2125, 0.7154, 0.0721, 0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 1.0,
                )
            };
        } else {
            for (attr, value) in attrs
                .iter()
                .filter(|(attr, _)| attr.expanded() == expanded_name!("", "values"))
            {
                let new_matrix = match operation_type {
                    OperationType::LuminanceToAlpha => unreachable!(),
                    OperationType::Matrix => {
                        let NumberList::<20, 20>(v) = attr.parse(value)?;
                        let matrix = Matrix4x5::from_row_slice(&v);
                        let mut matrix = matrix.fixed_resize(0.0);
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                    OperationType::Saturate => {
                        let s: f64 = attr.parse(value)?;

                        Matrix5::new(
                            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
                            0.0,               0.0,               0.0,               1.0, 0.0,
                            0.0,               0.0,               0.0,               0.0, 1.0,
                        )
                    }
                    OperationType::HueRotate => {
                        let degrees: f64 = attr.parse(value)?;
                        ColorMatrix::hue_rotate_matrix(degrees.to_radians())
                    }
                };

                self.params.matrix = new_matrix;
            }
        }

        Ok(())
    }
}

impl ColorMatrix {
    pub fn render(
        &self,
        bounds_builder: BoundsBuilder,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterOutput, FilterError> {
        let input_1 = ctx.get_input(
            acquired_nodes,
            draw_ctx,
            &self.in1,
            self.color_interpolation_filters,
        )?;
        let bounds: IRect = bounds_builder
            .add_input(&input_1)
            .compute(ctx)
            .clipped
            .into();

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input_1.surface().surface_type(),
        )?;

        surface.modify(&mut |data, stride| {
            for (x, y, pixel) in Pixels::within(input_1.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;

                let pixel_vec = if alpha == 0.0 {
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
                } else {
                    Vector5::new(
                        f64::from(pixel.r) / 255f64 / alpha,
                        f64::from(pixel.g) / 255f64 / alpha,
                        f64::from(pixel.b) / 255f64 / alpha,
                        alpha,
                        1.0,
                    )
                };
                let mut new_pixel_vec = Vector5::zeros();
                self.matrix.mul_to(&pixel_vec, &mut new_pixel_vec);

                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);

                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;

                let output_pixel = Pixel {
                    r: premultiply(new_pixel_vec[0]),
                    g: premultiply(new_pixel_vec[1]),
                    b: premultiply(new_pixel_vec[2]),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterOutput {
            surface: surface.share()?,
            bounds,
        })
    }

    pub fn hue_rotate_matrix(radians: f64) -> Matrix5<f64> {
        let (sin, cos) = radians.sin_cos();

        let a = Matrix3::new(
            0.213, 0.715, 0.072, 0.213, 0.715, 0.072, 0.213, 0.715, 0.072,
        );

        let b = Matrix3::new(
            0.787, -0.715, -0.072, -0.213, 0.285, -0.072, -0.213, -0.715, 0.928,
        );

        let c = Matrix3::new(
            -0.213, -0.715, 0.928, 0.143, 0.140, -0.283, -0.787, 0.715, 0.072,
        );

        let top_left = a + b * cos + c * sin;

        let mut matrix = top_left.fixed_resize(0.0);
        matrix[(3, 3)] = 1.0;
        matrix[(4, 4)] = 1.0;
        matrix
    }
}

impl FilterEffect for FeColorMatrix {
    fn resolve(
        &self,
        _acquired_nodes: &mut AcquiredNodes<'_>,
        node: &Node,
    ) -> Result<ResolvedPrimitive, FilterResolveError> {
        let cascaded = CascadedValues::new_from_node(node);
        let values = cascaded.get();

        let mut params = self.params.clone();
        params.color_interpolation_filters = values.color_interpolation_filters();

        Ok(ResolvedPrimitive {
            primitive: self.base.clone(),
            params: PrimitiveParams::ColorMatrix(params),
        })
    }
}

impl Parse for OperationType {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "matrix" => OperationType::Matrix,
            "saturate" => OperationType::Saturate,
            "hueRotate" => OperationType::HueRotate,
            "luminanceToAlpha" => OperationType::LuminanceToAlpha,
        )?)
    }
}
<|endoftext|>