496 // them and wrap them into a ComboHistory, which allows storage of multiple |
496 // them and wrap them into a ComboHistory, which allows storage of multiple |
497 // simultaneous edits with different type. This is what we ultimately store into |
497 // simultaneous edits with different type. This is what we ultimately store into |
498 // History. |
498 // History. |
499 // ============================================================================= |
499 // ============================================================================= |
500 MAKE_ACTION (invert, "Invert", "invert", "Reverse the winding of given objects.", CTRL_SHIFT (W)) { |
500 MAKE_ACTION (invert, "Invert", "invert", "Reverse the winding of given objects.", CTRL_SHIFT (W)) { |
501 std::vector<LDObject*> paSelection = g_win->sel (); |
501 std::vector<LDObject*> sel = g_win->sel (); |
502 std::vector<HistoryEntry*> paHistory; |
502 ComboHistory* history = new ComboHistory; |
503 |
503 |
504 for (LDObject* obj : paSelection) { |
504 for (LDObject* obj : sel) { |
505 // For the objects we end up editing, we store information into these |
505 *history << obj->invert (); |
506 // variables and we store them into an EditHistory after the switch |
506 g_win->R ()->compileObject (obj); |
507 // block. Subfile and radial management is stored into the history |
507 } |
508 // list immediately. |
508 |
509 ulong ulHistoryIndex = obj->getIndex (g_curfile); |
509 printf ("%lu entries\n", history->numEntries ()); |
510 LDObject* pOldCopy, *pNewCopy; |
510 if (history->numEntries () > 0) { |
511 bool bEdited = false; |
511 History::addEntry (history); |
512 |
512 g_win->buildObjList (); |
513 switch (obj->getType ()) { |
513 } else |
514 case LDObject::Line: |
514 delete history; |
515 case LDObject::CondLine: |
|
516 { |
|
517 // For lines, we swap the vertices. I don't think that a |
|
518 // cond-line's control points need to be swapped, do they? |
|
519 LDLine* pLine = static_cast<LDLine*> (obj); |
|
520 vertex vTemp = pLine->coords[0]; |
|
521 |
|
522 pOldCopy = pLine->clone (); |
|
523 pLine->coords[0] = pLine->coords[1]; |
|
524 pLine->coords[1] = vTemp; |
|
525 pNewCopy = pLine->clone (); |
|
526 bEdited = true; |
|
527 } |
|
528 break; |
|
529 |
|
530 case LDObject::Triangle: |
|
531 { |
|
532 // Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1. |
|
533 // Thus, we swap 1 and 2. |
|
534 LDTriangle* pTri = static_cast<LDTriangle*> (obj); |
|
535 vertex vTemp = pTri->coords[1]; |
|
536 |
|
537 pOldCopy = pTri->clone (); |
|
538 pTri->coords[1] = pTri->coords[2]; |
|
539 pTri->coords[2] = vTemp; |
|
540 pNewCopy = pTri->clone (); |
|
541 bEdited = true; |
|
542 } |
|
543 break; |
|
544 |
|
545 case LDObject::Quad: |
|
546 { |
|
547 // Quad: 0 -> 1 -> 2 -> 3 |
|
548 // rev: 0 -> 3 -> 2 -> 1 |
|
549 // Thus, we swap 1 and 3. |
|
550 LDQuad* pQuad = static_cast<LDQuad*> (obj); |
|
551 vertex vTemp = pQuad->coords[1]; |
|
552 |
|
553 pOldCopy = pQuad->clone (); |
|
554 pQuad->coords[1] = pQuad->coords[3]; |
|
555 pQuad->coords[3] = vTemp; |
|
556 pNewCopy = pQuad->clone (); |
|
557 bEdited = true; |
|
558 } |
|
559 break; |
|
560 |
|
561 case LDObject::Subfile: |
|
562 case LDObject::Radial: |
|
563 { |
|
564 // Subfiles and radials are inverted when they're prefixed with |
|
565 // a BFC INVERTNEXT statement. Thus we need to toggle this status. |
|
566 // For flat primitives it's sufficient that the determinant is |
|
567 // flipped but I don't have a method for checking flatness yet. |
|
568 // Food for thought... |
|
569 |
|
570 bool inverted = false; |
|
571 ulong idx = obj->getIndex (g_curfile); |
|
572 |
|
573 if (idx > 0) { |
|
574 LDObject* prev = g_curfile->object (idx - 1); |
|
575 LDBFC* bfc = dynamic_cast<LDBFC*> (prev); |
|
576 |
|
577 if (bfc && bfc->type == LDBFC::InvertNext) { |
|
578 // Object is prefixed with an invertnext, thus remove it. |
|
579 paHistory.push_back (new DelHistory ({idx - 1}, {bfc->clone ()})); |
|
580 |
|
581 inverted = true; |
|
582 g_curfile->forgetObject (bfc); |
|
583 delete bfc; |
|
584 } |
|
585 } |
|
586 |
|
587 if (!inverted) { |
|
588 // Not inverted, thus prefix it with a new invertnext. |
|
589 LDBFC* bfc = new LDBFC (LDBFC::InvertNext); |
|
590 g_curfile->insertObj (idx, bfc); |
|
591 |
|
592 paHistory.push_back (new AddHistory ({idx}, {bfc->clone ()})); |
|
593 } |
|
594 } |
|
595 break; |
|
596 |
|
597 default: |
|
598 break; |
|
599 } |
|
600 |
|
601 // If we edited this object, store the EditHistory based on collected |
|
602 // information now. |
|
603 if (bEdited == true) |
|
604 paHistory.push_back (new EditHistory ({ulHistoryIndex}, {pOldCopy}, {pNewCopy})); |
|
605 } |
|
606 |
|
607 if (paHistory.size () > 0) { |
|
608 History::addEntry (new ComboHistory (paHistory)); |
|
609 g_win->refresh (); |
|
610 } |
|
611 } |
515 } |
612 |
516 |
613 // ============================================================================= |
517 // ============================================================================= |
614 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
518 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
615 // ============================================================================= |
519 // ============================================================================= |
753 } |
657 } |
754 |
658 |
755 // ========================================================================================================================================= |
659 // ========================================================================================================================================= |
756 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
660 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
757 // ========================================================================================================================================= |
661 // ========================================================================================================================================= |
|
662 static CheckBoxGroup<Axis>* makeAxesBox () { |
|
663 CheckBoxGroup<Axis>* cbg_axes = new CheckBoxGroup<Axis> ("Axes", Qt::Horizontal); |
|
664 cbg_axes->addCheckBox ("X", X); |
|
665 cbg_axes->addCheckBox ("Y", Y); |
|
666 cbg_axes->addCheckBox ("Z", Z); |
|
667 return cbg_axes; |
|
668 } |
|
669 |
758 class ReplaceCoordsDialog : public QDialog { |
670 class ReplaceCoordsDialog : public QDialog { |
759 public: |
671 public: |
760 explicit ReplaceCoordsDialog (QWidget* parent = null, Qt::WindowFlags f = 0) : QDialog (parent, f) { |
672 explicit ReplaceCoordsDialog (QWidget* parent = null, Qt::WindowFlags f = 0) : QDialog (parent, f) { |
761 cbg_axes = new CheckBoxGroup<Axis> ("Axes", Qt::Horizontal); |
673 cbg_axes = makeAxesBox (); |
762 cbg_axes->addCheckBox ("X", X); |
|
763 cbg_axes->addCheckBox ("Y", Y); |
|
764 cbg_axes->addCheckBox ("Z", Z); |
|
765 |
674 |
766 lb_search = new QLabel ("Search:"); |
675 lb_search = new QLabel ("Search:"); |
767 lb_replacement = new QLabel ("Replacement:"); |
676 lb_replacement = new QLabel ("Replacement:"); |
768 |
677 |
769 dsb_search = new QDoubleSpinBox; |
678 dsb_search = new QDoubleSpinBox; |
811 vector<Axis> sel = dlg.axes (); |
720 vector<Axis> sel = dlg.axes (); |
812 |
721 |
813 EditHistory* history = new EditHistory; |
722 EditHistory* history = new EditHistory; |
814 |
723 |
815 for (LDObject* obj : g_win->sel ()) { |
724 for (LDObject* obj : g_win->sel ()) { |
816 bool altered = false; |
|
817 LDObject* copy = obj->clone (); |
725 LDObject* copy = obj->clone (); |
818 |
726 |
819 for (short i = 0; i < obj->vertices (); ++i) |
727 for (short i = 0; i < obj->vertices (); ++i) |
820 for (Axis ax : sel) { |
728 for (Axis ax : sel) { |
821 if (obj->coords[i][ax] == search) { |
729 if (obj->coords[i][ax] == search) { |
822 obj->coords[i][ax] = replacement; |
730 obj->coords[i][ax] = replacement; |
823 altered = true; |
|
824 } |
731 } |
825 } |
732 } |
826 |
733 |
|
734 history->addEntry (copy, obj, obj->getIndex (g_curfile)); |
|
735 |
|
736 delete copy; |
|
737 } |
|
738 |
|
739 History::addEntry (history); |
|
740 g_win->fullRefresh (); |
|
741 } |
|
742 |
|
743 // ========================================================================================================================================= |
|
744 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
745 // ========================================================================================================================================= |
|
746 class FlipDialog : public QDialog { |
|
747 public: |
|
748 explicit FlipDialog (QWidget* parent = 0, Qt::WindowFlags f = 0) : QDialog (parent, f) { |
|
749 cbg_axes = makeAxesBox (); |
|
750 |
|
751 QVBoxLayout* layout = new QVBoxLayout; |
|
752 layout->addWidget (cbg_axes); |
|
753 layout->addWidget (makeButtonBox (*this)); |
|
754 setLayout (layout); |
|
755 } |
|
756 |
|
757 vector<Axis> axes () { return cbg_axes->checkedValues (); } |
|
758 |
|
759 private: |
|
760 CheckBoxGroup<Axis>* cbg_axes; |
|
761 }; |
|
762 |
|
763 MAKE_ACTION (flip, "Flip", "flip", "Flip coordinates", CTRL_SHIFT (F)) { |
|
764 FlipDialog dlg; |
|
765 |
|
766 if (!dlg.exec ()) |
|
767 return; |
|
768 |
|
769 EditHistory* history = new EditHistory; |
|
770 vector<Axis> sel = dlg.axes (); |
|
771 |
|
772 for (LDObject* obj : g_win->sel ()) { |
|
773 bool altered = false; |
|
774 LDObject* copy = obj->clone (); |
|
775 |
|
776 for (short i = 0; i < obj->vertices (); ++i) |
|
777 for (Axis ax : sel) { |
|
778 obj->coords[i][ax] *= -1; |
|
779 altered = true; |
|
780 } |
|
781 |
827 if (altered) |
782 if (altered) |
828 history->addEntry (copy, obj, obj->getIndex (g_curfile)); |
783 history->addEntry (copy, obj, obj->getIndex (g_curfile)); |
829 |
784 |
830 delete copy; |
785 delete copy; |
831 } |
786 } |
832 |
787 |
833 History::addEntry (history); |
788 History::addEntry (history); |
834 g_win->refresh (); |
789 g_win->fullRefresh (); |
835 } |
790 } |