- added the magic wand tool

Thu, 03 Jul 2014 21:08:32 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Thu, 03 Jul 2014 21:08:32 +0300
changeset 818
748ba8818af8
parent 817
cee9f0ecac66
child 819
e3d59b6bf053

- added the magic wand tool

CMakeLists.txt file | annotate | diff | comparison | revisions
icons/mode-magicwand.png file | annotate | diff | comparison | revisions
ldforge.qrc file | annotate | diff | comparison | revisions
src/actions.cc file | annotate | diff | comparison | revisions
src/basics.h file | annotate | diff | comparison | revisions
src/glRenderer.cc file | annotate | diff | comparison | revisions
src/glRenderer.h file | annotate | diff | comparison | revisions
src/magicWand.cc file | annotate | diff | comparison | revisions
src/magicWand.h file | annotate | diff | comparison | revisions
src/mainWindow.cc file | annotate | diff | comparison | revisions
src/mainWindow.h file | annotate | diff | comparison | revisions
src/miscallenous.cc file | annotate | diff | comparison | revisions
ui/ldforge.ui file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Mon Jun 30 05:53:14 2014 +0300
+++ b/CMakeLists.txt	Thu Jul 03 21:08:32 2014 +0300
@@ -39,6 +39,7 @@
 	src/ldConfig.cc
 	src/ldDocument.cc
 	src/ldObject.cc
+	src/magicWand.cc
 	src/main.cc
 	src/mainWindow.cc
 	src/messageLog.cc
@@ -75,6 +76,7 @@
 	src/mainWindow.h
 	src/editHistory.h
 	src/format.h
+	src/magicWand.h
 )
 
 set (LDFORGE_FORMS
Binary file icons/mode-magicwand.png has changed
--- a/ldforge.qrc	Mon Jun 30 05:53:14 2014 +0300
+++ b/ldforge.qrc	Thu Jul 03 21:08:32 2014 +0300
@@ -31,6 +31,7 @@
 	<file>./icons/checkerboard.png</file>
 	<file>./icons/colorcursor.png</file>
 	<file>./icons/colorselect.png</file>
+	<file>./icons/colors.txt</file>
 	<file>./icons/comment.png</file>
 	<file>./icons/condline.png</file>
 	<file>./icons/copy.png</file>
@@ -69,6 +70,8 @@
 	<file>./icons/mode-angle.png</file>
 	<file>./icons/mode-circle.png</file>
 	<file>./icons/mode-draw.png</file>
+	<file>./icons/mode-line.png</file>
+	<file>./icons/mode-magicwand.png</file>
 	<file>./icons/mode-select.png</file>
 	<file>./icons/move-x-neg.png</file>
 	<file>./icons/move-x-pos.png</file>
--- a/src/actions.cc	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/actions.cc	Thu Jul 03 21:08:32 2014 +0300
@@ -606,6 +606,13 @@
 
 // =============================================================================
 //
+void MainWindow::slot_actionModeMagicWand()
+{
+ 	R()->setEditMode (EMagicWandMode);
+}
+
+// =============================================================================
+//
 void MainWindow::slot_actionDrawAngles()
 {
 	cfg::drawAngles = not cfg::drawAngles;
--- a/src/basics.h	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/basics.h	Thu Jul 03 21:08:32 2014 +0300
@@ -243,7 +243,8 @@
 	return isZero (a - (int) a);
 }
 
-template<class T> void removeDuplicates (QList<T>& a)
+template<typename T>
+void removeDuplicates (T& a)
 {
 	std::sort (a.begin(), a.end());
 	a.erase (std::unique (a.begin(), a.end()), a.end());
--- a/src/glRenderer.cc	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/glRenderer.cc	Thu Jul 03 21:08:32 2014 +0300
@@ -43,6 +43,7 @@
 #include "primitives.h"
 #include "misc/ringFinder.h"
 #include "glCompiler.h"
+#include "magicWand.h"
 
 static const LDFixedCameraInfo g_FixedCameras[6] =
 {
@@ -132,6 +133,7 @@
 	m_thickBorderPen = QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
 	m_thinBorderPen = m_thickBorderPen;
 	m_thinBorderPen.setWidth (1);
+	m_wand = null;
 	setAcceptDrops (true);
 	connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (slot_toolTipTimer()));
 
@@ -1028,6 +1030,19 @@
 				}
 				break;
 			}
+
+			case EMagicWandMode:
+			{
+				MagicWand::MagicType wandtype = MagicWand::Set;
+
+				if (m_keymods & Qt::ShiftModifier)
+					wandtype = MagicWand::Additive;
+				elif (m_keymods & Qt::ControlModifier)
+					wandtype = MagicWand::Subtractive;
+				
+				m_wand->doMagic (pickOneObject (ev->x(), ev->y()), wandtype);
+				break;
+			}
 		}
 
 		m_rangepick = false;
@@ -1335,15 +1350,43 @@
 	repaint();
 }
 
+//
+// Simpler version of GLRenderer::pick which simply picks whatever object on the screen
+//
+LDObjectPtr GLRenderer::pickOneObject (int mouseX, int mouseY)
+{
+	uchar pixel[4];
+	makeCurrent();
+	setPicking (true);
+	drawGLScene();
+	glReadPixels (mouseX, m_height - mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+	LDObjectPtr obj = LDObject::fromID ((pixel[0] * 0x10000) + (pixel[1] * 0x100) + pixel[2]);
+	setPicking (false);
+	repaint();
+	return obj;
+}
+
 // =============================================================================
 //
 void GLRenderer::setEditMode (EditMode const& a)
 {
+	if (m_editMode == a)
+		return;
+
 	m_editMode = a;
 
+	if (a == EMagicWandMode)
+		m_wand = new MagicWand;
+	else
+	{
+		delete m_wand;
+		m_wand = null;
+	}
+
 	switch (a)
 	{
 		case ESelectMode:
+		case EMagicWandMode:
 		{
 			unsetCursor();
 			setContextMenuPolicy (Qt::DefaultContextMenu);
@@ -1607,6 +1650,7 @@
 		} break;
 
 		case ESelectMode:
+		case EMagicWandMode:
 		{
 			// this shouldn't happen
 			assert (false);
--- a/src/glRenderer.h	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/glRenderer.h	Thu Jul 03 21:08:32 2014 +0300
@@ -32,12 +32,14 @@
 class QSpinBox;
 class QLineEdit;
 class QTimer;
+class MagicWand;
 
 enum EditMode
 {
 	ESelectMode,
 	EDrawMode,
 	ECircleMode,
+	EMagicWandMode,
 };
 
 //
@@ -230,6 +232,7 @@
 	bool					m_rectdraw;
 	Vertex					m_rectverts[4];
 	QColor					m_bgcolor;
+	MagicWand*				m_wand;
 
 	void					addDrawnVertex (Vertex m_hoverpos);
 	void					calcCameraIcons();
@@ -247,6 +250,7 @@
 	inline double&			pan (Axis ax);
 	inline const double&	pan (Axis ax) const;
 	void					pick (int mouseX, int mouseY);
+	LDObjectPtr				pickOneObject (int mouseX, int mouseY);
 	inline double&			rot (Axis ax);
 	void					updateRectVerts();
 	inline double&			zoom();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/magicWand.cc	Thu Jul 03 21:08:32 2014 +0300
@@ -0,0 +1,184 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013, 2014 Santeri Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "main.h"
+#include "magicWand.h"
+#include "ldDocument.h"
+#include "mainWindow.h"
+
+MagicWand::MagicWand()
+{
+	// Get vertex<->object data
+	for (LDObjectPtr obj : getCurrentDocument()->objects())
+	{
+		// Note: this deliberately only takes vertex-objects into account.
+		// The magic wand does not process subparts.
+		for (int i = 0; i < obj->numVertices(); ++i)
+			_vertices[obj->vertex (i)] << obj;
+	}
+}
+
+void MagicWand::fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates)
+{
+	// All boundaries obviously share vertices with the object, therefore they're all in the list
+	// of candidates.
+	for (auto it = candidates.begin(); it != candidates.end(); ++it)
+	{
+		if ((*it)->type() != OBJ_Line || (*it)->vertex (0) == (*it)->vertex (1))
+			continue;
+
+		int matches = 0;
+
+		for (int i = 0; i < obj->numVertices(); ++i)
+		{
+			if (not eq (obj->vertex (i), (*it)->vertex (0), (*it)->vertex (1)))
+				continue;
+
+			if (++matches == 2)
+			{
+				// Boundary found. Add to boundaries list and get it off the candidates list.
+				boundaries.append (std::make_tuple ((*it)->vertex (0), (*it)->vertex (1)));
+				break;
+			}
+		}
+	}
+}
+
+void MagicWand::doMagic (LDObjectPtr obj, MagicWand::MagicType type)
+{
+	if (obj == null)
+	{
+		if (type == Set)
+			getCurrentDocument()->clearSelection();
+
+		return;
+	}
+
+	int matchesneeded = 0;
+	QVector<BoundaryType> boundaries;
+	LDObjectType objtype = obj->type();
+
+	if (type != InternalRecursion)
+	{
+		_selection.clear();
+		_selection.append (obj);
+	}
+
+	switch (obj->type())
+	{
+		case OBJ_Line:
+		case OBJ_CondLine:
+			matchesneeded = 1;
+			break;
+
+		case OBJ_Triangle:
+		case OBJ_Quad:
+			matchesneeded = 2;
+			break;
+
+		default:
+			return;
+	}
+
+	QVector<LDObjectPtr> candidates;
+
+	// Get the list of objects that touch this object, i.e. share a vertex
+	// with this.
+	for (int i = 0; i < obj->numVertices(); ++i)
+		candidates += _vertices[obj->vertex (i)];
+
+	removeDuplicates (candidates);
+
+	// If we're dealing with surfaces, get a list of boundaries.
+	if (matchesneeded > 1)
+		fillBoundaries (obj, boundaries, candidates);
+
+	for (LDObjectPtr candidate : candidates)
+	{
+		try
+		{
+			// If we're doing this on lines, we need exact type match. Surface types (quads and
+			// triangles) can be mixed. Also don't consider self a candidate, and don't consider
+			// objects we have already processed.
+			if ((candidate == obj) ||
+				(candidate->color() != obj->color()) ||
+				(_selection.contains (candidate)) ||
+				(matchesneeded == 1 && (candidate->type() != objtype)) ||
+				((candidate->numVertices() > 2) ^ (matchesneeded == 2)))
+			{
+				throw 0;
+			}
+
+			// Now ensure the two objects share enough vertices.
+			QVector<Vertex> matches;
+
+			for (int i = 0; i < obj->numVertices(); ++i)
+			{
+				for (int j = 0; j < candidate->numVertices(); ++j)
+				{
+					if (obj->vertex(i) == candidate->vertex(j))
+					{
+						matches << obj->vertex(i);
+						break;
+					}
+				}
+			}
+
+			if (matches.size() < matchesneeded)
+				throw 0; // Not enough matches.
+
+			// Check if a boundary gets in between the objects.
+			for (auto boundary : boundaries)
+			{
+				if (eq (matches[0], std::get<0> (boundary), std::get<1> (boundary)) &&
+					eq (matches[1], std::get<0> (boundary), std::get<1> (boundary)))
+				{
+					throw 0;
+				}
+			}
+
+			_selection.append (candidate);
+			doMagic (candidate, InternalRecursion);
+		}
+		catch (int&)
+		{
+			continue;
+		}
+	}
+
+	switch (type)
+	{
+		case Set:
+			getCurrentDocument()->clearSelection();
+		case Additive:
+			for (LDObjectPtr obj : _selection)
+				obj->select();
+			break;
+
+		case Subtractive:
+			for (LDObjectPtr obj : _selection)
+				obj->deselect();
+			break;
+
+		case InternalRecursion:
+			break;
+	}
+
+	if (type != InternalRecursion)
+		g_win->buildObjList();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/magicWand.h	Thu Jul 03 21:08:32 2014 +0300
@@ -0,0 +1,44 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013, 2014 Santeri Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include "basics.h"
+#include <QMap>
+#include <QVector>
+
+class MagicWand
+{
+	QMap<Vertex, QVector<LDObjectPtr>> _vertices;
+	QVector<LDObjectPtr> _selection;
+
+public:
+	using BoundaryType = std::tuple<Vertex, Vertex>;
+	enum MagicType
+	{
+		Set,
+		Additive,
+		Subtractive,
+		InternalRecursion
+	};
+
+	MagicWand();
+	void doMagic (LDObjectPtr obj, MagicType type);
+
+private:
+	void fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates);
+};
--- a/src/mainWindow.cc	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/mainWindow.cc	Thu Jul 03 21:08:32 2014 +0300
@@ -694,6 +694,7 @@
 	ui->actionModeSelect->setChecked (mode == ESelectMode);
 	ui->actionModeDraw->setChecked (mode == EDrawMode);
 	ui->actionModeCircle->setChecked (mode == ECircleMode);
+	ui->actionModeMagicWand->setChecked (mode == EMagicWandMode);
 }
 
 // =============================================================================
--- a/src/mainWindow.h	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/mainWindow.h	Thu Jul 03 21:08:32 2014 +0300
@@ -219,6 +219,7 @@
 	void slot_actionModeDraw();
 	void slot_actionModeSelect();
 	void slot_actionModeCircle();
+	void slot_actionModeMagicWand();
 	void slot_actionSetDrawDepth();
 	void slot_actionSetColor();
 	void slot_actionAutocolor();
--- a/src/miscallenous.cc	Mon Jun 30 05:53:14 2014 +0300
+++ b/src/miscallenous.cc	Thu Jul 03 21:08:32 2014 +0300
@@ -146,9 +146,9 @@
 	{
 		repeat = false;
 
-		for (int x = 0; x < countof (primes); x++)
+		for (int x = 0; x < countof (g_primes); x++)
 		{
-			int const prime = primes[x];
+			int const prime = g_primes[x];
 
 			if (numer < prime && denom < prime)
 				break;
--- a/ui/ldforge.ui	Mon Jun 30 05:53:14 2014 +0300
+++ b/ui/ldforge.ui	Thu Jul 03 21:08:32 2014 +0300
@@ -451,6 +451,7 @@
    <addaction name="actionModeSelect"/>
    <addaction name="actionModeDraw"/>
    <addaction name="actionModeCircle"/>
+   <addaction name="actionModeMagicWand"/>
   </widget>
   <widget class="QToolBar" name="colorToolbar">
    <property name="windowTitle">
@@ -923,9 +924,6 @@
    <property name="text">
     <string>Set Draw Depth</string>
    </property>
-   <property name="shortcut">
-    <string>Ctrl+3</string>
-   </property>
   </action>
   <action name="actionSetColor">
    <property name="icon">
@@ -1479,6 +1477,9 @@
    <property name="text">
     <string>Circle Mode</string>
    </property>
+   <property name="shortcut">
+    <string>Ctrl+3</string>
+   </property>
   </action>
   <action name="actionVisibilityHide">
    <property name="icon">
@@ -1585,6 +1586,21 @@
     <string>Render conditional lines on the viewport.</string>
    </property>
   </action>
+  <action name="actionModeMagicWand">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="icon">
+    <iconset resource="../ldforge.qrc">
+     <normaloff>:/icons/mode-magicwand.png</normaloff>:/icons/mode-magicwand.png</iconset>
+   </property>
+   <property name="text">
+    <string>Magic wand</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+4</string>
+   </property>
+  </action>
  </widget>
  <resources>
   <include location="../ldforge.qrc"/>

mercurial