src/primitives.cpp

changeset 500
cad8cdc42a64
parent 498
791c831c8020
child 503
bebe09014dd6
equal deleted inserted replaced
499:ebd30d9eb667 500:cad8cdc42a64
59 { // No prims.cfg, build it 59 { // No prims.cfg, build it
60 PrimitiveLister::start(); 60 PrimitiveLister::start();
61 } 61 }
62 else 62 else
63 { // Read primitives from prims.cfg 63 { // Read primitives from prims.cfg
64 for (str line : conf) 64 for (str line : conf)
65 { int space = line.indexOf (" "); 65 { int space = line.indexOf (" ");
66 66
67 if (space == -1) 67 if (space == -1)
68 continue; 68 continue;
69 69
106 106
107 assert (dir.exists()); 107 assert (dir.exists());
108 recursiveGetFilenames (dir, fnames); 108 recursiveGetFilenames (dir, fnames);
109 emit starting (fnames.size()); 109 emit starting (fnames.size());
110 110
111 for (str fname : fnames) 111 for (str fname : fnames)
112 { File f (fname, File::Read); 112 { File f (fname, File::Read);
113 113
114 Primitive info; 114 Primitive info;
115 info.name = fname.mid (baselen + 1); // make full path relative 115 info.name = fname.mid (baselen + 1); // make full path relative
116 info.name.replace ('/', '\\'); // use DOS backslashes, they're expected 116 info.name.replace ('/', '\\'); // use DOS backslashes, they're expected
131 } 131 }
132 132
133 // Save to a config file 133 // Save to a config file
134 File conf (Config::filepath ("prims.cfg"), File::Write); 134 File conf (Config::filepath ("prims.cfg"), File::Write);
135 135
136 for (Primitive & info : m_prims) 136 for (Primitive & info : m_prims)
137 fprint (conf, "%1 %2\n", info.name, info.title); 137 fprint (conf, "%1 %2\n", info.name, info.title);
138 138
139 conf.close(); 139 conf.close();
140 140
141 g_primListerMutex = true; 141 g_primListerMutex = true;
164 } 164 }
165 165
166 // ============================================================================= 166 // =============================================================================
167 // ----------------------------------------------------------------------------- 167 // -----------------------------------------------------------------------------
168 static PrimitiveCategory* findCategory (str name) 168 static PrimitiveCategory* findCategory (str name)
169 { for (PrimitiveCategory& cat : g_PrimitiveCategories) 169 { for (PrimitiveCategory & cat : g_PrimitiveCategories)
170 if (cat.name() == name) 170 if (cat.name() == name)
171 return &cat; 171 return &cat;
172 172
173 return null; 173 return null;
174 } 174 }
186 PrimitiveCategory cat; 186 PrimitiveCategory cat;
187 cat.setName (g_Other); 187 cat.setName (g_Other);
188 unmatched = & (g_PrimitiveCategories << cat); 188 unmatched = & (g_PrimitiveCategories << cat);
189 } 189 }
190 190
191 for (Primitive& prim : g_primitives) 191 for (Primitive & prim : g_primitives)
192 { bool matched = false; 192 { bool matched = false;
193 prim.cat = null; 193 prim.cat = null;
194 194
195 // 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,
196 // 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.
197 for (PrimitiveCategory & cat : g_PrimitiveCategories) 197 for (PrimitiveCategory & cat : g_PrimitiveCategories)
198 { for (PrimitiveCategory::RegexEntry & entry : cat.regexes) 198 { for (PrimitiveCategory::RegexEntry & entry : cat.regexes)
199 { switch (entry.type) 199 { switch (entry.type)
200 { case PrimitiveCategory::Filename: 200 { case PrimitiveCategory::Filename:
201 // f-regex, check against filename 201 // f-regex, check against filename
202 matched = entry.regex.exactMatch (prim.name); 202 matched = entry.regex.exactMatch (prim.name);
203 break; 203 break;
204 204
205 case PrimitiveCategory::Title: 205 case PrimitiveCategory::Title:
206 // t-regex, check against title 206 // t-regex, check against title
207 matched = entry.regex.exactMatch (prim.title); 207 matched = entry.regex.exactMatch (prim.title);
208 break; 208 break;
209 } 209 }
210 210
211 if (matched) 211 if (matched)
212 { prim.cat = &cat; 212 { prim.cat = &cat;
213 break; 213 break;
241 critical (QObject::tr ("Failed to open primitive categories!")); 241 critical (QObject::tr ("Failed to open primitive categories!"));
242 242
243 if (f) 243 if (f)
244 { PrimitiveCategory cat; 244 { PrimitiveCategory cat;
245 245
246 for (str line : f) 246 for (str line : f)
247 { int colon; 247 { int colon;
248 248
249 if (line.length() == 0 || line[0] == '#') 249 if (line.length() == 0 || line[0] == '#')
250 continue; 250 continue;
251 251
261 { str cmd = line.left (colon); 261 { str cmd = line.left (colon);
262 PrimitiveCategory::Type type = PrimitiveCategory::Filename; 262 PrimitiveCategory::Type type = PrimitiveCategory::Filename;
263 263
264 if (cmd == "f") 264 if (cmd == "f")
265 type = PrimitiveCategory::Filename; 265 type = PrimitiveCategory::Filename;
266
266 elif (cmd == "t") 267 elif (cmd == "t")
267 type = PrimitiveCategory::Title; 268 type = PrimitiveCategory::Title;
268 else 269 else
269 continue; 270 continue;
270 271
271 QRegExp regex (line.mid (colon + 1)); 272 QRegExp regex (line.mid (colon + 1));
272 PrimitiveCategory::RegexEntry entry = { regex, type }; 273 PrimitiveCategory::RegexEntry entry = { regex, type };
291 } 292 }
292 293
293 // ============================================================================= 294 // =============================================================================
294 // ----------------------------------------------------------------------------- 295 // -----------------------------------------------------------------------------
295 static double radialPoint (int i, int divs, double (*func) (double)) 296 static double radialPoint (int i, int divs, double (*func) (double))
296 { return (*func) ((i * 2 * pi) / divs); 297 { return (*func) ( (i * 2 * pi) / divs);
298 }
299
300 // =============================================================================
301 // -----------------------------------------------------------------------------
302 void makeCircle (int segs, int divs, double radius, List<QLineF>& lines)
303 { for (int i = 0; i < segs; ++i)
304 { double x0 = radius * radialPoint (i, divs, cos),
305 x1 = radius * radialPoint (i + 1, divs, cos),
306 z0 = radius * radialPoint (i, divs, sin),
307 z1 = radius * radialPoint (i + 1, divs, sin);
308
309 lines << QLineF (QPointF (x0, z0), QPointF (x1, z1));
310 }
297 } 311 }
298 312
299 // ============================================================================= 313 // =============================================================================
300 // ----------------------------------------------------------------------------- 314 // -----------------------------------------------------------------------------
301 List<LDObject*> makePrimitive (PrimitiveType type, int segs, int divs, int num) 315 List<LDObject*> makePrimitive (PrimitiveType type, int segs, int divs, int num)
302 { List<LDObject*> objs; 316 { List<LDObject*> objs;
303 List<int> condLineSegs; 317 List<int> condLineSegs;
318 List<QLineF> circle;
319
320 makeCircle (segs, divs, 1, circle);
304 321
305 for (int i = 0; i < segs; ++i) 322 for (int i = 0; i < segs; ++i)
306 { double x0 = radialPoint (i, divs, cos), 323 { double x0 = circle[i].x1(),
307 x1 = radialPoint (i + 1, divs, cos), 324 x1 = circle[i].x2(),
308 z0 = radialPoint (i, divs, sin), 325 z0 = circle[i].y1(),
309 z1 = radialPoint (i + 1, divs, sin); 326 z1 = circle[i].y2();
310 327
311 switch (type) 328 switch (type)
312 { case Circle: 329 { case Circle:
313 { vertex v0 (x0, 0.0f, z0), 330 { vertex v0 (x0, 0.0f, z0),
314 v1 (x1, 0.0f, z1); 331 v1 (x1, 0.0f, z1);
316 LDLine* line = new LDLine; 333 LDLine* line = new LDLine;
317 line->setVertex (0, v0); 334 line->setVertex (0, v0);
318 line->setVertex (1, v1); 335 line->setVertex (1, v1);
319 line->setColor (edgecolor); 336 line->setColor (edgecolor);
320 objs << line; 337 objs << line;
321 } 338 } break;
322 break;
323 339
324 case Cylinder: 340 case Cylinder:
325 case Ring: 341 case Ring:
326 case Cone: 342 case Cone:
327 { double x2, x3, z2, z3; 343 { double x2, x3, z2, z3;
372 388
373 objs << quad; 389 objs << quad;
374 390
375 if (type == Cylinder || type == Cone) 391 if (type == Cylinder || type == Cone)
376 condLineSegs << i; 392 condLineSegs << i;
377 } 393 } break;
378 break;
379 394
380 case Disc: 395 case Disc:
381 case DiscNeg: 396 case DiscNeg:
382 { double x2, z2; 397 { double x2, z2;
383 398
398 seg->setColor (maincolor); 413 seg->setColor (maincolor);
399 seg->setVertex (type == Disc ? 0 : 2, v0); 414 seg->setVertex (type == Disc ? 0 : 2, v0);
400 seg->setVertex (1, v1); 415 seg->setVertex (1, v1);
401 seg->setVertex (type == Disc ? 2 : 0, v2); 416 seg->setVertex (type == Disc ? 2 : 0, v2);
402 objs << seg; 417 objs << seg;
403 } 418 } break;
404 break;
405
406 default:
407 break;
408 } 419 }
409 } 420 }
410 421
411 // If this is not a full circle, we need a conditional line at the other 422 // If this is not a full circle, we need a conditional line at the other
412 // end, too. 423 // end, too.
413 if (segs < divs && condLineSegs.size() != 0) 424 if (segs < divs && condLineSegs.size() != 0)
414 condLineSegs << segs; 425 condLineSegs << segs;
415 426
416 for (int i : condLineSegs) 427 for (int i : condLineSegs)
417 { vertex v0 (radialPoint (i, divs, cos), 0.0f, radialPoint (i, divs, sin)), 428 { vertex v0 (radialPoint (i, divs, cos), 0.0f, radialPoint (i, divs, sin)),
418 v1, 429 v1,
419 v2 (radialPoint (i + 1, divs, cos), 0.0f, radialPoint (i + 1, divs, sin)), 430 v2 (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)); 431 v3 (radialPoint (i - 1, divs, cos), 0.0f, radialPoint (i - 1, divs, sin));
421 432
422 if (type == Cylinder) 433 if (type == Cylinder)
423 v1 = vertex (v0[X], 1.0f, v0[Z]); 434 v1 = vertex (v0[X], 1.0f, v0[Z]);
435
424 elif (type == Cone) 436 elif (type == Cone)
425 { v1 = vertex (v0[X] * (num + 1), 0.0f, v0[Z] * (num + 1)); 437 { v1 = vertex (v0[X] * (num + 1), 0.0f, v0[Z] * (num + 1));
426 v0[X] *= num; 438 v0[X] *= num;
427 v0[Y] = 1.0f; 439 v0[Y] = 1.0f;
428 v0[Z] *= num; 440 v0[Z] *= num;
452 } 464 }
453 465
454 // ============================================================================= 466 // =============================================================================
455 // ----------------------------------------------------------------------------- 467 // -----------------------------------------------------------------------------
456 str radialFileName (PrimitiveType type, int segs, int divs, int num) 468 str radialFileName (PrimitiveType type, int segs, int divs, int num)
457 { short numer = segs, 469 { int numer = segs,
458 denom = divs; 470 denom = divs;
459 471
460 // Simplify the fractional part, but the denominator must be at least 4. 472 // Simplify the fractional part, but the denominator must be at least 4.
461 simplify (numer, denom); 473 simplify (numer, denom);
462 474
463 if (denom < 4) 475 if (denom < 4)
464 { const short factor = 4 / denom; 476 { const int factor = 4 / denom;
465
466 numer *= factor; 477 numer *= factor;
467 denom *= factor; 478 denom *= factor;
468 } 479 }
469 480
470 // Compose some general information: prefix, fraction, root, ring number 481 // Compose some general information: prefix, fraction, root, ring number
474 str numstr = (type == Ring || type == Cone) ? fmt ("%1", num) : ""; 485 str numstr = (type == Ring || type == Cone) ? fmt ("%1", num) : "";
475 486
476 // Truncate the root if necessary (7-16rin4.dat for instance). 487 // Truncate the root if necessary (7-16rin4.dat for instance).
477 // However, always keep the root at least 2 characters. 488 // However, always keep the root at least 2 characters.
478 int extra = (frac.length() + numstr.length() + root.length()) - 8; 489 int extra = (frac.length() + numstr.length() + root.length()) - 8;
479 root.chop (min<short> (max<short> (extra, 0), 2)); 490 root.chop (clamp (extra, 0, 2));
480 491
481 // Stick them all together and return the result. 492 // Stick them all together and return the result.
482 return prefix + frac + root + numstr + ".dat"; 493 return prefix + frac + root + numstr + ".dat";
483 } 494 }
484 495
485 // ============================================================================= 496 // =============================================================================
486 // ----------------------------------------------------------------------------- 497 // -----------------------------------------------------------------------------
487 LDFile* generatePrimitive (PrimitiveType type, int segs, int divs, int num) 498 LDFile* generatePrimitive (PrimitiveType type, int segs, int divs, int num)
488 { // Make the description 499 { // Make the description
489 str frac = ftoa (((float) segs) / divs); 500 str frac = ftoa ( ( (float) segs) / divs);
490 str name = radialFileName (type, segs, divs, num); 501 str name = radialFileName (type, segs, divs, num);
491 str descr; 502 str descr;
492 503
493 // Ensure that there's decimals, even if they're 0. 504 // Ensure that there's decimals, even if they're 0.
494 if (frac.indexOf (".") == -1) 505 if (frac.indexOf (".") == -1)
495 frac += ".0"; 506 frac += ".0";
496 507
497 if (type == Ring || type == Cone) 508 if (type == Ring || type == Cone)
498 { str spacing = 509 { str spacing =
499 (num < 10) ? " " : 510 (num < 10) ? " " :
500 (num < 100) ? " " : ""; 511 (num < 100) ? " " : "";
501 512
502 descr = fmt ("%1 %2%3 x %4", primitiveTypeName (type), spacing, num, frac); 513 descr = fmt ("%1 %2%3 x %4", primitiveTypeName (type), spacing, num, frac);
503 } 514 }
504 else 515 else
505 descr = fmt ("%1 %2", primitiveTypeName (type), frac); 516 descr = fmt ("%1 %2", primitiveTypeName (type), frac);
506 517
507 // Prepend "Hi-Res" if 48/ primitive. 518 // Prepend "Hi-Res" if 48/ primitive.
508 if (divs == hires) 519 if (divs == hires)
509 descr.insert (0, "Hi-Res "); 520 descr.insert (0, "Hi-Res ");
510 521
511 LDFile* f = new LDFile; 522 LDFile* f = new LDFile;
512 f->setName (QFileDialog::getSaveFileName (null, QObject::tr ("Save Primitive"), name)); 523 f->setDefaultName (name);
513 524
514 f->addObjects ( 525 f->addObjects (
515 { new LDComment (descr), 526 { new LDComment (descr),
516 new LDComment (fmt ("Name: %1", name)), 527 new LDComment (fmt ("Name: %1", name)),
517 new LDComment (fmt ("Author: LDForge")), 528 new LDComment (fmt ("Author: LDForge")),
518 new LDComment (fmt ("!LDRAW_ORG Unofficial_%1Primitive", divs == hires ? "48_" : "")), 529 new LDComment (fmt ("!LDRAW_ORG Unofficial_%1Primitive", divs == hires ? "48_" : "")),
519 new LDComment (CALicense), 530 new LDComment (CALicense),
520 new LDEmpty, 531 new LDEmpty,
521 new LDBFC (LDBFC::CertifyCCW), 532 new LDBFC (LDBFC::CertifyCCW),
522 new LDEmpty, 533 new LDEmpty,
523 }); 534 });
524 535
525 f->addObjects (makePrimitive (type, segs, divs, num)); 536 f->addObjects (makePrimitive (type, segs, divs, num));
526 return f; 537 return f;
527 } 538 }
528 539
529 // ============================================================================= 540 // =============================================================================
530 // ----------------------------------------------------------------------------- 541 // -----------------------------------------------------------------------------
531 LDFile* getPrimitive (PrimitiveType type, int segs, int divs, int num) 542 LDFile* getPrimitive (PrimitiveType type, int segs, int divs, int num)
532 { str name = radialFileName (type, segs, divs, num); 543 { str name = radialFileName (type, segs, divs, num);
533 LDFile* f = getFile (name); 544 LDFile* f = getFile (name);
545
534 if (f != null) 546 if (f != null)
535 return f; 547 return f;
536 548
537 return generatePrimitive (type, segs, divs, num); 549 return generatePrimitive (type, segs, divs, num);
538 } 550 }
539 551
540 // ============================================================================= 552 // =============================================================================
541 // ----------------------------------------------------------------------------- 553 // -----------------------------------------------------------------------------
542 PrimitivePrompt::PrimitivePrompt (QWidget* parent, Qt::WindowFlags f) : 554 PrimitivePrompt::PrimitivePrompt (QWidget* parent, Qt::WindowFlags f) :
543 QDialog (parent, f) 555 QDialog (parent, f)
544 { 556 { ui = new Ui_MakePrimUI;
545
546 ui = new Ui_MakePrimUI;
547 ui->setupUi (this); 557 ui->setupUi (this);
548 connect (ui->cb_hires, SIGNAL (toggled (bool)), this, SLOT (hiResToggled (bool))); 558 connect (ui->cb_hires, SIGNAL (toggled (bool)), this, SLOT (hiResToggled (bool)));
549 } 559 }
550 560
551 // ============================================================================= 561 // =============================================================================
556 566
557 // ============================================================================= 567 // =============================================================================
558 // ----------------------------------------------------------------------------- 568 // -----------------------------------------------------------------------------
559 void PrimitivePrompt::hiResToggled (bool on) 569 void PrimitivePrompt::hiResToggled (bool on)
560 { ui->sb_segs->setMaximum (on ? hires : lores); 570 { ui->sb_segs->setMaximum (on ? hires : lores);
561 571
562 // If the current value is 16 and we switch to hi-res, default the 572 // If the current value is 16 and we switch to hi-res, default the
563 // spinbox to 48. 573 // spinbox to 48.
564 if (on && ui->sb_segs->value() == lores) 574 if (on && ui->sb_segs->value() == lores)
565 ui->sb_segs->setValue (hires); 575 ui->sb_segs->setValue (hires);
566 } 576 }
567 577
568 // ============================================================================= 578 // =============================================================================
569 // ----------------------------------------------------------------------------- 579 // -----------------------------------------------------------------------------
570 DEFINE_ACTION (MakePrimitive, 0) 580 DEFINE_ACTION (MakePrimitive, 0)
571 { PrimitivePrompt* dlg = new PrimitivePrompt (g_win); 581 { PrimitivePrompt* dlg = new PrimitivePrompt (g_win);
572 582
573 if (!dlg->exec()) 583 if (!dlg->exec())
574 return; 584 return;
575 585
576 int segs = dlg->ui->sb_segs->value(); 586 int segs = dlg->ui->sb_segs->value();
577 int divs = dlg->ui->cb_hires->isChecked() ? hires : lores; 587 int divs = dlg->ui->cb_hires->isChecked() ? hires : lores;
578 int num = dlg->ui->sb_ringnum->value(); 588 int num = dlg->ui->sb_ringnum->value();
579 PrimitiveType type = 589 PrimitiveType type =
580 dlg->ui->rb_circle->isChecked() ? Circle : 590 dlg->ui->rb_circle->isChecked() ? Circle :
581 dlg->ui->rb_cylinder->isChecked() ? Cylinder : 591 dlg->ui->rb_cylinder->isChecked() ? Cylinder :
582 dlg->ui->rb_disc->isChecked() ? Disc : 592 dlg->ui->rb_disc->isChecked() ? Disc :
583 dlg->ui->rb_ndisc->isChecked() ? DiscNeg : 593 dlg->ui->rb_ndisc->isChecked() ? DiscNeg :
584 dlg->ui->rb_ring->isChecked() ? Ring : Cone; 594 dlg->ui->rb_ring->isChecked() ? Ring : Cone;
585 595
586 LDFile* f = generatePrimitive (type, segs, divs, num); 596 LDFile* f = generatePrimitive (type, segs, divs, num);
587 597
588 g_win->save (f, false); 598 g_win->save (f, false);
589 delete f; 599 delete f;
590 } 600 }

mercurial