Generates subdivided and truncated icosahedron spheres in Rust.

bin.rs 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #[macro_use]
  2. extern crate clap;
  3. extern crate byteorder;
  4. extern crate icosahedron;
  5. use std::fs::{metadata, File};
  6. use std::io::{BufWriter, Write};
  7. use std::path::Path;
  8. use byteorder::{LittleEndian, WriteBytesExt};
  9. use icosahedron::Polyhedron;
  10. fn write_to_binary_file(polyhedron: Polyhedron, path: &Path) {
  11. let bin_file = File::create(path).expect("Can't create file");
  12. let mut writer = BufWriter::new(bin_file);
  13. let write_error_message = "Error encountered while writing to binary file";
  14. writer
  15. .write_u32::<LittleEndian>(polyhedron.positions.len() as u32)
  16. .expect(write_error_message);
  17. writer
  18. .write_u32::<LittleEndian>(polyhedron.cells.len() as u32)
  19. .expect(write_error_message);
  20. for position in polyhedron.positions.iter() {
  21. writer
  22. .write_f32::<LittleEndian>(position.0.x)
  23. .expect(write_error_message);
  24. writer
  25. .write_f32::<LittleEndian>(position.0.y)
  26. .expect(write_error_message);
  27. writer
  28. .write_f32::<LittleEndian>(position.0.z)
  29. .expect(write_error_message);
  30. }
  31. for normal in polyhedron.normals.iter() {
  32. writer
  33. .write_f32::<LittleEndian>(normal.0.x)
  34. .expect(write_error_message);
  35. writer
  36. .write_f32::<LittleEndian>(normal.0.y)
  37. .expect(write_error_message);
  38. writer
  39. .write_f32::<LittleEndian>(normal.0.z)
  40. .expect(write_error_message);
  41. }
  42. for color in polyhedron.colors.iter() {
  43. writer
  44. .write_f32::<LittleEndian>(color.0.x)
  45. .expect(write_error_message);
  46. writer
  47. .write_f32::<LittleEndian>(color.0.y)
  48. .expect(write_error_message);
  49. writer
  50. .write_f32::<LittleEndian>(color.0.z)
  51. .expect(write_error_message);
  52. }
  53. for cell in polyhedron.cells.iter() {
  54. writer
  55. .write_u32::<LittleEndian>(cell.a as u32)
  56. .expect(write_error_message);
  57. writer
  58. .write_u32::<LittleEndian>(cell.b as u32)
  59. .expect(write_error_message);
  60. writer
  61. .write_u32::<LittleEndian>(cell.c as u32)
  62. .expect(write_error_message);
  63. }
  64. }
  65. fn write_to_json_file(polyhedron: Polyhedron, path: &Path) {
  66. let mut json_file = File::create(path).expect("Can't create file");
  67. let json = serde_json::to_string(&polyhedron).expect("Problem serializing");
  68. json_file
  69. .write_all(json.as_bytes())
  70. .expect("Can't write to file");
  71. }
  72. fn generate_files(
  73. dir: &str,
  74. format: Format,
  75. truncated: bool,
  76. colored: bool,
  77. param_list: Vec<(f32, u32)>,
  78. ) {
  79. let mesh_type = if truncated {
  80. "hexsphere"
  81. } else {
  82. "icosahedron"
  83. };
  84. for param in param_list {
  85. println!(
  86. "Generating {} with radius {} and detail {}...",
  87. mesh_type, param.0, param.1
  88. );
  89. let polyhedron = if truncated {
  90. let mut hexsphere = Polyhedron::new_truncated_isocahedron(param.0, param.1);
  91. hexsphere.compute_triangle_normals();
  92. hexsphere
  93. } else {
  94. let mut icosahedron = Polyhedron::new_isocahedron(param.0, param.1);
  95. icosahedron.compute_triangle_normals();
  96. icosahedron
  97. };
  98. let colored_polyhedron = if colored {
  99. let mut colored = Polyhedron::new();
  100. colored.unique_vertices(polyhedron);
  101. colored.assign_random_face_colors();
  102. colored
  103. } else {
  104. polyhedron
  105. };
  106. println!("triangles: {}", colored_polyhedron.cells.len());
  107. println!("vertices: {}", colored_polyhedron.positions.len());
  108. let filename = Path::new(dir).join(format!(
  109. "{}_r{}_d{}.{}",
  110. mesh_type,
  111. param.0,
  112. param.1,
  113. format.extension()
  114. ));
  115. match format {
  116. Format::Bin => write_to_binary_file(colored_polyhedron, &filename),
  117. Format::Json => write_to_json_file(colored_polyhedron, &filename),
  118. };
  119. }
  120. }
  121. arg_enum! {
  122. #[derive(Debug)]
  123. enum Format {
  124. Json,
  125. Bin,
  126. }
  127. }
  128. impl Format {
  129. fn extension(&self) -> String {
  130. match self {
  131. Format::Bin => "bin".to_string(),
  132. Format::Json => "json".to_string(),
  133. }
  134. }
  135. }
  136. fn main() {
  137. let dir_exists = |path: String| {
  138. let path_clone = path.clone();
  139. match metadata(path) {
  140. Ok(metadata) => {
  141. if metadata.is_dir() {
  142. Ok(())
  143. } else {
  144. Err(String::from(format!(
  145. "Output '{}' is not a directory",
  146. &path_clone
  147. )))
  148. }
  149. }
  150. Err(_) => Err(String::from(format!(
  151. "Directory '{}' doesn't exist",
  152. &path_clone
  153. ))),
  154. }
  155. };
  156. let matches = clap_app!(icosahedron =>
  157. (version: "0.1.1")
  158. (author: "Tyler Hallada <tyler@hallada.net>")
  159. (about: "Generates 3D icosahedra meshes")
  160. (@arg truncated: -t --truncated "Generate truncated icosahedra (hexspheres).")
  161. (@arg colored: -c --colored "Assigns a random color to every face \
  162. (increases vertices count).")
  163. (@arg detail: -d --detail +takes_value default_value("7")
  164. "Maximum detail level to generate. \
  165. Each level multiplies the number of triangles by 4.")
  166. (@arg radius: -r --radius +takes_value default_value("1.0")
  167. "Radius of the polyhedron,")
  168. (@arg format: -f --format +takes_value possible_values(&Format::variants())
  169. default_value("Bin")
  170. "Format to write the files in.")
  171. (@arg output: [OUTPUT] {dir_exists} default_value("output/")
  172. "Directory to write the output files to.")
  173. )
  174. .get_matches();
  175. let truncated = matches.is_present("truncated");
  176. let colored = matches.is_present("colored");
  177. let detail = value_t!(matches.value_of("detail"), u32).unwrap_or(7);
  178. let radius = value_t!(matches.value_of("radius"), f32).unwrap_or(1.0);
  179. let format = value_t!(matches.value_of("format"), Format).unwrap_or(Format::Bin);
  180. let output = matches.value_of("output").unwrap_or("output/");
  181. let param_list = |detail: u32, radius: f32| -> Vec<(f32, u32)> {
  182. let mut params = vec![];
  183. for detail in 0..(detail + 1) {
  184. params.push((radius, detail));
  185. }
  186. params
  187. };
  188. generate_files(
  189. output,
  190. format,
  191. truncated,
  192. colored,
  193. param_list(detail, radius),
  194. );
  195. }