--- a/src/editmodes/magicWandMode.cpp Thu Feb 15 14:59:04 2018 +0200 +++ b/src/editmodes/magicWandMode.cpp Thu Feb 22 11:41:58 2018 +0200 @@ -22,17 +22,36 @@ #include "../mainwindow.h" #include "../canvas.h" -MagicWandMode::MagicWandMode (Canvas* canvas) : - Super (canvas) +MagicWandMode::MagicWandMode(Canvas* canvas) : + Super {canvas} { + QSet<LineSegment> boundarySegments; + // Get vertex<->object data - for (LDObject* obj : currentDocument()->objects()) + for (const QModelIndex& index : currentDocument()->indices()) { // 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; + LDObject* object = currentDocument()->lookup(index); + + for (int i = 0; i < object->numVertices(); ++i) + { + LineSegment segment { + object->vertex(i), + object->vertex((i + 1) % object->numVertices()) + }; + m_vertices[object->vertex(i)].insert(index); + + if (object->type() == LDObjectType::EdgeLine) + boundarySegments.insert(segment); + else + this->segments[segment].insert(index); + } } + + // Remove all edge lines from the set of available segments because they get in the way. + for (const LineSegment& boundarySegment : boundarySegments) + this->segments.remove(boundarySegment); } EditModeType MagicWandMode::type() const @@ -40,157 +59,82 @@ return EditModeType::MagicWand; } -void MagicWandMode::fillBoundaries (LDObject* obj, QVector<BoundaryType>& boundaries, QSet<LDObject*>& candidates) -{ - // All boundaries obviously share vertices with the object, therefore they're all in the list - // of candidates. - for (LDObject* candidate : candidates) +void MagicWandMode::edgeFill( + QModelIndex index, + QItemSelection& selection, + QSet<QModelIndex>& processed +) const { + QItemSelection result; + processed.insert(index); + result.select(index, index); + QSet<QPersistentModelIndex> candidates; + LDObject* object = currentDocument()->lookup(index); + + // Get the list of objects that touch this object, i.e. share a vertex with it. + for (int i = 0; i < object->numVertices(); ++i) + candidates |= m_vertices[object->vertex(i)]; + + candidates.remove(index); + + for (const QModelIndex& candidate : candidates) { - if (not isOneOf(candidate->type(), LDObjectType::EdgeLine, LDObjectType::ConditionalEdge) - or candidate->vertex (0) == candidate->vertex (1)) - { - continue; - } + LDObject* candidateObject = currentDocument()->lookup(candidate); - int matches = 0; + if (candidateObject->type() == LDObjectType::EdgeLine + and candidateObject->color() == object->color() + ) { + selection.select(candidate, candidate); - for (int i = 0; i < obj->numVertices(); ++i) + if (not processed.contains(candidate)) + edgeFill(candidate, selection, processed); + } + } +} + +void MagicWandMode::surfaceFill( + QModelIndex index, + QItemSelection& selection, + QSet<QModelIndex>& processed +) const { + LDObject* object = currentDocument()->lookup(index); + selection.select(index, index); + processed.insert(index); + + for (int i = 0; i < object->numVertices(); i += 1) + { + Vertex v_1 = object->vertex(i); + Vertex v_2 = object->vertex((i + 1) % object->numVertices()); + LineSegment segment {v_1, v_2}; + + for (const QModelIndex& candidate : this->segments[segment]) { - if (not isOneOf (obj->vertex (i), candidate->vertex (0), candidate->vertex (1))) - continue; - - if (++matches == 2) + if (currentDocument()->lookup(candidate)->color() == object->color()) { - // Boundary found. If it's an edgeline, add it to the boundaries list, if a - // conditional line, select it. - if (candidate->type() == LDObjectType::ConditionalEdge) - m_selection << candidate; - else - boundaries.append (std::make_tuple (candidate->vertex (0), candidate->vertex (1))); + selection.select(candidate, candidate); - break; + if (not processed.contains(candidate)) + surfaceFill(candidate, selection, processed); } } } } -void MagicWandMode::doMagic (LDObject* obj, MagicWandMode::MagicType type) +QItemSelection MagicWandMode::doMagic(const QModelIndex& index) const { - if (obj == nullptr) - { - if (type == Set) - currentDocument()->clearSelection(); + QItemSelection selection; + LDObject* object = currentDocument()->lookup(index); - return; - } - - int matchesneeded = 0; - QVector<BoundaryType> boundaries; - LDObjectType objtype = obj->type(); - - if (type != InternalRecursion) + if (object) { - m_selection.clear(); - m_selection.append (obj); - } + QSet<QModelIndex> processed; - switch (obj->type()) - { - case LDObjectType::EdgeLine: - case LDObjectType::ConditionalEdge: - matchesneeded = 1; - break; - - case LDObjectType::Triangle: - case LDObjectType::Quadrilateral: - matchesneeded = 2; - break; - - default: - return; + if (object->type() == LDObjectType::EdgeLine) + edgeFill(index, selection, processed); + else if (object->numPolygonVertices() >= 3) + surfaceFill(index, selection, processed); } - QSet<LDObject*> 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)]; - - // If we're dealing with surfaces, get a list of boundaries. - if (matchesneeded > 1) - fillBoundaries (obj, boundaries, candidates); - - for (LDObject* 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<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 (countof(matches) < matchesneeded) - throw 0; // Not enough matches. - - // Check if a boundary gets in between the objects. - for (auto boundary : boundaries) - { - if (isOneOf (matches[0], std::get<0> (boundary), std::get<1> (boundary)) and - isOneOf (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 (LDObject* obj : m_selection) - currentDocument()->addToSelection(obj); - break; - - case Subtractive: - for (LDObject* obj : m_selection) - currentDocument()->removeFromSelection(obj); - break; - - case InternalRecursion: - break; - } + return selection; } bool MagicWandMode::mouseReleased (MouseEventData const& data) @@ -200,14 +144,15 @@ if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved) { - MagicType wandtype = MagicWandMode::Set; + QItemSelection selection = this->doMagic(renderer()->pick(data.ev->x(), data.ev->y())); + QItemSelectionModel::SelectionFlags command = QItemSelectionModel::ClearAndSelect; if (data.keymods & Qt::ShiftModifier) - wandtype = MagicWandMode::Additive; + command = QItemSelectionModel::Select; else if (data.keymods & Qt::ControlModifier) - wandtype = MagicWandMode::Subtractive; + command = QItemSelectionModel::Deselect; - doMagic (renderer()->pick (data.ev->x(), data.ev->y()), wandtype); + renderer()->selectionModel()->select(selection, command); return true; }