21 #include "algorithm/earcut.h" |
21 #include "algorithm/earcut.h" |
22 #include "document.h" |
22 #include "document.h" |
23 #include "model.h" |
23 #include "model.h" |
24 #include "ui/objecteditor.h" |
24 #include "ui/objecteditor.h" |
25 #include "gl/partrenderer.h" |
25 #include "gl/partrenderer.h" |
|
26 #include "circularprimitive.h" |
26 |
27 |
27 // Make mapbox::earcut work with glm::vec3 |
28 // Make mapbox::earcut work with glm::vec3 |
28 namespace mapbox { |
29 namespace mapbox { |
29 namespace util { |
30 namespace util { |
30 template<> struct nth<0, glm::vec3> |
31 template<> struct nth<0, glm::vec3> |
59 this->gridPlane = planeFromTriangle({ |
60 this->gridPlane = planeFromTriangle({ |
60 this->gridMatrix * glm::vec4{0, 0, 0, 1}, |
61 this->gridMatrix * glm::vec4{0, 0, 0, 1}, |
61 this->gridMatrix * glm::vec4{1, 0, 0, 1}, |
62 this->gridMatrix * glm::vec4{1, 0, 0, 1}, |
62 this->gridMatrix * glm::vec4{0, 1, 0, 1}, |
63 this->gridMatrix * glm::vec4{0, 1, 0, 1}, |
63 }); |
64 }); |
|
65 } |
|
66 |
|
67 void EditTools::setCircleToolOptions(const CircleToolOptions& options) |
|
68 { |
|
69 this->circleToolOptions = options; |
64 } |
70 } |
65 |
71 |
66 void EditTools::mvpMatrixChanged(const glm::mat4& matrix) |
72 void EditTools::mvpMatrixChanged(const glm::mat4& matrix) |
67 { |
73 { |
68 this->mvpMatrix = matrix; |
74 this->mvpMatrix = matrix; |
136 const PartRenderer* renderer) |
142 const PartRenderer* renderer) |
137 { |
143 { |
138 painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)}); |
144 painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)}); |
139 } |
145 } |
140 |
146 |
141 static opt<std::vector<glm::vec3>> modelActionPoints(const ModelAction& action) |
147 static std::vector<std::vector<glm::vec3>> modelActionPoints(const ModelAction& action) |
142 { |
148 { |
143 opt<std::vector<glm::vec3>> result; |
149 std::vector<std::vector<glm::vec3>> result; |
144 if (const AppendToModel* append = std::get_if<AppendToModel>(&action)) { |
150 if (const AppendToModel* append = std::get_if<AppendToModel>(&action)) { |
145 const ModelElement& newElement = append->newElement; |
151 const ModelElement& newElement = append->newElement; |
146 if (const LineSegment* seg = std::get_if<Colored<LineSegment>>(&newElement)) { |
152 if (const LineSegment* seg = std::get_if<Colored<LineSegment>>(&newElement)) { |
147 result = {seg->p1, seg->p2}; |
153 result.push_back({seg->p1, seg->p2}); |
148 } |
154 } |
149 else if (const Triangle* tri = std::get_if<Colored<Triangle>>(&newElement)) { |
155 else if (const Triangle* tri = std::get_if<Colored<Triangle>>(&newElement)) { |
150 result = {tri->p1, tri->p2, tri->p3}; |
156 result.push_back({tri->p1, tri->p2, tri->p3}); |
151 } |
157 } |
152 else if (const Quadrilateral* quad = std::get_if<Colored<Quadrilateral>>(&newElement)) { |
158 else if (const Quadrilateral* quad = std::get_if<Colored<Quadrilateral>>(&newElement)) { |
153 result = {quad->p1, quad->p2, quad->p3, quad->p4}; |
159 result.push_back({quad->p1, quad->p2, quad->p3, quad->p4}); |
|
160 } |
|
161 else if (const CircularPrimitive* circ = std::get_if<Colored<CircularPrimitive>>(&newElement)) { |
|
162 rasterize(*circ, [&](const ModelElement& element){ |
|
163 const auto& subpoints = modelActionPoints(AppendToModel{element}); |
|
164 std::copy(subpoints.begin(), subpoints.end(), std::back_inserter(result)); |
|
165 }); |
154 } |
166 } |
155 } |
167 } |
156 return result; |
168 return result; |
157 } |
169 } |
158 |
170 |
197 painter->drawEllipse(pos, 5, 5); |
209 painter->drawEllipse(pos, 5, 5); |
198 painter->drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition)); |
210 painter->drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition)); |
199 } |
211 } |
200 } |
212 } |
201 |
213 |
|
214 const std::vector<ModelAction> EditTools::modelActions() const |
|
215 { |
|
216 switch(this->mode) { |
|
217 case SelectMode: |
|
218 return {}; |
|
219 case DrawMode: |
|
220 return drawModeActions(); |
|
221 case CircleMode: |
|
222 return circleModeActions(); |
|
223 } |
|
224 } |
|
225 |
202 void EditTools::renderPreview(QPainter* painter, const void* pensptr) |
226 void EditTools::renderPreview(QPainter* painter, const void* pensptr) |
203 { |
227 { |
204 const Pens& pens = *reinterpret_cast<const Pens*>(pensptr); |
228 const Pens& pens = *reinterpret_cast<const Pens*>(pensptr); |
205 painter->setPen(pens.polygonPen); |
229 painter->setPen(pens.polygonPen); |
206 for (const ModelAction& action : this->actions()) { |
230 for (const ModelAction& action : this->modelActions()) { |
207 const std::vector<glm::vec3> points = modelActionPoints(action).value_or(std::vector<glm::vec3>{}); |
231 for (const std::vector<glm::vec3>& points : modelActionPoints(action)) { |
208 if (points.size() == 2) { |
232 if (points.size() == 2) { |
209 drawWorldPolyline(painter, points, renderer); |
233 drawWorldPolyline(painter, points, renderer); |
210 } |
|
211 else { |
|
212 if (worldPolygonWinding(points, this->renderer) == Winding::Clockwise) { |
|
213 painter->setBrush(pens.greenPolygonBrush); |
|
214 } |
234 } |
215 else { |
235 else { |
216 painter->setBrush(pens.redPolygonBrush); |
236 if (worldPolygonWinding(points, this->renderer) == Winding::Clockwise) { |
217 } |
237 painter->setBrush(pens.greenPolygonBrush); |
218 drawWorldPolygon(painter, points, this->renderer); |
238 } |
|
239 else { |
|
240 painter->setBrush(pens.redPolygonBrush); |
|
241 } |
|
242 drawWorldPolygon(painter, points, this->renderer); |
|
243 } |
219 } |
244 } |
220 } |
245 } |
221 painter->setBrush(pens.pointBrush); |
246 painter->setBrush(pens.pointBrush); |
222 painter->setPen(pens.pointPen); |
247 painter->setPen(pens.pointPen); |
223 for (const glm::vec3& point : this->polygon) { |
248 for (const glm::vec3& point : this->polygon) { |
266 } |
291 } |
267 else { |
292 else { |
268 this->polygon.push_back(*this->worldPosition); |
293 this->polygon.push_back(*this->worldPosition); |
269 } |
294 } |
270 } |
295 } |
271 else if (true |
|
272 and event->button() == Qt::RightButton |
|
273 and this->polygon.size() > 1 |
|
274 ) { |
|
275 this->removeLastPoint(); |
|
276 } |
|
277 break; |
296 break; |
278 } |
297 case CircleMode: |
279 } |
298 if (event->button() == Qt::LeftButton and this->worldPosition.has_value()) { |
280 |
299 if (this->polygon.size() == 2) { |
281 constexpr float distancesquared(const glm::vec3& p1, const glm::vec3& p2) |
300 this->closeShape(); |
282 { |
301 } |
283 const float dx = p2.x - p1.x; |
302 else { |
284 const float dy = p2.y - p1.y; |
303 this->polygon.push_back(*this->worldPosition); |
285 const float dz = p2.z - p1.z; |
304 } |
286 return (dx * dx) + (dy * dy) + (dz * dz); |
305 } |
287 } |
306 break; |
288 |
307 } |
289 inline float area(const Quadrilateral& q) |
308 if (event->button() == Qt::RightButton and this->polygon.size() > 1) { |
290 { |
309 this->removeLastPoint(); |
291 return 0.5 * ( |
310 } |
292 glm::length(glm::cross(q.p2 - q.p1, q.p3 - q.p1)) + |
|
293 glm::length(glm::cross(q.p3 - q.p2, q.p4 - q.p2)) |
|
294 ); |
|
295 } |
|
296 |
|
297 inline float energy(const Quadrilateral& q) |
|
298 { |
|
299 const float L2 = distancesquared(q.p1, q.p2) |
|
300 + distancesquared(q.p2, q.p3) |
|
301 + distancesquared(q.p3, q.p4) |
|
302 + distancesquared(q.p4, q.p1); |
|
303 return 1 - 6.928203230275509 * area(q) / L2; |
|
304 } |
311 } |
305 |
312 |
306 struct MergedTriangles |
313 struct MergedTriangles |
307 { |
314 { |
308 std::vector<Quadrilateral> quadrilaterals; |
315 std::vector<Quadrilateral> quadrilaterals; |
363 } |
370 } |
364 } |
371 } |
365 return result; |
372 return result; |
366 } |
373 } |
367 |
374 |
368 const std::vector<ModelAction> EditTools::actions() const |
375 |
|
376 const std::vector<ModelAction> EditTools::circleModeActions() const |
|
377 { |
|
378 std::vector<ModelAction> result; |
|
379 if (this->numpoints == 2) { |
|
380 const glm::vec3 x = polygon[1] - polygon[0]; |
|
381 glm::mat4 transform{ |
|
382 glm::vec4{x, 0}, |
|
383 this->gridMatrix[2], |
|
384 glm::vec4{glm::cross(glm::vec3{-this->gridMatrix[2]}, x), 0}, |
|
385 glm::vec4{this->polygon[0], 1}, |
|
386 }; |
|
387 Colored<CircularPrimitive> circ{ |
|
388 CircularPrimitive{ |
|
389 .type = this->circleToolOptions.type, |
|
390 .fraction = this->circleToolOptions.fraction, |
|
391 .transformation = transform, |
|
392 }, |
|
393 MAIN_COLOR |
|
394 }; |
|
395 result.push_back(AppendToModel{.newElement = circ}); |
|
396 } |
|
397 return result; |
|
398 } |
|
399 |
|
400 const std::vector<ModelAction> EditTools::drawModeActions() const |
369 { |
401 { |
370 std::vector<ModelAction> result; |
402 std::vector<ModelAction> result; |
371 if (this->numpoints == 2) { |
403 if (this->numpoints == 2) { |
372 result.push_back(AppendToModel{ |
404 result.push_back(AppendToModel{ |
373 .newElement = Colored<LineSegment>{ |
405 .newElement = Colored<LineSegment>{ |
413 return result; |
445 return result; |
414 } |
446 } |
415 |
447 |
416 void EditTools::closeShape() |
448 void EditTools::closeShape() |
417 { |
449 { |
418 for (const ModelAction& action : this->actions()) { |
450 for (const ModelAction& action : this->modelActions()) { |
419 Q_EMIT this->modelAction(action); |
451 Q_EMIT this->modelAction(action); |
420 } |
452 } |
421 this->polygon.clear(); |
453 this->polygon.clear(); |
422 this->polygon.push_back(this->worldPosition.value_or(glm::vec3{0, 0, 0})); |
454 this->polygon.push_back(this->worldPosition.value_or(glm::vec3{0, 0, 0})); |
423 } |
455 } |