Thu, 03 Jul 2014 21:08:32 +0300
- 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
--- 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"/>