Browse Source

Initial commit: code and output

Tyler Hallada 5 years ago
commit
ebc9fdfdb2

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
1
+/target
2
+**/*.rs.bk

+ 309 - 0
Cargo.lock

@@ -0,0 +1,309 @@
1
+[[package]]
2
+name = "approx"
3
+version = "0.3.1"
4
+source = "registry+https://github.com/rust-lang/crates.io-index"
5
+dependencies = [
6
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
7
+]
8
+
9
+[[package]]
10
+name = "autocfg"
11
+version = "0.1.2"
12
+source = "registry+https://github.com/rust-lang/crates.io-index"
13
+
14
+[[package]]
15
+name = "bitflags"
16
+version = "1.0.4"
17
+source = "registry+https://github.com/rust-lang/crates.io-index"
18
+
19
+[[package]]
20
+name = "cgmath"
21
+version = "0.17.0"
22
+source = "registry+https://github.com/rust-lang/crates.io-index"
23
+dependencies = [
24
+ "approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
25
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
26
+ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
27
+ "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
28
+]
29
+
30
+[[package]]
31
+name = "cloudabi"
32
+version = "0.0.3"
33
+source = "registry+https://github.com/rust-lang/crates.io-index"
34
+dependencies = [
35
+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
36
+]
37
+
38
+[[package]]
39
+name = "fuchsia-cprng"
40
+version = "0.1.1"
41
+source = "registry+https://github.com/rust-lang/crates.io-index"
42
+
43
+[[package]]
44
+name = "icosahedron"
45
+version = "0.1.0"
46
+dependencies = [
47
+ "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
48
+ "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
49
+ "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
50
+]
51
+
52
+[[package]]
53
+name = "itoa"
54
+version = "0.4.3"
55
+source = "registry+https://github.com/rust-lang/crates.io-index"
56
+
57
+[[package]]
58
+name = "libc"
59
+version = "0.2.48"
60
+source = "registry+https://github.com/rust-lang/crates.io-index"
61
+
62
+[[package]]
63
+name = "num-traits"
64
+version = "0.2.6"
65
+source = "registry+https://github.com/rust-lang/crates.io-index"
66
+
67
+[[package]]
68
+name = "proc-macro2"
69
+version = "0.4.27"
70
+source = "registry+https://github.com/rust-lang/crates.io-index"
71
+dependencies = [
72
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
73
+]
74
+
75
+[[package]]
76
+name = "quote"
77
+version = "0.6.11"
78
+source = "registry+https://github.com/rust-lang/crates.io-index"
79
+dependencies = [
80
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
81
+]
82
+
83
+[[package]]
84
+name = "rand"
85
+version = "0.6.5"
86
+source = "registry+https://github.com/rust-lang/crates.io-index"
87
+dependencies = [
88
+ "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
89
+ "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
90
+ "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
91
+ "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
92
+ "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
93
+ "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
94
+ "rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
95
+ "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
96
+ "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
97
+ "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
98
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
99
+]
100
+
101
+[[package]]
102
+name = "rand_chacha"
103
+version = "0.1.1"
104
+source = "registry+https://github.com/rust-lang/crates.io-index"
105
+dependencies = [
106
+ "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
107
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
108
+]
109
+
110
+[[package]]
111
+name = "rand_core"
112
+version = "0.3.1"
113
+source = "registry+https://github.com/rust-lang/crates.io-index"
114
+dependencies = [
115
+ "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
116
+]
117
+
118
+[[package]]
119
+name = "rand_core"
120
+version = "0.4.0"
121
+source = "registry+https://github.com/rust-lang/crates.io-index"
122
+
123
+[[package]]
124
+name = "rand_hc"
125
+version = "0.1.0"
126
+source = "registry+https://github.com/rust-lang/crates.io-index"
127
+dependencies = [
128
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
129
+]
130
+
131
+[[package]]
132
+name = "rand_isaac"
133
+version = "0.1.1"
134
+source = "registry+https://github.com/rust-lang/crates.io-index"
135
+dependencies = [
136
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
137
+]
138
+
139
+[[package]]
140
+name = "rand_jitter"
141
+version = "0.1.3"
142
+source = "registry+https://github.com/rust-lang/crates.io-index"
143
+dependencies = [
144
+ "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
145
+ "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
146
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
147
+]
148
+
149
+[[package]]
150
+name = "rand_os"
151
+version = "0.1.2"
152
+source = "registry+https://github.com/rust-lang/crates.io-index"
153
+dependencies = [
154
+ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
155
+ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
156
+ "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
157
+ "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
158
+ "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
159
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
160
+]
161
+
162
+[[package]]
163
+name = "rand_pcg"
164
+version = "0.1.1"
165
+source = "registry+https://github.com/rust-lang/crates.io-index"
166
+dependencies = [
167
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
168
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
169
+]
170
+
171
+[[package]]
172
+name = "rand_xorshift"
173
+version = "0.1.1"
174
+source = "registry+https://github.com/rust-lang/crates.io-index"
175
+dependencies = [
176
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
177
+]
178
+
179
+[[package]]
180
+name = "rdrand"
181
+version = "0.4.0"
182
+source = "registry+https://github.com/rust-lang/crates.io-index"
183
+dependencies = [
184
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
185
+]
186
+
187
+[[package]]
188
+name = "rustc_version"
189
+version = "0.2.3"
190
+source = "registry+https://github.com/rust-lang/crates.io-index"
191
+dependencies = [
192
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
193
+]
194
+
195
+[[package]]
196
+name = "ryu"
197
+version = "0.2.7"
198
+source = "registry+https://github.com/rust-lang/crates.io-index"
199
+
200
+[[package]]
201
+name = "semver"
202
+version = "0.9.0"
203
+source = "registry+https://github.com/rust-lang/crates.io-index"
204
+dependencies = [
205
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
206
+]
207
+
208
+[[package]]
209
+name = "semver-parser"
210
+version = "0.7.0"
211
+source = "registry+https://github.com/rust-lang/crates.io-index"
212
+
213
+[[package]]
214
+name = "serde"
215
+version = "1.0.88"
216
+source = "registry+https://github.com/rust-lang/crates.io-index"
217
+dependencies = [
218
+ "serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
219
+]
220
+
221
+[[package]]
222
+name = "serde_derive"
223
+version = "1.0.88"
224
+source = "registry+https://github.com/rust-lang/crates.io-index"
225
+dependencies = [
226
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
227
+ "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
228
+ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
229
+]
230
+
231
+[[package]]
232
+name = "serde_json"
233
+version = "1.0.38"
234
+source = "registry+https://github.com/rust-lang/crates.io-index"
235
+dependencies = [
236
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
237
+ "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
238
+ "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
239
+]
240
+
241
+[[package]]
242
+name = "syn"
243
+version = "0.15.26"
244
+source = "registry+https://github.com/rust-lang/crates.io-index"
245
+dependencies = [
246
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
247
+ "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
248
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
249
+]
250
+
251
+[[package]]
252
+name = "unicode-xid"
253
+version = "0.1.0"
254
+source = "registry+https://github.com/rust-lang/crates.io-index"
255
+
256
+[[package]]
257
+name = "winapi"
258
+version = "0.3.6"
259
+source = "registry+https://github.com/rust-lang/crates.io-index"
260
+dependencies = [
261
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
262
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
263
+]
264
+
265
+[[package]]
266
+name = "winapi-i686-pc-windows-gnu"
267
+version = "0.4.0"
268
+source = "registry+https://github.com/rust-lang/crates.io-index"
269
+
270
+[[package]]
271
+name = "winapi-x86_64-pc-windows-gnu"
272
+version = "0.4.0"
273
+source = "registry+https://github.com/rust-lang/crates.io-index"
274
+
275
+[metadata]
276
+"checksum approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c57ff1a5b00753647aebbbcf4ea67fa1e711a65ea7a30eb90dbf07de2485aee"
277
+"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
278
+"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
279
+"checksum cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"
280
+"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
281
+"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
282
+"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
283
+"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
284
+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
285
+"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
286
+"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
287
+"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
288
+"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
289
+"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
290
+"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
291
+"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
292
+"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
293
+"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832"
294
+"checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d"
295
+"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
296
+"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
297
+"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
298
+"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
299
+"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
300
+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
301
+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
302
+"checksum serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "9f301d728f2b94c9a7691c90f07b0b4e8a4517181d9461be94c04bddeb4bd850"
303
+"checksum serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "beed18e6f5175aef3ba670e57c60ef3b1b74d250d962a26604bff4c80e970dd4"
304
+"checksum serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "27dce848e7467aa0e2fcaf0a413641499c0b745452aaca1194d24dedde9e13c9"
305
+"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
306
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
307
+"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
308
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
309
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

+ 10 - 0
Cargo.toml

@@ -0,0 +1,10 @@
1
+[package]
2
+name = "icosahedron"
3
+version = "0.1.0"
4
+authors = ["Tyler Hallada <tyler@hallada.net>"]
5
+edition = "2018"
6
+
7
+[dependencies]
8
+cgmath = { version = "0.17.0", features = ["serde"] }
9
+serde = { version = "1.0", features = ["derive"] }
10
+serde_json = "1.0"

+ 18 - 0
README.md

@@ -0,0 +1,18 @@
1
+Still a work in progress.
2
+
3
+Generates the shapes and then serializes them to a JSON file with a list of 
4
+vertices (`positions`) and a list of triangle faces (`cells`) that index into 
5
+the list of vertices. Suitable for input into [Three.js's 
6
+BufferGeometry](https://threejs.org/docs/#api/en/core/BufferGeometry) or 
7
+[regl](https://github.com/regl-project/regl/blob/gh-pages/example/camera.js).
8
+
9
+Icosahedrons can be generated significantly faster than Three.js's version in 
10
+JavaScript (which I pretty much copied into Rust).
11
+
12
+Trunacated icosahedrons (I call them hexspheres) are a bit slower to generate 
13
+since they are made by generating a icosahedron and then subdividing it into 
14
+hexagon and pentagon faces. I still have some work to do to improve that code.
15
+
16
+I'm still having issues rendering hexspheres of detail level 5 and higher and 
17
+icosahedrons of detail level of 7 and higher, so I'm not sure if those are 
18
+accurate.

File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d0.json


File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d1.json


File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d2.json


File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d3.json


File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d4.json


File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d5.json


File diff suppressed because it is too large
+ 1 - 0
output/hexsphere_r1_d6.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d0.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d1.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d2.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d3.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d4.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d5.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d6.json


File diff suppressed because it is too large
+ 1 - 0
output/icosahedron_r1_d7.json


+ 427 - 0
src/main.rs

@@ -0,0 +1,427 @@
1
+extern crate cgmath;
2
+
3
+use std::collections::HashMap;
4
+use std::fs::File;
5
+use std::io::Write;
6
+use std::path::Path;
7
+
8
+use cgmath::prelude::*;
9
+use cgmath::Vector3;
10
+use serde::ser::{SerializeSeq, Serializer};
11
+use serde::Serialize;
12
+
13
+#[derive(Debug)]
14
+struct Triangle {
15
+    a: usize,
16
+    b: usize,
17
+    c: usize,
18
+}
19
+
20
+impl Triangle {
21
+    fn new(a: usize, b: usize, c: usize) -> Triangle {
22
+        Triangle { a, b, c }
23
+    }
24
+}
25
+
26
+impl Serialize for Triangle {
27
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28
+    where
29
+        S: Serializer,
30
+    {
31
+        let vec_indices = vec![self.a, self.b, self.c];
32
+        let mut seq = serializer.serialize_seq(Some(vec_indices.len()))?;
33
+        for index in vec_indices {
34
+            seq.serialize_element(&index)?;
35
+        }
36
+        seq.end()
37
+    }
38
+}
39
+
40
+#[derive(Debug)]
41
+struct ArraySerializedVector(Vector3<f32>);
42
+
43
+#[derive(Serialize, Debug)]
44
+struct Polyhedron {
45
+    positions: Vec<ArraySerializedVector>,
46
+    cells: Vec<Triangle>,
47
+}
48
+
49
+impl Serialize for ArraySerializedVector {
50
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51
+    where
52
+        S: Serializer,
53
+    {
54
+        let values = vec![self.0.x, self.0.y, self.0.z];
55
+        let mut seq = serializer.serialize_seq(Some(values.len()))?;
56
+        for value in values {
57
+            seq.serialize_element(&value)?;
58
+        }
59
+        seq.end()
60
+    }
61
+}
62
+
63
+impl Polyhedron {
64
+    fn new() -> Polyhedron {
65
+        Polyhedron {
66
+            positions: vec![],
67
+            cells: vec![],
68
+        }
69
+    }
70
+
71
+    fn new_isocahedron(radius: f32, detail: usize) -> Polyhedron {
72
+        let t = (1.0 + (5.0 as f32).sqrt()) / 2.0;
73
+        let base_isocahedron = Polyhedron {
74
+            positions: vec![
75
+                ArraySerializedVector(Vector3::new(-1.0, t, 0.0)),
76
+                ArraySerializedVector(Vector3::new(1.0, t, 0.0)),
77
+                ArraySerializedVector(Vector3::new(-1.0, -t, 0.0)),
78
+                ArraySerializedVector(Vector3::new(1.0, -t, 0.0)),
79
+                ArraySerializedVector(Vector3::new(0.0, -1.0, t)),
80
+                ArraySerializedVector(Vector3::new(0.0, 1.0, t)),
81
+                ArraySerializedVector(Vector3::new(0.0, -1.0, -t)),
82
+                ArraySerializedVector(Vector3::new(0.0, 1.0, -t)),
83
+                ArraySerializedVector(Vector3::new(t, 0.0, -1.0)),
84
+                ArraySerializedVector(Vector3::new(t, 0.0, 1.0)),
85
+                ArraySerializedVector(Vector3::new(-t, 0.0, -1.0)),
86
+                ArraySerializedVector(Vector3::new(-t, 0.0, 1.0)),
87
+            ],
88
+            cells: vec![
89
+                Triangle::new(0, 11, 5),
90
+                Triangle::new(0, 5, 1),
91
+                Triangle::new(0, 1, 7),
92
+                Triangle::new(0, 7, 10),
93
+                Triangle::new(0, 10, 11),
94
+                Triangle::new(1, 5, 9),
95
+                Triangle::new(5, 11, 4),
96
+                Triangle::new(11, 10, 2),
97
+                Triangle::new(10, 7, 6),
98
+                Triangle::new(7, 1, 8),
99
+                Triangle::new(3, 9, 4),
100
+                Triangle::new(3, 4, 2),
101
+                Triangle::new(3, 2, 6),
102
+                Triangle::new(3, 6, 8),
103
+                Triangle::new(3, 8, 9),
104
+                Triangle::new(4, 9, 5),
105
+                Triangle::new(2, 4, 11),
106
+                Triangle::new(6, 2, 10),
107
+                Triangle::new(8, 6, 7),
108
+                Triangle::new(9, 8, 1),
109
+            ],
110
+        };
111
+        let mut subdivided = Polyhedron::new();
112
+        subdivided.subdivide(base_isocahedron, radius, detail);
113
+        subdivided
114
+    }
115
+
116
+    fn new_dual_isocahedron(radius: f32, detail: usize) -> Polyhedron {
117
+        let isocahedron = Polyhedron::new_isocahedron(radius, detail);
118
+        let mut dual_isocahedron = Polyhedron::new();
119
+        dual_isocahedron.dual(isocahedron);
120
+        dual_isocahedron
121
+    }
122
+
123
+    fn subdivide(&mut self, other: Polyhedron, radius: f32, detail: usize) {
124
+        // TODO: maybe make this part of the polygon stuct to avoid having to pass it around
125
+        let mut added_vert_cache: HashMap<(i32, i32, i32), usize> = HashMap::new();
126
+        let precision = 10_f32.powi(4);
127
+        for triangle in other.cells {
128
+            let a = other.positions[triangle.a].0;
129
+            let b = other.positions[triangle.b].0;
130
+            let c = other.positions[triangle.c].0;
131
+            self.subdivide_triangle(a, b, c, radius, detail, precision, &mut added_vert_cache);
132
+        }
133
+    }
134
+
135
+    fn subdivide_triangle(
136
+        &mut self,
137
+        a: Vector3<f32>,
138
+        b: Vector3<f32>,
139
+        c: Vector3<f32>,
140
+        radius: f32,
141
+        detail: usize,
142
+        precision: f32,
143
+        added_vert_cache: &mut HashMap<(i32, i32, i32), usize>,
144
+    ) {
145
+        let cols = 2usize.pow(detail as u32);
146
+        let mut new_vertices: Vec<Vec<Vector3<f32>>> = vec![];
147
+
148
+        for i in 0..=cols {
149
+            new_vertices.push(vec![]);
150
+            let aj = a.clone().lerp(c, i as f32 / cols as f32);
151
+            let bj = b.clone().lerp(c, i as f32 / cols as f32);
152
+            let rows = cols - i;
153
+
154
+            for j in 0..=rows {
155
+                if j == 0 && i == cols {
156
+                    new_vertices[i].push(aj.normalize() * radius);
157
+                } else {
158
+                    new_vertices[i]
159
+                        .push(aj.clone().lerp(bj, j as f32 / rows as f32).normalize() * radius);
160
+                }
161
+            }
162
+        }
163
+
164
+        for i in 0..cols {
165
+            for j in 0..2 * (cols - i) - 1 {
166
+                let k = j / 2;
167
+
168
+                let mut triangle = Triangle { a: 0, b: 0, c: 0 };
169
+                if j % 2 == 0 {
170
+                    triangle.a =
171
+                        self.add_position(new_vertices[i][k + 1], precision, added_vert_cache);
172
+                    triangle.b =
173
+                        self.add_position(new_vertices[i + 1][k], precision, added_vert_cache);
174
+                    triangle.c = self.add_position(new_vertices[i][k], precision, added_vert_cache);
175
+                } else {
176
+                    triangle.a =
177
+                        self.add_position(new_vertices[i][k + 1], precision, added_vert_cache);
178
+                    triangle.b =
179
+                        self.add_position(new_vertices[i + 1][k + 1], precision, added_vert_cache);
180
+                    triangle.c =
181
+                        self.add_position(new_vertices[i + 1][k], precision, added_vert_cache);
182
+                }
183
+
184
+                self.cells.push(triangle);
185
+            }
186
+        }
187
+    }
188
+
189
+    fn add_position(
190
+        &mut self,
191
+        vertex: Vector3<f32>,
192
+        precision: f32,
193
+        added_vert_cache: &mut HashMap<(i32, i32, i32), usize>,
194
+    ) -> usize {
195
+        let vertex_key = (
196
+            (vertex.x * precision).round() as i32,
197
+            (vertex.y * precision).round() as i32,
198
+            (vertex.z * precision).round() as i32,
199
+        );
200
+        if let Some(added_vert_index) = added_vert_cache.get(&vertex_key) {
201
+            return *added_vert_index;
202
+        } else {
203
+            self.positions.push(ArraySerializedVector(vertex));
204
+            let added_index = self.positions.len() - 1;
205
+            added_vert_cache.insert(vertex_key, added_index);
206
+            return added_index;
207
+        }
208
+    }
209
+
210
+    fn dual(&mut self, other: Polyhedron) {
211
+        let vert_to_faces = other.vert_to_faces();
212
+        let original_vert_count = other.positions.len();
213
+        let triangle_centroids = other.triangle_centroids();
214
+        let mut mid_centroid_cache: HashMap<(usize, usize), Vector3<f32>> = HashMap::new();
215
+        let mut hex_count = 0;
216
+        let mut pent_count = 0;
217
+        for i in 0..original_vert_count {
218
+            let faces = &vert_to_faces[&i];
219
+            if faces.len() == 6 {
220
+                hex_count += 1;
221
+            } else {
222
+                pent_count += 1;
223
+            }
224
+
225
+            let center_point = find_center_of_triangles(faces, &triangle_centroids);
226
+
227
+            for face_index in faces {
228
+                let triangle = &other.cells[*face_index];
229
+                let other_verts: Vec<usize> = vec![triangle.a, triangle.b, triangle.c]
230
+                    .drain(..)
231
+                    .filter(|vert| *vert != i)
232
+                    .collect();
233
+                let sorted_triangle = Triangle::new(i, other_verts[0], other_verts[1]);
234
+
235
+                let centroid = triangle_centroids[face_index];
236
+                let mid_b_centroid = other.calculate_mid_centroid(
237
+                    sorted_triangle.b,
238
+                    faces,
239
+                    centroid,
240
+                    &triangle_centroids,
241
+                    &mut mid_centroid_cache,
242
+                );
243
+                let mid_c_centroid = other.calculate_mid_centroid(
244
+                    sorted_triangle.c,
245
+                    faces,
246
+                    centroid,
247
+                    &triangle_centroids,
248
+                    &mut mid_centroid_cache,
249
+                );
250
+
251
+                let positions_start = self.positions.len();
252
+                // TODO: remove duplication here:
253
+                // push all triangle_centroids at beginning, same index as face_index
254
+                // push center_point once in outer loop and save index
255
+                // (is it okay if vertices show up in positions out of order like that?)
256
+                // -- yes, I think it is okay
257
+                self.positions.push(ArraySerializedVector(center_point));
258
+                self.positions.push(ArraySerializedVector(centroid));
259
+                self.positions.push(ArraySerializedVector(mid_b_centroid));
260
+                self.positions.push(ArraySerializedVector(mid_c_centroid));
261
+
262
+                self.cells.push(Triangle::new(
263
+                    positions_start,
264
+                    positions_start + 1,
265
+                    positions_start + 2,
266
+                ));
267
+                self.cells.push(Triangle::new(
268
+                    positions_start,
269
+                    positions_start + 1,
270
+                    positions_start + 3,
271
+                ));
272
+            }
273
+        }
274
+        println!("hexagons: {}", hex_count);
275
+        println!("pentagons: {}", pent_count);
276
+    }
277
+
278
+    fn vert_to_faces(&self) -> HashMap<usize, Vec<usize>> {
279
+        let mut vert_to_faces: HashMap<usize, Vec<usize>> = HashMap::new();
280
+        for i in 0..self.cells.len() {
281
+            let triangle = &self.cells[i];
282
+
283
+            if let Some(faces) = vert_to_faces.get_mut(&triangle.a) {
284
+                faces.push(i);
285
+            } else {
286
+                vert_to_faces.insert(triangle.a, vec![i]);
287
+            }
288
+
289
+            if let Some(faces) = vert_to_faces.get_mut(&triangle.b) {
290
+                faces.push(i);
291
+            } else {
292
+                vert_to_faces.insert(triangle.b, vec![i]);
293
+            }
294
+
295
+            if let Some(faces) = vert_to_faces.get_mut(&triangle.c) {
296
+                faces.push(i);
297
+            } else {
298
+                vert_to_faces.insert(triangle.c, vec![i]);
299
+            }
300
+        }
301
+        vert_to_faces
302
+    }
303
+
304
+    fn triangle_centroids(&self) -> HashMap<usize, Vector3<f32>> {
305
+        let mut triangle_centroids: HashMap<usize, Vector3<f32>> = HashMap::new();
306
+        for i in 0..self.cells.len() {
307
+            let a = self.positions[self.cells[i].a].0;
308
+            let b = self.positions[self.cells[i].b].0;
309
+            let c = self.positions[self.cells[i].c].0;
310
+            triangle_centroids.insert(i, calculate_centroid(a, b, c));
311
+        }
312
+        triangle_centroids
313
+    }
314
+
315
+    fn calculate_mid_centroid(
316
+        &self,
317
+        vertex_index: usize,
318
+        faces: &Vec<usize>,
319
+        centroid: Vector3<f32>,
320
+        triangle_centroids: &HashMap<usize, Vector3<f32>>,
321
+        mid_centroid_cache: &mut HashMap<(usize, usize), Vector3<f32>>,
322
+    ) -> Vector3<f32> {
323
+        let adj_face_index = self.find_adjacent_face(vertex_index, faces).unwrap();
324
+        let adj_centroid = triangle_centroids[&adj_face_index];
325
+        if let Some(mid_centroid) = mid_centroid_cache.get(&(vertex_index, adj_face_index)) {
326
+            return *mid_centroid;
327
+        } else {
328
+            let mid_centroid = centroid.clone().lerp(adj_centroid, 0.5);
329
+            mid_centroid_cache.insert((vertex_index, adj_face_index), mid_centroid);
330
+            return mid_centroid;
331
+        }
332
+    }
333
+
334
+    fn find_adjacent_face(&self, vertex_index: usize, faces: &Vec<usize>) -> Option<usize> {
335
+        for face_index in faces {
336
+            let triangle = &self.cells[*face_index];
337
+            if triangle.a == vertex_index
338
+                || triangle.b == vertex_index
339
+                || triangle.c == vertex_index
340
+            {
341
+                return Some(*face_index);
342
+            }
343
+        }
344
+        None
345
+    }
346
+}
347
+
348
+fn calculate_centroid(pa: Vector3<f32>, pb: Vector3<f32>, pc: Vector3<f32>) -> Vector3<f32> {
349
+    let vab_half = (pb.clone() - pa) / 2.0;
350
+    let pab_half = pa.clone() + vab_half;
351
+    ((pc.clone() - pab_half) * (1.0 / 3.0)) + pab_half
352
+}
353
+
354
+fn find_center_of_triangles(
355
+    triangle_indices: &Vec<usize>,
356
+    triangle_centroids: &HashMap<usize, Vector3<f32>>,
357
+) -> Vector3<f32> {
358
+    let mut center_point: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0);
359
+    for triangle_index in triangle_indices.iter() {
360
+        center_point += triangle_centroids[triangle_index];
361
+    }
362
+    center_point /= triangle_indices.len() as f32;
363
+    center_point
364
+}
365
+
366
+fn generate_icosahedron_files(dir: &str, param_list: Vec<(f32, usize)>) {
367
+    for param in param_list {
368
+        println!(
369
+            "Generating icosahedron with radius {} and detail {}...",
370
+            param.0, param.1
371
+        );
372
+        let filename = Path::new(dir).join(format!("icosahedron_r{}_d{}.json", param.0, param.1));
373
+        let mut file = File::create(filename).expect("Can't create file");
374
+        let icosahedron = Polyhedron::new_isocahedron(param.0, param.1);
375
+        println!("triangles: {}", icosahedron.cells.len());
376
+        println!("vertices: {}", icosahedron.positions.len());
377
+        let icosahedron_json = serde_json::to_string(&icosahedron).expect("Problem serializing");
378
+        file.write_all(icosahedron_json.as_bytes())
379
+            .expect("Can't write to file");
380
+    }
381
+}
382
+
383
+fn generate_hexsphere_files(dir: &str, param_list: Vec<(f32, usize)>) {
384
+    for param in param_list {
385
+        println!(
386
+            "Generating hexsphere with radius {} and detail {}...",
387
+            param.0, param.1
388
+        );
389
+        let filename = Path::new(dir).join(format!("hexsphere_r{}_d{}.json", param.0, param.1));
390
+        let mut file = File::create(filename).expect("Can't create file");
391
+        let hexsphere = Polyhedron::new_dual_isocahedron(param.0, param.1);
392
+        println!("triangles: {}", hexsphere.cells.len());
393
+        println!("vertices: {}", hexsphere.positions.len());
394
+        let hexsphere_json = serde_json::to_string(&hexsphere).expect("Problem serializing");
395
+        file.write_all(hexsphere_json.as_bytes())
396
+            .expect("Can't write to file");
397
+    }
398
+}
399
+
400
+fn main() {
401
+    generate_hexsphere_files(
402
+        "output/",
403
+        vec![
404
+            (1.0, 0),
405
+            (1.0, 1),
406
+            (1.0, 2),
407
+            (1.0, 3),
408
+            (1.0, 4),
409
+            (1.0, 5),
410
+            (1.0, 6),
411
+            (1.0, 7),
412
+        ],
413
+    );
414
+    generate_icosahedron_files(
415
+        "output/",
416
+        vec![
417
+            (1.0, 0),
418
+            (1.0, 1),
419
+            (1.0, 2),
420
+            (1.0, 3),
421
+            (1.0, 4),
422
+            (1.0, 5),
423
+            (1.0, 6),
424
+            (1.0, 7),
425
+        ],
426
+    );
427
+}