src/primitives.cpp

changeset 493
16766ac1bbd9
parent 491
7d1b5ecd76c0
child 497
c51941e590b6
equal deleted inserted replaced
492:e964085e6913 493:16766ac1bbd9
24 #include "gui.h" 24 #include "gui.h"
25 #include "primitives.h" 25 #include "primitives.h"
26 #include "ui_makeprim.h" 26 #include "ui_makeprim.h"
27 #include "misc.h" 27 #include "misc.h"
28 #include "colors.h" 28 #include "colors.h"
29 #include "moc_primitives.cpp"
29 30
30 List<PrimitiveCategory> g_PrimitiveCategories; 31 List<PrimitiveCategory> g_PrimitiveCategories;
32 List<Primitive> g_primitives;
31 static PrimitiveLister* g_activePrimLister = null; 33 static PrimitiveLister* g_activePrimLister = null;
32 static bool g_primListerMutex = false; 34 static bool g_primListerMutex = false;
33 List<Primitive> g_primitives;
34
35 static const str g_Other = PrimitiveLister::tr ("Other"); 35 static const str g_Other = PrimitiveLister::tr ("Other");
36 36
37 static const str g_radialNameRoots[] = { 37 static const str g_radialNameRoots[] =
38 "edge", 38 { "edge",
39 "cyli", 39 "cyli",
40 "disc", 40 "disc",
41 "ndis", 41 "ndis",
42 "ring", 42 "ring",
43 "con" 43 "con"
46 static void populateCategories(); 46 static void populateCategories();
47 static void loadPrimitiveCatgories(); 47 static void loadPrimitiveCatgories();
48 48
49 // ============================================================================= 49 // =============================================================================
50 // ----------------------------------------------------------------------------- 50 // -----------------------------------------------------------------------------
51 void loadPrimitives() { 51 void loadPrimitives()
52 print ("Loading primitives...\n"); 52 { print ("Loading primitives...\n");
53 loadPrimitiveCatgories(); 53 loadPrimitiveCatgories();
54 54
55 // Try to load prims.cfg 55 // Try to load prims.cfg
56 File conf (Config::filepath ("prims.cfg"), File::Read); 56 File conf (Config::filepath ("prims.cfg"), File::Read);
57 57
58 if (!conf) { 58 if (!conf)
59 // No prims.cfg, build it 59 { // No prims.cfg, build it
60 PrimitiveLister::start(); 60 PrimitiveLister::start();
61 } else { 61 }
62 // Read primitives from prims.cfg 62 else
63 for (str line : conf) { 63 { // Read primitives from prims.cfg
64 int space = line.indexOf (" "); 64 for (str line : conf)
65 65 { int space = line.indexOf (" ");
66
66 if (space == -1) 67 if (space == -1)
67 continue; 68 continue;
68 69
69 Primitive info; 70 Primitive info;
70 info.name = line.left (space); 71 info.name = line.left (space);
71 info.title = line.mid (space + 1); 72 info.title = line.mid (space + 1);
72 g_primitives << info; 73 g_primitives << info;
73 } 74 }
74 75
75 populateCategories(); 76 populateCategories();
76 } 77 }
77 } 78 }
78 79
79 // ============================================================================= 80 // =============================================================================
80 // ----------------------------------------------------------------------------- 81 // -----------------------------------------------------------------------------
81 static void recursiveGetFilenames (QDir dir, List<str>& fnames) { 82 static void recursiveGetFilenames (QDir dir, List<str>& fnames)
82 QFileInfoList flist = dir.entryInfoList(); 83 { QFileInfoList flist = dir.entryInfoList();
83 84
84 for (const QFileInfo & info : flist) { 85 for (const QFileInfo & info : flist)
85 if (info.fileName() == "." || info.fileName() == "..") 86 { if (info.fileName() == "." || info.fileName() == "..")
86 continue; // skip . and .. 87 continue; // skip . and ..
87 88
88 if (info.isDir()) 89 if (info.isDir())
89 recursiveGetFilenames (QDir (info.absoluteFilePath()), fnames); 90 recursiveGetFilenames (QDir (info.absoluteFilePath()), fnames);
90 else 91 else
91 fnames << info.absoluteFilePath(); 92 fnames << info.absoluteFilePath();
92 } 93 }
93 } 94 }
94 95
95 // ============================================================================= 96 // =============================================================================
96 // ----------------------------------------------------------------------------- 97 // -----------------------------------------------------------------------------
97 void PrimitiveLister::work() { 98 void PrimitiveLister::work()
98 g_activePrimLister = this; 99 { g_activePrimLister = this;
99 m_prims.clear(); 100 m_prims.clear();
100 101
101 QDir dir (LDPaths::prims()); 102 QDir dir (LDPaths::prims());
102 ulong baselen = dir.absolutePath().length(); 103 ulong baselen = dir.absolutePath().length();
103 ulong i = 0; 104 ulong i = 0;
104 List<str> fnames; 105 List<str> fnames;
105 106
106 assert (dir.exists()); 107 assert (dir.exists());
107 recursiveGetFilenames (dir, fnames); 108 recursiveGetFilenames (dir, fnames);
108 emit starting (fnames.size()); 109 emit starting (fnames.size());
109 110
110 for (str fname : fnames) { 111 for (str fname : fnames)
111 File f (fname, File::Read); 112 { File f (fname, File::Read);
112 113
113 Primitive info; 114 Primitive info;
114 info.name = fname.mid (baselen + 1); // make full path relative 115 info.name = fname.mid (baselen + 1); // make full path relative
115 info.name.replace ('/', '\\'); // use DOS backslashes, they're expected 116 info.name.replace ('/', '\\'); // use DOS backslashes, they're expected
116 info.cat = null; 117 info.cat = null;
117 118
118 if (!f.readLine (info.title)) 119 if (!f.readLine (info.title))
119 info.title = ""; 120 info.title = "";
120 121
121 info.title = info.title.simplified(); 122 info.title = info.title.simplified();
122 123
123 if (info.title[0] == '0') { 124 if (info.title[0] == '0')
124 info.title.remove (0, 1); // remove 0 125 { info.title.remove (0, 1); // remove 0
125 info.title = info.title.simplified(); 126 info.title = info.title.simplified();
126 } 127 }
127 128
128 m_prims << info; 129 m_prims << info;
129 emit update (++i); 130 emit update (++i);
130 } 131 }
131 132
132 // Save to a config file 133 // Save to a config file
133 File conf (Config::filepath ("prims.cfg"), File::Write); 134 File conf (Config::filepath ("prims.cfg"), File::Write);
134 135
135 for (Primitive & info : m_prims) 136 for (Primitive & info : m_prims)
136 fprint (conf, "%1 %2\n", info.name, info.title); 137 fprint (conf, "%1 %2\n", info.name, info.title);
137 138
138 conf.close(); 139 conf.close();
139 140
140 g_primListerMutex = true; 141 g_primListerMutex = true;
141 g_primitives = m_prims; 142 g_primitives = m_prims;
142 populateCategories(); 143 populateCategories();
143 g_primListerMutex = false; 144 g_primListerMutex = false;
144 g_activePrimLister = null; 145 g_activePrimLister = null;
145 emit workDone(); 146 emit workDone();
146 } 147 }
147 148
148 // ============================================================================= 149 // =============================================================================
149 // ----------------------------------------------------------------------------- 150 // -----------------------------------------------------------------------------
150 void PrimitiveLister::start() { 151 void PrimitiveLister::start()
151 if (g_activePrimLister) 152 { if (g_activePrimLister)
152 return; 153 return;
153 154
154 PrimitiveLister* lister = new PrimitiveLister; 155 PrimitiveLister* lister = new PrimitiveLister;
155 QThread* listerThread = new QThread; 156 QThread* listerThread = new QThread;
156 lister->moveToThread (listerThread); 157 lister->moveToThread (listerThread);
157 connect (lister, SIGNAL (starting (ulong)), g_win, SLOT (primitiveLoaderStart (ulong))); 158 connect (lister, SIGNAL (starting (ulong)), g_win, SLOT (primitiveLoaderStart (ulong)));
158 connect (lister, SIGNAL (update (ulong)), g_win, SLOT (primitiveLoaderUpdate (ulong))); 159 connect (lister, SIGNAL (update (ulong)), g_win, SLOT (primitiveLoaderUpdate (ulong)));
162 listerThread->start(); 163 listerThread->start();
163 } 164 }
164 165
165 // ============================================================================= 166 // =============================================================================
166 // ----------------------------------------------------------------------------- 167 // -----------------------------------------------------------------------------
167 static PrimitiveCategory* findCategory (str name) { 168 static PrimitiveCategory* findCategory (str name)
168 for (PrimitiveCategory& cat : g_PrimitiveCategories) 169 { for (PrimitiveCategory& cat : g_PrimitiveCategories)
169 if (cat.name() == name) 170 if (cat.name() == name)
170 return &cat; 171 return &cat;
171 172
172 return null; 173 return null;
173 } 174 }
174 175
175 // ============================================================================= 176 // =============================================================================
176 // ----------------------------------------------------------------------------- 177 // -----------------------------------------------------------------------------
177 static void populateCategories() { 178 static void populateCategories()
178 for (PrimitiveCategory& cat : g_PrimitiveCategories) 179 { for (PrimitiveCategory & cat : g_PrimitiveCategories)
179 cat.prims.clear(); 180 cat.prims.clear();
180 181
181 PrimitiveCategory* unmatched = findCategory (g_Other); 182 PrimitiveCategory* unmatched = findCategory (g_Other);
182 183
183 if (!unmatched) { 184 if (!unmatched)
184 // Shouldn't happen.. but catch it anyway. 185 { // Shouldn't happen.. but catch it anyway.
185 PrimitiveCategory cat; 186 PrimitiveCategory cat;
186 cat.setName (g_Other); 187 cat.setName (g_Other);
187 unmatched = & (g_PrimitiveCategories << cat); 188 unmatched = & (g_PrimitiveCategories << cat);
188 } 189 }
189 190
190 for (Primitive& prim : g_primitives) { 191 for (Primitive& prim : g_primitives)
191 bool matched = false; 192 { bool matched = false;
192 prim.cat = null; 193 prim.cat = null;
193 194
194 // Go over the categories and their regexes, if and when there's a match, 195 // Go over the categories and their regexes, if and when there's a match,
195 // the primitive's category is set to the category the regex beloings to. 196 // the primitive's category is set to the category the regex beloings to.
196 for (PrimitiveCategory& cat : g_PrimitiveCategories) { 197 for (PrimitiveCategory & cat : g_PrimitiveCategories)
197 for (PrimitiveCategory::RegexEntry& entry : cat.regexes) { 198 { for (PrimitiveCategory::RegexEntry & entry : cat.regexes)
198 switch (entry.type) { 199 { switch (entry.type)
199 case PrimitiveCategory::Filename: 200 { case PrimitiveCategory::Filename:
200 // f-regex, check against filename 201 // f-regex, check against filename
201 matched = entry.regex.exactMatch (prim.name); 202 matched = entry.regex.exactMatch (prim.name);
202 break; 203 break;
203 204
204 case PrimitiveCategory::Title: 205 case PrimitiveCategory::Title:
205 // t-regex, check against title 206 // t-regex, check against title
206 matched = entry.regex.exactMatch (prim.title); 207 matched = entry.regex.exactMatch (prim.title);
207 break; 208 break;
208 } 209 }
209 210
210 if (matched) { 211 if (matched)
211 prim.cat = &cat; 212 { prim.cat = &cat;
212 break; 213 break;
213 } 214 }
214 } 215 }
215 216
216 // Drop out if a category was decided on. 217 // Drop out if a category was decided on.
217 if (prim.cat) 218 if (prim.cat)
218 break; 219 break;
219 } 220 }
220 221
221 // If there was a match, add the primitive to the category. 222 // If there was a match, add the primitive to the category.
222 // Otherwise, add it to the list of unmatched primitives. 223 // Otherwise, add it to the list of unmatched primitives.
223 if (prim.cat) 224 if (prim.cat)
224 prim.cat->prims << prim; 225 prim.cat->prims << prim;
225 else 226 else
227 } 228 }
228 } 229 }
229 230
230 // ============================================================================= 231 // =============================================================================
231 // ----------------------------------------------------------------------------- 232 // -----------------------------------------------------------------------------
232 static void loadPrimitiveCatgories() { 233 static void loadPrimitiveCatgories()
233 g_PrimitiveCategories.clear(); 234 { g_PrimitiveCategories.clear();
234 File f (Config::dirpath() + "primregexps.cfg", File::Read); 235 File f (Config::dirpath() + "primregexps.cfg", File::Read);
235 236
236 if (!f) 237 if (!f)
237 f.open (":/data/primitive-categories.cfg", File::Read); 238 f.open (":/data/primitive-categories.cfg", File::Read);
238 239
239 if (!f) 240 if (!f)
240 critical (QObject::tr ("Failed to open primitive categories!")); 241 critical (QObject::tr ("Failed to open primitive categories!"));
241 242
242 if (f) { 243 if (f)
243 PrimitiveCategory cat; 244 { PrimitiveCategory cat;
244 245
245 for (str line : f) { 246 for (str line : f)
246 int colon; 247 { int colon;
247 248
248 if (line.length() == 0 || line[0] == '#') 249 if (line.length() == 0 || line[0] == '#')
249 continue; 250 continue;
250 251
251 if ((colon = line.indexOf (":")) == -1) { 252 if ( (colon = line.indexOf (":")) == -1)
252 if (cat.regexes.size() > 0) 253 { if (cat.regexes.size() > 0)
253 g_PrimitiveCategories << cat; 254 g_PrimitiveCategories << cat;
254 255
255 cat.regexes.clear(); 256 cat.regexes.clear();
256 cat.prims.clear(); 257 cat.prims.clear();
257 cat.setName (line); 258 cat.setName (line);
258 } else { 259 }
259 str cmd = line.left (colon); 260 else
261 { str cmd = line.left (colon);
260 PrimitiveCategory::Type type = PrimitiveCategory::Filename; 262 PrimitiveCategory::Type type = PrimitiveCategory::Filename;
261 263
262 if (cmd == "f") 264 if (cmd == "f")
263 type = PrimitiveCategory::Filename; 265 type = PrimitiveCategory::Filename;
264 elif (cmd == "t") 266 elif (cmd == "t")
265 type = PrimitiveCategory::Title; 267 type = PrimitiveCategory::Title;
266 else 268 else
267 continue; 269 continue;
268 270
269 QRegExp regex (line.mid (colon + 1)); 271 QRegExp regex (line.mid (colon + 1));
270 PrimitiveCategory::RegexEntry entry = { regex, type }; 272 PrimitiveCategory::RegexEntry entry = { regex, type };
271 cat.regexes << entry; 273 cat.regexes << entry;
272 } 274 }
273 } 275 }
274 276
275 if (cat.regexes.size() > 0) 277 if (cat.regexes.size() > 0)
276 g_PrimitiveCategories << cat; 278 g_PrimitiveCategories << cat;
277 } 279 }
278 280
279 // Add a category for unmatched primitives 281 // Add a category for unmatched primitives
280 PrimitiveCategory cat; 282 PrimitiveCategory cat;
281 cat.setName (g_Other); 283 cat.setName (g_Other);
282 g_PrimitiveCategories << cat; 284 g_PrimitiveCategories << cat;
283 } 285 }
284 286
285 // ============================================================================= 287 // =============================================================================
286 // ----------------------------------------------------------------------------- 288 // -----------------------------------------------------------------------------
287 bool primitiveLoaderBusy() { 289 bool primitiveLoaderBusy()
288 return g_primListerMutex; 290 { return g_primListerMutex;
289 } 291 }
290 292
291 // ============================================================================= 293 // =============================================================================
292 // ----------------------------------------------------------------------------- 294 // -----------------------------------------------------------------------------
293 static double radialPoint (int i, int divs, double (*func) (double)) { 295 static double radialPoint (int i, int divs, double (*func) (double))
294 return (*func) ((i * 2 * pi) / divs); 296 { return (*func) ((i * 2 * pi) / divs);
295 } 297 }
296 298
297 // ============================================================================= 299 // =============================================================================
298 // ----------------------------------------------------------------------------- 300 // -----------------------------------------------------------------------------
299 List<LDObject*> makePrimitive (PrimitiveType type, int segs, int divs, int num) { 301 List<LDObject*> makePrimitive (PrimitiveType type, int segs, int divs, int num)
300 List<LDObject*> objs; 302 { List<LDObject*> objs;
301 List<int> condLineSegs; 303 List<int> condLineSegs;
302 304
303 for (int i = 0; i < segs; ++i) { 305 for (int i = 0; i < segs; ++i)
304 double x0 = radialPoint (i, divs, cos), 306 { double x0 = radialPoint (i, divs, cos),
305 x1 = radialPoint (i + 1, divs, cos), 307 x1 = radialPoint (i + 1, divs, cos),
306 z0 = radialPoint (i, divs, sin), 308 z0 = radialPoint (i, divs, sin),
307 z1 = radialPoint (i + 1, divs, sin); 309 z1 = radialPoint (i + 1, divs, sin);
308 310
309 switch (type) { 311 switch (type)
310 case Circle: { 312 { case Circle:
311 vertex v0 (x0, 0.0f, z0), 313 { vertex v0 (x0, 0.0f, z0),
312 v1 (x1, 0.0f, z1); 314 v1 (x1, 0.0f, z1);
313 315
314 LDLine* line = new LDLine; 316 LDLine* line = new LDLine;
315 line->setVertex (0, v0); 317 line->setVertex (0, v0);
316 line->setVertex (1, v1); 318 line->setVertex (1, v1);
317 line->setColor (edgecolor); 319 line->setColor (edgecolor);
318 objs << line; 320 objs << line;
319 } 321 }
320 break; 322 break;
321 323
322 case Cylinder: 324 case Cylinder:
323 case Ring: 325 case Ring:
324 case Cone: 326 case Cone:
325 { 327 { double x2, x3, z2, z3;
326 double x2, x3, z2, z3;
327 double y0, y1, y2, y3; 328 double y0, y1, y2, y3;
328 329
329 if (type == Cylinder) { 330 if (type == Cylinder)
330 x2 = x1; 331 { x2 = x1;
331 x3 = x0; 332 x3 = x0;
332 z2 = z1; 333 z2 = z1;
333 z3 = z0; 334 z3 = z0;
334 335
335 y0 = y1 = 0.0f; 336 y0 = y1 = 0.0f;
336 y2 = y3 = 1.0f; 337 y2 = y3 = 1.0f;
337 } else { 338 }
338 x2 = x1 * (num + 1); 339 else
340 { x2 = x1 * (num + 1);
339 x3 = x0 * (num + 1); 341 x3 = x0 * (num + 1);
340 z2 = z1 * (num + 1); 342 z2 = z1 * (num + 1);
341 z3 = z0 * (num + 1); 343 z3 = z0 * (num + 1);
342 344
343 x0 *= num; 345 x0 *= num;
344 x1 *= num; 346 x1 *= num;
345 z0 *= num; 347 z0 *= num;
346 z1 *= num; 348 z1 *= num;
347 349
348 if (type == Ring) 350 if (type == Ring)
349 y0 = y1 = y2 = y3 = 0.0f; 351 y0 = y1 = y2 = y3 = 0.0f;
350 else { 352 else
351 y0 = y1 = 1.0f; 353 { y0 = y1 = 1.0f;
352 y2 = y3 = 0.0f; 354 y2 = y3 = 0.0f;
353 } 355 }
354 } 356 }
355 357
356 vertex v0 (x0, y0, z0), 358 vertex v0 (x0, y0, z0),
357 v1 (x1, y1, z1), 359 v1 (x1, y1, z1),
358 v2 (x2, y2, z2), 360 v2 (x2, y2, z2),
359 v3 (x3, y3, z3); 361 v3 (x3, y3, z3);
360 362
361 LDQuad* quad = new LDQuad; 363 LDQuad* quad = new LDQuad;
362 quad->setColor (maincolor); 364 quad->setColor (maincolor);
363 quad->setVertex (0, v0); 365 quad->setVertex (0, v0);
364 quad->setVertex (1, v1); 366 quad->setVertex (1, v1);
365 quad->setVertex (2, v2); 367 quad->setVertex (2, v2);
366 quad->setVertex (3, v3); 368 quad->setVertex (3, v3);
367 369
368 if (type == Cylinder) 370 if (type == Cylinder)
369 quad->invert(); 371 quad->invert();
370 372
371 objs << quad; 373 objs << quad;
372 374
373 if (type == Cylinder || type == Cone) 375 if (type == Cylinder || type == Cone)
374 condLineSegs << i; 376 condLineSegs << i;
375 } 377 }
376 break; 378 break;
377 379
378 case Disc: 380 case Disc:
379 case DiscNeg: 381 case DiscNeg:
380 { 382 { double x2, z2;
381 double x2, z2; 383
382
383 if (type == Disc) 384 if (type == Disc)
384 x2 = z2 = 0.0f; 385 x2 = z2 = 0.0f;
385 else { 386 else
386 x2 = (x0 >= 0.0f) ? 1.0f : -1.0f; 387 { x2 = (x0 >= 0.0f) ? 1.0f : -1.0f;
387 z2 = (z0 >= 0.0f) ? 1.0f : -1.0f; 388 z2 = (z0 >= 0.0f) ? 1.0f : -1.0f;
388 } 389 }
389 390
390 vertex v0 (x0, 0.0f, z0), 391 vertex v0 (x0, 0.0f, z0),
391 v1 (x1, 0.0f, z1), 392 v1 (x1, 0.0f, z1),
392 v2 (x2, 0.0f, z2); 393 v2 (x2, 0.0f, z2);
393 394
394 // Disc negatives need to go the other way around, otherwise 395 // Disc negatives need to go the other way around, otherwise
395 // they'll end up upside-down. 396 // they'll end up upside-down.
396 LDTriangle* seg = new LDTriangle; 397 LDTriangle* seg = new LDTriangle;
397 seg->setColor (maincolor); 398 seg->setColor (maincolor);
398 seg->setVertex (type == Disc ? 0 : 2, v0); 399 seg->setVertex (type == Disc ? 0 : 2, v0);
399 seg->setVertex (1, v1); 400 seg->setVertex (1, v1);
400 seg->setVertex (type == Disc ? 2 : 0, v2); 401 seg->setVertex (type == Disc ? 2 : 0, v2);
401 objs << seg; 402 objs << seg;
402 } 403 }
403 break; 404 break;
404 405
405 default: 406 default:
406 break; 407 break;
407 } 408 }
408 } 409 }
409 410
410 // If this is not a full circle, we need a conditional line at the other 411 // If this is not a full circle, we need a conditional line at the other
411 // end, too. 412 // end, too.
412 if (segs < divs && condLineSegs.size() != 0) 413 if (segs < divs && condLineSegs.size() != 0)
413 condLineSegs << segs; 414 condLineSegs << segs;
414 415
415 for (int i : condLineSegs) { 416 for (int i : condLineSegs)
416 vertex v0 (radialPoint (i, divs, cos), 0.0f, radialPoint (i, divs, sin)), 417 { vertex v0 (radialPoint (i, divs, cos), 0.0f, radialPoint (i, divs, sin)),
417 v1, 418 v1,
418 v2 (radialPoint (i + 1, divs, cos), 0.0f, radialPoint (i + 1, divs, sin)), 419 v2 (radialPoint (i + 1, divs, cos), 0.0f, radialPoint (i + 1, divs, sin)),
419 v3 (radialPoint (i - 1, divs, cos), 0.0f, radialPoint (i - 1, divs, sin)); 420 v3 (radialPoint (i - 1, divs, cos), 0.0f, radialPoint (i - 1, divs, sin));
420 421
421 if (type == Cylinder) 422 if (type == Cylinder)
422 v1 = vertex (v0[X], 1.0f, v0[Z]); 423 v1 = vertex (v0[X], 1.0f, v0[Z]);
423 elif (type == Cone) { 424 elif (type == Cone)
424 v1 = vertex (v0[X] * (num + 1), 0.0f, v0[Z] * (num + 1)); 425 { v1 = vertex (v0[X] * (num + 1), 0.0f, v0[Z] * (num + 1));
425 v0[X] *= num; 426 v0[X] *= num;
426 v0[Y] = 1.0f; 427 v0[Y] = 1.0f;
427 v0[Z] *= num; 428 v0[Z] *= num;
428 } 429 }
429 430
430 LDCndLine* line = new LDCndLine; 431 LDCndLine* line = new LDCndLine;
431 line->setColor (edgecolor); 432 line->setColor (edgecolor);
432 line->setVertex (0, v0); 433 line->setVertex (0, v0);
433 line->setVertex (1, v1); 434 line->setVertex (1, v1);
434 line->setVertex (2, v2); 435 line->setVertex (2, v2);
435 line->setVertex (3, v3); 436 line->setVertex (3, v3);
436 objs << line; 437 objs << line;
437 } 438 }
438 439
439 return objs; 440 return objs;
440 } 441 }
441 442
442 // ============================================================================= 443 // =============================================================================
443 // ----------------------------------------------------------------------------- 444 // -----------------------------------------------------------------------------
444 static str primitiveTypeName (PrimitiveType type) { 445 static str primitiveTypeName (PrimitiveType type)
445 // Not translated as primitives are in English. 446 { // Not translated as primitives are in English.
446 return type == Circle ? "Circle" : 447 return type == Circle ? "Circle" :
447 type == Cylinder ? "Cylinder" : 448 type == Cylinder ? "Cylinder" :
448 type == Disc ? "Disc" : 449 type == Disc ? "Disc" :
449 type == DiscNeg ? "Disc Negative" : 450 type == DiscNeg ? "Disc Negative" :
450 type == Ring ? "Ring" : "Cone"; 451 type == Ring ? "Ring" : "Cone";
451 } 452 }
452 453
453 // ============================================================================= 454 // =============================================================================
454 // ----------------------------------------------------------------------------- 455 // -----------------------------------------------------------------------------
455 str radialFileName (PrimitiveType type, int segs, int divs, int num) { 456 str radialFileName (PrimitiveType type, int segs, int divs, int num)
456 short numer = segs, 457 { short numer = segs,
457 denom = divs; 458 denom = divs;
458 459
459 // Simplify the fractional part, but the denominator must be at least 4. 460 // Simplify the fractional part, but the denominator must be at least 4.
460 simplify (numer, denom); 461 simplify (numer, denom);
461 462
462 if (denom < 4) { 463 if (denom < 4)
463 const short factor = 4 / denom; 464 { const short factor = 4 / denom;
464 465
465 numer *= factor; 466 numer *= factor;
466 denom *= factor; 467 denom *= factor;
467 } 468 }
468 469
469 // Compose some general information: prefix, fraction, root, ring number 470 // Compose some general information: prefix, fraction, root, ring number
470 str prefix = (divs == lores) ? "" : fmt ("%1/", divs); 471 str prefix = (divs == lores) ? "" : fmt ("%1/", divs);
471 str frac = fmt ("%1-%2", numer, denom); 472 str frac = fmt ("%1-%2", numer, denom);
472 str root = g_radialNameRoots[type]; 473 str root = g_radialNameRoots[type];
473 str numstr = (type == Ring || type == Cone) ? fmt ("%1", num) : ""; 474 str numstr = (type == Ring || type == Cone) ? fmt ("%1", num) : "";
474 475
475 // Truncate the root if necessary (7-16rin4.dat for instance). 476 // Truncate the root if necessary (7-16rin4.dat for instance).
476 // However, always keep the root at least 2 characters. 477 // However, always keep the root at least 2 characters.
477 int extra = (frac.length() + numstr.length() + root.length()) - 8; 478 int extra = (frac.length() + numstr.length() + root.length()) - 8;
478 root.chop (min<short> (max<short> (extra, 0), 2)); 479 root.chop (min<short> (max<short> (extra, 0), 2));
479 480
480 // Stick them all together and return the result. 481 // Stick them all together and return the result.
481 return prefix + frac + root + numstr + ".dat"; 482 return prefix + frac + root + numstr + ".dat";
482 } 483 }
483 484
484 // ============================================================================= 485 // =============================================================================
485 // ----------------------------------------------------------------------------- 486 // -----------------------------------------------------------------------------
486 void generatePrimitive() { 487 void generatePrimitive()
487 PrimitivePrompt* dlg = new PrimitivePrompt (g_win); 488 { PrimitivePrompt* dlg = new PrimitivePrompt (g_win);
488 489
489 if (!dlg->exec()) 490 if (!dlg->exec())
490 return; 491 return;
491 492
492 int segs = dlg->ui->sb_segs->value(); 493 int segs = dlg->ui->sb_segs->value();
493 int divs = dlg->ui->cb_hires->isChecked() ? hires : lores; 494 int divs = dlg->ui->cb_hires->isChecked() ? hires : lores;
494 int num = dlg->ui->sb_ringnum->value(); 495 int num = dlg->ui->sb_ringnum->value();
495 PrimitiveType type = 496 PrimitiveType type =
496 dlg->ui->rb_circle->isChecked() ? Circle : 497 dlg->ui->rb_circle->isChecked() ? Circle :
497 dlg->ui->rb_cylinder->isChecked() ? Cylinder : 498 dlg->ui->rb_cylinder->isChecked() ? Cylinder :
498 dlg->ui->rb_disc->isChecked() ? Disc : 499 dlg->ui->rb_disc->isChecked() ? Disc :
499 dlg->ui->rb_ndisc->isChecked() ? DiscNeg : 500 dlg->ui->rb_ndisc->isChecked() ? DiscNeg :
500 dlg->ui->rb_ring->isChecked() ? Ring : Cone; 501 dlg->ui->rb_ring->isChecked() ? Ring : Cone;
501 502
502 // Make the description 503 // Make the description
503 str frac = ftoa (((float) segs) / divs); 504 str frac = ftoa ( ( (float) segs) / divs);
504 str name = radialFileName (type, segs, divs, num); 505 str name = radialFileName (type, segs, divs, num);
505 str descr; 506 str descr;
506 507
507 // Ensure that there's decimals, even if they're 0. 508 // Ensure that there's decimals, even if they're 0.
508 if (frac.indexOf (".") == -1) 509 if (frac.indexOf (".") == -1)
509 frac += ".0"; 510 frac += ".0";
510 511
511 if (type == Ring || type == Cone) { 512 if (type == Ring || type == Cone)
512 str spacing = 513 { str spacing =
513 (num < 10 ) ? " " : 514 (num < 10) ? " " :
514 (num < 100) ? " " : ""; 515 (num < 100) ? " " : "";
515 516
516 descr = fmt ("%1 %2%3 x %4", primitiveTypeName (type), spacing, num, frac); 517 descr = fmt ("%1 %2%3 x %4", primitiveTypeName (type), spacing, num, frac);
517 } else 518 }
519 else
518 descr = fmt ("%1 %2", primitiveTypeName (type), frac); 520 descr = fmt ("%1 %2", primitiveTypeName (type), frac);
519 521
520 // Prepend "Hi-Res" if 48/ primitive. 522 // Prepend "Hi-Res" if 48/ primitive.
521 if (divs == hires) 523 if (divs == hires)
522 descr.insert (0, "Hi-Res "); 524 descr.insert (0, "Hi-Res ");
523 525
524 LDFile* f = new LDFile; 526 LDFile* f = new LDFile;
525 f->setName (QFileDialog::getSaveFileName (null, QObject::tr ("Save Primitive"), name)); 527 f->setName (QFileDialog::getSaveFileName (null, QObject::tr ("Save Primitive"), name));
526 528
527 f->addObjects ({ 529 f->addObjects (
528 new LDComment (descr), 530 { new LDComment (descr),
529 new LDComment (fmt ("Name: %1", name)), 531 new LDComment (fmt ("Name: %1", name)),
530 new LDComment (fmt ("Author: LDForge")), 532 new LDComment (fmt ("Author: LDForge")),
531 new LDComment (fmt ("!LDRAW_ORG Unofficial_%1Primitive", divs == hires ? "48_" : "")), 533 new LDComment (fmt ("!LDRAW_ORG Unofficial_%1Primitive", divs == hires ? "48_" : "")),
532 new LDComment (CALicense), 534 new LDComment (CALicense),
533 new LDEmpty, 535 new LDEmpty,
534 new LDBFC (LDBFC::CertifyCCW), 536 new LDBFC (LDBFC::CertifyCCW),
535 new LDEmpty, 537 new LDEmpty,
536 }); 538 });
537 539
538 f->addObjects (makePrimitive (type, segs, divs, num)); 540 f->addObjects (makePrimitive (type, segs, divs, num));
539 541
540 g_win->save (f, false); 542 g_win->save (f, false);
541 delete f; 543 delete f;
542 } 544 }
543 545
544 // ============================================================================= 546 // =============================================================================
545 // ----------------------------------------------------------------------------- 547 // -----------------------------------------------------------------------------
546 PrimitivePrompt::PrimitivePrompt (QWidget* parent, Qt::WindowFlags f) : 548 PrimitivePrompt::PrimitivePrompt (QWidget* parent, Qt::WindowFlags f) :
547 QDialog (parent, f) { 549 QDialog (parent, f)
548 550 {
551
549 ui = new Ui_MakePrimUI; 552 ui = new Ui_MakePrimUI;
550 ui->setupUi (this); 553 ui->setupUi (this);
551 connect (ui->cb_hires, SIGNAL (toggled(bool)), this, SLOT (hiResToggled (bool))); 554 connect (ui->cb_hires, SIGNAL (toggled (bool)), this, SLOT (hiResToggled (bool)));
552 } 555 }
553 556
554 // ============================================================================= 557 // =============================================================================
555 // ----------------------------------------------------------------------------- 558 // -----------------------------------------------------------------------------
556 PrimitivePrompt::~PrimitivePrompt() { 559 PrimitivePrompt::~PrimitivePrompt()
557 delete ui; 560 { delete ui;
558 } 561 }
559 562
560 // ============================================================================= 563 // =============================================================================
561 // ----------------------------------------------------------------------------- 564 // -----------------------------------------------------------------------------
562 void PrimitivePrompt::hiResToggled (bool on) { 565 void PrimitivePrompt::hiResToggled (bool on)
563 ui->sb_segs->setMaximum (on ? hires : lores); 566 { ui->sb_segs->setMaximum (on ? hires : lores);
564 567
565 // If the current value is 16 and we switch to hi-res, default the 568 // If the current value is 16 and we switch to hi-res, default the
566 // spinbox to 48. 569 // spinbox to 48.
567 if (on && ui->sb_segs->value() == lores) 570 if (on && ui->sb_segs->value() == lores)
568 ui->sb_segs->setValue (hires); 571 ui->sb_segs->setValue (hires);
569 } 572 }
570 #include "moc_primitives.cpp"

mercurial