Last active
May 11, 2019 18:42
-
-
Save swiftcoder/f2d7086d7ad565b59f4c130cddf555ca to your computer and use it in GitHub Desktop.
Recursive Icosphere subdivision in rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate cgmath; | |
use std::collections::BTreeMap; | |
use cgmath::Vector3; | |
struct Face { | |
pub indices: [usize; 3], | |
} | |
impl Face { | |
pub fn new(indices: [usize; 3]) -> Face { | |
Face { indices } | |
} | |
} | |
struct Primitive { | |
vertices: Vec<Vector3<f32>>, | |
faces: Vec<Face>, | |
} | |
impl Primitive { | |
pub fn new_icosahedron() -> Primitive { | |
let phi = (1.0 + 5.0_f32.sqrt()) / 2.0; | |
let du = 1.0 / (phi * phi + 1.0).sqrt(); | |
let dv = phi * du; | |
let vertices = vec![ | |
Vector3::new(0.0, dv, du), | |
Vector3::new(0.0, dv, -du), | |
Vector3::new(0.0, -dv, du), | |
Vector3::new(0.0, -dv, -du), | |
Vector3::new(du, 0.0, dv), | |
Vector3::new(-du, 0.0, dv), | |
Vector3::new(du, 0.0, -dv), | |
Vector3::new(-du, 0.0, -dv), | |
Vector3::new(dv, du, 0.0), | |
Vector3::new(dv, -du, 0.0), | |
Vector3::new(-dv, du, 0.0), | |
Vector3::new(-dv, -du, 0.0), | |
]; | |
let faces = vec![ | |
Face::new([0, 1, 8]), | |
Face::new([0, 4, 5]), | |
Face::new([0, 5, 10]), | |
Face::new([0, 8, 4]), | |
Face::new([0, 10, 1]), | |
Face::new([1, 6, 8]), | |
Face::new([1, 7, 6]), | |
Face::new([1, 10, 7]), | |
Face::new([2, 3, 11]), | |
Face::new([2, 4, 9]), | |
Face::new([2, 5, 4]), | |
Face::new([2, 9, 3]), | |
Face::new([2, 11, 5]), | |
Face::new([3, 6, 7]), | |
Face::new([3, 7, 11]), | |
Face::new([3, 9, 6]), | |
Face::new([4, 8, 9]), | |
Face::new([5, 11, 10]), | |
Face::new([6, 9, 8]), | |
Face::new([7, 10, 11]), | |
]; | |
Primitive { | |
vertices, | |
faces, | |
} | |
} | |
pub fn subdivide(&mut self) { | |
let mut edge_to_vert_mapping = BTreeMap::new(); | |
let mut old_faces = vec![]; | |
std::mem::swap(&mut self.faces, &mut old_faces); | |
for face in old_faces.iter() { | |
let n = face.indices.clone(); | |
let a = self.ensure_edge_is_split(&mut edge_to_vert_mapping, n[0], n[1]); | |
let b = self.ensure_edge_is_split(&mut edge_to_vert_mapping, n[1], n[2]); | |
let c = self.ensure_edge_is_split(&mut edge_to_vert_mapping, n[2], n[0]); | |
self.faces.push(Face::new([n[0], a, c])); | |
self.faces.push(Face::new([a, n[1], b])); | |
self.faces.push(Face::new([c, b, n[2]])); | |
self.faces.push(Face::new([c, a, b])); | |
} | |
} | |
// splits the edge into two (creating a new vertex at the center), or returns the existing vertex if this edge was already split | |
fn ensure_edge_is_split( | |
&mut self, | |
edge_to_vert_mapping: &mut BTreeMap<(usize, usize), usize>, | |
a: usize, | |
b: usize, | |
) -> usize { | |
let key = ascending_order(a, b); | |
if !edge_to_vert_mapping.contains_key(&key) { | |
let n = self.vertices.len(); | |
let p = self.vertices[a].lerp(self.vertices[b], 0.5).normalize(); | |
self.vertices.push(p); | |
edge_to_vert_mapping.insert(key, n); | |
} | |
return edge_to_vert_mapping[&key]; | |
} | |
} | |
// given a pair of numbers, returns them in ascending order | |
fn ascending_order(a: usize, b: usize) -> (usize, usize) { | |
if a < b { | |
return (a, b); | |
} | |
return (b, a); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment