| 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 |