├── examples ├── bspline-example-2d.json ├── bspline-example-3d.json ├── saddle-surface-interpolation.json └── saddle-surface.json ├── Cargo.toml ├── .gitignore ├── src ├── camera2d.rs ├── point.rs ├── polyline.rs ├── bspline_basis.rs ├── bezier.rs ├── bspline_surf.rs ├── display_curve3d.rs ├── imgui_support.rs ├── display_surf_interp.rs ├── display_curve.rs ├── bspline.rs ├── display_surf.rs └── main.rs ├── LICENSE.md ├── README.md └── Cargo.lock /examples/bspline-example-2d.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "bspline2d", 3 | "degree": 2, 4 | "points": [{"x": 1, "y": 0}, {"x": 0, "y": 0}, 5 | {"x": 0, "y": 1}, {"x": 0, "y": 0}, {"x": -1, "y": 0}, 6 | {"x": 0, "y": 0}, {"x": 0, "y": -1}, {"x": 0, "y": 0}, 7 | {"x": 1, "y": 0}], 8 | "knots": [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spline-viewer" 3 | version = "0.1.0" 4 | authors = ["Will Usher "] 5 | 6 | [dependencies] 7 | glium = "0.16.0" 8 | imgui = "0.0.13" 9 | imgui-glium-renderer = "0.0.13" 10 | imgui-sys = "0.0.13" 11 | cgmath = "0.14.0" 12 | docopt = "0.7.0" 13 | serde = "1.0.2" 14 | serde_json = "1.0.1" 15 | num-traits = "0.1.37" 16 | rulinalg = "0.4.2" 17 | arcball = "0.2.0" 18 | 19 | -------------------------------------------------------------------------------- /examples/bspline-example-3d.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "bspline3d", 3 | "degree": 2, 4 | "points": [{"x": 1, "y": 0, "z": -1}, {"x": 0, "y": 0, "z": -0.53}, 5 | {"x": 0, "y": 1, "z": -0.5}, {"x": 0, "y": 0, "z": -0.176}, 6 | {"x": -1, "y": 0, "z": 0}, {"x": 0, "y": 0, "z": 0.176}, 7 | {"x": 0, "y": -1, "z": 0.5}, {"x": 0, "y": 0, "z": 0.53}, 8 | {"x": 1, "y": 0, "z": 1}], 9 | "knots": [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1] 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # All LaTeX build artifacts should be ignored 2 | *.aux 3 | *.dvi 4 | *.log 5 | *.pdf 6 | *.swp 7 | *.gz 8 | *.bbl 9 | *.blg 10 | *.out 11 | *.lbl 12 | *.brf 13 | __pycache__ 14 | eps/ 15 | *.swp 16 | *target/ 17 | *.ppm 18 | *.png 19 | *.bmp 20 | *.obj 21 | *.mtl 22 | *.binary 23 | *.mp4 24 | imgui.ini 25 | programming/bezierdatafiles/ 26 | programming/bspline2Ddata/ 27 | programming/bspline3Ddispdata/ 28 | programming/bsplinesurfaces/ 29 | programming/bsplinesurfdata/ 30 | 31 | -------------------------------------------------------------------------------- /examples/saddle-surface-interpolation.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "interpolation_u", 3 | "num_curves": 3, 4 | "u": { 5 | "degree": 2, 6 | "knots": [0, 0, 0, 1, 1, 1] 7 | }, 8 | "mesh": [ 9 | [{"x": -1.056, "y": 0.953, "z": 0}, {"x": -0.048, "y": 0.035, "z": 0}, {"x": 1.008, "y": 0.868, "z": 0}], 10 | [{"x": -1.944, "y": 3.047, "z": 1}, {"x": -0.072, "y": 1.758, "z": 1}, {"x": 1.992, "y": 3.132, "z": 1}], 11 | [{"x": -1.056, "y": 0.953, "z": 2}, {"x": -0.048, "y": 0.035, "z": 2}, {"x": 1.008, "y": 0.868, "z": 2}] 12 | ] 13 | } 14 | 15 | -------------------------------------------------------------------------------- /examples/saddle-surface.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "surface", 3 | "u": { 4 | "degree": 2, 5 | "knots": [0, 0, 0, 1, 1, 1] 6 | }, 7 | "v": { 8 | "degree": 2, 9 | "knots": [0, 0, 0, 1, 1, 1] 10 | }, 11 | "mesh": [ 12 | [{"x": -1.056, "y": 0.953, "z": 0}, {"x": -0.048, "y": 0.035, "z": 0}, {"x": 1.008, "y": 0.868, "z": 0}], 13 | [{"x": -1.944, "y": 3.047, "z": 1}, {"x": -0.072, "y": 1.758, "z": 1}, {"x": 1.992, "y": 3.132, "z": 1}], 14 | [{"x": -1.056, "y": 0.953, "z": 2}, {"x": -0.048, "y": 0.035, "z": 2}, {"x": 1.008, "y": 0.868, "z": 2}] 15 | ] 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/camera2d.rs: -------------------------------------------------------------------------------- 1 | use cgmath::{Vector3, vec3, Matrix4}; 2 | 3 | /// Camera for 2D scenes which can zoom in an out and pan around 4 | pub struct Camera2d { 5 | pub position: Vector3, 6 | pub zoom: f32 7 | } 8 | 9 | impl Camera2d { 10 | pub fn new() -> Camera2d { 11 | Camera2d { position: vec3(0.0, 0.0, 2.0), zoom: 1.0 } 12 | } 13 | pub fn translate(&mut self, x: f32, y: f32) { 14 | self.position += vec3(x, y, 0.0) / self.zoom; 15 | } 16 | pub fn zoom(&mut self, z: f32) { 17 | self.zoom += z; 18 | // Only allow up to 10x zoom out 19 | if self.zoom < 0.1 { 20 | self.zoom = 0.1; 21 | } 22 | } 23 | pub fn get_mat4(&self) -> Matrix4 { 24 | Matrix4::from_nonuniform_scale(self.zoom, self.zoom, 1.0) 25 | * Matrix4::from_translation(self.position) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Will Usher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spline Viewer 2 | 3 | A viewer for B-spline curves and surfaces, initially written for a course on 4 | computer aided geometric design. You can edit and create 2D B-splines 5 | and tweak some properties of loaded 3D curves and surfaces, but can't move points on 3D objects. 6 | 7 | ## Running and Loading Curves 8 | 9 | To run the program and specify some curves or data files to load on the command line you can 10 | run through cargo with arguments to the program following a second `--` or run the program directly. 11 | Examples of each JSON curve format can be found in the examples. 12 | 13 | ``` 14 | ./spline-viewer 15 | ``` 16 | 17 | You can also pass -h as an argument to print the program options. 18 | 19 | ## Controls 20 | 21 | - Left click somewhere on the scene to add a new control point to the active curve, 22 | if you hold left click after adding you can continue dragging the new point. 23 | 24 | - Left click and drag an existing control point to move it around. 25 | 26 | - Shift + Left click on a control point to remove it. 27 | 28 | - Right click and drag to pan the camera around 29 | 30 | - Scroll to zoom in and out. 31 | 32 | - To add a curve scroll to the bottom of the curve list to find the add curve button, 33 | this new curve will have 0 control points initially and will be selected automatically. You 34 | can also drop a curve JSON file on the window to load it, see `examples/` for example curves. 35 | 36 | ## Screenshot 37 | 38 | Here's what you'll see if you load all the provided examples and tweak the colors a bit. 39 | 40 | ![spline viewer](http://i.imgur.com/EzAQZyM.png) 41 | 42 | -------------------------------------------------------------------------------- /src/point.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Mul, Add, Sub, Div}; 2 | use std::f32; 3 | 4 | use bezier::ProjectToSegment; 5 | 6 | pub fn clamp(x: f32, min: f32, max: f32) -> f32 { 7 | if x < min { 8 | min 9 | } else if x > max { 10 | max 11 | } else { 12 | x 13 | } 14 | } 15 | 16 | #[derive(Copy, Clone, Debug)] 17 | pub struct Point { 18 | pub pos: [f32; 3], 19 | } 20 | impl Point { 21 | pub fn new(x: f32, y: f32, z: f32) -> Point { 22 | Point { pos: [x, y, z] } 23 | } 24 | pub fn dot(&self, a: &Point) -> f32 { 25 | self.pos[0] * a.pos[0] + self.pos[1] * a.pos[1] + self.pos[2] * a.pos[2] 26 | } 27 | pub fn length(&self) -> f32 { 28 | f32::sqrt(self.dot(&self)) 29 | } 30 | } 31 | implement_vertex!(Point, pos); 32 | 33 | impl Mul for Point { 34 | type Output = Point; 35 | fn mul(self, rhs: f32) -> Point { 36 | Point { pos: [self.pos[0] * rhs, self.pos[1] * rhs, self.pos[2] * rhs] } 37 | } 38 | } 39 | impl Div for Point { 40 | type Output = Point; 41 | fn div(self, rhs: f32) -> Point { 42 | Point { pos: [self.pos[0] / rhs, self.pos[1] / rhs, self.pos[2] / rhs] } 43 | } 44 | } 45 | impl Add for Point { 46 | type Output = Point; 47 | fn add(self, rhs: Point) -> Point { 48 | Point { pos: [self.pos[0] + rhs.pos[0], self.pos[1] + rhs.pos[1], self.pos[2] + rhs.pos[2]] } 49 | } 50 | } 51 | impl Sub for Point { 52 | type Output = Point; 53 | fn sub(self, rhs: Point) -> Point { 54 | Point { pos: [self.pos[0] - rhs.pos[0], self.pos[1] - rhs.pos[1], self.pos[2] - rhs.pos[2]] } 55 | } 56 | } 57 | impl ProjectToSegment for Point { 58 | fn project(&self, a: &Point, b: &Point) -> (f32, f32) { 59 | let v = *b - *a; 60 | let dir = *self - *a; 61 | let t = clamp(dir.dot(&v) / v.dot(&v), 0.0, 1.0); 62 | let p = *a + v * t; 63 | let d = (p - *self).length(); 64 | (d, t) 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /src/polyline.rs: -------------------------------------------------------------------------------- 1 | //! Manages displaying and toggling interaction modes with 2 | //! a specific Bezier curve in the scene. 3 | #![allow(dead_code)] 4 | 5 | use std::f32; 6 | 7 | use glium::{Surface, VertexBuffer, Program, DrawParameters}; 8 | use glium::backend::Facade; 9 | use glium::index::{NoIndices, PrimitiveType}; 10 | use imgui::Ui; 11 | 12 | use point::Point; 13 | 14 | pub struct Polyline { 15 | points_vbo: VertexBuffer, 16 | draw_lines: bool, 17 | draw_points: bool, 18 | color: [f32; 3], 19 | } 20 | 21 | impl Polyline { 22 | pub fn new(points: Vec, display: &F) -> Polyline { 23 | let points_vbo = VertexBuffer::immutable(display, &points[..]).unwrap(); 24 | Polyline { points_vbo: points_vbo, 25 | draw_lines: true, 26 | draw_points: true, 27 | color: [0.8, 0.8, 0.8], 28 | } 29 | } 30 | pub fn render(&self, target: &mut S, program: &Program, draw_params: &DrawParameters, 31 | proj_view: &[[f32; 4]; 4]) { 32 | let uniforms = uniform! { 33 | proj_view: *proj_view, 34 | pcolor: self.color, 35 | }; 36 | // Draw the control polygon 37 | if self.draw_lines { 38 | target.draw(&self.points_vbo, &NoIndices(PrimitiveType::LineStrip), 39 | &program, &uniforms, &draw_params).unwrap(); 40 | } 41 | // Draw the control points 42 | if self.draw_points { 43 | target.draw(&self.points_vbo, &NoIndices(PrimitiveType::Points), 44 | &program, &uniforms, &draw_params).unwrap(); 45 | } 46 | } 47 | pub fn draw_ui(&mut self, ui: &Ui) { 48 | ui.text(im_str!("Number of Points: {}", self.points_vbo.len())); 49 | ui.checkbox(im_str!("Draw Polyline"), &mut self.draw_lines); 50 | ui.checkbox(im_str!("Draw Points"), &mut self.draw_points); 51 | ui.color_edit3(im_str!("Color"), &mut self.color).build(); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/bspline_basis.rs: -------------------------------------------------------------------------------- 1 | use std::f32; 2 | 3 | /// Just the basis functions for a B-spline, can return the B-spline 4 | /// basis function values for specific basis functions at desired t values 5 | pub struct BSplineBasis { 6 | /// Degree of the polynomial that we use to make the curve segments 7 | degree: usize, 8 | /// The knot vector 9 | pub knots: Vec, 10 | modified_knot: usize, 11 | } 12 | 13 | impl BSplineBasis { 14 | pub fn new(degree: usize, mut knots: Vec) -> BSplineBasis { 15 | knots.sort_by(|a, b| a.partial_cmp(b).unwrap()); 16 | let mut modified_knot = 0; 17 | for i in 0..knots.len() - 1 { 18 | if knots[i] < knots[i + 1] { 19 | modified_knot = i; 20 | } 21 | } 22 | BSplineBasis { degree: degree, knots: knots, modified_knot: modified_knot } 23 | } 24 | /// Make a new basis with a generated uniform clamped knot vector 25 | pub fn clamped_uniform(degree: usize, num_points: usize) -> BSplineBasis { 26 | let knots = BSplineBasis::generate_knot_vector(true, num_points + degree + 1, degree); 27 | let mut modified_knot = 0; 28 | for i in 0..knots.len() - 1 { 29 | if knots[i] < knots[i + 1] { 30 | modified_knot = i; 31 | } 32 | } 33 | BSplineBasis { degree: degree, knots: knots, modified_knot: modified_knot } 34 | } 35 | /// Get the curve degree 36 | pub fn degree(&self) -> usize { 37 | self.degree 38 | } 39 | pub fn knot_domain(&self) -> (f32, f32) { 40 | (self.knots[self.degree], self.knots[self.knots.len() - 1 - self.degree]) 41 | } 42 | pub fn greville_abscissa(&self) -> Vec { 43 | let num_abscissa = self.knots.len() - self.degree - 1; 44 | let mut abscissa = Vec::with_capacity(num_abscissa); 45 | let domain = self.knot_domain(); 46 | for i in 0..num_abscissa { 47 | let g = self.knots.iter().enumerate().skip_while(|&(c, _)| c < i + 1) 48 | .take_while(|&(c, _)| c <= i + self.degree) 49 | .map(|(_, x)| x) 50 | .fold(0.0, |acc, x| acc + *x) / self.degree as f32; 51 | // TODO: Shouldn't this not be necessary? How can I get an abscissa outside 52 | // the knot domain? 53 | if g >= domain.0 && g <= domain.1 { 54 | abscissa.push(g); 55 | } 56 | } 57 | abscissa 58 | } 59 | pub fn eval(&self, t: f32, fcn: usize) -> f32 { 60 | debug_assert!(t >= self.knot_domain().0 && t <= self.knot_domain().1); 61 | self.evaluate_basis(t, fcn, self.degree) 62 | } 63 | /// TODO: Make this fucking work. 64 | fn evaluate_basis(&self, t: f32, i: usize, k: usize) -> f32 { 65 | if k == 0 { 66 | if t >= self.knots[i] { 67 | // Modified open end condition 68 | if i == self.modified_knot && t <= self.knots[i + 1] { 69 | 1.0 70 | } else if t < self.knots[i + 1] { 71 | 1.0 72 | } else { 73 | 0.0 74 | } 75 | } else { 76 | 0.0 77 | } 78 | } else if t >= self.knots[i] && t <= self.knots[i + k + 1] { 79 | let mut a = (t - self.knots[i]) / (self.knots[i + k] - self.knots[i]); 80 | let mut b = (self.knots[i + k + 1] - t) / (self.knots[i + k + 1] - self.knots[i + 1]); 81 | if !a.is_finite() { 82 | a = 0.0; 83 | } 84 | if !b.is_finite() { 85 | b = 0.0; 86 | } 87 | let c = self.evaluate_basis(t, i, k - 1); 88 | let d = self.evaluate_basis(t, i + 1, k - 1); 89 | a * c + b * d 90 | } else { 91 | 0.0 92 | } 93 | } 94 | /// Fill the knot vector for this curve for the new number of points/degree 95 | fn generate_knot_vector(clamped: bool, knots_required: usize, degree: usize) -> Vec { 96 | let mut knots = Vec::with_capacity(knots_required); 97 | let mut x = 0.0; 98 | for i in 0..knots_required { 99 | knots.push(x); 100 | if !(clamped && i < degree) && !(clamped && i >= knots_required - 1 - degree) { 101 | x += 1.0; 102 | } 103 | } 104 | knots 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/bezier.rs: -------------------------------------------------------------------------------- 1 | //! This module provides functionality for computing a Bezier curve 2 | //! defined by a set of control points on any type that can be linearly interpolated. 3 | #![allow(dead_code)] 4 | 5 | use std::ops::{Mul, Add}; 6 | use std::fmt::Debug; 7 | use std::slice::Iter; 8 | use std::f32; 9 | 10 | /// The interpolate trait is used to linearly interpolate between two types (or in the 11 | /// case of Quaternions, spherically linearly interpolate). The B-spline curve uses this 12 | /// trait to compute points on the curve for the given parameter value. 13 | /// 14 | /// A default implementation of this trait is provided for all `T` that are `Mul 15 | /// + Add + Copy` as these are the only operations needed to linearly interpolate the 16 | /// values. Any type implementing this trait should perform whatever the appropriate linear 17 | /// interpolaton is for the type. 18 | pub trait Interpolate { 19 | /// Linearly interpolate between `self` and `other` using `t`, for example with floats: 20 | /// 21 | /// ```text 22 | /// self * (1.0 - t) + other * t 23 | /// ``` 24 | /// 25 | /// If the result returned is not a correct linear interpolation of the values the 26 | /// curve produced using the value may not be correct. 27 | fn interpolate(&self, other: &Self, t: f32) -> Self; 28 | } 29 | 30 | impl + Add + Copy> Interpolate for T { 31 | fn interpolate(&self, other: &Self, t: f32) -> Self { 32 | *self * (1.0 - t) + *other * t 33 | } 34 | } 35 | 36 | /// A trait for types that can be projected on to a line segment 37 | pub trait ProjectToSegment { 38 | /// Should return the distance from the projected point on the segment 39 | /// to self. The second element of the tuple is out location along the segment 40 | /// starting from a to b. if < 0 we're before a, if > 1 we're after b and if 41 | /// in [0, 1] we're inside the segment 42 | fn project(&self, a: &Self, b: &Self) -> (f32, f32); 43 | } 44 | 45 | /// Represents a Bezier curve that will use polynomials of the specified degree 46 | /// to interpolate between the control points given the knots. 47 | #[derive(Clone, Debug)] 48 | pub struct Bezier { 49 | /// Control points for the curve 50 | pub control_points: Vec, 51 | } 52 | 53 | impl Bezier { 54 | /// Create a new Bezier curve of formed by interpolating the `control_points` 55 | pub fn new(control_points: Vec) -> Bezier { 56 | Bezier { control_points: control_points } 57 | } 58 | /// Compute a point on the curve at `t`, the parameter **must** be in the inclusive 59 | /// range [0, 1]. If `t` is out of bounds this function will assert 60 | /// on debug builds and on release builds you'll likely get an out of bounds crash. 61 | pub fn point(&self, t: f32) -> T { 62 | debug_assert!(t >= 0.0 && t <= 1.0); 63 | self.de_casteljau(t, self.control_points.len() - 1) 64 | } 65 | /// Get an iterator over the control points. 66 | pub fn control_points(&self) -> Iter { 67 | self.control_points.iter() 68 | } 69 | /// Insert a new point into the curve. The point will be inserted near the existing 70 | /// control points that it's closest too. Returns the index the point was 71 | /// inserted at. 72 | pub fn insert_point(&mut self, t: T) -> usize { 73 | if self.control_points.len() == 1 { 74 | self.control_points.push(t); 75 | return 1; 76 | } 77 | // Go through all segments of the control polygon and find the nearest one 78 | let nearest = self.control_points.windows(2).enumerate() 79 | .map(|(i, x)| { 80 | let proj = t.project(&x[0], &x[1]); 81 | (i, proj.0, proj.1) 82 | }) 83 | .fold((0, f32::MAX, 0.0), |acc, (i, d, l)| { 84 | if d < acc.1 { 85 | (i, d, l) 86 | } else { 87 | acc 88 | } 89 | }); 90 | // Check if we're appending or prepending the point 91 | if nearest.0 == 0 && nearest.2 == 0.0 { 92 | self.control_points.insert(0, t); 93 | 0 94 | } else if nearest.0 == self.control_points.len() - 2 && nearest.2 == 1.0 { 95 | self.control_points.push(t); 96 | self.control_points.len() - 1 97 | } else { 98 | self.control_points.insert(nearest.0 + 1, t); 99 | nearest.0 + 1 100 | } 101 | } 102 | /// Iteratively use de Casteljau's algorithm to compute the desired point 103 | fn de_casteljau(&self, t: f32, r: usize) -> T { 104 | let mut tmp = self.control_points.clone(); 105 | for lvl in 0..r { 106 | for i in 0..r - lvl { 107 | tmp[i] = tmp[i].interpolate(&tmp[i + 1], t); 108 | } 109 | } 110 | tmp[0] 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /src/bspline_surf.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::fmt::Debug; 4 | use std::iter; 5 | use std::slice; 6 | 7 | use bezier::Interpolate; 8 | use bspline::BSpline; 9 | 10 | /// Represents a B-spline surface that will use polynomials of the 11 | /// specified degree along u and v to to interpolate the control mesh 12 | /// using the knots along u and v. 13 | #[derive(Clone, Debug)] 14 | pub struct BSplineSurf { 15 | degree_u: usize, 16 | degree_v: usize, 17 | pub knots_u: Vec, 18 | pub knots_v: Vec, 19 | pub control_mesh: Vec>, 20 | } 21 | 22 | 23 | impl BSplineSurf { 24 | /// Make a new tensor product B-spline surface. The surface will be the product 25 | /// of a degree.0, knots.0 and degree.1, knots.1 B-spline using the control mesh. 26 | pub fn new(degree: (usize, usize), knots: (Vec, Vec), control_mesh: Vec>) -> BSplineSurf { 27 | if control_mesh.is_empty() { 28 | panic!("Surface control mesh cannot be empty!"); 29 | } 30 | // TODO: Validate params 31 | BSplineSurf { degree_u: degree.0, degree_v: degree.1, 32 | knots_u: knots.0, knots_v: knots.1, 33 | control_mesh: control_mesh 34 | } 35 | } 36 | /// Get the u curve degree 37 | pub fn degree_u(&self) -> usize { 38 | self.degree_u 39 | } 40 | /// Get the v curve degree 41 | pub fn degree_v(&self) -> usize { 42 | self.degree_v 43 | } 44 | /// Get the min and max knot domain values for finding the `t` range to compute 45 | /// the curve over in the u axis. The curve is only defined over the inclusive range `[min, max]`, 46 | /// passing a `t` value outside of this range will result in an assert on debug builds 47 | /// and likely a crash on release builds. 48 | pub fn knot_domain_u(&self) -> (f32, f32) { 49 | (self.knots_u[self.degree_u], self.knots_u[self.knots_u.len() - 1 - self.degree_u]) 50 | } 51 | /// Get an iterator over the knots within the domain 52 | pub fn knot_domain_u_iter(&self) -> iter::Take>> { 53 | self.knots_u.iter().skip(self.degree_u).take(self.knots_u.len() - 2 * self.degree_u) 54 | } 55 | pub fn greville_abscissa_u(&self) -> Vec { 56 | let mut abscissa = Vec::with_capacity(self.control_mesh.len()); 57 | let domain = self.knot_domain_u(); 58 | for i in 0..self.control_mesh.len() { 59 | let g = self.knots_u.iter().enumerate().skip_while(|&(c, _)| c < i + 1) 60 | .take_while(|&(c, _)| c <= i + self.degree_u) 61 | .map(|(_, x)| x) 62 | .fold(0.0, |acc, x| acc + *x) / self.degree_u as f32; 63 | // TODO: Shouldn't this not be necessary? How can I get an abscissa outside 64 | // the knot domain? 65 | if g >= domain.0 && g <= domain.1 { 66 | abscissa.push(g); 67 | } 68 | } 69 | abscissa 70 | } 71 | /// Get a vector with the Greville abscissa for the u axis 72 | /// Get the min and max knot domain values for finding the `t` range to compute 73 | /// the curve over in the v axis. The curve is only defined over the inclusive range `[min, max]`, 74 | /// passing a `t` value outside of this range will result in an assert on debug builds 75 | /// and likely a crash on release builds. 76 | pub fn knot_domain_v(&self) -> (f32, f32) { 77 | (self.knots_v[self.degree_v], self.knots_v[self.knots_v.len() - 1 - self.degree_v]) 78 | } 79 | pub fn knot_domain_v_iter(&self) -> iter::Take>> { 80 | self.knots_v.iter().skip(self.degree_v).take(self.knots_v.len() - 2 * self.degree_v) 81 | } 82 | /// Get a vector with the Greville abscissa for the v axis 83 | pub fn greville_abscissa_v(&self) -> Vec { 84 | let mut abscissa = Vec::with_capacity(self.control_mesh[0].len()); 85 | let domain = self.knot_domain_v(); 86 | for i in 0..self.control_mesh[0].len() { 87 | let g = self.knots_v.iter().enumerate().skip_while(|&(c, _)| c < i + 1) 88 | .take_while(|&(c, _)| c <= i + self.degree_v) 89 | .map(|(_, x)| x) 90 | .fold(0.0, |acc, x| acc + *x) / self.degree_v as f32; 91 | // TODO: Shouldn't this not be necessary? How can I get an abscissa outside 92 | // the knot domain? 93 | if g >= domain.0 && g <= domain.1 { 94 | abscissa.push(g); 95 | } 96 | } 97 | abscissa 98 | } 99 | /// Compute an isoline along v for a fixed value of u 100 | pub fn isoline_v(&self, u: f32) -> BSpline { 101 | // Build and evaluate B-splines for each column of the control mesh to build the control 102 | // points for the isoline. 103 | let mut isoline_ctrl_pts = Vec::with_capacity(self.control_mesh.len()); 104 | for j in 0..self.control_mesh[0].len() { 105 | let mut column = Vec::with_capacity(self.control_mesh.len()); 106 | for i in 0..self.control_mesh.len() { 107 | column.push(self.control_mesh[i][j]); 108 | } 109 | // Build the column of points 110 | let spline = BSpline::new(self.degree_u, column, self.knots_u.clone()); 111 | isoline_ctrl_pts.push(spline.point(u)); 112 | } 113 | BSpline::new(self.degree_v, isoline_ctrl_pts, self.knots_v.clone()) 114 | } 115 | /// Compute an isoline along u for a fixed value of v 116 | pub fn isoline_u(&self, v: f32) -> BSpline { 117 | // Build and evaluate B-splines for each row of the control mesh to build the control 118 | // points for the isoline. 119 | let mut isoline_ctrl_pts = Vec::with_capacity(self.control_mesh.len()); 120 | for i in 0..self.control_mesh.len() { 121 | let spline = BSpline::new(self.degree_v, self.control_mesh[i].clone(), self.knots_v.clone()); 122 | isoline_ctrl_pts.push(spline.point(v)); 123 | } 124 | BSpline::new(self.degree_u, isoline_ctrl_pts, self.knots_u.clone()) 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /src/display_curve3d.rs: -------------------------------------------------------------------------------- 1 | /// Manages displaying and toggling interaction modes with 2 | /// a specific BSpline curve in the scene. 3 | 4 | use std::f32; 5 | 6 | use glium::{Surface, VertexBuffer, Program, DrawParameters}; 7 | use glium::backend::Facade; 8 | use glium::index::{NoIndices, PrimitiveType}; 9 | use imgui::Ui; 10 | 11 | use bspline::BSpline; 12 | use point::Point; 13 | 14 | pub struct DisplayCurve3D<'a, F: 'a + Facade> { 15 | display: &'a F, 16 | pub curve: BSpline, 17 | curve_points_vbo: VertexBuffer, 18 | control_points_vbo: VertexBuffer, 19 | draw_curve: bool, 20 | draw_control_poly: bool, 21 | draw_control_points: bool, 22 | curve_color: [f32; 3], 23 | control_color: [f32; 3], 24 | } 25 | 26 | impl<'a, F: 'a + Facade> DisplayCurve3D<'a, F> { 27 | pub fn new(curve: BSpline, display: &'a F) -> DisplayCurve3D<'a, F> { 28 | let control_points_vbo; 29 | let curve_points_vbo; 30 | if !curve.control_points.is_empty() { 31 | let step_size = 0.01; 32 | let t_range = curve.knot_domain(); 33 | let steps = ((t_range.1 - t_range.0) / step_size) as usize; 34 | control_points_vbo = VertexBuffer::new(display, &curve.control_points[..]).unwrap(); 35 | let mut points = Vec::with_capacity(steps); 36 | // Just draw the first one for now 37 | for s in 0..steps + 1 { 38 | let t = step_size * s as f32 + t_range.0; 39 | points.push(curve.point(t)); 40 | } 41 | curve_points_vbo = VertexBuffer::new(display, &points[..]).unwrap(); 42 | } else { 43 | control_points_vbo = VertexBuffer::empty(display, 10).unwrap(); 44 | curve_points_vbo = VertexBuffer::empty(display, 10).unwrap(); 45 | } 46 | DisplayCurve3D { display: display, 47 | curve: curve, 48 | curve_points_vbo: curve_points_vbo, 49 | control_points_vbo: control_points_vbo, 50 | draw_curve: true, 51 | draw_control_poly: true, 52 | draw_control_points: true, 53 | curve_color: [0.8, 0.8, 0.1], 54 | control_color: [0.8, 0.8, 0.8], 55 | } 56 | } 57 | pub fn render(&self, target: &mut S, program: &Program, draw_params: &DrawParameters, 58 | proj_view: &[[f32; 4]; 4], selected: bool, attenuation: f32) { 59 | let (curve_color, control_color) = 60 | if selected { 61 | (self.curve_color, self.control_color) 62 | } else { 63 | ([attenuation * self.curve_color[0], attenuation * self.curve_color[1], 64 | attenuation * self.curve_color[2]], 65 | [attenuation * self.control_color[0], attenuation * self.control_color[1], 66 | attenuation * self.control_color[2]]) 67 | }; 68 | if !self.curve.control_points.is_empty() { 69 | let uniforms = uniform! { 70 | proj_view: *proj_view, 71 | pcolor: curve_color, 72 | }; 73 | // Draw the curve 74 | if self.draw_curve { 75 | target.draw(&self.curve_points_vbo, &NoIndices(PrimitiveType::LineStrip), 76 | &program, &uniforms, &draw_params).unwrap(); 77 | } 78 | let uniforms = uniform! { 79 | proj_view: *proj_view, 80 | pcolor: control_color, 81 | }; 82 | // Draw the control polygon 83 | if self.draw_control_poly { 84 | target.draw(&self.control_points_vbo, &NoIndices(PrimitiveType::LineStrip), 85 | &program, &uniforms, &draw_params).unwrap(); 86 | } 87 | if self.draw_control_points { 88 | // Draw the control points 89 | target.draw(&self.control_points_vbo, &NoIndices(PrimitiveType::Points), 90 | &program, &uniforms, &draw_params).unwrap(); 91 | } 92 | } 93 | } 94 | pub fn draw_ui(&mut self, ui: &Ui) { 95 | ui.text(im_str!("3D Curve")); 96 | ui.text(im_str!("Number of Control Points: {}", self.curve.control_points.len())); 97 | ui.checkbox(im_str!("Draw Curve"), &mut self.draw_curve); 98 | ui.checkbox(im_str!("Draw Control Polygon"), &mut self.draw_control_poly); 99 | ui.checkbox(im_str!("Draw Control Points"), &mut self.draw_control_points); 100 | let mut curve_changed = false; 101 | // I use the open curve term b/c Elaine will be interacting with it and she 102 | // calls clamped curves open. 103 | let mut curve_clamped = self.curve.is_clamped(); 104 | if ui.checkbox(im_str!("Open Curve"), &mut curve_clamped) { 105 | self.curve.set_clamped(curve_clamped); 106 | curve_changed = true; 107 | } 108 | let mut curve_degree = self.curve.degree() as i32; 109 | if ui.slider_int(im_str!("Curve Degree"), &mut curve_degree, 1, 110 | self.curve.max_possible_degree() as i32).build() 111 | { 112 | if self.curve.max_possible_degree() != 0 { 113 | self.curve.set_degree(curve_degree as usize); 114 | curve_changed = true; 115 | } 116 | } 117 | if curve_changed && !self.curve.control_points.is_empty() { 118 | let step_size = 0.01; 119 | let t_range = self.curve.knot_domain(); 120 | let steps = ((t_range.1 - t_range.0) / step_size) as usize; 121 | self.control_points_vbo = VertexBuffer::new(self.display, &self.curve.control_points[..]).unwrap(); 122 | let mut points = Vec::with_capacity(steps); 123 | // Just draw the first one for now 124 | for s in 0..steps + 1 { 125 | let t = step_size * s as f32 + t_range.0; 126 | points.push(self.curve.point(t)); 127 | } 128 | self.curve_points_vbo = VertexBuffer::new(self.display, &points[..]).unwrap(); 129 | } 130 | ui.color_edit3(im_str!("Curve Color"), &mut self.curve_color).build(); 131 | ui.color_edit3(im_str!("Control Color"), &mut self.control_color).build(); 132 | } 133 | } 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/imgui_support.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use glium; 4 | use glium::glutin::{ElementState, Event, MouseButton, MouseScrollDelta, VirtualKeyCode, TouchPhase}; 5 | use imgui::{self, ImGui, ImGuiKey, ImStr}; 6 | use imgui_sys; 7 | 8 | pub fn is_mouse_hovering_any_window() -> bool { 9 | unsafe { imgui_sys::igIsMouseHoveringAnyWindow() } 10 | } 11 | pub fn is_any_item_active() -> bool { 12 | unsafe { imgui_sys::igIsAnyItemActive() } 13 | } 14 | pub fn radio_button(label: ImStr, value: &mut i32, button: i32) { 15 | unsafe { imgui_sys::igRadioButton(label.as_ptr(), value as *mut i32, button); } 16 | } 17 | pub fn push_id_int(id: i32) { 18 | unsafe { imgui_sys::igPushIdInt(id); } 19 | } 20 | pub fn pop_id() { 21 | unsafe { imgui_sys::igPopId(); } 22 | } 23 | 24 | /// Manages giving ImGui key presses, mouse motion and so on 25 | pub struct ImGuiSupport { 26 | pub imgui: ImGui, 27 | pub mouse_pos: (i32, i32), 28 | pub mouse_pressed: (bool, bool, bool), 29 | pub mouse_wheel: f32, 30 | pub last_frame: Instant, 31 | } 32 | 33 | impl ImGuiSupport { 34 | pub fn init() -> ImGuiSupport { 35 | let mut imgui = ImGui::init(); 36 | imgui.set_imgui_key(ImGuiKey::Tab, 0); 37 | imgui.set_imgui_key(ImGuiKey::LeftArrow, 1); 38 | imgui.set_imgui_key(ImGuiKey::RightArrow, 2); 39 | imgui.set_imgui_key(ImGuiKey::UpArrow, 3); 40 | imgui.set_imgui_key(ImGuiKey::DownArrow, 4); 41 | imgui.set_imgui_key(ImGuiKey::PageUp, 5); 42 | imgui.set_imgui_key(ImGuiKey::PageDown, 6); 43 | imgui.set_imgui_key(ImGuiKey::Home, 7); 44 | imgui.set_imgui_key(ImGuiKey::End, 8); 45 | imgui.set_imgui_key(ImGuiKey::Delete, 9); 46 | imgui.set_imgui_key(ImGuiKey::Backspace, 10); 47 | imgui.set_imgui_key(ImGuiKey::Enter, 11); 48 | imgui.set_imgui_key(ImGuiKey::Escape, 12); 49 | imgui.set_imgui_key(ImGuiKey::A, 13); 50 | imgui.set_imgui_key(ImGuiKey::C, 14); 51 | imgui.set_imgui_key(ImGuiKey::V, 15); 52 | imgui.set_imgui_key(ImGuiKey::X, 16); 53 | imgui.set_imgui_key(ImGuiKey::Y, 17); 54 | imgui.set_imgui_key(ImGuiKey::Z, 18); 55 | 56 | ImGuiSupport { 57 | imgui: imgui, 58 | mouse_pos: (0, 0), 59 | mouse_pressed: (false, false, false), 60 | mouse_wheel: 0.0, 61 | last_frame: Instant::now(), 62 | } 63 | } 64 | pub fn render_ui(&mut self, display: &glium::backend::glutin_backend::GlutinFacade) -> imgui::Ui { 65 | let now = Instant::now(); 66 | let delta = now - self.last_frame; 67 | let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0; 68 | self.last_frame = now; 69 | 70 | let window = display.get_window().unwrap(); 71 | let size_pts = window.get_inner_size_points().unwrap(); 72 | let size_pixels = window.get_inner_size_pixels().unwrap(); 73 | self.imgui.frame(size_pts, size_pixels, delta_s) 74 | } 75 | pub fn update_mouse(&mut self) { 76 | let scale = self.imgui.display_framebuffer_scale(); 77 | self.imgui.set_mouse_pos(self.mouse_pos.0 as f32 / scale.0, self.mouse_pos.1 as f32 / scale.1); 78 | self.imgui.set_mouse_down(&[self.mouse_pressed.0, self.mouse_pressed.1, self.mouse_pressed.2, 79 | false, false]); 80 | self.imgui.set_mouse_wheel(self.mouse_wheel / scale.1); 81 | self.mouse_wheel = 0.0; 82 | } 83 | pub fn update_event(&mut self, e: &Event) { 84 | match *e { 85 | Event::KeyboardInput(state, _, code) => { 86 | let pressed = state == ElementState::Pressed; 87 | match code { 88 | Some(VirtualKeyCode::Tab) => self.imgui.set_key(0, pressed), 89 | Some(VirtualKeyCode::Left) => self.imgui.set_key(1, pressed), 90 | Some(VirtualKeyCode::Right) => self.imgui.set_key(2, pressed), 91 | Some(VirtualKeyCode::Up) => self.imgui.set_key(3, pressed), 92 | Some(VirtualKeyCode::Down) => self.imgui.set_key(4, pressed), 93 | Some(VirtualKeyCode::PageUp) => self.imgui.set_key(5, pressed), 94 | Some(VirtualKeyCode::PageDown) => self.imgui.set_key(6, pressed), 95 | Some(VirtualKeyCode::Home) => self.imgui.set_key(7, pressed), 96 | Some(VirtualKeyCode::End) => self.imgui.set_key(8, pressed), 97 | Some(VirtualKeyCode::Delete) => self.imgui.set_key(9, pressed), 98 | Some(VirtualKeyCode::Back) => self.imgui.set_key(10, pressed), 99 | Some(VirtualKeyCode::Return) => self.imgui.set_key(11, pressed), 100 | Some(VirtualKeyCode::Escape) => self.imgui.set_key(12, pressed), 101 | Some(VirtualKeyCode::A) => self.imgui.set_key(13, pressed), 102 | Some(VirtualKeyCode::C) => self.imgui.set_key(14, pressed), 103 | Some(VirtualKeyCode::V) => self.imgui.set_key(15, pressed), 104 | Some(VirtualKeyCode::X) => self.imgui.set_key(16, pressed), 105 | Some(VirtualKeyCode::Y) => self.imgui.set_key(17, pressed), 106 | Some(VirtualKeyCode::Z) => self.imgui.set_key(18, pressed), 107 | Some(VirtualKeyCode::LControl) | Some(VirtualKeyCode::RControl) => 108 | self.imgui.set_key_ctrl(pressed), 109 | Some(VirtualKeyCode::LShift) | Some(VirtualKeyCode::RShift) => 110 | self.imgui.set_key_shift(pressed), 111 | Some(VirtualKeyCode::LAlt) | Some(VirtualKeyCode::RAlt) => 112 | self.imgui.set_key_alt(pressed), 113 | Some(VirtualKeyCode::LWin) | Some(VirtualKeyCode::RWin) => 114 | self.imgui.set_key_super(pressed), 115 | _ => {}, 116 | } 117 | }, 118 | Event::MouseMoved(x, y) => self.mouse_pos = (x, y), 119 | Event::MouseInput(state, MouseButton::Left) => 120 | self.mouse_pressed.0 = state == ElementState::Pressed, 121 | Event::MouseInput(state, MouseButton::Right) => 122 | self.mouse_pressed.1 = state == ElementState::Pressed, 123 | Event::MouseInput(state, MouseButton::Middle) => 124 | self.mouse_pressed.2 = state == ElementState::Pressed, 125 | Event::MouseWheel(MouseScrollDelta::LineDelta(_, y), TouchPhase::Moved) => 126 | self.mouse_wheel = y, 127 | Event::MouseWheel(MouseScrollDelta::PixelDelta(_, y), TouchPhase::Moved) => 128 | self.mouse_wheel = y, 129 | Event::ReceivedCharacter(c) => self.imgui.add_input_character(c), 130 | _ => {}, 131 | } 132 | } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/display_surf_interp.rs: -------------------------------------------------------------------------------- 1 | //! Manages displaying and toggling interaction modes with 2 | //! a specific BSpline surface in the scene. 3 | 4 | use std::f32; 5 | 6 | use glium::{Surface, VertexBuffer, Program, DrawParameters}; 7 | use glium::backend::Facade; 8 | use glium::index::{NoIndices, PrimitiveType}; 9 | use imgui::Ui; 10 | use rulinalg::matrix::Matrix; 11 | use rulinalg::vector::Vector; 12 | 13 | use bspline::BSpline; 14 | use bspline_surf::BSplineSurf; 15 | use bspline_basis::BSplineBasis; 16 | use display_surf::DisplaySurf; 17 | use point::Point; 18 | 19 | pub struct DisplaySurfInterpolation<'a, F: 'a + Facade> { 20 | display: &'a F, 21 | curves: Vec>, 22 | surf: DisplaySurf, 23 | interpolation_degree: usize, 24 | // The input curves 25 | input_curves_vbo: Vec>, 26 | // The input control points 27 | input_points_vbo: VertexBuffer, 28 | draw_input_curves: bool, 29 | draw_input_points: bool, 30 | curve_color: [f32; 3], 31 | } 32 | 33 | impl<'a, F: 'a + Facade> DisplaySurfInterpolation<'a, F> { 34 | pub fn new(curves: Vec>, display: &'a F) -> DisplaySurfInterpolation<'a, F> { 35 | let mut control_points = Vec::new(); 36 | let mut input_curves_vbo = Vec::with_capacity(curves.len()); 37 | let step_size = 0.01; 38 | for c in curves.iter() { 39 | let t_range = c.knot_domain(); 40 | let steps = ((t_range.1 - t_range.0) / step_size) as usize; 41 | let mut points = Vec::with_capacity(steps); 42 | // Just draw the first one for now 43 | for s in 0..steps + 1 { 44 | let t = step_size * s as f32 + t_range.0; 45 | points.push(c.point(t)); 46 | } 47 | input_curves_vbo.push(VertexBuffer::new(display, &points[..]).unwrap()); 48 | 49 | for pt in &c.control_points[..] { 50 | control_points.push(*pt); 51 | } 52 | } 53 | let control_points_vbo = VertexBuffer::new(display, &control_points[..]).unwrap(); 54 | let surf = compute_nodal_interpolation(&curves[..], 1); 55 | 56 | DisplaySurfInterpolation { display: display, 57 | curves: curves, 58 | surf: DisplaySurf::new(surf, display), 59 | interpolation_degree: 1, 60 | input_curves_vbo: input_curves_vbo, 61 | input_points_vbo: control_points_vbo, 62 | draw_input_curves: true, 63 | draw_input_points: true, 64 | curve_color: [0.1, 0.8, 0.1], 65 | } 66 | } 67 | pub fn render(&self, target: &mut S, program: &Program, draw_params: &DrawParameters, 68 | proj_view: &[[f32; 4]; 4], selected: bool, attenuation: f32) { 69 | let curve_color = 70 | if selected { 71 | self.curve_color 72 | } else { 73 | [attenuation * self.curve_color[0], attenuation * self.curve_color[1], 74 | attenuation * self.curve_color[2]] 75 | }; 76 | let uniforms = uniform! { 77 | proj_view: *proj_view, 78 | pcolor: curve_color, 79 | }; 80 | // Draw the curve 81 | if self.draw_input_curves { 82 | for iso in &self.input_curves_vbo[..] { 83 | target.draw(iso, &NoIndices(PrimitiveType::LineStrip), 84 | &program, &uniforms, &draw_params).unwrap(); 85 | } 86 | } 87 | if self.draw_input_points { 88 | target.draw(&self.input_points_vbo, &NoIndices(PrimitiveType::Points), 89 | &program, &uniforms, &draw_params).unwrap(); 90 | } 91 | self.surf.render(target, program, draw_params, proj_view, selected, attenuation); 92 | } 93 | pub fn draw_ui(&mut self, ui: &Ui) { 94 | ui.text(im_str!("3D Surface Interpolation")); 95 | ui.checkbox(im_str!("Draw Input Curves"), &mut self.draw_input_curves); 96 | ui.checkbox(im_str!("Draw Input Control Points"), &mut self.draw_input_points); 97 | ui.color_edit3(im_str!("Input Color"), &mut self.curve_color).build(); 98 | let max_degree = (self.curves.len() - 1) as i32; 99 | let mut current_degree = self.interpolation_degree as i32; 100 | if ui.slider_int(im_str!("Interp. Degree"), &mut current_degree, 1, max_degree).build() { 101 | self.interpolation_degree = current_degree as usize; 102 | self.surf = DisplaySurf::new(compute_nodal_interpolation(&self.curves[..], self.interpolation_degree), 103 | self.display); 104 | } 105 | self.surf.draw_ui(ui); 106 | } 107 | } 108 | 109 | fn compute_nodal_interpolation(curves: &[BSpline], degree: usize) -> BSplineSurf { 110 | let mut control_points = Vec::new(); 111 | for c in curves.iter() { 112 | for pt in &c.control_points[..] { 113 | control_points.push(*pt); 114 | } 115 | } 116 | // Setup the bases for u and v so we can build the matrices 117 | let basis_u = BSplineBasis::new(curves[0].degree(), curves[0].knots().map(|x| *x).collect()); 118 | //let abscissa_u = basis_u.greville_abscissa(); 119 | // Is the result right for cubic? 120 | let basis_v = BSplineBasis::clamped_uniform(degree, curves.len()); 121 | let abscissa_v = basis_v.greville_abscissa(); 122 | 123 | // This is actually the N matrix in the 12/5 notes. 124 | let f = Matrix::from_fn(curves.len(), abscissa_v.len(), 125 | |i, j| basis_v.eval(abscissa_v[j], i)); 126 | let x_pos: Vec<_> = control_points.iter().map(|x| x.pos[0]).collect(); 127 | let y_pos: Vec<_> = control_points.iter().map(|x| x.pos[1]).collect(); 128 | let z_pos: Vec<_> = control_points.iter().map(|x| x.pos[2]).collect(); 129 | // Are these dimensions right? 130 | let r_mats = vec![Matrix::new(curves.len(), curves[0].control_points.len(), x_pos), 131 | Matrix::new(curves.len(), curves[0].control_points.len(), y_pos), 132 | Matrix::new(curves.len(), curves[0].control_points.len(), z_pos)]; 133 | let mut result_mats = Vec::with_capacity(r_mats.len()); 134 | 135 | // Solve each column 136 | let mut x = 0; 137 | for r in &r_mats[..] { 138 | let mut res_mat = Matrix::zeros(curves.len(), curves[0].control_points.len()); 139 | for j in 0..curves[0].control_points.len() { 140 | let rhs = Vector::new((0..curves.len()).map(|i| r[[i, j]]).collect::>()); 141 | let result = f.clone().solve(rhs).expect("System could not be solved!?"); 142 | for i in 0..curves.len() { 143 | res_mat[[i, j]] = result[i]; 144 | } 145 | } 146 | result_mats.push(res_mat); 147 | x = x + 1; 148 | } 149 | let mut surf_mesh = Vec::new(); 150 | for i in 0..curves.len() { 151 | let mut mesh_row = Vec::new(); 152 | for j in 0..curves[0].control_points.len() { 153 | let p = Point::new(result_mats[0][[i, j]], result_mats[1][[i, j]], result_mats[2][[i, j]]); 154 | mesh_row.push(p); 155 | } 156 | surf_mesh.push(mesh_row); 157 | } 158 | BSplineSurf::new((basis_v.degree(), basis_u.degree()), (basis_v.knots, basis_u.knots), surf_mesh) 159 | } 160 | 161 | -------------------------------------------------------------------------------- /src/display_curve.rs: -------------------------------------------------------------------------------- 1 | /// Manages displaying and toggling interaction modes with 2 | /// a specific BSpline curve in the scene. 3 | 4 | use std::f32; 5 | 6 | use glium::{Surface, VertexBuffer, Program, DrawParameters}; 7 | use glium::backend::Facade; 8 | use glium::index::{NoIndices, PrimitiveType}; 9 | use imgui::Ui; 10 | 11 | use bspline::BSpline; 12 | use point::Point; 13 | 14 | pub struct DisplayCurve<'a, F: 'a + Facade> { 15 | display: &'a F, 16 | pub curve: BSpline, 17 | curve_points_vbo: VertexBuffer, 18 | control_points_vbo: VertexBuffer, 19 | break_points_vbo: VertexBuffer, 20 | draw_curve: bool, 21 | draw_control_poly: bool, 22 | draw_control_points: bool, 23 | draw_break_points: bool, 24 | moving_point: Option, 25 | curve_color: [f32; 3], 26 | control_color: [f32; 3], 27 | break_point_color: [f32; 3], 28 | } 29 | 30 | impl<'a, F: 'a + Facade> DisplayCurve<'a, F> { 31 | pub fn new(curve: BSpline, display: &'a F) -> DisplayCurve<'a, F> { 32 | let control_points_vbo; 33 | let curve_points_vbo; 34 | let break_points_vbo; 35 | if !curve.control_points.is_empty() { 36 | let step_size = 0.01; 37 | let t_range = curve.knot_domain(); 38 | let steps = ((t_range.1 - t_range.0) / step_size) as usize; 39 | control_points_vbo = VertexBuffer::new(display, &curve.control_points[..]).unwrap(); 40 | let mut points = Vec::with_capacity(steps); 41 | // Just draw the first one for now 42 | for s in 0..steps + 1 { 43 | let t = step_size * s as f32 + t_range.0; 44 | points.push(curve.point(t)); 45 | } 46 | curve_points_vbo = VertexBuffer::new(display, &points[..]).unwrap(); 47 | let break_points: Vec<_> = curve.knot_domain_iter().map(|b| curve.point(*b)).collect(); 48 | break_points_vbo = VertexBuffer::new(display, &break_points[..]).unwrap(); 49 | } else { 50 | control_points_vbo = VertexBuffer::empty(display, 10).unwrap(); 51 | curve_points_vbo = VertexBuffer::empty(display, 10).unwrap(); 52 | break_points_vbo = VertexBuffer::empty(display, 10).unwrap(); 53 | } 54 | DisplayCurve { display: display, 55 | curve: curve, 56 | curve_points_vbo: curve_points_vbo, 57 | control_points_vbo: control_points_vbo, 58 | break_points_vbo: break_points_vbo, 59 | draw_curve: true, 60 | draw_control_poly: true, 61 | draw_control_points: true, 62 | draw_break_points: true, 63 | moving_point: None, 64 | curve_color: [0.8, 0.8, 0.1], 65 | control_color: [0.8, 0.8, 0.8], 66 | break_point_color: [0.1, 0.8, 0.8], 67 | } 68 | } 69 | pub fn handle_click(&mut self, pos: Point, shift_down: bool, zoom_factor: f32) { 70 | // If we're close to control point of the selected curve we're dragging it, 71 | // otherwise we're adding a new point 72 | let nearest = self.curve.control_points().enumerate().map(|(i, x)| (i, (*x - pos).length())) 73 | .fold((0, f32::MAX), |acc, (i, d)| if d < acc.1 { (i, d) } else { acc }); 74 | let point_size = 12.0 / (100.0 * zoom_factor); 75 | if shift_down { 76 | self.moving_point = None; 77 | if nearest.1 < point_size { 78 | self.curve.remove_point(nearest.0); 79 | } 80 | } else if let Some(p) = self.moving_point { 81 | self.curve.control_points[p] = pos; 82 | } else if nearest.1 < point_size { 83 | self.moving_point = Some(nearest.0); 84 | self.curve.control_points[nearest.0] = pos; 85 | } else { 86 | self.moving_point = Some(self.curve.insert_point(pos)); 87 | } 88 | if !self.curve.control_points.is_empty() { 89 | let step_size = 0.01; 90 | let t_range = self.curve.knot_domain(); 91 | let steps = ((t_range.1 - t_range.0) / step_size) as usize; 92 | self.control_points_vbo = VertexBuffer::new(self.display, &self.curve.control_points[..]).unwrap(); 93 | let mut points = Vec::with_capacity(steps); 94 | // Just draw the first one for now 95 | for s in 0..steps + 1 { 96 | let t = step_size * s as f32 + t_range.0; 97 | points.push(self.curve.point(t)); 98 | } 99 | self.curve_points_vbo = VertexBuffer::new(self.display, &points[..]).unwrap(); 100 | let break_points: Vec<_> = self.curve.knot_domain_iter().map(|b| self.curve.point(*b)).collect(); 101 | self.break_points_vbo = VertexBuffer::new(self.display, &break_points[..]).unwrap(); 102 | } 103 | } 104 | /// Release any held point that was being dragged 105 | pub fn release_point(&mut self) { 106 | self.moving_point = None; 107 | } 108 | pub fn render(&self, target: &mut S, program: &Program, draw_params: &DrawParameters, 109 | proj_view: &[[f32; 4]; 4], selected: bool, attenuation: f32) { 110 | let (curve_color, control_color, break_color) = 111 | if selected { 112 | (self.curve_color, self.control_color, self.break_point_color) 113 | } else { 114 | ([attenuation * self.curve_color[0], attenuation * self.curve_color[1], 115 | attenuation * self.curve_color[2]], 116 | [attenuation * self.control_color[0], attenuation * self.control_color[1], 117 | attenuation * self.control_color[2]], 118 | [attenuation * self.break_point_color[0], attenuation * self.break_point_color[1], 119 | attenuation * self.break_point_color[2]]) 120 | }; 121 | if !self.curve.control_points.is_empty() { 122 | let uniforms = uniform! { 123 | proj_view: *proj_view, 124 | pcolor: curve_color, 125 | }; 126 | // Draw the curve 127 | if self.draw_curve { 128 | target.draw(&self.curve_points_vbo, &NoIndices(PrimitiveType::LineStrip), 129 | &program, &uniforms, &draw_params).unwrap(); 130 | } 131 | let uniforms = uniform! { 132 | proj_view: *proj_view, 133 | pcolor: control_color, 134 | }; 135 | // Draw the control polygon 136 | if self.draw_control_poly { 137 | target.draw(&self.control_points_vbo, &NoIndices(PrimitiveType::LineStrip), 138 | &program, &uniforms, &draw_params).unwrap(); 139 | } 140 | if self.draw_control_points { 141 | // Draw the control points 142 | target.draw(&self.control_points_vbo, &NoIndices(PrimitiveType::Points), 143 | &program, &uniforms, &draw_params).unwrap(); 144 | } 145 | if self.draw_break_points { 146 | let uniforms = uniform! { 147 | proj_view: *proj_view, 148 | pcolor: break_color, 149 | }; 150 | // Draw the control points 151 | target.draw(&self.break_points_vbo, &NoIndices(PrimitiveType::Points), 152 | &program, &uniforms, &draw_params).unwrap(); 153 | } 154 | } 155 | } 156 | pub fn draw_ui(&mut self, ui: &Ui) { 157 | ui.text(im_str!("2D Curve")); 158 | ui.text(im_str!("Number of Control Points: {}", self.curve.control_points.len())); 159 | ui.checkbox(im_str!("Draw Curve"), &mut self.draw_curve); 160 | ui.checkbox(im_str!("Draw Control Polygon"), &mut self.draw_control_poly); 161 | ui.checkbox(im_str!("Draw Control Points"), &mut self.draw_control_points); 162 | ui.checkbox(im_str!("Draw Break Points"), &mut self.draw_break_points); 163 | let mut curve_changed = false; 164 | // I use the open curve term b/c Elaine will be interacting with it and she 165 | // calls clamped curves open. 166 | let mut curve_clamped = self.curve.is_clamped(); 167 | if ui.checkbox(im_str!("Open Curve"), &mut curve_clamped) { 168 | self.curve.set_clamped(curve_clamped); 169 | curve_changed = true; 170 | } 171 | let mut curve_degree = self.curve.degree() as i32; 172 | if ui.slider_int(im_str!("Curve Degree"), &mut curve_degree, 1, 173 | self.curve.max_possible_degree() as i32).build() 174 | { 175 | if self.curve.max_possible_degree() != 0 { 176 | self.curve.set_degree(curve_degree as usize); 177 | curve_changed = true; 178 | } 179 | } 180 | if curve_changed && !self.curve.control_points.is_empty() { 181 | let step_size = 0.01; 182 | let t_range = self.curve.knot_domain(); 183 | let steps = ((t_range.1 - t_range.0) / step_size) as usize; 184 | self.control_points_vbo = VertexBuffer::new(self.display, &self.curve.control_points[..]).unwrap(); 185 | let mut points = Vec::with_capacity(steps); 186 | // Just draw the first one for now 187 | for s in 0..steps + 1 { 188 | let t = step_size * s as f32 + t_range.0; 189 | points.push(self.curve.point(t)); 190 | } 191 | self.curve_points_vbo = VertexBuffer::new(self.display, &points[..]).unwrap(); 192 | let break_points: Vec<_> = self.curve.knot_domain_iter().map(|b| self.curve.point(*b)).collect(); 193 | self.break_points_vbo = VertexBuffer::new(self.display, &break_points[..]).unwrap(); 194 | } 195 | ui.color_edit3(im_str!("Curve Color"), &mut self.curve_color).build(); 196 | ui.color_edit3(im_str!("Control Color"), &mut self.control_color).build(); 197 | ui.color_edit3(im_str!("Break Point Color"), &mut self.break_point_color).build(); 198 | } 199 | } 200 | 201 | -------------------------------------------------------------------------------- /src/bspline.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::slice::Iter; 3 | use std::f32; 4 | use std::iter; 5 | use std::slice; 6 | 7 | use bezier::{Interpolate, ProjectToSegment}; 8 | 9 | /// Represents a B-spline curve that will use polynomials of the specified degree 10 | /// to interpolate between the control points given the knots. 11 | #[derive(Clone, Debug)] 12 | pub struct BSpline { 13 | /// Degree of the polynomial that we use to make the curve segments 14 | degree: usize, 15 | /// Control points for the curve 16 | pub control_points: Vec, 17 | /// The knot vector 18 | knots: Vec, 19 | } 20 | 21 | impl BSpline { 22 | /// Create a new B-spline curve of the desired `degree` that will interpolate 23 | /// the `control_points` using the `knots`. The knots should be sorted in non-decreasing 24 | /// order otherwise they will be sorted for you, which may lead to undesired knots 25 | /// for control points. Note that here we use the interpolating polynomial degree, 26 | /// if you're familiar with the convention of "B-spline curve order" the degree is `curve_order - 1`. 27 | /// 28 | /// Your curve must have a valid number of control points and knots or the function will panic. A B-spline 29 | /// curve requires at least as one more control point than the degree (`control_points.len() > 30 | /// degree`) and the number of knots should be equal to `control_points.len() + degree + 1`. 31 | pub fn new(degree: usize, control_points: Vec, mut knots: Vec) -> BSpline { 32 | if control_points.len() <= degree { 33 | panic!("Too few control points for curve"); 34 | } 35 | if !knots.is_empty() && knots.len() != control_points.len() + degree + 1 { 36 | panic!(format!("Invalid number of knots, got {}, expected {}", knots.len(), 37 | control_points.len() + degree + 1)); 38 | } 39 | knots.sort_by(|a, b| a.partial_cmp(b).unwrap()); 40 | let mut spline = BSpline { degree: degree, control_points: control_points, knots: knots }; 41 | if spline.knots.is_empty() { 42 | spline.fill_knot_vector(true, true); 43 | } 44 | spline 45 | } 46 | /// Create a new empty BSpline. 47 | pub fn empty() -> BSpline { 48 | BSpline { degree: 0, control_points: Vec::new(), knots: Vec::new() } 49 | } 50 | /// Compute a point on the curve at `t`, the parameter **must** be in the inclusive range 51 | /// of values returned by `knot_domain`. If `t` is out of bounds this function will assert 52 | /// on debug builds and on release builds you'll likely get an out of bounds crash. 53 | pub fn point(&self, t: f32) -> T { 54 | debug_assert!(t >= self.knot_domain().0 && t <= self.knot_domain().1); 55 | // Find the first index with a knot value greater than the t we're searching for. We want 56 | // to find i such that: knot[i] <= t < knot[i + 1] 57 | let i = match upper_bounds(&self.knots[..], t) { 58 | Some(x) if x == 0 => self.degree, 59 | Some(x) if x >= self.knots.len() - self.degree - 1 => 60 | self.knots.len() - self.degree - 1, 61 | Some(x) => x, 62 | None => self.knots.len() - self.degree - 1, 63 | }; 64 | self.de_boor_iterative(t, i) 65 | } 66 | /// Get an iterator over the control points. 67 | pub fn control_points(&self) -> Iter { 68 | self.control_points.iter() 69 | } 70 | /// Get an iterator over the knots. 71 | pub fn knots(&self) -> Iter { 72 | self.knots.iter() 73 | } 74 | /// Get the curve degree 75 | pub fn degree(&self) -> usize { 76 | self.degree 77 | } 78 | /// Get the min and max knot domain values for finding the `t` range to compute 79 | /// the curve over. The curve is only defined over the inclusive range `[min, max]`, 80 | /// passing a `t` value outside of this range will result in an assert on debug builds 81 | /// and likely a crash on release builds. 82 | pub fn knot_domain(&self) -> (f32, f32) { 83 | (self.knots[self.degree], self.knots[self.knots.len() - 1 - self.degree]) 84 | } 85 | /// Get an iterator over the knots within the domain 86 | pub fn knot_domain_iter(&self) -> iter::Take>> { 87 | self.knots.iter().skip(self.degree).take(self.knots.len() - 2 * self.degree) 88 | } 89 | /// Get the max degree of curve that this set of control points can support 90 | pub fn max_possible_degree(&self) -> usize { 91 | if self.control_points.is_empty() { 92 | 0 93 | } else { 94 | self.control_points.len() - 1 95 | } 96 | } 97 | /// Change the degree of the curve 98 | pub fn set_degree(&mut self, degree: usize) { 99 | assert!(degree <= self.max_possible_degree()); 100 | let was_clamped = self.is_clamped(); 101 | self.degree = degree; 102 | self.fill_knot_vector(was_clamped, was_clamped); 103 | } 104 | /// Remove a point from the curve 105 | pub fn remove_point(&mut self, i: usize) { 106 | self.control_points.remove(i); 107 | if self.control_points.len() <= self.degree { 108 | self.degree -= 1; 109 | } 110 | self.generate_knot_vector(); 111 | } 112 | /// Toggle whether the curve should be open/clamped (Elaine: floating/open) 113 | pub fn set_clamped(&mut self, clamped: bool) { 114 | self.fill_knot_vector(clamped, clamped); 115 | } 116 | pub fn is_clamped(&self) -> bool { 117 | let left_clamped = self.knots.iter().take(self.degree + 1) 118 | .fold(true, |acc, x| *x == self.knots[0] && acc); 119 | let right_clamped = self.knots.iter().rev().take(self.degree + 1) 120 | .fold(true, |acc, x| *x == self.knots[self.knots.len() - 1] && acc); 121 | left_clamped && right_clamped 122 | } 123 | /// Compute the number of knots required for this curve 124 | fn knots_required(&self) -> usize { 125 | self.control_points.len() + self.degree + 1 126 | } 127 | /// Regenerate the knot vector to update it for changing degree/control points based on 128 | /// whether it was open/clamped before (Elaine: terms floating/open) 129 | fn generate_knot_vector(&mut self) { 130 | // Check if we're clamped on the left/right (Elaine calls this end condition open) 131 | let left_clamped = self.knots.iter().take(self.degree + 1) 132 | .fold(true, |acc, x| *x == self.knots[0] && acc); 133 | let right_clamped = self.knots.iter().rev().take(self.degree + 1) 134 | .fold(true, |acc, x| *x == self.knots[self.knots.len() - 1] && acc); 135 | self.fill_knot_vector(left_clamped, right_clamped); 136 | } 137 | /// Fill the knot vector for this curve for the new number of points/degree 138 | fn fill_knot_vector(&mut self, left_clamped: bool, right_clamped: bool) { 139 | self.knots.clear(); 140 | let mut x = 0.0; 141 | for i in 0..self.knots_required() { 142 | self.knots.push(x); 143 | if !(left_clamped && i < self.degree) 144 | && !(right_clamped && i >= self.knots_required() - 1 - self.degree) { 145 | x += 1.0; 146 | } 147 | } 148 | } 149 | /// Iteratively compute de Boor's B-spline algorithm, this computes the recursive 150 | /// de Boor algorithm tree from the bottom up. At each level we use the results 151 | /// from the previous one to compute this level and store the results in the 152 | /// array indices we no longer need to compute the current level (the left one 153 | /// used computing node j). 154 | fn de_boor_iterative(&self, t: f32, i_start: usize) -> T { 155 | let mut tmp = Vec::with_capacity(self.degree + 1); 156 | for j in 0..self.degree + 1 { 157 | let p = j + i_start - self.degree - 1; 158 | tmp.push(self.control_points[p]); 159 | } 160 | for lvl in 0..self.degree { 161 | let k = lvl + 1; 162 | for j in 0..self.degree - lvl { 163 | let i = j + k + i_start - self.degree; 164 | let alpha = (t - self.knots[i - 1]) / (self.knots[i + self.degree - k] - self.knots[i - 1]); 165 | debug_assert!(!alpha.is_nan()); 166 | tmp[j] = tmp[j].interpolate(&tmp[j + 1], alpha); 167 | } 168 | } 169 | tmp[0] 170 | } 171 | } 172 | 173 | impl BSpline { 174 | /// Insert a new point into the curve. The point will be inserted near the existing 175 | /// control points that it's closest too. Returns the index the point was 176 | /// inserted at. 177 | pub fn insert_point(&mut self, t: T) -> usize { 178 | if self.control_points.len() == 1 { 179 | self.control_points.push(t); 180 | return 1; 181 | } 182 | // Go through all segments of the control polygon and find the nearest one 183 | let nearest = self.control_points.windows(2).enumerate() 184 | .map(|(i, x)| { 185 | let proj = t.project(&x[0], &x[1]); 186 | (i, proj.0, proj.1) 187 | }) 188 | .fold((0, f32::MAX, 0.0), |acc, (i, d, l)| { 189 | if d < acc.1 { 190 | (i, d, l) 191 | } else { 192 | acc 193 | } 194 | }); 195 | // Check if we're appending or prepending the point 196 | let idx = if nearest.0 == 0 && nearest.2 == 0.0 { 197 | self.control_points.insert(0, t); 198 | 0 199 | } else if nearest.0 == self.control_points.len() - 2 && nearest.2 == 1.0 { 200 | self.control_points.push(t); 201 | self.control_points.len() - 1 202 | } else { 203 | self.control_points.insert(nearest.0 + 1, t); 204 | nearest.0 + 1 205 | }; 206 | self.generate_knot_vector(); 207 | idx 208 | } 209 | } 210 | 211 | /// Return the index of the first element greater than the value passed. 212 | /// The data **must** be sorted. If no element greater than the value 213 | /// passed is found the function returns None. 214 | fn upper_bounds(data: &[f32], value: f32) -> Option { 215 | let mut first = 0usize; 216 | let mut step; 217 | let mut count = data.len() as isize; 218 | while count > 0 { 219 | step = count / 2; 220 | let it = first + step as usize; 221 | if !value.lt(&data[it]) { 222 | first = it + 1; 223 | count -= step + 1; 224 | } else { 225 | count = step; 226 | } 227 | } 228 | // If we didn't find an element greater than value 229 | if first == data.len() { 230 | None 231 | } else { 232 | Some(first) 233 | } 234 | } 235 | 236 | -------------------------------------------------------------------------------- /src/display_surf.rs: -------------------------------------------------------------------------------- 1 | /// Manages displaying and toggling interaction modes with 2 | /// a specific BSpline surface in the scene. 3 | 4 | use std::f32; 5 | 6 | use glium::{Surface, VertexBuffer, Program, DrawParameters}; 7 | use glium::backend::Facade; 8 | use glium::index::{NoIndices, PrimitiveType}; 9 | use imgui::Ui; 10 | 11 | use bspline_surf::BSplineSurf; 12 | use point::Point; 13 | 14 | pub struct DisplaySurf { 15 | // Plain isolines along the curve 16 | isolines_u_vbos: Vec>, 17 | isolines_v_vbos: Vec>, 18 | // Isolines along the greville abscissa 19 | greville_u_vbos: Vec>, 20 | greville_v_vbos: Vec>, 21 | // Isolines at each knot value 22 | knot_u_vbos: Vec>, 23 | knot_v_vbos: Vec>, 24 | control_points_vbo: VertexBuffer, 25 | draw_surf: bool, 26 | draw_greville: bool, 27 | draw_knots: bool, 28 | draw_control_points: bool, 29 | curve_color: [f32; 3], 30 | greville_color: [f32; 3], 31 | knot_color: [f32; 3], 32 | control_color: [f32; 3], 33 | } 34 | 35 | impl DisplaySurf { 36 | pub fn new<'a, F: 'a + Facade>(surf: BSplineSurf, display: &'a F) -> DisplaySurf { 37 | let isoline_step_size = 0.1; 38 | let step_size = 0.01; 39 | 40 | let t_range_u = surf.knot_domain_u(); 41 | let t_range_v = surf.knot_domain_v(); 42 | 43 | let isoline_start_steps_u = ((t_range_u.1 - t_range_u.0) / isoline_step_size) as usize; 44 | let isoline_start_steps_v = ((t_range_v.1 - t_range_v.0) / isoline_step_size) as usize; 45 | let steps_u = ((t_range_u.1 - t_range_u.0) / step_size) as usize; 46 | let steps_v = ((t_range_v.1 - t_range_v.0) / step_size) as usize; 47 | 48 | let abscissa_u = surf.greville_abscissa_u(); 49 | let abscissa_v = surf.greville_abscissa_v(); 50 | 51 | // We need in addition to the regular line sample steps to also sample where 52 | // an isoline is along the other axis, or greville point, or knot value so that 53 | // when the lines cross they both have that crossing point 54 | // Every knot value along u that we're going to have an isoline on 55 | let mut t_vals_u: Vec<_> = (0..isoline_start_steps_u + 1).map(|us| isoline_step_size * us as f32 + t_range_u.0) 56 | .chain(abscissa_u.iter().map(|x| *x)) 57 | .chain(surf.knots_u.iter() 58 | .filter_map(|x| if *x >= t_range_u.0 && *x <= t_range_u.1 { Some(*x) } else { None })) 59 | .collect(); 60 | t_vals_u.sort_by(|a, b| a.partial_cmp(b).unwrap()); 61 | t_vals_u.dedup(); 62 | 63 | // Every knot value along v that we're going to have an isoline on 64 | let mut t_vals_v: Vec<_> = (0..isoline_start_steps_v + 1).map(|vs| isoline_step_size * vs as f32 + t_range_v.0) 65 | .chain(abscissa_v.iter().map(|x| *x)) 66 | .chain(surf.knots_v.iter() 67 | .filter_map(|x| if *x >= t_range_v.0 && *x <= t_range_v.1 { Some(*x) } else { None })) 68 | .collect(); 69 | t_vals_v.sort_by(|a, b| a.partial_cmp(b).unwrap()); 70 | t_vals_v.dedup(); 71 | 72 | // t values for an isoline along u 73 | let mut isoline_u_t_vals: Vec<_> = (0..steps_u + 1).map(|u| step_size * u as f32 + t_range_u.0) 74 | .chain(t_vals_u.iter().map(|x| *x)).collect(); 75 | isoline_u_t_vals.sort_by(|a, b| a.partial_cmp(b).unwrap()); 76 | isoline_u_t_vals.dedup(); 77 | 78 | // t values for an isoline along v 79 | let mut isoline_v_t_vals: Vec<_> = (0..steps_v + 1).map(|u| step_size * u as f32 + t_range_v.0) 80 | .chain(t_vals_v.iter().map(|x| *x)).collect(); 81 | isoline_v_t_vals.sort_by(|a, b| a.partial_cmp(b).unwrap()); 82 | isoline_v_t_vals.dedup(); 83 | 84 | let mut greville_u_vbos = Vec::with_capacity(abscissa_u.len()); 85 | let mut greville_v_vbos = Vec::with_capacity(abscissa_v.len()); 86 | // For each Greville abscissa on u draw an isoline along v 87 | for u in &abscissa_u[..] { 88 | let curve = surf.isoline_v(*u); 89 | let mut points = Vec::with_capacity(steps_v); 90 | for t in &isoline_v_t_vals[..] { 91 | points.push(curve.point(*t)); 92 | } 93 | greville_u_vbos.push(VertexBuffer::new(display, &points[..]).unwrap()); 94 | } 95 | // For each Greville abscissa on v draw an isoline along u 96 | for v in &abscissa_v[..] { 97 | let curve = surf.isoline_u(*v); 98 | let mut points = Vec::with_capacity(steps_u); 99 | for t in &isoline_u_t_vals[..] { 100 | points.push(curve.point(*t)); 101 | } 102 | greville_v_vbos.push(VertexBuffer::new(display, &points[..]).unwrap()); 103 | } 104 | 105 | let mut knot_u_vbos = Vec::with_capacity(surf.knots_u.len()); 106 | let mut knot_v_vbos = Vec::with_capacity(surf.knots_v.len()); 107 | // For each knot on u draw an isoline along v 108 | for u in surf.knot_domain_u_iter() { 109 | let curve = surf.isoline_v(*u); 110 | let mut points = Vec::with_capacity(steps_v); 111 | for t in &isoline_v_t_vals[..] { 112 | points.push(curve.point(*t)); 113 | } 114 | knot_u_vbos.push(VertexBuffer::new(display, &points[..]).unwrap()); 115 | } 116 | // For each knot on v draw an isoline along u 117 | for v in surf.knot_domain_v_iter() { 118 | let curve = surf.isoline_u(*v); 119 | let mut points = Vec::with_capacity(steps_u); 120 | for t in &isoline_u_t_vals[..] { 121 | points.push(curve.point(*t)); 122 | } 123 | knot_v_vbos.push(VertexBuffer::new(display, &points[..]).unwrap()); 124 | } 125 | 126 | let mut isolines_u_vbos = Vec::with_capacity(isoline_start_steps_v); 127 | let mut isolines_v_vbos = Vec::with_capacity(isoline_start_steps_u); 128 | // Compute isolines along u 129 | for vs in 0..isoline_start_steps_v + 1 { 130 | let v = isoline_step_size * vs as f32 + t_range_v.0; 131 | if !abscissa_v.iter().chain(surf.knots_v.iter()).any(|x| *x == v) { 132 | let curve = surf.isoline_u(v); 133 | let mut points = Vec::with_capacity(steps_u); 134 | for t in &isoline_u_t_vals[..] { 135 | points.push(curve.point(*t)); 136 | } 137 | isolines_u_vbos.push(VertexBuffer::new(display, &points[..]).unwrap()); 138 | } 139 | } 140 | // Compute isolines along v 141 | for us in 0..isoline_start_steps_u + 1 { 142 | let u = isoline_step_size * us as f32 + t_range_u.0; 143 | if !abscissa_u.iter().chain(surf.knots_u.iter()).any(|x| *x == u) { 144 | let curve = surf.isoline_v(u); 145 | let mut points = Vec::with_capacity(steps_v); 146 | for t in &isoline_v_t_vals[..] { 147 | points.push(curve.point(*t)); 148 | } 149 | isolines_v_vbos.push(VertexBuffer::new(display, &points[..]).unwrap()); 150 | } 151 | } 152 | 153 | let mut control_points = Vec::new(); 154 | for r in &surf.control_mesh[..] { 155 | for p in &r[..] { 156 | control_points.push(*p); 157 | } 158 | } 159 | let control_points_vbo = VertexBuffer::new(display, &control_points[..]).unwrap(); 160 | 161 | DisplaySurf { isolines_u_vbos: isolines_u_vbos, 162 | isolines_v_vbos: isolines_v_vbos, 163 | greville_u_vbos: greville_u_vbos, 164 | greville_v_vbos: greville_v_vbos, 165 | knot_u_vbos: knot_u_vbos, 166 | knot_v_vbos: knot_v_vbos, 167 | control_points_vbo: control_points_vbo, 168 | draw_surf: true, 169 | draw_greville: true, 170 | draw_knots: true, 171 | draw_control_points: true, 172 | curve_color: [0.8, 0.8, 0.1], 173 | greville_color: [0.1, 0.8, 0.8], 174 | knot_color: [0.8, 0.1, 0.8], 175 | control_color: [0.8, 0.8, 0.8], 176 | } 177 | } 178 | pub fn render(&self, target: &mut S, program: &Program, draw_params: &DrawParameters, 179 | proj_view: &[[f32; 4]; 4], selected: bool, attenuation: f32) { 180 | let (curve_color, control_color, greville_color, knot_color) = 181 | if selected { 182 | (self.curve_color, self.control_color, self.greville_color, self.knot_color) 183 | } else { 184 | ([attenuation * self.curve_color[0], attenuation * self.curve_color[1], 185 | attenuation * self.curve_color[2]], 186 | 187 | [attenuation * self.control_color[0], attenuation * self.control_color[1], 188 | attenuation * self.control_color[2]], 189 | 190 | [attenuation * self.greville_color[0], attenuation * self.greville_color[1], 191 | attenuation * self.greville_color[2]], 192 | 193 | [attenuation * self.knot_color[0], attenuation * self.knot_color[1], 194 | attenuation * self.knot_color[2]]) 195 | }; 196 | let uniforms = uniform! { 197 | proj_view: *proj_view, 198 | pcolor: curve_color, 199 | }; 200 | // Draw the curve 201 | if self.draw_surf { 202 | for iso in self.isolines_u_vbos.iter().chain(self.isolines_v_vbos.iter()) { 203 | target.draw(iso, &NoIndices(PrimitiveType::LineStrip), 204 | &program, &uniforms, &draw_params).unwrap(); 205 | } 206 | } 207 | let uniforms = uniform! { 208 | proj_view: *proj_view, 209 | pcolor: greville_color, 210 | }; 211 | if self.draw_greville { 212 | for iso in self.greville_u_vbos.iter().chain(self.greville_v_vbos.iter()) { 213 | target.draw(iso, &NoIndices(PrimitiveType::LineStrip), 214 | &program, &uniforms, &draw_params).unwrap(); 215 | } 216 | } 217 | let uniforms = uniform! { 218 | proj_view: *proj_view, 219 | pcolor: knot_color, 220 | }; 221 | if self.draw_knots { 222 | for iso in self.knot_u_vbos.iter().chain(self.knot_v_vbos.iter()) { 223 | target.draw(iso, &NoIndices(PrimitiveType::LineStrip), 224 | &program, &uniforms, &draw_params).unwrap(); 225 | } 226 | } 227 | let uniforms = uniform! { 228 | proj_view: *proj_view, 229 | pcolor: control_color, 230 | }; 231 | if self.draw_control_points { 232 | // Draw the control points 233 | target.draw(&self.control_points_vbo, &NoIndices(PrimitiveType::Points), 234 | &program, &uniforms, &draw_params).unwrap(); 235 | } 236 | } 237 | pub fn draw_ui(&mut self, ui: &Ui) { 238 | ui.text(im_str!("3D Surface")); 239 | ui.checkbox(im_str!("Draw Surface"), &mut self.draw_surf); 240 | ui.checkbox(im_str!("Draw Greville Isolines"), &mut self.draw_greville); 241 | ui.checkbox(im_str!("Draw Knot Isolines"), &mut self.draw_knots); 242 | ui.checkbox(im_str!("Draw Control Points"), &mut self.draw_control_points); 243 | ui.color_edit3(im_str!("Curve Color"), &mut self.curve_color).build(); 244 | ui.color_edit3(im_str!("Greville Color"), &mut self.greville_color).build(); 245 | ui.color_edit3(im_str!("Knot Color"), &mut self.knot_color).build(); 246 | ui.color_edit3(im_str!("Control Color"), &mut self.control_color).build(); 247 | } 248 | } 249 | 250 | 251 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate glium; 3 | #[macro_use] 4 | extern crate imgui; 5 | extern crate imgui_sys; 6 | extern crate imgui_glium_renderer; 7 | extern crate cgmath; 8 | extern crate docopt; 9 | extern crate num_traits; 10 | extern crate rulinalg; 11 | extern crate serde; 12 | extern crate serde_json; 13 | extern crate arcball; 14 | 15 | mod imgui_support; 16 | mod bezier; 17 | mod bspline; 18 | mod point; 19 | mod camera2d; 20 | mod display_curve; 21 | mod display_curve3d; 22 | mod polyline; 23 | mod bspline_surf; 24 | mod display_surf; 25 | mod display_surf_interp; 26 | mod bspline_basis; 27 | 28 | use std::fs::File; 29 | use std::io::BufReader; 30 | use std::f32; 31 | 32 | use glium::{DisplayBuild, Surface, DrawParameters}; 33 | use glium::glutin::{self, ElementState, Event, VirtualKeyCode, MouseButton}; 34 | use cgmath::{SquareMatrix, Transform, Vector2, Matrix4}; 35 | use docopt::Docopt; 36 | use imgui_glium_renderer::Renderer; 37 | use arcball::ArcballCamera; 38 | 39 | use imgui_support::ImGuiSupport; 40 | use bspline::BSpline; 41 | use bspline_surf::BSplineSurf; 42 | use point::Point; 43 | use camera2d::Camera2d; 44 | use display_curve::DisplayCurve; 45 | use display_curve3d::DisplayCurve3D; 46 | use display_surf::DisplaySurf; 47 | use display_surf_interp::DisplaySurfInterpolation; 48 | 49 | /// Import a 2D BSpline curve from the file 50 | fn import_bspline(json: &serde_json::Value) -> BSpline { 51 | let degree = json["degree"].as_u64().expect("A curve degree must be specified") as usize; 52 | let points = json["points"].as_array().expect("A list of points must be specified").iter() 53 | .map(|p| Point::new(p["x"].as_f64().expect("Invalid x coord") as f32, 54 | p["y"].as_f64().expect("Invalid y coord") as f32, 55 | p["z"].as_f64().unwrap_or(0.0) as f32)).collect(); 56 | let mut knots = Vec::new(); 57 | if let Some(k) = json["knots"].as_array() { 58 | knots = k.iter().map(|x| x.as_f64().expect("Invalid knot value") as f32).collect(); 59 | } 60 | BSpline::new(degree, points, knots) 61 | } 62 | 63 | /// Import a B-spline surface file 64 | fn import_surf(json: &serde_json::Value) -> BSplineSurf { 65 | let u_data = json["u"].as_object().expect("Surface u component is required"); 66 | let v_data = json["v"].as_object().expect("Surface v component is required"); 67 | 68 | let degree_u = u_data["degree"].as_u64().expect("Surface u degree is required") as usize; 69 | let degree_v = v_data["degree"].as_u64().expect("Surface v degree is required") as usize; 70 | 71 | let knots_u = u_data["knots"].as_array().expect("Surface u knots are required").iter() 72 | .map(|x| x.as_f64().expect("Invalid knot value") as f32).collect(); 73 | let knots_v = v_data["knots"].as_array().expect("Surface v knots are required").iter() 74 | .map(|x| x.as_f64().expect("Invalid knot value") as f32).collect(); 75 | 76 | let mut mesh = Vec::new(); 77 | for r in json["mesh"].as_array().expect("Surface control mesh is required") { 78 | let points = r.as_array().expect("A list of points must be specified").iter() 79 | .map(|p| Point::new(p["x"].as_f64().expect("Invalid x coord") as f32, 80 | p["y"].as_f64().expect("Invalid y coord") as f32, 81 | p["z"].as_f64().expect("Invalid z coord") as f32)).collect(); 82 | mesh.push(points); 83 | } 84 | BSplineSurf::new((degree_u, degree_v), (knots_u, knots_v), mesh) 85 | } 86 | 87 | /// Import a B-spline nodal interpolation data file 88 | /// Note: for the assignment we only did interpolation on one axis, so it assumes 89 | /// the passed control points are the curve along v's control points 90 | fn import_surf_interpolation(json: &serde_json::Value) -> Vec> { 91 | let u_data = json["u"].as_object().expect("Surface u component is required"); 92 | let degree_u = u_data["degree"].as_u64().expect("Surface u degree is required") as usize; 93 | let knots_u: Vec = u_data["knots"].as_array().expect("Surface u knots are required").iter() 94 | .map(|x| x.as_f64().expect("Invalid knot value") as f32).collect(); 95 | 96 | let mut splines = Vec::new(); 97 | for r in json["mesh"].as_array().expect("Surface control mesh is required") { 98 | let points = r.as_array().expect("A list of points must be specified").iter() 99 | .map(|p| Point::new(p["x"].as_f64().expect("Invalid x coord") as f32, 100 | p["y"].as_f64().expect("Invalid y coord") as f32, 101 | p["z"].as_f64().expect("Invalid z coord") as f32)).collect(); 102 | splines.push(BSpline::new(degree_u, points, knots_u.clone())); 103 | } 104 | splines 105 | } 106 | 107 | const USAGE: &'static str = " 108 | Usage: 109 | spline-viewer [...] 110 | spline-viewer (-h | --help) 111 | 112 | Options: 113 | -h, --help Show this message. 114 | "; 115 | 116 | fn main() { 117 | let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit()); 118 | let target_gl_versions = glutin::GlRequest::GlThenGles { 119 | opengl_version: (3, 3), 120 | opengles_version: (3, 2), 121 | }; 122 | let mut width = 1280; 123 | let mut height = 720; 124 | let display = glutin::WindowBuilder::new() 125 | .with_dimensions(width, height) 126 | .with_gl(target_gl_versions) 127 | .with_gl_profile(glutin::GlProfile::Core) 128 | .with_title("Spline Viewer") 129 | .with_vsync() 130 | .build_glium().unwrap(); 131 | 132 | let mut curves = Vec::new(); 133 | let mut curves3d = Vec::new(); 134 | let mut surfaces = Vec::new(); 135 | let mut surface_interpolations = Vec::new(); 136 | for f in args.get_vec("") { 137 | let file = match File::open(&f) { 138 | Ok(f) => f, 139 | Err(e) => panic!("Failed to open file: {}", e), 140 | }; 141 | let reader = BufReader::new(file); 142 | let json: serde_json::Value = serde_json::from_reader(reader).expect("Failed to read input file"); 143 | let ty = json["type"].as_str().expect("A curve type must be specified"); 144 | if ty == "bspline2d" { 145 | curves.push(DisplayCurve::new(import_bspline(&json), &display)); 146 | } else if ty == "bspline3d" { 147 | curves3d.push(DisplayCurve3D::new(import_bspline(&json), &display)); 148 | } else if ty == "surface" { 149 | surfaces.push(DisplaySurf::new(import_surf(&json), &display)); 150 | } else if ty == "interpolation_u" { 151 | surface_interpolations.push(DisplaySurfInterpolation::new( 152 | import_surf_interpolation(&json), &display)); 153 | } else { 154 | println!("Unrecognized file type header {}", ty); 155 | } 156 | } 157 | 158 | println!("Got OpenGL: {:?}", display.get_opengl_version()); 159 | println!("Got GLSL: {:?}", display.get_supported_glsl_version()); 160 | 161 | let mut imgui = ImGuiSupport::init(); 162 | let mut imgui_renderer = Renderer::init(&mut imgui.imgui, &display).unwrap(); 163 | 164 | let mut camera_2d = Camera2d::new(); 165 | let mut arcball_camera = { 166 | use cgmath::{Point3, Vector3}; 167 | let look_at = Matrix4::::look_at(Point3::new(0.0, 0.0, 6.0), 168 | Point3::new(0.0, 0.0, 0.0), 169 | Vector3::new(0.0, 1.0, 0.0)); 170 | ArcballCamera::new(&look_at, 1.0, 5.0, [width as f32, height as f32]) 171 | }; 172 | 173 | let mut ortho_proj = cgmath::ortho(width as f32 / -200.0, width as f32 / 200.0, height as f32 / -200.0, 174 | height as f32 / 200.0, -0.01, -100.0); 175 | let mut persp_proj = cgmath::perspective(cgmath::Deg(65.0), width as f32 / height as f32, 0.01, 100.0); 176 | let draw_params = DrawParameters { 177 | point_size: Some(6.0), 178 | .. Default::default() 179 | }; 180 | let shader_program = program!(&display, 181 | 330 => { 182 | vertex: " 183 | #version 330 core 184 | uniform mat4 proj_view; 185 | in vec3 pos; 186 | void main(void) { 187 | gl_Position = proj_view * vec4(pos, 1.0); 188 | } 189 | ", 190 | fragment: " 191 | #version 330 core 192 | uniform vec3 pcolor; 193 | out vec4 color; 194 | void main(void) { 195 | color = vec4(pcolor, 1); 196 | } 197 | " 198 | }, 199 | ).unwrap(); 200 | 201 | let mut shift_down = false; 202 | let mut selected_curve: i32 = 0; 203 | let mut ui_interaction = false; 204 | let mut color_attenuation = true; 205 | let mut render_3d = true; 206 | 'outer: loop { 207 | let fbscale = imgui.imgui.display_framebuffer_scale(); 208 | for e in display.poll_events() { 209 | match e { 210 | glutin::Event::Closed => break 'outer, 211 | Event::KeyboardInput(state, _, code) => { 212 | let pressed = state == ElementState::Pressed; 213 | match code { 214 | Some(VirtualKeyCode::Escape) if pressed => break 'outer, 215 | Some(VirtualKeyCode::RShift) => shift_down = pressed, 216 | Some(VirtualKeyCode::LShift) => shift_down = pressed, 217 | _ => {} 218 | } 219 | }, 220 | Event::MouseMoved(x, y) if imgui.mouse_pressed.1 && !ui_interaction && !render_3d => { 221 | let delta = ((x - imgui.mouse_pos.0) as f32 / (fbscale.0 * 100.0), 222 | -(y - imgui.mouse_pos.1) as f32 / (fbscale.1 * 100.0)); 223 | camera_2d.translate(delta.0, delta.1); 224 | }, 225 | Event::MouseMoved(x, y) if !ui_interaction && render_3d => { 226 | if imgui.mouse_pressed.0 { 227 | arcball_camera.rotate(Vector2::new(imgui.mouse_pos.0 as f32, imgui.mouse_pos.1 as f32), 228 | Vector2::new(x as f32, y as f32)); 229 | } else if imgui.mouse_pressed.1 { 230 | let mouse_delta = Vector2::new((x - imgui.mouse_pos.0) as f32, 231 | -(y - imgui.mouse_pos.1) as f32); 232 | arcball_camera.pan(mouse_delta, 0.16); 233 | } 234 | }, 235 | Event::MouseInput(state, button) => { 236 | if !render_3d && state == ElementState::Released 237 | && button == MouseButton::Left && selected_curve < curves.len() as i32 238 | { 239 | curves[selected_curve as usize].release_point(); 240 | } 241 | }, 242 | Event::Resized(w, h) => { 243 | width = w; 244 | height = h; 245 | ortho_proj = cgmath::ortho(width as f32 / -200.0, width as f32 / 200.0, 246 | height as f32 / -200.0, height as f32 / 200.0, -1.0, -1000.0); 247 | persp_proj = cgmath::perspective(cgmath::Deg(65.0), width as f32 / height as f32, 248 | 1.0, 1000.0); 249 | arcball_camera.update_screen(width as f32, height as f32); 250 | }, 251 | Event::DroppedFile(ref p) => { 252 | let file = match File::open(p) { 253 | Ok(f) => f, 254 | Err(e) => panic!("Failed to open file: {}", e), 255 | }; 256 | let reader = BufReader::new(file); 257 | let json: serde_json::Value = serde_json::from_reader(reader) 258 | .expect("Failed to read input file"); 259 | let ty = json["type"].as_str().expect("A curve type must be specified"); 260 | if ty == "bspline2d" { 261 | curves.push(DisplayCurve::new(import_bspline(&json), &display)); 262 | } else if ty == "bspline3d" { 263 | curves3d.push(DisplayCurve3D::new(import_bspline(&json), &display)); 264 | } else if ty == "surface" { 265 | surfaces.push(DisplaySurf::new(import_surf(&json), &display)); 266 | } else if ty == "interpolation_u" { 267 | surface_interpolations.push(DisplaySurfInterpolation::new( 268 | import_surf_interpolation(&json), &display)); 269 | } else { 270 | println!("Unrecognized file type header {}", ty); 271 | } 272 | }, 273 | _ => {} 274 | } 275 | imgui.update_event(&e); 276 | } 277 | if !ui_interaction { 278 | if render_3d { 279 | if imgui.mouse_wheel != 0.0 { 280 | arcball_camera.zoom(imgui.mouse_wheel / (fbscale.1 * 10.0), 0.16); 281 | } 282 | } else { 283 | if imgui.mouse_wheel != 0.0 { 284 | camera_2d.zoom(imgui.mouse_wheel / (fbscale.1 * 10.0)); 285 | } 286 | if imgui.mouse_pressed.0 && selected_curve < curves.len() as i32 { 287 | let unproj = (ortho_proj * camera_2d.get_mat4()).invert() 288 | .expect("Uninvertable proj * view!?"); 289 | let click_pos = 290 | cgmath::Point3::::new(2.0 * imgui.mouse_pos.0 as f32 / width as f32 - 1.0, 291 | -2.0 * imgui.mouse_pos.1 as f32 / height as f32 + 1.0, 292 | 0.0); 293 | let pos = unproj.transform_point(click_pos); 294 | let pos = Point::new(pos.x, pos.y, 0.0); 295 | curves[selected_curve as usize].handle_click(pos, shift_down, camera_2d.zoom); 296 | } 297 | } 298 | } 299 | imgui.update_mouse(); 300 | 301 | ui_interaction = imgui_support::is_mouse_hovering_any_window() || imgui_support::is_any_item_active(); 302 | 303 | let mut target = display.draw(); 304 | target.clear_color(0.05, 0.05, 0.05, 1.0); 305 | 306 | let proj_view: [[f32; 4]; 4] = 307 | if !render_3d { 308 | (ortho_proj * camera_2d.get_mat4()).into() 309 | } else { 310 | (persp_proj * arcball_camera.get_mat4()).into() 311 | }; 312 | let attenuation = if color_attenuation { 0.4 } else { 1.0 }; 313 | 314 | for (i, c) in curves.iter().enumerate() { 315 | c.render(&mut target, &shader_program, &draw_params, &proj_view, i as i32 == selected_curve, 316 | attenuation); 317 | } 318 | for (i, c) in curves3d.iter().enumerate() { 319 | let sel_curve = selected_curve - curves.len() as i32; 320 | c.render(&mut target, &shader_program, &draw_params, &proj_view, i as i32 == sel_curve, 321 | attenuation); 322 | } 323 | for (i, s) in surfaces.iter().enumerate() { 324 | let sel_curve = selected_curve - curves.len() as i32 - curves3d.len() as i32; 325 | s.render(&mut target, &shader_program, &draw_params, &proj_view, i as i32 == sel_curve, 326 | attenuation); 327 | } 328 | for (i, s) in surface_interpolations.iter().enumerate() { 329 | let sel_curve = selected_curve - curves.len() as i32 - curves3d.len() as i32 - surfaces.len() as i32; 330 | s.render(&mut target, &shader_program, &draw_params, &proj_view, i as i32 == sel_curve, 331 | attenuation); 332 | } 333 | 334 | let ui = imgui.render_ui(&display); 335 | ui.window(im_str!("Curve Control Panel")) 336 | .size((300.0, 100.0), imgui::ImGuiSetCond_FirstUseEver) 337 | .build(|| { 338 | let fps = ui.framerate(); 339 | let frame_time = 1000.0 / fps; 340 | let gl_version = display.get_opengl_version(); 341 | let glsl_version = display.get_supported_glsl_version(); 342 | ui.text(im_str!("Framerate: {:.3} FPS ({:.3} ms)", fps, frame_time)); 343 | ui.text(im_str!("OpenGL Version: {}.{}", gl_version.1, gl_version.2)); 344 | ui.text(im_str!("GLSL Version: {}.{}", glsl_version.1, glsl_version.2)); 345 | ui.checkbox(im_str!("Fade Unselected Curves"), &mut color_attenuation); 346 | ui.checkbox(im_str!("Render 3D"), &mut render_3d); 347 | 348 | let mut removing = None; 349 | for (i, c) in curves.iter_mut().enumerate() { 350 | ui.separator(); 351 | imgui_support::push_id_int(i as i32); 352 | imgui_support::radio_button(im_str!("Select Curve"), &mut selected_curve, i as i32); 353 | c.draw_ui(&ui); 354 | if ui.small_button(im_str!("Remove Curve")) { 355 | removing = Some(i); 356 | } 357 | imgui_support::pop_id(); 358 | } 359 | for (i, c) in curves3d.iter_mut().enumerate() { 360 | let id = i + curves.len(); 361 | ui.separator(); 362 | imgui_support::push_id_int(id as i32); 363 | imgui_support::radio_button(im_str!("Select Curve"), &mut selected_curve, id as i32); 364 | c.draw_ui(&ui); 365 | if ui.small_button(im_str!("Remove Curve")) { 366 | removing = Some(id); 367 | } 368 | imgui_support::pop_id(); 369 | } 370 | for (i, c) in surfaces.iter_mut().enumerate() { 371 | let id = i + curves.len() + curves3d.len(); 372 | ui.separator(); 373 | imgui_support::push_id_int(id as i32); 374 | imgui_support::radio_button(im_str!("Select Surface"), &mut selected_curve, id as i32); 375 | c.draw_ui(&ui); 376 | if ui.small_button(im_str!("Remove Surface")) { 377 | removing = Some(id); 378 | } 379 | imgui_support::pop_id(); 380 | } 381 | for (i, c) in surface_interpolations.iter_mut().enumerate() { 382 | let id = i + curves.len() + curves3d.len() + surfaces.len(); 383 | ui.separator(); 384 | imgui_support::push_id_int(id as i32); 385 | imgui_support::radio_button(im_str!("Select Surface"), &mut selected_curve, id as i32); 386 | c.draw_ui(&ui); 387 | if ui.small_button(im_str!("Remove Surface")) { 388 | removing = Some(id); 389 | } 390 | imgui_support::pop_id(); 391 | } 392 | 393 | if let Some(i) = removing { 394 | if selected_curve as usize >= i && selected_curve != 0 { 395 | selected_curve -= 1; 396 | } 397 | if i >= curves.len() + curves3d.len() + surfaces.len() { 398 | surface_interpolations.remove(i - curves.len() - curves3d.len() - surfaces.len()); 399 | } else if i >= curves.len() + curves3d.len() { 400 | surfaces.remove(i - curves.len() - curves3d.len()); 401 | } else if i >= curves.len() { 402 | curves3d.remove(i - curves.len()); 403 | } else { 404 | curves.remove(i); 405 | } 406 | } 407 | if ui.small_button(im_str!("Add Curve")) { 408 | curves.push(DisplayCurve::new(BSpline::empty(), &display)); 409 | selected_curve = (curves.len() - 1) as i32; 410 | } 411 | }); 412 | imgui_renderer.render(&mut target, ui).unwrap(); 413 | 414 | target.finish().unwrap(); 415 | } 416 | } 417 | 418 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "spline-viewer" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "arcball 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "cgmath 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "glium 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "imgui 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "imgui-glium-renderer 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 11 | "imgui-sys 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 13 | "rulinalg 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 14 | "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "serde_json 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 16 | ] 17 | 18 | [[package]] 19 | name = "aho-corasick" 20 | version = "0.6.3" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | dependencies = [ 23 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "android_glue" 28 | version = "0.2.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | 31 | [[package]] 32 | name = "approx" 33 | version = "0.1.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | 36 | [[package]] 37 | name = "arcball" 38 | version = "0.2.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | dependencies = [ 41 | "cgmath 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", 42 | ] 43 | 44 | [[package]] 45 | name = "backtrace" 46 | version = "0.2.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | dependencies = [ 49 | "backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 56 | ] 57 | 58 | [[package]] 59 | name = "backtrace-sys" 60 | version = "0.1.10" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | dependencies = [ 63 | "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "bitflags" 69 | version = "0.3.3" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | 72 | [[package]] 73 | name = "bitflags" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | 77 | [[package]] 78 | name = "bitflags" 79 | version = "0.8.2" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | 82 | [[package]] 83 | name = "block" 84 | version = "0.1.6" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | 87 | [[package]] 88 | name = "byteorder" 89 | version = "1.0.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "0.1.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | 97 | [[package]] 98 | name = "cgl" 99 | version = "0.1.5" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | dependencies = [ 102 | "gleam 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 104 | ] 105 | 106 | [[package]] 107 | name = "cgmath" 108 | version = "0.14.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | dependencies = [ 111 | "approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 114 | ] 115 | 116 | [[package]] 117 | name = "cocoa" 118 | version = "0.3.3" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | dependencies = [ 121 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 122 | "core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "cocoa" 129 | version = "0.5.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | dependencies = [ 132 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 137 | ] 138 | 139 | [[package]] 140 | name = "core-foundation" 141 | version = "0.2.3" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | dependencies = [ 144 | "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 146 | ] 147 | 148 | [[package]] 149 | name = "core-foundation-sys" 150 | version = "0.2.3" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | dependencies = [ 153 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 154 | ] 155 | 156 | [[package]] 157 | name = "core-graphics" 158 | version = "0.3.2" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)", 164 | ] 165 | 166 | [[package]] 167 | name = "core-graphics" 168 | version = "0.4.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | dependencies = [ 171 | "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", 174 | ] 175 | 176 | [[package]] 177 | name = "dbghelp-sys" 178 | version = "0.2.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | dependencies = [ 181 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 182 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 183 | ] 184 | 185 | [[package]] 186 | name = "dlib" 187 | version = "0.3.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | dependencies = [ 190 | "libloading 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 191 | ] 192 | 193 | [[package]] 194 | name = "docopt" 195 | version = "0.7.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | dependencies = [ 198 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 202 | ] 203 | 204 | [[package]] 205 | name = "dtoa" 206 | version = "0.4.1" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | 209 | [[package]] 210 | name = "dwmapi-sys" 211 | version = "0.1.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | dependencies = [ 214 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 216 | ] 217 | 218 | [[package]] 219 | name = "fnv" 220 | version = "1.0.5" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | 223 | [[package]] 224 | name = "fs2" 225 | version = "0.2.5" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | dependencies = [ 228 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 229 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 230 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 231 | ] 232 | 233 | [[package]] 234 | name = "gcc" 235 | version = "0.3.45" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | 238 | [[package]] 239 | name = "gdi32-sys" 240 | version = "0.1.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | dependencies = [ 243 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 244 | ] 245 | 246 | [[package]] 247 | name = "gl_generator" 248 | version = "0.5.2" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | dependencies = [ 251 | "khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 252 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 253 | "xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 254 | ] 255 | 256 | [[package]] 257 | name = "gleam" 258 | version = "0.2.32" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | dependencies = [ 261 | "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 262 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 263 | ] 264 | 265 | [[package]] 266 | name = "glium" 267 | version = "0.16.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | dependencies = [ 270 | "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "glutin 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 276 | ] 277 | 278 | [[package]] 279 | name = "glutin" 280 | version = "0.7.4" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | dependencies = [ 283 | "android_glue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 284 | "cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 285 | "cocoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 286 | "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 287 | "core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 288 | "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 289 | "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 290 | "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 291 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 292 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 299 | "wayland-client 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 300 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "winit 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "x11-dl 2.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 303 | ] 304 | 305 | [[package]] 306 | name = "imgui" 307 | version = "0.0.13" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | dependencies = [ 310 | "imgui-sys 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 311 | ] 312 | 313 | [[package]] 314 | name = "imgui-glium-renderer" 315 | version = "0.0.13" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | dependencies = [ 318 | "glium 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", 319 | "imgui 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 320 | "imgui-sys 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 321 | ] 322 | 323 | [[package]] 324 | name = "imgui-sys" 325 | version = "0.0.13" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | dependencies = [ 328 | "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 329 | "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", 330 | "glium 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", 331 | ] 332 | 333 | [[package]] 334 | name = "itoa" 335 | version = "0.3.1" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | 338 | [[package]] 339 | name = "kernel32-sys" 340 | version = "0.2.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | dependencies = [ 343 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 344 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 345 | ] 346 | 347 | [[package]] 348 | name = "khronos_api" 349 | version = "1.0.0" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | 352 | [[package]] 353 | name = "lazy_static" 354 | version = "0.2.8" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | 357 | [[package]] 358 | name = "libc" 359 | version = "0.2.22" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | 362 | [[package]] 363 | name = "libloading" 364 | version = "0.3.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | dependencies = [ 367 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "target_build_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 371 | ] 372 | 373 | [[package]] 374 | name = "log" 375 | version = "0.3.7" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | 378 | [[package]] 379 | name = "malloc_buf" 380 | version = "0.0.6" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | dependencies = [ 383 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 384 | ] 385 | 386 | [[package]] 387 | name = "matrixmultiply" 388 | version = "0.1.13" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | dependencies = [ 391 | "rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 392 | ] 393 | 394 | [[package]] 395 | name = "memchr" 396 | version = "1.0.1" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | dependencies = [ 399 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 400 | ] 401 | 402 | [[package]] 403 | name = "memmap" 404 | version = "0.4.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | dependencies = [ 407 | "fs2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 408 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 409 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 410 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 411 | ] 412 | 413 | [[package]] 414 | name = "num" 415 | version = "0.1.37" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | dependencies = [ 418 | "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 419 | "num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", 420 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 421 | ] 422 | 423 | [[package]] 424 | name = "num-integer" 425 | version = "0.1.34" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | dependencies = [ 428 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 429 | ] 430 | 431 | [[package]] 432 | name = "num-iter" 433 | version = "0.1.33" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | dependencies = [ 436 | "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 437 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 438 | ] 439 | 440 | [[package]] 441 | name = "num-traits" 442 | version = "0.1.37" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | 445 | [[package]] 446 | name = "objc" 447 | version = "0.2.2" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | dependencies = [ 450 | "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 451 | ] 452 | 453 | [[package]] 454 | name = "osmesa-sys" 455 | version = "0.1.2" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | dependencies = [ 458 | "shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 459 | ] 460 | 461 | [[package]] 462 | name = "phf" 463 | version = "0.7.21" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | dependencies = [ 466 | "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 467 | ] 468 | 469 | [[package]] 470 | name = "phf_codegen" 471 | version = "0.7.21" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | dependencies = [ 474 | "phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 475 | "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 476 | ] 477 | 478 | [[package]] 479 | name = "phf_generator" 480 | version = "0.7.21" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | dependencies = [ 483 | "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 484 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 485 | ] 486 | 487 | [[package]] 488 | name = "phf_shared" 489 | version = "0.7.21" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | dependencies = [ 492 | "siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 493 | ] 494 | 495 | [[package]] 496 | name = "pkg-config" 497 | version = "0.3.9" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | 500 | [[package]] 501 | name = "rand" 502 | version = "0.3.15" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 506 | ] 507 | 508 | [[package]] 509 | name = "rawpointer" 510 | version = "0.1.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | 513 | [[package]] 514 | name = "regex" 515 | version = "0.2.1" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | dependencies = [ 518 | "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 519 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 520 | "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 521 | "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 522 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 523 | ] 524 | 525 | [[package]] 526 | name = "regex-syntax" 527 | version = "0.4.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | 530 | [[package]] 531 | name = "rulinalg" 532 | version = "0.4.2" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | dependencies = [ 535 | "matrixmultiply 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 536 | "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 537 | ] 538 | 539 | [[package]] 540 | name = "rustc-demangle" 541 | version = "0.1.4" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | 544 | [[package]] 545 | name = "rustc-serialize" 546 | version = "0.3.24" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | 549 | [[package]] 550 | name = "rustc_version" 551 | version = "0.1.7" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | dependencies = [ 554 | "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", 555 | ] 556 | 557 | [[package]] 558 | name = "semver" 559 | version = "0.1.20" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | 562 | [[package]] 563 | name = "serde" 564 | version = "0.7.15" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | 567 | [[package]] 568 | name = "serde" 569 | version = "0.8.23" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | 572 | [[package]] 573 | name = "serde" 574 | version = "0.9.15" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | 577 | [[package]] 578 | name = "serde" 579 | version = "1.0.2" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | 582 | [[package]] 583 | name = "serde_json" 584 | version = "0.9.10" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | dependencies = [ 587 | "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 588 | "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 589 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 590 | "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", 591 | ] 592 | 593 | [[package]] 594 | name = "serde_json" 595 | version = "1.0.1" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | dependencies = [ 598 | "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 599 | "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 600 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 601 | "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 602 | ] 603 | 604 | [[package]] 605 | name = "shared_library" 606 | version = "0.1.5" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | dependencies = [ 609 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 610 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 611 | ] 612 | 613 | [[package]] 614 | name = "shell32-sys" 615 | version = "0.1.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | dependencies = [ 618 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 619 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 620 | ] 621 | 622 | [[package]] 623 | name = "siphasher" 624 | version = "0.2.2" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | 627 | [[package]] 628 | name = "smallvec" 629 | version = "0.1.8" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | 632 | [[package]] 633 | name = "strsim" 634 | version = "0.6.0" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | 637 | [[package]] 638 | name = "target_build_utils" 639 | version = "0.3.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | dependencies = [ 642 | "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 643 | "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 644 | "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 645 | ] 646 | 647 | [[package]] 648 | name = "tempfile" 649 | version = "2.1.5" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | dependencies = [ 652 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 653 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 654 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 655 | "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 656 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 657 | ] 658 | 659 | [[package]] 660 | name = "thread-id" 661 | version = "3.0.0" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | dependencies = [ 664 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 665 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 666 | ] 667 | 668 | [[package]] 669 | name = "thread_local" 670 | version = "0.3.3" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | dependencies = [ 673 | "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 674 | "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 675 | ] 676 | 677 | [[package]] 678 | name = "unreachable" 679 | version = "0.1.1" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | dependencies = [ 682 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 683 | ] 684 | 685 | [[package]] 686 | name = "user32-sys" 687 | version = "0.1.2" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | dependencies = [ 690 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 691 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 692 | ] 693 | 694 | [[package]] 695 | name = "utf8-ranges" 696 | version = "1.0.0" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | 699 | [[package]] 700 | name = "void" 701 | version = "1.0.2" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | 704 | [[package]] 705 | name = "wayland-client" 706 | version = "0.7.8" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | dependencies = [ 709 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 710 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 711 | "wayland-scanner 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 712 | "wayland-sys 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 713 | ] 714 | 715 | [[package]] 716 | name = "wayland-kbd" 717 | version = "0.6.3" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | dependencies = [ 720 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 721 | "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 722 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 723 | "memmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 724 | "wayland-client 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 725 | ] 726 | 727 | [[package]] 728 | name = "wayland-scanner" 729 | version = "0.7.8" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | dependencies = [ 732 | "xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 733 | ] 734 | 735 | [[package]] 736 | name = "wayland-sys" 737 | version = "0.7.8" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | dependencies = [ 740 | "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 741 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 742 | ] 743 | 744 | [[package]] 745 | name = "wayland-window" 746 | version = "0.4.4" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | dependencies = [ 749 | "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 750 | "tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 751 | "wayland-client 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 752 | ] 753 | 754 | [[package]] 755 | name = "winapi" 756 | version = "0.2.8" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | 759 | [[package]] 760 | name = "winapi-build" 761 | version = "0.1.1" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | 764 | [[package]] 765 | name = "winit" 766 | version = "0.5.11" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | dependencies = [ 769 | "android_glue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 770 | "cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 771 | "cocoa 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 772 | "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 773 | "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 774 | "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 775 | "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 776 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 777 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 778 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 779 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 780 | "shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 781 | "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 782 | "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 783 | "wayland-client 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 784 | "wayland-kbd 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 785 | "wayland-window 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 786 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 787 | "x11-dl 2.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 788 | ] 789 | 790 | [[package]] 791 | name = "x11-dl" 792 | version = "2.13.0" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | dependencies = [ 795 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 796 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 797 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 798 | ] 799 | 800 | [[package]] 801 | name = "xml-rs" 802 | version = "0.3.6" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | dependencies = [ 805 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 806 | ] 807 | 808 | [metadata] 809 | "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" 810 | "checksum android_glue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d8289e9637439939cc92b1995b0972117905be88bc28116c86b64d6e589bcd38" 811 | "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" 812 | "checksum arcball 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "84d5cc6479f8cf2a8150b5df4eee1605e7a2219b92a7c434ffaf839a4d5110fd" 813 | "checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f" 814 | "checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842" 815 | "checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c" 816 | "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" 817 | "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" 818 | "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 819 | "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" 820 | "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" 821 | "checksum cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8bdd78cca65a739cb5475dbf6b6bbb49373e327f4a6f2b499c0f98632df38c10" 822 | "checksum cgmath 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e3f26779a7a5d4a011379f3569f80aec56db43924b55036988d2a7a7d6eac53" 823 | "checksum cocoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3afe4613f57a171039a98db1773f5840b5743cf85aaf03afb65ddfade4f4a9db" 824 | "checksum cocoa 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e1be5fd98bb7e8ef0eea233a4984f4e85ecdcfa002a90b8b12b7a20faf44dc1" 825 | "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" 826 | "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" 827 | "checksum core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0c56c6022ba22aedbaa7d231be545778becbe1c7aceda4c82ba2f2084dd4c723" 828 | "checksum core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "66e998abb8823fecd2a8a7205429b17a340d447d8c69b3bce86846dcdea3e33b" 829 | "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" 830 | "checksum dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "148bce4ce1c36c4509f29cb54e62c2bd265551a9b00b38070fad551a851866ec" 831 | "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" 832 | "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" 833 | "checksum dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07c4c7cc7b396419bc0a4d90371d0cee16cb5053b53647d287c0b728000c41fe" 834 | "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" 835 | "checksum fs2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bcd414e5a1a979b931bb92f41b7a54106d3f6d2e6c253e9ce943b7cd468251ef" 836 | "checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae" 837 | "checksum gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "65256ec4dc2592e6f05bfc1ca3b956a4e0698aa90b1dff1f5687d55a5a3fd59a" 838 | "checksum gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1d8edc81c5ae84605a62f5dac661a2313003b26d59839f81d47d46cf0f16a55" 839 | "checksum gleam 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9590e0e578d528a080c5abac678e7efbe349a73c7316faafd4073edf5f462d01" 840 | "checksum glium 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c468bf7855f25954a1140f066ebacc1ad5342fd33bf96be28e184c084176f11" 841 | "checksum glutin 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1f95cc9a8363627259b4a25db878eb5b1a159857bc41f525412302fa9de0f12b" 842 | "checksum imgui 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e925c75d7d04c72cf6f5737484c23a5cd419340f99a582277beab4deb52bf391" 843 | "checksum imgui-glium-renderer 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "131bf7761774195ed3cd9b9dd7afb8a337093c2d4c02e49ed472184f62174fe6" 844 | "checksum imgui-sys 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "efdf6e0b8b48b29ab838cd7c813ac44f1e602f7148c861d59d6baf6e5fdba11a" 845 | "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" 846 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 847 | "checksum khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09c9d3760673c427d46f91a0350f0a84a52e6bc5a84adf26dc610b6c52436630" 848 | "checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" 849 | "checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" 850 | "checksum libloading 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0a020ac941774eb37e9d13d418c37b522e76899bfc4e7b1a600d529a53f83a66" 851 | "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" 852 | "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 853 | "checksum matrixmultiply 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7ce012d2c43046267a74283eaa7e9a51941479901e2b86702be10f27e2779158" 854 | "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" 855 | "checksum memmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69253224aa10070855ea8fe9dbe94a03fc2b1d7930bb340c9e586a7513716fea" 856 | "checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40" 857 | "checksum num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1a4bf6f9174aa5783a9b4cc892cacd11aebad6c69ad027a0b65c6ca5f8aa37" 858 | "checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e" 859 | "checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" 860 | "checksum objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "877f30f37acef6749b1841cceab289707f211aecfc756553cd63976190e6cc2e" 861 | "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" 862 | "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" 863 | "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" 864 | "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" 865 | "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" 866 | "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" 867 | "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" 868 | "checksum rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019" 869 | "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" 870 | "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" 871 | "checksum rulinalg 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "04ada202c9685e1d72a7420c578e92b358dbf807d3dfabb676a3dab9cc3bb12f" 872 | "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" 873 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 874 | "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" 875 | "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" 876 | "checksum serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0e0732aa8ec4267f61815a396a942ba3525062e3bd5520aa8419927cfc0a92" 877 | "checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" 878 | "checksum serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" 879 | "checksum serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3b46a59dd63931010fdb1d88538513f3279090d88b5c22ef4fe8440cfffcc6e3" 880 | "checksum serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ad8bcf487be7d2e15d3d543f04312de991d631cfe1b43ea0ade69e6a8a5b16a1" 881 | "checksum serde_json 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1c62115693d0a9ed8c32d1c760f0fdbe7d4b05cb13c135b9b54137ac0d59fccb" 882 | "checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a" 883 | "checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" 884 | "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" 885 | "checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410" 886 | "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" 887 | "checksum target_build_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f42dc058080c19c6a58bdd1bf962904ee4f5ef1fe2a81b529f31dacc750c679f" 888 | "checksum tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3213fd2b7ed87e39306737ccfac04b1233b57a33ca64cfbf52f2ffaa2b765e2f" 889 | "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" 890 | "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" 891 | "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" 892 | "checksum user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6717129de5ac253f5642fc78a51d0c7de6f9f53d617fc94e9bae7f6e71cf5504" 893 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 894 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 895 | "checksum wayland-client 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b2b9876c6c97ece4f1ac699b5172550df443f36942fdcdcc27768c8f1437b4" 896 | "checksum wayland-kbd 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2b4b69d43d6cce82d95a2c5e81605abd1fa4783bf49d09cd85aa092f16081ef1" 897 | "checksum wayland-scanner 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "21fd38866b7539ec70300596a905ca838e9f8212aa114fa1cebc13801fbeecff" 898 | "checksum wayland-sys 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "604257d049da3dc9c49a0bac58f0f09265d838959721da2c41f19db5ca8cc59f" 899 | "checksum wayland-window 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7595fbe537dee3a380f32104ddfcf2f43db8cb8843031531e1426eb524d1c608" 900 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 901 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 902 | "checksum winit 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f68c756743f68e5420a93f72c43c9cd8d3b89163692e09a5b53c12caf82386ba" 903 | "checksum x11-dl 2.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f8f229cb66ab27440d0b8c8e37f8c62e60e169145e084b49795a1a22b62f1e" 904 | "checksum xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7ec6c39eaa68382c8e31e35239402c0a9489d4141a8ceb0c716099a0b515b562" 905 | --------------------------------------------------------------------------------