diff -r 738673e8a6b4 -r f4c80d92e71e src/editmodes/magicWandMode.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/magicWandMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,220 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu 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 . + */ + +#include +#include "magicWandMode.h" +#include "../ldDocument.h" +#include "../mainWindow.h" +#include "../glRenderer.h" + +MagicWandMode::MagicWandMode (GLRenderer* renderer) : + Super (renderer) +{ + // Get vertex<->object data + for (LDObjectPtr obj : CurrentDocument()->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) + m_vertices[obj->vertex (i)] << obj; + } +} + +EditModeType MagicWandMode::type() const +{ + return EditModeType::MagicWand; +} + +void MagicWandMode::fillBoundaries (LDObjectPtr obj, QVector& boundaries, QVector& 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 (not Eq ((*it)->type(), OBJ_Line, OBJ_CondLine) or (*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. If it's an edgeline, add it to the boundaries list, if a + // conditional line, select it. + if ((*it)->type() == OBJ_CondLine) + m_selection << *it; + else + boundaries.append (std::make_tuple ((*it)->vertex (0), (*it)->vertex (1))); + + break; + } + } + } +} + +void MagicWandMode::doMagic (LDObjectPtr obj, MagicWandMode::MagicType type) +{ + if (obj == null) + { + if (type == Set) + { + CurrentDocument()->clearSelection(); + g_win->buildObjList(); + } + + return; + } + + int matchesneeded = 0; + QVector boundaries; + LDObjectType objtype = obj->type(); + + if (type != InternalRecursion) + { + m_selection.clear(); + m_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 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 += m_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) or + (candidate->color() != obj->color()) or + (m_selection.contains (candidate)) or + (matchesneeded == 1 and (candidate->type() != objtype)) or + ((candidate->numVertices() > 2) ^ (matchesneeded == 2))) + { + throw 0; + } + + // Now ensure the two objects share enough vertices. + QVector 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)) and + Eq (matches[1], std::get<0> (boundary), std::get<1> (boundary))) + { + throw 0; + } + } + + m_selection.append (candidate); + doMagic (candidate, InternalRecursion); + } + catch (int&) + { + continue; + } + } + + switch (type) + { + case Set: + CurrentDocument()->clearSelection(); + case Additive: + for (LDObjectPtr obj : m_selection) + obj->select(); + break; + + case Subtractive: + for (LDObjectPtr obj : m_selection) + obj->deselect(); + break; + + case InternalRecursion: + break; + } + + if (type != InternalRecursion) + g_win->buildObjList(); +} + +bool MagicWandMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved) + { + MagicType wandtype = MagicWandMode::Set; + + if (data.keymods & Qt::ShiftModifier) + wandtype = MagicWandMode::Additive; + elif (data.keymods & Qt::ControlModifier) + wandtype = MagicWandMode::Subtractive; + + doMagic (renderer()->pickOneObject (data.ev->x(), data.ev->y()), wandtype); + return true; + } + + return false; +}