| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| |
|
| | #include <QMenu>
|
| | #include <QTimer>
|
| | #include <algorithm>
|
| | #include <functional>
|
| | #include <limits>
|
| |
|
| | #include <Inventor/SbLine.h>
|
| | #include <Inventor/SoPickedPoint.h>
|
| | #include <Inventor/details/SoFaceDetail.h>
|
| | #include <Inventor/events/SoKeyboardEvent.h>
|
| | #include <Inventor/events/SoLocation2Event.h>
|
| | #include <Inventor/events/SoMouseButtonEvent.h>
|
| | #include <Inventor/nodes/SoBaseColor.h>
|
| | #include <Inventor/nodes/SoCamera.h>
|
| | #include <Inventor/nodes/SoCoordinate3.h>
|
| | #include <Inventor/nodes/SoDirectionalLight.h>
|
| | #include <Inventor/nodes/SoDrawStyle.h>
|
| | #include <Inventor/nodes/SoFaceSet.h>
|
| | #include <Inventor/nodes/SoPickStyle.h>
|
| | #include <Inventor/nodes/SoPointSet.h>
|
| | #include <Inventor/nodes/SoSeparator.h>
|
| | #include <Inventor/nodes/SoShapeHints.h>
|
| |
|
| | #include <App/Application.h>
|
| | #include <App/Document.h>
|
| | #include <Gui/View3DInventor.h>
|
| | #include <Gui/View3DInventorViewer.h>
|
| | #include <Gui/WaitCursor.h>
|
| | #include <Mod/Mesh/App/Core/Algorithm.h>
|
| | #include <Mod/Mesh/App/MeshFeature.h>
|
| |
|
| | #include "MeshEditor.h"
|
| | #include "SoFCMeshObject.h"
|
| | #include "SoPolygon.h"
|
| |
|
| |
|
| | using namespace MeshGui;
|
| | namespace sp = std::placeholders;
|
| |
|
| | PROPERTY_SOURCE(MeshGui::ViewProviderFace, Gui::ViewProviderDocumentObject)
|
| |
|
| | ViewProviderFace::ViewProviderFace()
|
| | {
|
| |
|
| | pcCoords = new SoCoordinate3();
|
| | pcCoords->ref();
|
| | pcCoords->point.setNum(0);
|
| | pcFaces = new SoFaceSet;
|
| | pcFaces->ref();
|
| | pcMeshPick = new SoFCMeshPickNode();
|
| | pcMeshPick->ref();
|
| |
|
| | }
|
| |
|
| | ViewProviderFace::~ViewProviderFace()
|
| | {
|
| | pcCoords->unref();
|
| | pcFaces->unref();
|
| | pcMeshPick->unref();
|
| | }
|
| |
|
| | void ViewProviderFace::attach(App::DocumentObject* obj)
|
| | {
|
| | ViewProviderDocumentObject::attach(obj);
|
| |
|
| | pcMeshPick->mesh.setValue(static_cast<Mesh::Feature*>(obj)->Mesh.getValuePtr());
|
| |
|
| |
|
| | SoGroup* markers = new SoGroup();
|
| | SoDrawStyle* pointStyle = new SoDrawStyle();
|
| | pointStyle->style = SoDrawStyle::POINTS;
|
| | pointStyle->pointSize = 8.0F;
|
| | markers->addChild(pointStyle);
|
| |
|
| | SoBaseColor* markcol = new SoBaseColor;
|
| | markcol->rgb.setValue(1.0F, 1.0F, 0.0F);
|
| | SoPointSet* marker = new SoPointSet();
|
| | markers->addChild(markcol);
|
| | markers->addChild(pcCoords);
|
| | markers->addChild(marker);
|
| |
|
| |
|
| | SoGroup* faces = new SoGroup();
|
| | SoDrawStyle* faceStyle = new SoDrawStyle();
|
| | faceStyle->style = SoDrawStyle::FILLED;
|
| | faces->addChild(faceStyle);
|
| |
|
| | SoShapeHints* flathints = new SoShapeHints;
|
| |
|
| |
|
| | faces->addChild(flathints);
|
| |
|
| | SoBaseColor* basecol = new SoBaseColor;
|
| | if (mesh) {
|
| | Base::Color col = mesh->ShapeAppearance.getDiffuseColor();
|
| | basecol->rgb.setValue(col.r, col.g, col.b);
|
| | }
|
| | else {
|
| | basecol->rgb.setValue(1.0F, 0.0F, 0.0F);
|
| | }
|
| |
|
| | faces->addChild(basecol);
|
| | faces->addChild(pcCoords);
|
| | faces->addChild(pcFaces);
|
| |
|
| | SoGroup* face_marker = new SoGroup();
|
| | face_marker->addChild(faces);
|
| | face_marker->addChild(markers);
|
| |
|
| | addDisplayMaskMode(markers, "Marker");
|
| | addDisplayMaskMode(face_marker, "Face");
|
| | setDisplayMode("Marker");
|
| | }
|
| |
|
| | void ViewProviderFace::setDisplayMode(const char* ModeName)
|
| | {
|
| | if (strcmp(ModeName, "Face") == 0) {
|
| | setDisplayMaskMode("Face");
|
| | }
|
| | else if (strcmp(ModeName, "Marker") == 0) {
|
| | setDisplayMaskMode("Marker");
|
| | }
|
| | ViewProviderDocumentObject::setDisplayMode(ModeName);
|
| | }
|
| |
|
| | const char* ViewProviderFace::getDefaultDisplayMode() const
|
| | {
|
| | return "Marker";
|
| | }
|
| |
|
| | std::vector<std::string> ViewProviderFace::getDisplayModes() const
|
| | {
|
| | std::vector<std::string> modes;
|
| | modes.emplace_back("Marker");
|
| | modes.emplace_back("Face");
|
| | return modes;
|
| | }
|
| |
|
| | SoPickedPoint* ViewProviderFace::getPickedPoint(
|
| | const SbVec2s& pos,
|
| | const Gui::View3DInventorViewer* viewer
|
| | ) const
|
| | {
|
| | SoSeparator* root = new SoSeparator;
|
| | root->ref();
|
| | root->addChild(viewer->getHeadlight());
|
| | root->addChild(viewer->getSoRenderManager()->getCamera());
|
| | root->addChild(this->pcMeshPick);
|
| |
|
| | SoRayPickAction rp(viewer->getSoRenderManager()->getViewportRegion());
|
| | rp.setPoint(pos);
|
| | rp.apply(root);
|
| | root->unref();
|
| |
|
| |
|
| | SoPickedPoint* pick = rp.getPickedPoint();
|
| |
|
| | return (pick ? new SoPickedPoint(*pick) : nullptr);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | MeshFaceAddition::MeshFaceAddition(Gui::View3DInventor* parent)
|
| | : QObject(parent)
|
| | , faceView(new MeshGui::ViewProviderFace())
|
| | {}
|
| |
|
| | MeshFaceAddition::~MeshFaceAddition()
|
| | {
|
| | delete faceView;
|
| | }
|
| |
|
| | void MeshFaceAddition::startEditing(MeshGui::ViewProviderMesh* vp)
|
| | {
|
| | Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
|
| | Gui::View3DInventorViewer* viewer = view->getViewer();
|
| | viewer->setEditing(true);
|
| | viewer->setSelectionEnabled(false);
|
| | viewer->setRedirectToSceneGraph(true);
|
| | viewer->setRedirectToSceneGraphEnabled(true);
|
| |
|
| | faceView->mesh = vp;
|
| | faceView->attach(vp->getObject());
|
| | viewer->addViewProvider(faceView);
|
| |
|
| | viewer->addEventCallback(SoEvent::getClassTypeId(), MeshFaceAddition::addFacetCallback, this);
|
| | }
|
| |
|
| | void MeshFaceAddition::finishEditing()
|
| | {
|
| | Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
|
| | Gui::View3DInventorViewer* viewer = view->getViewer();
|
| | viewer->setEditing(false);
|
| | viewer->setSelectionEnabled(true);
|
| | viewer->setRedirectToSceneGraph(false);
|
| | viewer->setRedirectToSceneGraphEnabled(false);
|
| |
|
| | viewer->removeViewProvider(faceView);
|
| |
|
| | viewer->removeEventCallback(SoEvent::getClassTypeId(), MeshFaceAddition::addFacetCallback, this);
|
| | this->deleteLater();
|
| | }
|
| |
|
| | void MeshFaceAddition::addFace()
|
| | {
|
| | Mesh::Feature* mf = faceView->mesh->getObject<Mesh::Feature>();
|
| | App::Document* doc = mf->getDocument();
|
| | doc->openTransaction("Add triangle");
|
| | Mesh::MeshObject* mesh = mf->Mesh.startEditing();
|
| | MeshCore::MeshFacet f;
|
| | f._aulPoints[0] = faceView->index[0];
|
| | f._aulPoints[1] = faceView->index[1];
|
| | f._aulPoints[2] = faceView->index[2];
|
| | std::vector<MeshCore::MeshFacet> faces;
|
| | faces.push_back(f);
|
| | mesh->addFacets(faces, true);
|
| | mf->Mesh.finishEditing();
|
| | doc->commitTransaction();
|
| |
|
| | clearPoints();
|
| | }
|
| |
|
| | void MeshFaceAddition::clearPoints()
|
| | {
|
| | faceView->index.clear();
|
| | faceView->current_index = -1;
|
| | faceView->pcCoords->point.setNum(0);
|
| | faceView->setDisplayMode("Marker");
|
| | }
|
| |
|
| | void MeshFaceAddition::flipNormal()
|
| | {
|
| | if (faceView->index.size() < 3) {
|
| | return;
|
| | }
|
| | std::swap(faceView->index[0], faceView->index[1]);
|
| | SbVec3f v1 = faceView->pcCoords->point[0];
|
| | SbVec3f v2 = faceView->pcCoords->point[1];
|
| | faceView->pcCoords->point.set1Value(0, v2);
|
| | faceView->pcCoords->point.set1Value(1, v1);
|
| | }
|
| |
|
| | bool MeshFaceAddition::addMarkerPoint()
|
| | {
|
| | if (faceView->current_index < 0) {
|
| | return false;
|
| | }
|
| | if (faceView->index.size() >= 3) {
|
| | return false;
|
| | }
|
| | faceView->index.push_back(faceView->current_index);
|
| | faceView->current_index = -1;
|
| | if (faceView->index.size() == 3) {
|
| | faceView->setDisplayMode("Face");
|
| | }
|
| | return true;
|
| | }
|
| |
|
| | void MeshFaceAddition::showMarker(SoPickedPoint* pp)
|
| | {
|
| | const SbVec3f& vec = pp->getPoint();
|
| | const SoDetail* detail = pp->getDetail();
|
| | if (detail) {
|
| | if (detail->isOfType(SoFaceDetail::getClassTypeId())) {
|
| | const SoFaceDetail* fd = static_cast<const SoFaceDetail*>(detail);
|
| | Mesh::Feature* mf = faceView->mesh->getObject<Mesh::Feature>();
|
| | const MeshCore::MeshFacetArray& facets = mf->Mesh.getValuePtr()->getKernel().GetFacets();
|
| | const MeshCore::MeshPointArray& points = mf->Mesh.getValuePtr()->getKernel().GetPoints();
|
| |
|
| | int face_index = fd->getFaceIndex();
|
| | if (face_index >= (int)facets.size()) {
|
| | return;
|
| | }
|
| |
|
| | MeshCore::MeshFacet f = facets[face_index];
|
| | if (!f.HasOpenEdge()) {
|
| |
|
| | bool ok = false;
|
| | for (Mesh::FacetIndex nbIndex : f._aulNeighbours) {
|
| | if (facets[nbIndex].HasOpenEdge()) {
|
| | f = facets[nbIndex];
|
| | ok = true;
|
| | break;
|
| | }
|
| | }
|
| | if (!ok) {
|
| | return;
|
| | }
|
| | }
|
| |
|
| | int point_index = -1;
|
| | float distance = std::numeric_limits<float>::max();
|
| | Base::Vector3f pnt;
|
| | SbVec3f face_pnt;
|
| |
|
| | for (int i = 0; i < 3; i++) {
|
| | int index = static_cast<int>(f._aulPoints[i]);
|
| | if (std::ranges::find(faceView->index, index) != faceView->index.end()) {
|
| | continue;
|
| | }
|
| | if (f._aulNeighbours[i] == MeshCore::FACET_INDEX_MAX
|
| | || f._aulNeighbours[(i + 2) % 3] == MeshCore::FACET_INDEX_MAX) {
|
| | pnt = points[index];
|
| | float len = Base::DistanceP2(pnt, Base::Vector3f(vec[0], vec[1], vec[2]));
|
| | if (len < distance) {
|
| | distance = len;
|
| | point_index = index;
|
| | face_pnt.setValue(pnt.x, pnt.y, pnt.z);
|
| | }
|
| | }
|
| | }
|
| |
|
| | if (point_index < 0) {
|
| | return;
|
| | }
|
| |
|
| | int num = faceView->pcCoords->point.getNum();
|
| | if (faceView->current_index >= 0) {
|
| | num = std::max<int>(num - 1, 0);
|
| | }
|
| | faceView->current_index = point_index;
|
| | faceView->pcCoords->point.set1Value(num, face_pnt);
|
| | return;
|
| | }
|
| | }
|
| | }
|
| |
|
| | void MeshFaceAddition::addFacetCallback(void* ud, SoEventCallback* n)
|
| | {
|
| | MeshFaceAddition* that = static_cast<MeshFaceAddition*>(ud);
|
| | ViewProviderFace* face = that->faceView;
|
| | Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
|
| |
|
| | const SoEvent* ev = n->getEvent();
|
| |
|
| | if (!view->isRedirectedToSceneGraph()) {
|
| | if (!ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
|
| | return;
|
| | }
|
| | }
|
| | if (ev->getTypeId() == SoLocation2Event::getClassTypeId()) {
|
| | n->setHandled();
|
| | if (face->index.size() < 3) {
|
| | SoPickedPoint* point = face->getPickedPoint(ev->getPosition(), view);
|
| | if (point) {
|
| | that->showMarker(point);
|
| | delete point;
|
| | }
|
| | }
|
| | }
|
| | else if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) {
|
| | const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(ev);
|
| | if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
|
| | || mbe->getButton() == SoMouseButtonEvent::BUTTON2
|
| | || mbe->getButton() == SoMouseButtonEvent::BUTTON3) {
|
| | n->setHandled();
|
| | }
|
| | if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
|
| | && mbe->getState() == SoButtonEvent::DOWN) {
|
| | that->addMarkerPoint();
|
| | }
|
| | else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
|
| | && mbe->getState() == SoButtonEvent::UP) {
|
| | if (face->index.size() == 3) {
|
| | QMenu menu;
|
| | QAction* add = menu.addAction(MeshFaceAddition::tr("Add Triangle"));
|
| | QAction* swp = menu.addAction(MeshFaceAddition::tr("Flip Normal"));
|
| | QAction* clr = menu.addAction(MeshFaceAddition::tr("Clear"));
|
| | QAction* act = menu.exec(QCursor::pos());
|
| | if (act == add) {
|
| | QTimer::singleShot(300, that, &MeshFaceAddition::addFace);
|
| | }
|
| | else if (act == swp) {
|
| | QTimer::singleShot(300, that, &MeshFaceAddition::flipNormal);
|
| | }
|
| | else if (act == clr) {
|
| | QTimer::singleShot(300, that, &MeshFaceAddition::clearPoints);
|
| | }
|
| | }
|
| | }
|
| | else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2
|
| | && mbe->getState() == SoButtonEvent::UP) {
|
| | QMenu menu;
|
| | QAction* fin = menu.addAction(MeshFaceAddition::tr("Finish"));
|
| | QAction* act = menu.exec(QCursor::pos());
|
| | if (act == fin) {
|
| | QTimer::singleShot(300, that, &MeshFaceAddition::finishEditing);
|
| | }
|
| | }
|
| | }
|
| |
|
| | else if (ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
|
| | const SoKeyboardEvent* const ke = static_cast<const SoKeyboardEvent*>(ev);
|
| | if (ke->getState() == SoButtonEvent::DOWN && ke->getKey() == SoKeyboardEvent::ESCAPE) {
|
| | SbBool toggle = view->isRedirectedToSceneGraph();
|
| | view->setRedirectToSceneGraph(!toggle);
|
| | n->setHandled();
|
| | }
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| | namespace MeshGui
|
| | {
|
| |
|
| | struct NofFacetsCompare
|
| | {
|
| | bool operator()(const std::vector<Mesh::PointIndex>& rclC1, const std::vector<Mesh::PointIndex>& rclC2)
|
| | {
|
| | return rclC1.size() < rclC2.size();
|
| | }
|
| | };
|
| | }
|
| |
|
| |
|
| |
|
| | MeshFillHole::MeshFillHole(MeshHoleFiller& hf, Gui::View3DInventor* parent)
|
| | : QObject(parent)
|
| | , myHoleFiller(hf)
|
| | {
|
| |
|
| | myBoundariesRoot = new SoSeparator;
|
| | myBoundariesRoot->ref();
|
| | myBoundaryRoot = new SoSeparator;
|
| | myBoundaryRoot->ref();
|
| | myBoundariesGroup = new SoSeparator();
|
| | myBoundariesGroup->ref();
|
| | myBridgeRoot = new SoSeparator;
|
| | myBridgeRoot->ref();
|
| |
|
| | SoDrawStyle* pointStyle = new SoDrawStyle();
|
| | pointStyle->style = SoDrawStyle::POINTS;
|
| | pointStyle->pointSize = 8.0f;
|
| | myBridgeRoot->addChild(pointStyle);
|
| |
|
| | SoBaseColor* markcol = new SoBaseColor;
|
| | markcol->rgb.setValue(1.0f, 1.0f, 0.0f);
|
| | myBridgeRoot->addChild(markcol);
|
| |
|
| | myVertex = new SoCoordinate3();
|
| | myBridgeRoot->addChild(myVertex);
|
| | myBridgeRoot->addChild(new SoPointSet);
|
| |
|
| | }
|
| |
|
| | MeshFillHole::~MeshFillHole()
|
| | {
|
| | myBoundariesRoot->unref();
|
| | myBoundariesGroup->unref();
|
| | myBoundaryRoot->unref();
|
| | myBridgeRoot->unref();
|
| | }
|
| |
|
| | void MeshFillHole::startEditing(MeshGui::ViewProviderMesh* vp)
|
| | {
|
| | this->myMesh = vp->getObject<Mesh::Feature>();
|
| |
|
| | Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
|
| | Gui::View3DInventorViewer* viewer = view->getViewer();
|
| | viewer->setEditing(true);
|
| |
|
| | viewer->addEventCallback(SoEvent::getClassTypeId(), MeshFillHole::fileHoleCallback, this);
|
| |
|
| | myConnection = App::GetApplication().signalChangedObject.connect(
|
| | std::bind(&MeshFillHole::slotChangedObject, this, sp::_1, sp::_2)
|
| | );
|
| |
|
| |
|
| | Gui::coinRemoveAllChildren(myBoundariesRoot);
|
| | myBoundariesRoot->addChild(viewer->getHeadlight());
|
| | myBoundariesRoot->addChild(viewer->getSoRenderManager()->getCamera());
|
| | myBoundariesRoot->addChild(myBoundariesGroup);
|
| | Gui::coinRemoveAllChildren(myBoundaryRoot);
|
| | myBoundaryRoot->addChild(viewer->getHeadlight());
|
| | myBoundaryRoot->addChild(viewer->getSoRenderManager()->getCamera());
|
| | createPolygons();
|
| | static_cast<SoGroup*>(viewer->getSceneGraph())->addChild(myBridgeRoot);
|
| | }
|
| |
|
| | void MeshFillHole::finishEditing()
|
| | {
|
| | Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(parent());
|
| | Gui::View3DInventorViewer* viewer = view->getViewer();
|
| | viewer->setEditing(false);
|
| |
|
| | viewer->removeEventCallback(SoEvent::getClassTypeId(), MeshFillHole::fileHoleCallback, this);
|
| | myConnection.disconnect();
|
| | this->deleteLater();
|
| | static_cast<SoGroup*>(viewer->getSceneGraph())->removeChild(myBridgeRoot);
|
| | }
|
| |
|
| | void MeshFillHole::closeBridge()
|
| | {
|
| |
|
| | Gui::WaitCursor wc;
|
| | auto it = std::ranges::find(myPolygon, myVertex1);
|
| | if (auto jt = std::ranges::find(myPolygon, myVertex2);
|
| | it != myPolygon.end() && jt != myPolygon.end()) {
|
| |
|
| | if (jt < it) {
|
| | std::swap(it, jt);
|
| | }
|
| |
|
| | std::list<TBoundary> bounds;
|
| | TBoundary loop1;
|
| | TBoundary loop2;
|
| | loop1.insert(loop1.end(), myPolygon.begin(), it);
|
| | loop1.insert(loop1.end(), jt, myPolygon.end());
|
| | loop2.insert(loop2.end(), it, jt);
|
| |
|
| | if (loop2.empty()) {
|
| | bounds.push_back(loop1);
|
| | }
|
| | else if (loop1.size() < loop2.size()) {
|
| | bounds.push_back(loop1);
|
| | }
|
| | else {
|
| | bounds.push_back(loop2);
|
| | }
|
| |
|
| | App::Document* doc = myMesh->getDocument();
|
| | doc->openTransaction("Bridge && Fill hole");
|
| | Mesh::MeshObject* pMesh = myMesh->Mesh.startEditing();
|
| | bool ok = myHoleFiller.fillHoles(*pMesh, bounds, myVertex1, myVertex2);
|
| | myMesh->Mesh.finishEditing();
|
| | if (ok) {
|
| | doc->commitTransaction();
|
| | }
|
| | else {
|
| | doc->abortTransaction();
|
| | }
|
| | }
|
| | }
|
| |
|
| | void MeshFillHole::slotChangedObject(const App::DocumentObject& Obj, const App::Property& Prop)
|
| | {
|
| | if (&Obj == myMesh && strcmp(Prop.getName(), "Mesh") == 0) {
|
| | Gui::coinRemoveAllChildren(myBoundariesGroup);
|
| | myVertex->point.setNum(0);
|
| | myNumPoints = 0;
|
| | myPolygon.clear();
|
| |
|
| | createPolygons();
|
| | }
|
| | }
|
| |
|
| | void MeshFillHole::createPolygons()
|
| | {
|
| | Gui::WaitCursor wc;
|
| | myPolygons.clear();
|
| |
|
| | SoPickStyle* pickStyle = new SoPickStyle();
|
| | pickStyle->style = SoPickStyle::BOUNDING_BOX;
|
| | myBoundariesGroup->addChild(pickStyle);
|
| | myBoundaryRoot->addChild(pickStyle);
|
| |
|
| |
|
| | const MeshCore::MeshKernel& rMesh = this->myMesh->Mesh.getValue().getKernel();
|
| |
|
| |
|
| | std::list<std::vector<Mesh::PointIndex>> borders;
|
| | MeshCore::MeshAlgorithm cAlgo(rMesh);
|
| | MeshCore::MeshPointIterator p_iter(rMesh);
|
| | cAlgo.GetMeshBorders(borders);
|
| | cAlgo.SplitBoundaryLoops(borders);
|
| |
|
| |
|
| | borders.sort(NofFacetsCompare());
|
| |
|
| | int32_t count = 0;
|
| | for (auto& border : borders) {
|
| | if (border.front() == border.back()) {
|
| | border.pop_back();
|
| | }
|
| | count += border.size();
|
| | }
|
| |
|
| | SoCoordinate3* coords = new SoCoordinate3();
|
| | myBoundariesGroup->addChild(coords);
|
| | myBoundaryRoot->addChild(coords);
|
| |
|
| | coords->point.setNum(count);
|
| | int32_t index = 0;
|
| | for (const auto& border : borders) {
|
| | SoPolygon* polygon = new SoPolygon();
|
| | polygon->startIndex = index;
|
| | polygon->numVertices = border.size();
|
| | myBoundariesGroup->addChild(polygon);
|
| | myPolygons[polygon] = border;
|
| | for (Mesh::PointIndex jt : border) {
|
| | p_iter.Set(jt);
|
| | coords->point.set1Value(index++, p_iter->x, p_iter->y, p_iter->z);
|
| | }
|
| | }
|
| | }
|
| |
|
| | SoNode* MeshFillHole::getPickedPolygon(const SoRayPickAction& action ) const
|
| | {
|
| | SoPolygon* poly = nullptr;
|
| | const SoPickedPointList& points = action.getPickedPointList();
|
| | for (int i = 0; i < points.getLength(); i++) {
|
| | const SoPickedPoint* point = points[i];
|
| | if (point
|
| | && point->getPath()->getTail()->getTypeId() == MeshGui::SoPolygon::getClassTypeId()) {
|
| |
|
| | SoPolygon* node = static_cast<SoPolygon*>(point->getPath()->getTail());
|
| | if (!poly) {
|
| | poly = node;
|
| | }
|
| |
|
| | else if (node->numVertices.getValue() < poly->numVertices.getValue()) {
|
| | poly = node;
|
| | }
|
| | }
|
| | }
|
| |
|
| | return poly;
|
| | }
|
| |
|
| | float MeshFillHole::findClosestPoint(
|
| | const SbLine& ray,
|
| | const TBoundary& polygon,
|
| | Mesh::PointIndex& vertex_index,
|
| | SbVec3f& closestPoint
|
| | ) const
|
| | {
|
| |
|
| | float minDist = std::numeric_limits<float>::max();
|
| | vertex_index = MeshCore::POINT_INDEX_MAX;
|
| |
|
| | const MeshCore::MeshKernel& rMesh = myMesh->Mesh.getValue().getKernel();
|
| | const MeshCore::MeshPointArray& pts = rMesh.GetPoints();
|
| | for (Mesh::PointIndex it : polygon) {
|
| | SbVec3f vertex;
|
| | const Base::Vector3f& v = pts[it];
|
| | vertex.setValue(v.x, v.y, v.z);
|
| | SbVec3f point = ray.getClosestPoint(vertex);
|
| | float distance = (vertex - point).sqrLength();
|
| | if (distance < minDist) {
|
| | minDist = distance;
|
| | vertex_index = it;
|
| | closestPoint = vertex;
|
| | }
|
| | }
|
| |
|
| | return minDist;
|
| | }
|
| |
|
| | void MeshFillHole::fileHoleCallback(void* ud, SoEventCallback* n)
|
| | {
|
| | MeshFillHole* self = static_cast<MeshFillHole*>(ud);
|
| | Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
|
| |
|
| | const SoEvent* ev = n->getEvent();
|
| | if (ev->getTypeId() == SoLocation2Event::getClassTypeId()) {
|
| | n->setHandled();
|
| | SoRayPickAction rp(view->getSoRenderManager()->getViewportRegion());
|
| | rp.setPoint(ev->getPosition());
|
| | rp.setPickAll(true);
|
| | if (self->myNumPoints == 0) {
|
| | rp.apply(self->myBoundariesRoot);
|
| | }
|
| | else {
|
| | rp.apply(self->myBoundaryRoot);
|
| | }
|
| | SoNode* node = self->getPickedPolygon(rp);
|
| | if (node) {
|
| | auto it = self->myPolygons.find(node);
|
| | if (it != self->myPolygons.end()) {
|
| |
|
| | Mesh::PointIndex vertex_index {};
|
| | SbVec3f closestPoint;
|
| | float minDist
|
| | = self->findClosestPoint(rp.getLine(), it->second, vertex_index, closestPoint);
|
| | if (minDist < 1.0F) {
|
| | if (self->myNumPoints == 0) {
|
| | self->myVertex->point.set1Value(0, closestPoint);
|
| | }
|
| | else {
|
| | self->myVertex->point.set1Value(1, closestPoint);
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| | else if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) {
|
| | n->setHandled();
|
| | const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(ev);
|
| | if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::UP) {
|
| | if (self->myNumPoints > 1) {
|
| | return;
|
| | }
|
| | SoRayPickAction rp(view->getSoRenderManager()->getViewportRegion());
|
| | rp.setPoint(ev->getPosition());
|
| | rp.setPickAll(true);
|
| | if (self->myNumPoints == 0) {
|
| | rp.apply(self->myBoundariesRoot);
|
| | }
|
| | else {
|
| | rp.apply(self->myBoundaryRoot);
|
| | }
|
| | SoNode* node = self->getPickedPolygon(rp);
|
| | if (node) {
|
| | auto it = self->myPolygons.find(node);
|
| | if (it != self->myPolygons.end()) {
|
| |
|
| | Mesh::PointIndex vertex_index {};
|
| | SbVec3f closestPoint;
|
| | float minDist
|
| | = self->findClosestPoint(rp.getLine(), it->second, vertex_index, closestPoint);
|
| | if (minDist < 1.0F) {
|
| | if (self->myNumPoints == 0) {
|
| | self->myBoundaryRoot->addChild(node);
|
| | self->myVertex->point.set1Value(0, closestPoint);
|
| | self->myNumPoints = 1;
|
| | self->myVertex1 = vertex_index;
|
| | }
|
| | else {
|
| |
|
| | self->myBoundaryRoot->removeChild(node);
|
| | self->myVertex->point.set1Value(1, closestPoint);
|
| | self->myNumPoints = 2;
|
| | self->myVertex2 = vertex_index;
|
| | self->myPolygon = it->second;
|
| | QTimer::singleShot(300, self, &MeshFillHole::closeBridge);
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| | else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2
|
| | && mbe->getState() == SoButtonEvent::UP) {
|
| | QMenu menu;
|
| | QAction* fin = menu.addAction(MeshFillHole::tr("Finish"));
|
| | QAction* act = menu.exec(QCursor::pos());
|
| | if (act == fin) {
|
| | QTimer::singleShot(300, self, &MeshFillHole::finishEditing);
|
| | }
|
| | }
|
| | }
|
| | }
|
| |
|
| | #include "moc_MeshEditor.cpp"
|
| |
|