16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 */ |
17 */ |
18 |
18 |
19 #include <QMouseEvent> |
19 #include <QMouseEvent> |
20 #include <QPainter> |
20 #include <QPainter> |
21 #include "thirdparty/earcut.h" |
|
22 #include "src/model.h" |
21 #include "src/model.h" |
23 #include "src/ui/objecteditor.h" |
22 #include "src/ui/objecteditor.h" |
24 #include "src/gl/partrenderer.h" |
23 #include "src/gl/partrenderer.h" |
25 #include "src/circularprimitive.h" |
24 #include "src/circularprimitive.h" |
26 #include "src/layers/edittools.h" |
25 #include "src/layers/edittools.h" |
27 #include "src/invert.h" |
26 #include "src/invert.h" |
28 |
27 #include "src/triangulate.h" |
29 // Make mapbox::earcut work with glm::vec3 |
|
30 template<> struct mapbox::util::nth<0, glm::vec3> |
|
31 { |
|
32 static constexpr float get(const glm::vec3& t) { return t.x; } |
|
33 }; |
|
34 |
|
35 template<> struct mapbox::util::nth<1, glm::vec3> |
|
36 { |
|
37 static constexpr float get(const glm::vec3& t) { return t.y; } |
|
38 }; |
|
39 |
28 |
40 EditTools::EditTools(QObject* parent) : |
29 EditTools::EditTools(QObject* parent) : |
41 QObject{parent}, |
30 QObject{parent}, |
42 RenderLayer{} |
31 RenderLayer{} |
43 { |
32 { |
412 break; |
401 break; |
413 } |
402 } |
414 if (event->button() == Qt::RightButton and this->polygon.size() > 1) { |
403 if (event->button() == Qt::RightButton and this->polygon.size() > 1) { |
415 this->removeLastPoint(); |
404 this->removeLastPoint(); |
416 } |
405 } |
417 } |
|
418 |
|
419 struct MergedTriangles |
|
420 { |
|
421 std::vector<Quadrilateral> quadrilaterals; |
|
422 std::set<std::size_t> cutTriangles; |
|
423 }; |
|
424 |
|
425 static MergedTriangles mergeTriangles( |
|
426 const std::vector<std::uint16_t>& indices, |
|
427 const std::vector<glm::vec3>& polygon) |
|
428 { |
|
429 MergedTriangles result; |
|
430 using indextype = std::uint16_t; |
|
431 using indexpair = std::pair<indextype, indextype>; |
|
432 struct boundaryinfo { indextype third; std::size_t triangleid; }; |
|
433 std::map<indexpair, boundaryinfo> boundaries; |
|
434 for (std::size_t i = 0; i < indices.size(); i += 3) { |
|
435 const auto add = [&](const std::size_t o1, const std::size_t o2, const std::size_t o3){ |
|
436 const auto key = std::make_pair(indices[i + o1], indices[i + o2]); |
|
437 boundaries[key] = {indices[i + o3], i}; |
|
438 }; |
|
439 add(0, 1, 2); |
|
440 add(1, 2, 0); |
|
441 add(2, 0, 1); |
|
442 } |
|
443 std::vector<std::array<indextype, 4>> quadindices; |
|
444 std::vector<Quadrilateral> quads; |
|
445 bool repeat = true; |
|
446 const auto iscut = [&result](const std::size_t i){ |
|
447 return result.cutTriangles.find(i) != result.cutTriangles.end(); |
|
448 }; |
|
449 while (repeat) { |
|
450 repeat = false; |
|
451 // Go through triangle boundaries |
|
452 for (const auto& it1 : boundaries) { |
|
453 const indexpair& pair1 = it1.first; |
|
454 const boundaryinfo& boundary1 = it1.second; |
|
455 // .. the ones we haven't already merged anyway |
|
456 if (not iscut(boundary1.triangleid)) { |
|
457 // Look for its inverse boundary to find the touching triangle |
|
458 const auto pair2 = std::make_pair(pair1.second, pair1.first); |
|
459 const auto it2 = boundaries.find(pair2); |
|
460 // Also if that hasn't been cut |
|
461 if (it2 != boundaries.end() and not iscut(it2->second.triangleid)) { |
|
462 const Quadrilateral quad{ |
|
463 polygon[pair1.first], |
|
464 polygon[it2->second.third], |
|
465 polygon[pair1.second], |
|
466 polygon[boundary1.third], |
|
467 }; |
|
468 if (isConvex(quad)) { |
|
469 result.quadrilaterals.push_back(quad); |
|
470 result.cutTriangles.insert(boundary1.triangleid); |
|
471 result.cutTriangles.insert(it2->second.triangleid); |
|
472 repeat = true; |
|
473 } |
|
474 } |
|
475 } |
|
476 } |
|
477 } |
|
478 return result; |
|
479 } |
406 } |
480 |
407 |
481 |
408 |
482 const std::vector<ModelAction> EditTools::circleModeActions() const |
409 const std::vector<ModelAction> EditTools::circleModeActions() const |
483 { |
410 { |
509 std::vector<ModelAction> result; |
436 std::vector<ModelAction> result; |
510 if (this->numpoints == 2) { |
437 if (this->numpoints == 2) { |
511 result.push_back(AppendToModel{edge(this->polygon[0], this->polygon[1])}); |
438 result.push_back(AppendToModel{edge(this->polygon[0], this->polygon[1])}); |
512 } |
439 } |
513 else if (this->numpoints > 2) { |
440 else if (this->numpoints > 2) { |
514 const glm::mat4 inverseGrid = glm::inverse(this->gridMatrix); |
441 for (const PlainPolygonElement& poly : polygonize( |
515 std::vector<std::vector<glm::vec3>> polygons{1}; |
442 this->polygon.begin(), |
516 std::vector<glm::vec3>& polygon2d = polygons.back(); |
443 this->polygon.begin() + static_cast<long>(this->numpoints), |
517 polygon2d.reserve(this->numpoints); |
444 this->gridMatrix) |
518 for (std::size_t i = 0; i < this->numpoints; ++i) { |
445 ) { |
519 polygon2d.push_back(inverseGrid * glm::vec4{this->polygon[i], 1}); |
|
520 } |
|
521 // mapbox::earcut will always produce a CCW polygon, so if we're drawing |
|
522 // a CW polygon, we should invert the result afterwards |
|
523 const float shouldInvert = glm::dot( |
|
524 glm::vec3{inverseGrid[2]}, |
|
525 glm::cross(this->polygon[0] - this->polygon[1], this->polygon[2] - this->polygon[1])); |
|
526 using indextype = std::uint16_t; |
|
527 const std::vector<indextype> indices = mapbox::earcut<std::uint16_t>(polygons); |
|
528 MergedTriangles mergedTriangles = mergeTriangles(indices, this->polygon); |
|
529 for (Quadrilateral& quad : mergedTriangles.quadrilaterals) { |
|
530 if (shouldInvert < 0) { |
|
531 invert(quad); |
|
532 } |
|
533 result.push_back(AppendToModel{ |
446 result.push_back(AppendToModel{ |
534 .newElement = Colored<Quadrilateral>{quad, MAIN_COLOR}, |
447 .newElement = elementFromPolygonAndColor(poly, MAIN_COLOR), |
535 }); |
448 }); |
536 } |
|
537 for (std::size_t i = 0; i < indices.size(); i += 3) { |
|
538 if (mergedTriangles.cutTriangles.find(i) == mergedTriangles.cutTriangles.end()) { |
|
539 Colored<Triangle> tri = triangle( |
|
540 this->polygon[indices[i]], |
|
541 this->polygon[indices[i + 1]], |
|
542 this->polygon[indices[i + 2]]); |
|
543 if (shouldInvert < 0) { |
|
544 invert(tri); |
|
545 } |
|
546 result.push_back(AppendToModel{tri}); |
|
547 } |
|
548 } |
449 } |
549 } |
450 } |
550 return result; |
451 return result; |
551 } |
452 } |
552 |
453 |