Download src/Mod/Mesh/Gui/MeshEditor.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 28.1 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/Gui/MeshEditor.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Mesh/Gui/MeshEditor.cpp
-
curl -L -o MeshEditor.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/Gui/MeshEditor.cpp
28.1 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library is distributed in the hope that it will be useful, * | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| * GNU Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| ***************************************************************************/ | |
| using namespace MeshGui; | |
| namespace sp = std::placeholders; | |
| PROPERTY_SOURCE(MeshGui::ViewProviderFace, Gui::ViewProviderDocumentObject) | |
| ViewProviderFace::ViewProviderFace() | |
| { | |
| // NOLINTBEGIN | |
| pcCoords = new SoCoordinate3(); | |
| pcCoords->ref(); | |
| pcCoords->point.setNum(0); | |
| pcFaces = new SoFaceSet; | |
| pcFaces->ref(); | |
| pcMeshPick = new SoFCMeshPickNode(); | |
| pcMeshPick->ref(); | |
| // NOLINTEND | |
| } | |
| 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()); | |
| // Draw markers | |
| 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); | |
| // Draw face | |
| SoGroup* faces = new SoGroup(); | |
| SoDrawStyle* faceStyle = new SoDrawStyle(); | |
| faceStyle->style = SoDrawStyle::FILLED; | |
| faces->addChild(faceStyle); | |
| SoShapeHints* flathints = new SoShapeHints; | |
| // flathints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE ; | |
| // flathints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; | |
| 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(); | |
| // returns a copy of the point | |
| SoPickedPoint* pick = rp.getPickedPoint(); | |
| // return (pick ? pick->copy() : 0); // needs the same instance of CRT under MS Windows | |
| return (pick ? new SoPickedPoint(*pick) : nullptr); | |
| } | |
| // ---------------------------------------------------------------------- | |
| /* TRANSLATOR MeshGui::MeshFaceAddition */ | |
| 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); | |
| // faceView->mesh->startEditing(); | |
| 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); | |
| // faceView->mesh->finishEditing(); | |
| 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(); | |
| // is the face index valid? | |
| int face_index = fd->getFaceIndex(); | |
| if (face_index >= (int)facets.size()) { | |
| return; | |
| } | |
| // is a border facet picked? | |
| MeshCore::MeshFacet f = facets[face_index]; | |
| if (!f.HasOpenEdge()) { | |
| // check if a neighbour facet is at the border | |
| 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; // already inside | |
| } | |
| 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; // picked point is rejected | |
| } | |
| 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 we are in navigation mode then ignore all but key events | |
| 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); | |
| } | |
| } | |
| } | |
| // toggle between edit and navigation mode | |
| 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 | |
| { | |
| // for sorting of elements | |
| struct NofFacetsCompare | |
| { | |
| bool operator()(const std::vector<Mesh::PointIndex>& rclC1, const std::vector<Mesh::PointIndex>& rclC2) | |
| { | |
| return rclC1.size() < rclC2.size(); | |
| } | |
| }; | |
| } // namespace MeshGui | |
| /* TRANSLATOR MeshGui::MeshFillHole */ | |
| MeshFillHole::MeshFillHole(MeshHoleFiller& hf, Gui::View3DInventor* parent) | |
| : QObject(parent) | |
| , myHoleFiller(hf) | |
| { | |
| // NOLINTBEGIN | |
| 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); | |
| // NOLINTEND | |
| } | |
| 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->setRedirectToSceneGraph(true); | |
| viewer->addEventCallback(SoEvent::getClassTypeId(), MeshFillHole::fileHoleCallback, this); | |
| // NOLINTBEGIN | |
| myConnection = App::GetApplication().signalChangedObject.connect( | |
| std::bind(&MeshFillHole::slotChangedObject, this, sp::_1, sp::_2) | |
| ); | |
| // NOLINTEND | |
| 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->setRedirectToSceneGraph(false); | |
| viewer->removeEventCallback(SoEvent::getClassTypeId(), MeshFillHole::fileHoleCallback, this); | |
| myConnection.disconnect(); | |
| this->deleteLater(); | |
| static_cast<SoGroup*>(viewer->getSceneGraph())->removeChild(myBridgeRoot); | |
| } | |
| void MeshFillHole::closeBridge() | |
| { | |
| // Do the hole-filling | |
| Gui::WaitCursor wc; | |
| auto it = std::ranges::find(myPolygon, myVertex1); | |
| if (auto jt = std::ranges::find(myPolygon, myVertex2); | |
| it != myPolygon.end() && jt != myPolygon.end()) { | |
| // which iterator comes first | |
| if (jt < it) { | |
| std::swap(it, jt); | |
| } | |
| // split the boundary into two loops and take the shorter one | |
| 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); | |
| // this happens when myVertex1 == myVertex2 | |
| 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); | |
| // get mesh kernel | |
| const MeshCore::MeshKernel& rMesh = this->myMesh->Mesh.getValue().getKernel(); | |
| // get the mesh boundaries as an array of point indices | |
| std::list<std::vector<Mesh::PointIndex>> borders; | |
| MeshCore::MeshAlgorithm cAlgo(rMesh); | |
| MeshCore::MeshPointIterator p_iter(rMesh); | |
| cAlgo.GetMeshBorders(borders); | |
| cAlgo.SplitBoundaryLoops(borders); | |
| // sort the borders in ascending order of the number of edges | |
| 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 /*SoNode* root, const SbVec2s& pos*/) 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()) { | |
| // we have something picked, now check if it was an SoPolygon node | |
| SoPolygon* node = static_cast<SoPolygon*>(point->getPath()->getTail()); | |
| if (!poly) { | |
| poly = node; | |
| } | |
| // check which polygon has less edges | |
| 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 | |
| { | |
| // now check which vertex of the polygon is closest to the ray | |
| 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()) { | |
| // now check which vertex of the polygon is closest to the ray | |
| 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()) { | |
| // now check which vertex of the polygon is closest to the ray | |
| 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 { | |
| // myVertex2 can be equal to myVertex1 which does a full hole-filling | |
| 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); | |
| } | |
| } | |
| } | |
| } | |