src/editmodes/magicWandMode.cpp

changeset 1251
e75cc5bff076
parent 1240
cebb7ef54f41
child 1326
69a90bd2dba2
equal deleted inserted replaced
1250:e2755ccf3667 1251:e75cc5bff076
20 #include "magicWandMode.h" 20 #include "magicWandMode.h"
21 #include "../lddocument.h" 21 #include "../lddocument.h"
22 #include "../mainwindow.h" 22 #include "../mainwindow.h"
23 #include "../canvas.h" 23 #include "../canvas.h"
24 24
25 MagicWandMode::MagicWandMode (Canvas* canvas) : 25 MagicWandMode::MagicWandMode(Canvas* canvas) :
26 Super (canvas) 26 Super {canvas}
27 { 27 {
28 QSet<LineSegment> boundarySegments;
29
28 // Get vertex<->object data 30 // Get vertex<->object data
29 for (LDObject* obj : currentDocument()->objects()) 31 for (const QModelIndex& index : currentDocument()->indices())
30 { 32 {
31 // Note: this deliberately only takes vertex-objects into account. 33 // Note: this deliberately only takes vertex-objects into account.
32 // The magic wand does not process subparts. 34 // The magic wand does not process subparts.
33 for (int i = 0; i < obj->numVertices(); ++i) 35 LDObject* object = currentDocument()->lookup(index);
34 m_vertices[obj->vertex (i)] << obj; 36
37 for (int i = 0; i < object->numVertices(); ++i)
38 {
39 LineSegment segment {
40 object->vertex(i),
41 object->vertex((i + 1) % object->numVertices())
42 };
43 m_vertices[object->vertex(i)].insert(index);
44
45 if (object->type() == LDObjectType::EdgeLine)
46 boundarySegments.insert(segment);
47 else
48 this->segments[segment].insert(index);
49 }
35 } 50 }
51
52 // Remove all edge lines from the set of available segments because they get in the way.
53 for (const LineSegment& boundarySegment : boundarySegments)
54 this->segments.remove(boundarySegment);
36 } 55 }
37 56
38 EditModeType MagicWandMode::type() const 57 EditModeType MagicWandMode::type() const
39 { 58 {
40 return EditModeType::MagicWand; 59 return EditModeType::MagicWand;
41 } 60 }
42 61
43 void MagicWandMode::fillBoundaries (LDObject* obj, QVector<BoundaryType>& boundaries, QSet<LDObject*>& candidates) 62 void MagicWandMode::edgeFill(
44 { 63 QModelIndex index,
45 // All boundaries obviously share vertices with the object, therefore they're all in the list 64 QItemSelection& selection,
46 // of candidates. 65 QSet<QModelIndex>& processed
47 for (LDObject* candidate : candidates) 66 ) const {
67 QItemSelection result;
68 processed.insert(index);
69 result.select(index, index);
70 QSet<QPersistentModelIndex> candidates;
71 LDObject* object = currentDocument()->lookup(index);
72
73 // Get the list of objects that touch this object, i.e. share a vertex with it.
74 for (int i = 0; i < object->numVertices(); ++i)
75 candidates |= m_vertices[object->vertex(i)];
76
77 candidates.remove(index);
78
79 for (const QModelIndex& candidate : candidates)
48 { 80 {
49 if (not isOneOf(candidate->type(), LDObjectType::EdgeLine, LDObjectType::ConditionalEdge) 81 LDObject* candidateObject = currentDocument()->lookup(candidate);
50 or candidate->vertex (0) == candidate->vertex (1)) 82
83 if (candidateObject->type() == LDObjectType::EdgeLine
84 and candidateObject->color() == object->color()
85 ) {
86 selection.select(candidate, candidate);
87
88 if (not processed.contains(candidate))
89 edgeFill(candidate, selection, processed);
90 }
91 }
92 }
93
94 void MagicWandMode::surfaceFill(
95 QModelIndex index,
96 QItemSelection& selection,
97 QSet<QModelIndex>& processed
98 ) const {
99 LDObject* object = currentDocument()->lookup(index);
100 selection.select(index, index);
101 processed.insert(index);
102
103 for (int i = 0; i < object->numVertices(); i += 1)
104 {
105 Vertex v_1 = object->vertex(i);
106 Vertex v_2 = object->vertex((i + 1) % object->numVertices());
107 LineSegment segment {v_1, v_2};
108
109 for (const QModelIndex& candidate : this->segments[segment])
51 { 110 {
52 continue; 111 if (currentDocument()->lookup(candidate)->color() == object->color())
53 } 112 {
113 selection.select(candidate, candidate);
54 114
55 int matches = 0; 115 if (not processed.contains(candidate))
56 116 surfaceFill(candidate, selection, processed);
57 for (int i = 0; i < obj->numVertices(); ++i)
58 {
59 if (not isOneOf (obj->vertex (i), candidate->vertex (0), candidate->vertex (1)))
60 continue;
61
62 if (++matches == 2)
63 {
64 // Boundary found. If it's an edgeline, add it to the boundaries list, if a
65 // conditional line, select it.
66 if (candidate->type() == LDObjectType::ConditionalEdge)
67 m_selection << candidate;
68 else
69 boundaries.append (std::make_tuple (candidate->vertex (0), candidate->vertex (1)));
70
71 break;
72 } 117 }
73 } 118 }
74 } 119 }
75 } 120 }
76 121
77 void MagicWandMode::doMagic (LDObject* obj, MagicWandMode::MagicType type) 122 QItemSelection MagicWandMode::doMagic(const QModelIndex& index) const
78 { 123 {
79 if (obj == nullptr) 124 QItemSelection selection;
125 LDObject* object = currentDocument()->lookup(index);
126
127 if (object)
80 { 128 {
81 if (type == Set) 129 QSet<QModelIndex> processed;
82 currentDocument()->clearSelection();
83 130
84 return; 131 if (object->type() == LDObjectType::EdgeLine)
132 edgeFill(index, selection, processed);
133 else if (object->numPolygonVertices() >= 3)
134 surfaceFill(index, selection, processed);
85 } 135 }
86 136
87 int matchesneeded = 0; 137 return selection;
88 QVector<BoundaryType> boundaries;
89 LDObjectType objtype = obj->type();
90
91 if (type != InternalRecursion)
92 {
93 m_selection.clear();
94 m_selection.append (obj);
95 }
96
97 switch (obj->type())
98 {
99 case LDObjectType::EdgeLine:
100 case LDObjectType::ConditionalEdge:
101 matchesneeded = 1;
102 break;
103
104 case LDObjectType::Triangle:
105 case LDObjectType::Quadrilateral:
106 matchesneeded = 2;
107 break;
108
109 default:
110 return;
111 }
112
113 QSet<LDObject*> candidates;
114
115 // Get the list of objects that touch this object, i.e. share a vertex
116 // with this.
117 for (int i = 0; i < obj->numVertices(); ++i)
118 candidates += m_vertices[obj->vertex (i)];
119
120 // If we're dealing with surfaces, get a list of boundaries.
121 if (matchesneeded > 1)
122 fillBoundaries (obj, boundaries, candidates);
123
124 for (LDObject* candidate : candidates)
125 {
126 try
127 {
128 // If we're doing this on lines, we need exact type match. Surface types (quads and
129 // triangles) can be mixed. Also don't consider self a candidate, and don't consider
130 // objects we have already processed.
131 if ((candidate == obj) or
132 (candidate->color() != obj->color()) or
133 (m_selection.contains (candidate)) or
134 (matchesneeded == 1 and (candidate->type() != objtype)) or
135 ((candidate->numVertices() > 2) ^ (matchesneeded == 2)))
136 {
137 throw 0;
138 }
139
140 // Now ensure the two objects share enough vertices.
141 QVector<Vertex> matches;
142
143 for (int i = 0; i < obj->numVertices(); ++i)
144 {
145 for (int j = 0; j < candidate->numVertices(); ++j)
146 {
147 if (obj->vertex(i) == candidate->vertex(j))
148 {
149 matches << obj->vertex(i);
150 break;
151 }
152 }
153 }
154
155 if (countof(matches) < matchesneeded)
156 throw 0; // Not enough matches.
157
158 // Check if a boundary gets in between the objects.
159 for (auto boundary : boundaries)
160 {
161 if (isOneOf (matches[0], std::get<0> (boundary), std::get<1> (boundary)) and
162 isOneOf (matches[1], std::get<0> (boundary), std::get<1> (boundary)))
163 {
164 throw 0;
165 }
166 }
167
168 m_selection.append (candidate);
169 doMagic (candidate, InternalRecursion);
170 }
171 catch (int&)
172 {
173 continue;
174 }
175 }
176
177 switch (type)
178 {
179 case Set:
180 currentDocument()->clearSelection();
181 case Additive:
182 for (LDObject* obj : m_selection)
183 currentDocument()->addToSelection(obj);
184 break;
185
186 case Subtractive:
187 for (LDObject* obj : m_selection)
188 currentDocument()->removeFromSelection(obj);
189 break;
190
191 case InternalRecursion:
192 break;
193 }
194 } 138 }
195 139
196 bool MagicWandMode::mouseReleased (MouseEventData const& data) 140 bool MagicWandMode::mouseReleased (MouseEventData const& data)
197 { 141 {
198 if (Super::mouseReleased (data)) 142 if (Super::mouseReleased (data))
199 return true; 143 return true;
200 144
201 if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved) 145 if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved)
202 { 146 {
203 MagicType wandtype = MagicWandMode::Set; 147 QItemSelection selection = this->doMagic(renderer()->pick(data.ev->x(), data.ev->y()));
148 QItemSelectionModel::SelectionFlags command = QItemSelectionModel::ClearAndSelect;
204 149
205 if (data.keymods & Qt::ShiftModifier) 150 if (data.keymods & Qt::ShiftModifier)
206 wandtype = MagicWandMode::Additive; 151 command = QItemSelectionModel::Select;
207 else if (data.keymods & Qt::ControlModifier) 152 else if (data.keymods & Qt::ControlModifier)
208 wandtype = MagicWandMode::Subtractive; 153 command = QItemSelectionModel::Deselect;
209 154
210 doMagic (renderer()->pick (data.ev->x(), data.ev->y()), wandtype); 155 renderer()->selectionModel()->select(selection, command);
211 return true; 156 return true;
212 } 157 }
213 158
214 return false; 159 return false;
215 } 160 }

mercurial