src/layers/edittools.cpp

changeset 319
9727e545b0bc
parent 316
aab8e139a149
child 320
af6633412a6c
equal deleted inserted replaced
318:216f02b50b0a 319:9727e545b0bc
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

mercurial