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; |
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); |
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; |
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 } |