68 // Then round the coordinates to integer precision... |
68 // Then round the coordinates to integer precision... |
69 this->worldPosition = glm::round(*this->worldPosition); |
69 this->worldPosition = glm::round(*this->worldPosition); |
70 // And finally transform it back to grid coordinates by transforming it with the |
70 // And finally transform it back to grid coordinates by transforming it with the |
71 // grid matrix. |
71 // grid matrix. |
72 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1}; |
72 this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1}; |
73 } |
73 this->polygon.back() = *this->worldPosition; |
74 this->updatePreviewPolygon(); |
74 } |
75 } |
75 } |
76 |
76 |
77 static QVector<QPointF> convertWorldPointsToScreenPoints( |
77 static QVector<QPointF> convertWorldPointsToScreenPoints( |
78 const std::vector<glm::vec3> &worldPoints, |
78 const std::vector<glm::vec3> &worldPoints, |
79 const PartRenderer* renderer) |
79 const PartRenderer* renderer) |
115 QPainter* painter, |
115 QPainter* painter, |
116 const std::vector<glm::vec3> &points, |
116 const std::vector<glm::vec3> &points, |
117 const PartRenderer* renderer) |
117 const PartRenderer* renderer) |
118 { |
118 { |
119 painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)}); |
119 painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)}); |
|
120 } |
|
121 |
|
122 static opt<std::vector<glm::vec3>> modelActionPoints(const ModelAction& action) |
|
123 { |
|
124 opt<std::vector<glm::vec3>> result; |
|
125 if (const AppendToModel* append = std::get_if<AppendToModel>(&action)) { |
|
126 const ModelElement& newElement = append->newElement; |
|
127 if (const LineSegment* seg = std::get_if<Colored<LineSegment>>(&newElement)) { |
|
128 result = {seg->p1, seg->p2}; |
|
129 } |
|
130 else if (const Triangle* tri = std::get_if<Colored<Triangle>>(&newElement)) { |
|
131 result = {tri->p1, tri->p2, tri->p3}; |
|
132 } |
|
133 else if (const Quadrilateral* quad = std::get_if<Colored<Quadrilateral>>(&newElement)) { |
|
134 result = {quad->p1, quad->p2, quad->p3, quad->p4}; |
|
135 } |
|
136 } |
|
137 return result; |
120 } |
138 } |
121 |
139 |
122 void EditTools::overpaint(QPainter* painter) |
140 void EditTools::overpaint(QPainter* painter) |
123 { |
141 { |
124 struct Pens |
142 struct Pens |
149 const Pens& pens = (this->renderer->isDark() ? darkPens : brightPens); |
167 const Pens& pens = (this->renderer->isDark() ? darkPens : brightPens); |
150 switch(this->mode) { |
168 switch(this->mode) { |
151 case SelectMode: |
169 case SelectMode: |
152 break; |
170 break; |
153 case DrawMode: |
171 case DrawMode: |
154 { |
172 painter->setPen(this->isconcave ? pens.badPolygonPen : pens.polygonPen); |
155 painter->setPen(this->isconcave ? pens.badPolygonPen : pens.polygonPen); |
173 for (const ModelAction& action : this->actions()) { |
156 if (this->previewPolygon.size() > 2 and not this->isconcave) |
174 const opt<std::vector<glm::vec3>> points = modelActionPoints(action); |
157 { |
175 if (points.has_value()) { |
158 if (worldPolygonWinding(this->previewPolygon, this->renderer) == Winding::Clockwise) { |
176 if (worldPolygonWinding(*points, this->renderer) == Winding::Clockwise) { |
159 painter->setBrush(pens.greenPolygonBrush); |
177 painter->setBrush(pens.greenPolygonBrush); |
160 } |
178 } |
161 else { |
179 else { |
162 painter->setBrush(pens.redPolygonBrush); |
180 painter->setBrush(pens.redPolygonBrush); |
163 } |
181 } |
164 drawWorldPolygon(painter, this->previewPolygon, this->renderer); |
182 drawWorldPolygon(painter, *points, this->renderer); |
165 } |
183 } |
166 else { |
184 } |
167 drawWorldPolyline(painter, this->previewPolygon, this->renderer); |
185 //drawWorldPolyline(painter, this->previewPolygon, this->renderer); |
168 } |
186 painter->setBrush(pens.pointBrush); |
169 painter->setBrush(pens.pointBrush); |
187 painter->setPen(pens.pointPen); |
170 painter->setPen(pens.pointPen); |
188 for (const glm::vec3& point : this->polygon) { |
171 for (const glm::vec3& point : this->polygon) { |
189 drawWorldPoint(painter, point, this->renderer); |
172 drawWorldPoint(painter, point, this->renderer); |
|
173 } |
|
174 } |
190 } |
175 break; |
191 break; |
176 } |
192 } |
177 if (this->worldPosition.has_value()) |
193 if (this->worldPosition.has_value()) |
178 { |
194 { |
185 } |
201 } |
186 } |
202 } |
187 |
203 |
188 void EditTools::updatePreviewPolygon() |
204 void EditTools::updatePreviewPolygon() |
189 { |
205 { |
190 this->previewPolygon = this->polygon; |
206 if (this->polygon.size() > 2) { |
191 if (this->worldPosition.has_value()) { |
207 this->isconcave = not isConvex(this->polygon); |
192 this->previewPolygon.resize(this->polygon.size() + 1); |
|
193 this->previewPolygon.back() = *this->worldPosition; |
|
194 } |
|
195 if (this->previewPolygon.size() > 2) |
|
196 { |
|
197 this->isconcave = not isConvex(this->previewPolygon); |
|
198 } |
208 } |
199 } |
209 } |
200 |
210 |
201 void EditTools::removeLastPoint() |
211 void EditTools::removeLastPoint() |
202 { |
212 { |
203 if (this->polygon.size() > 0) |
213 if (this->polygon.size() > 1) { |
204 { |
|
205 this->polygon.erase(this->polygon.end() - 1); |
214 this->polygon.erase(this->polygon.end() - 1); |
206 this->updatePreviewPolygon(); |
215 } |
207 } |
216 } |
208 } |
217 |
209 |
218 template<typename T> |
210 bool isCloseToExistingPoints(const std::vector<glm::vec3>& points, const glm::vec3 &pos) |
219 bool isCloseToExistingPoints(T begin, T end, const glm::vec3 &pos) |
211 { |
220 { |
212 return any(points, std::bind(isclose, std::placeholders::_1, pos)); |
221 return std::any_of(begin, end, [&pos](const glm::vec3& p){ |
|
222 return isclose(pos, p); |
|
223 }); |
213 } |
224 } |
214 |
225 |
215 EditingMode EditTools::currentEditingMode() const |
226 EditingMode EditTools::currentEditingMode() const |
216 { |
227 { |
217 return this->mode; |
228 return this->mode; |
226 Q_EMIT this->select({highlighted}, false); |
237 Q_EMIT this->select({highlighted}, false); |
227 } |
238 } |
228 break; |
239 break; |
229 case DrawMode: |
240 case DrawMode: |
230 if (event->button() == Qt::LeftButton and this->worldPosition.has_value()) { |
241 if (event->button() == Qt::LeftButton and this->worldPosition.has_value()) { |
231 if (isCloseToExistingPoints(this->polygon, *this->worldPosition)) { |
242 if (isCloseToExistingPoints(this->polygon.begin(), this->polygon.end() - 1, *this->worldPosition)) { |
232 this->closeShape(); |
243 this->closeShape(); |
233 } |
244 } |
234 else { |
245 else { |
235 this->polygon.push_back(*this->worldPosition); |
246 this->polygon.push_back(*this->worldPosition); |
236 this->updatePreviewPolygon(); |
247 this->updatePreviewPolygon(); |
237 } |
248 } |
238 } |
249 } |
239 else if (true |
250 else if (true |
240 and event->button() == Qt::RightButton |
251 and event->button() == Qt::RightButton |
241 and this->polygon.size() > 0 |
252 and this->polygon.size() > 1 |
242 ) { |
253 ) { |
243 this->polygon.erase(this->polygon.end() - 1); |
254 this->removeLastPoint(); |
244 updatePreviewPolygon(); |
255 updatePreviewPolygon(); |
245 } |
256 } |
246 break; |
257 break; |
247 } |
258 } |
248 } |
259 } |
249 |
260 |
250 void EditTools::closeShape() |
261 const std::vector<ModelAction> EditTools::actions() const |
251 { |
262 { |
|
263 std::vector<ModelAction> result; |
252 if (this->polygon.size() >= 2 and this->polygon.size() <= 4) { |
264 if (this->polygon.size() >= 2 and this->polygon.size() <= 4) { |
253 switch (this->polygon.size()) { |
265 switch (this->polygon.size()) { |
254 case 2: |
266 case 2: |
255 Q_EMIT this->modelAction(AppendToModel{ |
267 result.push_back(AppendToModel{ |
256 .newElement = Colored<LineSegment>{ |
268 .newElement = Colored<LineSegment>{ |
257 LineSegment{ |
269 LineSegment{ |
258 .p1 = this->polygon[0], |
270 .p1 = this->polygon[0], |
259 .p2 = this->polygon[1], |
271 .p2 = this->polygon[1], |
260 }, |
272 }, |
261 EDGE_COLOR, |
273 EDGE_COLOR, |
262 } |
274 } |
263 }); |
275 }); |
264 break; |
276 break; |
265 case 3: |
277 case 3: |
266 Q_EMIT this->modelAction(AppendToModel{ |
278 result.push_back(AppendToModel{ |
267 .newElement = Colored<Triangle>{ |
279 .newElement = Colored<Triangle>{ |
268 Triangle{ |
280 Triangle{ |
269 .p1 = this->polygon[0], |
281 .p1 = this->polygon[0], |
270 .p2 = this->polygon[1], |
282 .p2 = this->polygon[1], |
271 .p3 = this->polygon[2], |
283 .p3 = this->polygon[2], |
273 MAIN_COLOR, |
285 MAIN_COLOR, |
274 } |
286 } |
275 }); |
287 }); |
276 break; |
288 break; |
277 case 4: |
289 case 4: |
278 Q_EMIT this->modelAction(AppendToModel{ |
290 result.push_back(AppendToModel{ |
279 .newElement = Colored<Quadrilateral>{ |
291 .newElement = Colored<Quadrilateral>{ |
280 Quadrilateral{ |
292 Quadrilateral{ |
281 .p1 = this->polygon[0], |
293 .p1 = this->polygon[0], |
282 .p2 = this->polygon[1], |
294 .p2 = this->polygon[1], |
283 .p3 = this->polygon[2], |
295 .p3 = this->polygon[2], |