-
Notifications
You must be signed in to change notification settings - Fork 1
/
makerobin.cpp
225 lines (195 loc) · 8.15 KB
/
makerobin.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
* makerobin.cpp - generate the ROBIN helicopter body model
*
* Stores mesh in 2 .obj files:
* robin_fuselage
* robin_pylon
*/
#include <cstdlib>
#include <cmath>
#include <complex>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <array>
int get_fuselage_section (const double x) {
int idx = -1;
if (x >= 0.0 and x < 0.4) idx = 0;
else if (x >= 0.4 and x < 0.8) idx = 1;
else if (x >= 0.8 and x < 1.9) idx = 2;
else if (x >= 1.9 and x <= 2.0) idx = 3;
return idx;
}
int get_pylon_section (const double x) {
int idx = -1;
if (x >= 0.4 and x < 0.8) idx = 4;
else if (x >= 0.8 and x <= 1.018) idx = 5;
return idx;
}
using SupEll = std::array<double, 8>;
double getsuperval (const double x, const SupEll& c) {
return c[5] + c[6]*std::pow(std::max(0.0,c[0]+c[1]*std::pow((x+c[2])/c[3], c[4])), 1.0/c[7]); // nan
}
double getRadialCoord(double H, double W, double theta, double N) {
double numer = 0.25*H*W;
// Note the new std::abs - this is to ensure that values are positive, we really only compute one quadrant
double denom = std::pow(0.5*H*std::abs(std::sin(theta)), N) + std::pow(0.5*W*std::abs(std::cos(theta)), N);
if (!denom) { denom = 1;}
return numer / std::pow(denom, 1.0/N);
}
double chebeshev_node(double a, double b, double k, double n) {
if (k == 0) {
return a;
} else if (k==n) {
return b;
} else {
return (a+b)*0.5 + (b-a)*0.5*std::cos((2.0*(n-k))*M_PI*0.5/n);
}
}
void create_vertices(const size_t nx, const size_t nt, const std::string fileName,
int (*getSection)(double), const double xBegin, const double xEnd) {
// first 4 rows of each section are the fuselage
// next 2 more rows each for the pylon
std::vector<SupEll> hcoeff = { {1.0, -1.0, -0.4, -0.4, 1.8, 0.0, 0.25, 1.8},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.25, 0.0, 1.0},
{1.0, -1.0, -0.8, 1.1, 1.5, 0.05, 0.2, 0.6},
{1.0, -1.0, -1.9, 0.1, 2.0, 0.0, 0.05, 2.0},
{1.0, -1.0, -0.8, -0.4, 3.0, 0.0, 0.145, 3.0},
{1.0, -1.0, -0.8, 0.218, 2.0, 0.0, 0.145, 2.0} };
std::vector<SupEll> wcoeff = { {1.0, -1.0, -0.4, -0.4, 2.0, 0.0, 0.25, 2.0},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.25, 0.0, 1.0},
{1.0, -1.0, -0.8, 1.1, 1.5, 0.05, 0.2, 0.6},
{1.0, -1.0, -1.9, 0.1, 2.0, 0.0, 0.05, 2.0},
{1.0, -1.0, -0.8, -0.4, 3.0, 0.0, 0.166, 3.0},
{1.0, -1.0, -0.8, 0.218, 2.0, 0.0, 0.166, 2.0} };
std::vector<SupEll> zcoeff = { {1.0, -1.0, -0.4, -0.4, 1.8, -0.08, 0.08, 1.8},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0},
{1.0, -1.0, -0.8, 1.1, 1.5, 0.04, -0.04, 0.6},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.04, 0.0, 1.0},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.125, 0.0, 1.0},
{1.0, -1.0, -0.8, 1.1, 1.5, 0.065, 0.06, 0.6} };
std::vector<SupEll> ncoeff = { {2.0, 3.0, 0.0, 0.4, 1.0, 0.0, 1.0, 1.0},
{0.0, 0.0, 0.0, 1.0, 0.0, 5.0, 0.0, 1.0},
{5.0, -3.0, -0.8, 1.1, 1.0, 0.0, 1.0, 1.0},
{0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 1.0},
{0.0, 0.0, 0.0, 1.0, 0.0, 5.0, 0.0, 1.0},
{0.0, 0.0, 0.0, 1.0, 0.0, 5.0, 0.0, 1.0} };
// fixes:
// 1) if there's a 0.0 in the second col, then change the 4th and 5th cols to 1.0
// 2) if there's a 0.0 in C7, change C8 to 1.0, same as above, to prevent nan/inf
// 3) the 0.4..0.8 section (row 2) coefficients in C1 needed to go into C6
// 4) C4 is wrong in the first section of fuse and pyl - it needed to be negative
// Open file to write to
std::ofstream file;
file.open(fileName);
file << "# Vertices\n";
std::cout << " generating nodes" << std::endl;
for (size_t ix=0; ix<nx+1; ix++) {
const double xol = chebeshev_node(xBegin, xEnd, ix, nx);
//std::cout << ix << " " << xol << std::endl;
const int sec = getSection(xol);
if (sec == -1) {
std::cout << "ERROR: sec == " << sec << " x == " << xol << std::endl;
exit(0);
}
// compute H, W, Z0, and N from xol and the constants
const double H = getsuperval(xol, hcoeff[sec]);
const double W = getsuperval(xol, wcoeff[sec]);
const double Z0 = getsuperval(xol, zcoeff[sec]);
const double N = getsuperval(xol, ncoeff[sec]);
for (size_t it=0; it<nt; it++) {
const double theta = 2.0*M_PI*it/(double)nt;
// compute r from H, W, N, theta
const double r = getRadialCoord(H, W, theta, N);
// compute yol, zol from r, theta, Z0
const double yol = r * std::sin(theta);
const double zol = r * std::cos(theta) + Z0;
//std::cout << xol << " " << yol << " " << zol << std::endl;
// Write vertex to file
file << "v " << xol << " " << yol << " " << zol << "\n";
if ((ix == 0) || (ix == nx)) { break; }
}
}
file.close();
}
void create_faces(const std::string fileName, const size_t nx, const size_t nt) {
std::ofstream file;
file.open(fileName, std::ios_base::app);
std::cout << " generating faces" << std::endl;
// Label faces
file << "\n# Faces\n";
for (size_t i=2; i<nt+1; i++) {
file << "f " << 1 << " " << i << " " << i+1 << "\n";
}
file << "f " << 1 << " " << nt+1 << " " << 2 << "\n";
// Link the two rings together to make faces
for (size_t r=0; r<nx-2; r++) {
// node indices for rings 1 and 2
const size_t ir1 = nt*r + 2;
const size_t ir2 = nt*(r+1) + 2;
// Runs through all nodes in the ring
for (size_t n=0; n<nt-1; n++) {
file << "f " << ir1+n << " " << ir2+n << " " << ir1+n+1 << "\n";
file << "f " << ir2+n << " " << ir2+n+1 << " " << ir1+n+1 << "\n";
}
file << "f " << ir1+nt-1 << " " << ir2+nt-1 << " " << ir1 << "\n";
file << "f " << ir2+nt-1 << " " << ir2 << " " << ir1 << "\n";
}
size_t lastVert = nt*(nx-1)+2;
for (size_t i=2; i<nt+1; i++) {
file << "f " << (i+1)+nt*(nx-2) << " " << i+nt*(nx-2) << " " << lastVert << "\n";
}
file << "f " << 2+nt*(nx-2) << " " << lastVert-1 << " " << lastVert << "\n";
// Done writing faces
file.close();
}
long long input_check(std::string num) {
long long n;
try {
std::size_t pos;
n = std::stoll(num, &pos);
// check if input is mix of numbers and chars
// 1zz converts to 1
if (pos < num.size()) {
std::cerr << "Trailing characters after number: " << num << '\n';
std::cerr << "Using: " << n << std::endl;
}
} catch (std::invalid_argument const &ex) {
std::cerr << "Invalid number: " << num << '\n';
exit(0);
} catch (std::out_of_range const &ex) {
std::cerr << "Number out of range: " << num << '\n';
exit(0);
}
return n;
}
// execution starts here
int main(int argc, char const *argv[]) {
std::cout << "Generating ROBIN model" << std::endl;
// Actual coefficients are in create_vertices
// Read in command line inputs
if (argc != 5) {
std::cerr << "Incorrect number of arguments: " << argc-1 << std::endl;
std::cerr << "Argument order is [nx fuselage] [nt fuselage] [nx pylon] [nt pylon]\n";
exit(0);
}
const long long fnx = input_check((std::string)argv[1]);
const long long fnt = input_check((std::string)argv[2]);
const long long pnx = input_check((std::string)argv[3]);
const long long pnt = input_check((std::string)argv[4]);
const std::string fileName = "robin_fuselage.obj";
const double fusBegin = 0.0;
const double fusEnd = 2.0;
const std::string pFileName = "robin_pylon.obj";
const double pylBegin = 0.4;
const double pylEnd = 1.018;
std::cout << "Creating fuselage mesh..." << std::endl;
create_vertices(fnx, fnt, fileName, get_fuselage_section, fusBegin, fusEnd);
create_faces(fileName, fnx, fnt);
std::cout << "Creating pylon mesh..." << std::endl;
create_vertices(pnx, pnt, pFileName, get_pylon_section, pylBegin, pylEnd);
create_faces(pFileName, pnx, pnt);
// CSG these two meshes together
return 0;
}