Added winding reversal, though undoing it isn't quite ready yet.

Sat, 20 Apr 2013 04:18:22 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Sat, 20 Apr 2013 04:18:22 +0300
changeset 118
649110bb36a8
parent 117
7eb7a43a511b
child 119
b93a64f5ee67

Added winding reversal, though undoing it isn't quite ready yet.

gui.cpp file | annotate | diff | comparison | revisions
gui_editactions.cpp file | annotate | diff | comparison | revisions
history.cpp file | annotate | diff | comparison | revisions
history.h file | annotate | diff | comparison | revisions
--- a/gui.cpp	Fri Apr 19 15:21:46 2013 +0300
+++ b/gui.cpp	Sat Apr 20 04:18:22 2013 +0300
@@ -69,6 +69,7 @@
 EXTERN_ACTION (moveXPos)
 EXTERN_ACTION (moveYPos)
 EXTERN_ACTION (moveZPos)
+EXTERN_ACTION (invert)
 
 #ifndef RELEASE
 EXTERN_ACTION (addTestQuad)
@@ -118,6 +119,8 @@
 	createMenus ();
 	createToolbars ();
 	
+	setStatusBar (new QStatusBar);
+	
 	slot_selectionChanged ();
 	
 	setWindowIcon (QIcon ("icons/ldforge.png"));
@@ -198,6 +201,7 @@
 	qEditMenu->addAction (ACTION_NAME (selectByType));		// Select by Type
 	qEditMenu->addSeparator ();								// -----
 	qEditMenu->addAction (ACTION_NAME (setColor));			// Set Color
+	qEditMenu->addAction (ACTION_NAME (invert));			// Invert
 	qEditMenu->addAction (ACTION_NAME (inlineContents));	// Inline
 	qEditMenu->addAction (ACTION_NAME (deepInline));		// Deep Inline
 	qEditMenu->addAction (ACTION_NAME (splitQuads));		// Split Quads
@@ -323,6 +327,7 @@
 	g_ToolBarArea = Qt::LeftToolBarArea;
 	initSingleToolBar ("Objects");
 	ADD_TOOLBAR_ITEM (setColor)
+	ADD_TOOLBAR_ITEM (invert)
 	ADD_TOOLBAR_ITEM (inlineContents)
 	ADD_TOOLBAR_ITEM (deepInline)
 	ADD_TOOLBAR_ITEM (splitQuads)
--- a/gui_editactions.cpp	Fri Apr 19 15:21:46 2013 +0300
+++ b/gui_editactions.cpp	Sat Apr 20 04:18:22 2013 +0300
@@ -435,4 +435,129 @@
 
 ACTION (moveZPos, "Move +Z", "move-z-pos", "Move selected objects positive on the Z axis.", KEY (Up)) {
 	doMoveObjects ({0, 0, 1});
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// invert - reverse winding
+// 
+// NOTE: History management gets a little tricky here. For lines, cond-lines,
+// triangles and quads, we edit the object, thus we record an EditHistory. For
+// subfiles and radials we create or delete invertnext objects. Since we have
+// multiple actions of different types, we store a history entry for each of
+// them and wrap them into a ComboHistory, which allows storage of multiple
+// simultaneous edits with different type. This is what we ultimately store into
+// History.
+// =============================================================================
+ACTION (invert, "Invert", "invert", "Reverse the winding of given objects.", CTRL_SHIFT (W)) {
+	std::vector<LDObject*> paSelection = g_ForgeWindow->selection ();
+	std::vector<HistoryEntry*> paHistory;
+	
+	for (LDObject* obj : paSelection) {
+		// For the objects we end up editing, we store information into these
+		// variables and we store them into an EditHistory after the switch
+		// block. Subfile and radial management is stored into the history
+		// list immediately.
+		ulong ulHistoryIndex = obj->getIndex (g_CurrentFile);
+		LDObject* pOldCopy, *pNewCopy;
+		bool bEdited = false;
+		
+		switch (obj->getType ()) {
+		case OBJ_Line:
+		case OBJ_CondLine:
+			{
+				// For lines, we swap the vertices. I don't think that a
+				// cond-line's control points need to be swapped, do they?
+				LDLine* pLine = static_cast<LDLine*> (obj);
+				vertex vTemp = pLine->vaCoords[0];
+				
+				pOldCopy = pLine->clone ();
+				pLine->vaCoords[0] = pLine->vaCoords[1];
+				pLine->vaCoords[1] = vTemp;
+				pNewCopy = pLine->clone ();
+				bEdited = true;
+			}
+			break;
+		
+		case OBJ_Triangle:
+			{
+				// Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1.
+				// Thus, we swap 1 and 2.
+				LDTriangle* pTri = static_cast<LDTriangle*> (obj);
+				vertex vTemp = pTri->vaCoords[1];
+				
+				pOldCopy = pTri->clone ();
+				pTri->vaCoords[1] = pTri->vaCoords[2];
+				pTri->vaCoords[2] = vTemp;
+				pNewCopy = pTri->clone ();
+				bEdited = true;
+			}
+			break;
+		
+		case OBJ_Quad:
+			{
+				// Quad: 0 -> 1 -> 2 -> 3
+				// rev:  0 -> 3 -> 2 -> 1
+				// Thus, we swap 1 and 3.
+				LDQuad* pQuad = static_cast<LDQuad*> (obj);
+				vertex vTemp = pQuad->vaCoords[1];
+				
+				pOldCopy = pQuad->clone ();
+				pQuad->vaCoords[1] = pQuad->vaCoords[3];
+				pQuad->vaCoords[3] = vTemp;
+				pNewCopy = pQuad->clone ();
+				bEdited = true;
+			}
+			break;
+		
+		case OBJ_Subfile:
+		case OBJ_Radial:
+			{
+				// Subfiles and radials are inverted when they're prefixed with
+				// a BFC INVERTNEXT statement. Thus we need to toggle this status.
+				// For flat primitives it's sufficient that the determinant is
+				// flipped but I don't have a method for checking flatness yet.
+				// Food for thought...
+				
+				bool inverted = false;
+				ulong idx = obj->getIndex (g_CurrentFile);
+				
+				if (idx > 0) {
+					LDObject* prev = g_CurrentFile->object (idx - 1);
+					LDBFC* bfc = dynamic_cast<LDBFC*> (prev);
+					
+					if (bfc && bfc->eStatement == LDBFC::InvertNext) {
+						// Object is prefixed with an invertnext, thus remove it.
+						paHistory.push_back (new DelHistory ({idx - 1}, {bfc->clone ()}));
+						
+						inverted = true;
+						g_CurrentFile->forgetObject (bfc);
+						delete bfc;
+					}
+				}
+				
+				if (!inverted) {
+					// Not inverted, thus prefix it with a new invertnext.
+					LDBFC* bfc = new LDBFC (LDBFC::InvertNext);
+					g_CurrentFile->objects.insert (g_CurrentFile->objects.begin () + idx, bfc);
+					
+					paHistory.push_back (new AddHistory ({idx}, {bfc->clone ()}));
+				}
+			}
+			break;
+		
+		default:
+			break;
+		}
+		
+		// If we edited this object, store the EditHistory based on collected
+		// information now.
+		if (bEdited == true)
+			paHistory.push_back (new EditHistory ({ulHistoryIndex}, {pOldCopy}, {pNewCopy}));
+	}
+	
+	if (paHistory.size () > 0) {
+		History::addEntry (new ComboHistory (paHistory));
+		g_ForgeWindow->refresh ();
+	}
 }
\ No newline at end of file
--- a/history.cpp	Fri Apr 19 15:21:46 2013 +0300
+++ b/history.cpp	Sat Apr 20 04:18:22 2013 +0300
@@ -144,18 +144,16 @@
 // =============================================================================
 void EditHistory::undo () {
 	for (ulong idx : ulaIndices) {
-		LDObject* obj = g_CurrentFile->objects[idx];
-		obj->replace (paOldObjs[idx]->clone ());
+		printf ("undo %lu\n", idx);
+		g_CurrentFile->object (idx)->replace (paOldObjs[idx]->clone ());
 	}
 	
 	g_ForgeWindow->refresh ();
 }
 
 void EditHistory::redo () {
-	for (ulong idx : ulaIndices) {
-		LDObject* obj = g_CurrentFile->objects[idx];
-		obj->replace (paNewObjs[idx]->clone ());
-	}
+	for (ulong idx : ulaIndices)
+		g_CurrentFile->object (idx)->replace (paNewObjs[idx]->clone ());
 	
 	g_ForgeWindow->refresh ();
 }
@@ -324,4 +322,24 @@
 	for (ulong i : ulaIndices)
 		g_CurrentFile->object (i)->move (vVector);
 	g_ForgeWindow->refresh ();
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+ComboHistory::~ComboHistory () {
+	for (HistoryEntry* pEntry : paEntries)
+		delete pEntry;
+}
+
+void ComboHistory::undo () {
+	for (long i = paEntries.size() - 1; i >= 0; --i) {
+		HistoryEntry* pEntry = paEntries[i];
+		pEntry->undo ();
+	}
+}
+
+void ComboHistory::redo () {
+	for (HistoryEntry* pEntry : paEntries)
+		pEntry->redo ();
 }
\ No newline at end of file
--- a/history.h	Fri Apr 19 15:21:46 2013 +0300
+++ b/history.h	Sat Apr 20 04:18:22 2013 +0300
@@ -38,6 +38,7 @@
 	HISTORY_QuadSplit,
 	HISTORY_Inline,
 	HISTORY_Move,
+	HISTORY_Combo,
 };
 
 // =============================================================================
@@ -184,6 +185,18 @@
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
+class ComboHistory : public HistoryEntry {
+public:
+	IMPLEMENT_HISTORY_TYPE (Combo)
+	
+	const std::vector<HistoryEntry*> paEntries;
+	
+	ComboHistory (const std::vector<HistoryEntry*> paEntries) : paEntries (paEntries) {}
+};
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 namespace History {
 	extern std::vector<HistoryEntry*> entries;
 	

mercurial