Download src/Mod/MeshPart/Gui/CurveOnMesh.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 22 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/MeshPart/Gui/CurveOnMesh.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/MeshPart/Gui/CurveOnMesh.cpp
-
curl -L -o CurveOnMesh.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/MeshPart/Gui/CurveOnMesh.cpp
22 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2017 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 * | |
| * * | |
| ***************************************************************************/ | |
| /* XPM */ | |
| // clang-format off | |
| static const char* cursor_curveonmesh[] = { | |
| "32 32 3 1", | |
| "+ c white", | |
| "# c red", | |
| ". c None", | |
| "......+.........................", | |
| "......+.........................", | |
| "......+.........................", | |
| "......+.........................", | |
| "......+.........................", | |
| "................................", | |
| "+++++...+++++...................", | |
| "................................", | |
| "......+...............###.......", | |
| "......+...............#.#.......", | |
| "......+...............###.......", | |
| "......+..............#..#.......", | |
| "......+.............#....#......", | |
| "....................#.+..#......", | |
| "..................+#+..+..#...+.", | |
| "................++#.....+.#..+..", | |
| "......+........+..#......++#+...", | |
| ".......+......+..#.........#....", | |
| "........++..++..#..........###..", | |
| "..........++....#..........#.#..", | |
| "......#........#...........###..", | |
| ".......#......#.................", | |
| "........#.....#.................", | |
| ".........#...#..................", | |
| "..........###...................", | |
| "..........#.#...................", | |
| "..........###...................", | |
| "................................", | |
| "................................", | |
| "................................", | |
| "................................", | |
| "................................"}; | |
| // clang-format on | |
| using namespace MeshPartGui; | |
| PROPERTY_SOURCE(MeshPartGui::ViewProviderCurveOnMesh, Gui::ViewProviderDocumentObject) | |
| ViewProviderCurveOnMesh::ViewProviderCurveOnMesh() | |
| { | |
| // the lines | |
| pcCoords = new SoCoordinate3; | |
| pcCoords->ref(); | |
| pcCoords->point.setNum(0); | |
| pcLinesStyle = new SoDrawStyle; | |
| pcLinesStyle->style = SoDrawStyle::LINES; | |
| pcLinesStyle->lineWidth = 3; | |
| pcLinesStyle->ref(); | |
| SoGroup* pcLineRoot = new SoSeparator(); | |
| pcLineRoot->addChild(pcLinesStyle); | |
| SoBaseColor* linecol = new SoBaseColor; | |
| linecol->rgb.setValue(1.0f, 1.0f, 0.0f); | |
| pcLineRoot->addChild(linecol); | |
| pcLineRoot->addChild(pcCoords); | |
| pcLineRoot->addChild(new SoLineSet); | |
| // the nodes | |
| pcNodes = new SoCoordinate3; | |
| pcNodes->ref(); | |
| pcNodes->point.setNum(0); | |
| pcPointStyle = new SoDrawStyle; | |
| pcPointStyle->style = SoDrawStyle::POINTS; | |
| pcPointStyle->pointSize = 15; | |
| pcPointStyle->ref(); | |
| SoGroup* pcPointRoot = new SoSeparator(); | |
| pcPointRoot->addChild(pcPointStyle); | |
| SoBaseColor* pointcol = new SoBaseColor; | |
| pointcol->rgb.setValue(1.0f, 0.5f, 0.0f); | |
| pcPointRoot->addChild(pointcol); | |
| pcPointRoot->addChild(pcNodes); | |
| pcPointRoot->addChild(new SoPointSet); | |
| SoGroup* group = new SoGroup; | |
| group->addChild(pcLineRoot); | |
| group->addChild(pcPointRoot); | |
| addDisplayMaskMode(group, "Point"); | |
| } | |
| ViewProviderCurveOnMesh::~ViewProviderCurveOnMesh() | |
| { | |
| pcCoords->unref(); | |
| pcLinesStyle->unref(); | |
| pcNodes->unref(); | |
| pcPointStyle->unref(); | |
| } | |
| void ViewProviderCurveOnMesh::setDisplayMode(const char* ModeName) | |
| { | |
| setDisplayMaskMode(ModeName); | |
| ViewProviderDocumentObject::setDisplayMode(ModeName); | |
| } | |
| void ViewProviderCurveOnMesh::addVertex(const SbVec3f& v) | |
| { | |
| int num = pcNodes->point.getNum(); | |
| pcNodes->point.set1Value(num, v); | |
| } | |
| void ViewProviderCurveOnMesh::clearVertex() | |
| { | |
| pcNodes->point.setNum(0); | |
| } | |
| void ViewProviderCurveOnMesh::setPoints(const std::vector<SbVec3f>& pts) | |
| { | |
| pcCoords->point.setNum(pts.size()); | |
| SbVec3f* coords = pcCoords->point.startEditing(); | |
| int index = 0; | |
| for (auto it : pts) { | |
| coords[index] = it; | |
| index++; | |
| } | |
| pcCoords->point.finishEditing(); | |
| } | |
| void ViewProviderCurveOnMesh::clearPoints() | |
| { | |
| pcCoords->point.setNum(0); | |
| } | |
| // ------------------------------------------------------------------ | |
| class CurveOnMeshHandler::Private | |
| { | |
| public: | |
| struct PickedPoint | |
| { | |
| MeshCore::FacetIndex facet; | |
| SbVec3f point; | |
| SbVec3f normal; | |
| }; | |
| struct ApproxPar | |
| { | |
| double weight1; | |
| double weight2; | |
| double weight3; | |
| double tol3d; | |
| int maxDegree; | |
| GeomAbs_Shape cont; | |
| ApproxPar() | |
| { | |
| weight1 = 0.2; | |
| weight2 = 0.4; | |
| weight3 = 0.2; | |
| tol3d = 1.0e-2; | |
| maxDegree = 5; | |
| cont = GeomAbs_C2; | |
| } | |
| }; | |
| Private() | |
| : curve(new ViewProviderCurveOnMesh) | |
| , editcursor(QPixmap(cursor_curveonmesh), 7, 7) | |
| {} | |
| ~Private() | |
| { | |
| delete curve; | |
| delete grid; | |
| } | |
| static void vertexCallback(void* ud, SoEventCallback* n); | |
| std::vector<SbVec3f> convert(const std::vector<Base::Vector3f>& points) const | |
| { | |
| std::vector<SbVec3f> pts; | |
| pts.reserve(points.size()); | |
| for (const auto& it : points) { | |
| pts.push_back(Base::convertTo<SbVec3f>(it)); | |
| } | |
| return pts; | |
| } | |
| void createGrid() | |
| { | |
| Mesh::Feature* mf = mesh->getObject<Mesh::Feature>(); | |
| const Mesh::MeshObject& meshObject = mf->Mesh.getValue(); | |
| kernel = meshObject.getKernel(); | |
| kernel.Transform(meshObject.getTransform()); | |
| MeshCore::MeshAlgorithm alg(kernel); | |
| float fAvgLen = alg.GetAverageEdgeLength(); | |
| grid = new MeshCore::MeshFacetGrid(kernel, 5.0f * fAvgLen); | |
| } | |
| bool projectLineOnMesh(const PickedPoint& pick) | |
| { | |
| PickedPoint last = pickedPoints.back(); | |
| std::vector<Base::Vector3f> polyline; | |
| MeshCore::MeshProjection meshProjection(kernel); | |
| Base::Vector3f v1 = Base::convertTo<Base::Vector3f>(last.point); | |
| Base::Vector3f v2 = Base::convertTo<Base::Vector3f>(pick.point); | |
| Base::Vector3f vd = Base::convertTo<Base::Vector3f>(viewer->getViewer()->getViewDirection()); | |
| if (meshProjection.projectLineOnMesh(*grid, v1, last.facet, v2, pick.facet, vd, polyline)) { | |
| if (polyline.size() > 1) { | |
| if (cutLines.empty()) { | |
| cutLines.push_back(polyline); | |
| } | |
| else { | |
| SbVec3f dir1(0.0f, 0.0f, 0.0f); | |
| SbVec3f dir2 = pick.point - last.point; | |
| dir2.normalize(); | |
| std::size_t num = pickedPoints.size(); | |
| if (num >= 2) { | |
| dir1 = pickedPoints[num - 1].point - pickedPoints[num - 2].point; | |
| dir1.normalize(); | |
| } | |
| // if the angle between two line segments is greater than the angle | |
| // split the curve in this position | |
| if (dir1.dot(dir2) < cosAngle) { | |
| cutLines.push_back(polyline); | |
| } | |
| else { | |
| std::vector<Base::Vector3f>& segm = cutLines.back(); | |
| segm.insert(segm.end(), polyline.begin() + 1, polyline.end()); | |
| } | |
| } | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| std::vector<PickedPoint> pickedPoints; | |
| std::list<std::vector<Base::Vector3f>> cutLines; | |
| bool wireClosed {false}; | |
| double distance {1}; | |
| double cosAngle {0.7071}; // 45 degree | |
| bool approximate {true}; | |
| ViewProviderCurveOnMesh* curve; | |
| Gui::ViewProviderDocumentObject* mesh {0}; | |
| MeshCore::MeshFacetGrid* grid {nullptr}; | |
| MeshCore::MeshKernel kernel; | |
| QPointer<Gui::View3DInventor> viewer; | |
| QCursor editcursor; | |
| ApproxPar par; | |
| }; | |
| CurveOnMeshHandler::CurveOnMeshHandler(QObject* parent) | |
| : QObject(parent) | |
| , d_ptr(new Private) | |
| {} | |
| CurveOnMeshHandler::~CurveOnMeshHandler() | |
| { | |
| disableCallback(); | |
| } | |
| void CurveOnMeshHandler::enableApproximation(bool on) | |
| { | |
| d_ptr->approximate = on; | |
| } | |
| void CurveOnMeshHandler::setParameters(int maxDegree, GeomAbs_Shape cont, double tol3d, double angle) | |
| { | |
| d_ptr->par.maxDegree = maxDegree; | |
| d_ptr->par.cont = cont; | |
| d_ptr->par.tol3d = tol3d; | |
| d_ptr->cosAngle = cos(angle); | |
| } | |
| void CurveOnMeshHandler::onContextMenu() | |
| { | |
| QMenu menu; | |
| menu.addAction(tr("Create"), this, &CurveOnMeshHandler::onCreate); | |
| if (!d_ptr->wireClosed && d_ptr->pickedPoints.size() >= 3) { | |
| menu.addAction(tr("Close wire"), this, &CurveOnMeshHandler::onCloseWire); | |
| } | |
| menu.addAction(tr("Clear"), this, &CurveOnMeshHandler::onClear); | |
| menu.addAction(tr("Cancel"), this, &CurveOnMeshHandler::onCancel); | |
| menu.exec(QCursor::pos()); | |
| } | |
| void CurveOnMeshHandler::onCreate() | |
| { | |
| for (auto it = d_ptr->cutLines.begin(); it != d_ptr->cutLines.end(); ++it) { | |
| std::vector<SbVec3f> segm = d_ptr->convert(*it); | |
| if (d_ptr->approximate) { | |
| Handle(Geom_BSplineCurve) spline = approximateSpline(segm); | |
| if (!spline.IsNull()) { | |
| displaySpline(spline); | |
| } | |
| } | |
| else { | |
| TopoDS_Wire wire; | |
| if (makePolyline(segm, wire)) { | |
| displayPolyline(wire); | |
| } | |
| } | |
| } | |
| d_ptr->curve->clearVertex(); | |
| d_ptr->curve->clearPoints(); | |
| d_ptr->pickedPoints.clear(); | |
| d_ptr->cutLines.clear(); | |
| d_ptr->wireClosed = false; | |
| disableCallback(); | |
| } | |
| void CurveOnMeshHandler::onCloseWire() | |
| { | |
| if (d_ptr->wireClosed || d_ptr->pickedPoints.size() < 3) { | |
| return; | |
| } | |
| closeWire(); | |
| } | |
| void CurveOnMeshHandler::onClear() | |
| { | |
| d_ptr->curve->clearVertex(); | |
| d_ptr->curve->clearPoints(); | |
| d_ptr->pickedPoints.clear(); | |
| d_ptr->cutLines.clear(); | |
| d_ptr->wireClosed = false; | |
| } | |
| void CurveOnMeshHandler::onCancel() | |
| { | |
| d_ptr->curve->clearVertex(); | |
| d_ptr->curve->clearPoints(); | |
| d_ptr->pickedPoints.clear(); | |
| d_ptr->cutLines.clear(); | |
| d_ptr->wireClosed = false; | |
| disableCallback(); | |
| } | |
| void CurveOnMeshHandler::enableCallback(Gui::View3DInventor* v) | |
| { | |
| if (v && !d_ptr->viewer) { | |
| d_ptr->viewer = v; | |
| Gui::View3DInventorViewer* view3d = d_ptr->viewer->getViewer(); | |
| view3d->addEventCallback(SoEvent::getClassTypeId(), Private::vertexCallback, this); | |
| view3d->addViewProvider(d_ptr->curve); | |
| view3d->setEditing(true); | |
| view3d->setEditingCursor(d_ptr->editcursor); | |
| d_ptr->curve->setDisplayMode("Point"); | |
| } | |
| } | |
| void CurveOnMeshHandler::disableCallback() | |
| { | |
| if (d_ptr->viewer) { | |
| Gui::View3DInventorViewer* view3d = d_ptr->viewer->getViewer(); | |
| view3d->setEditing(false); | |
| view3d->removeViewProvider(d_ptr->curve); | |
| view3d->removeEventCallback(SoEvent::getClassTypeId(), Private::vertexCallback, this); | |
| } | |
| d_ptr->viewer = nullptr; | |
| } | |
| std::vector<SbVec3f> CurveOnMeshHandler::getVertexes() const | |
| { | |
| std::vector<SbVec3f> pts; | |
| pts.reserve(d_ptr->pickedPoints.size()); | |
| for (const auto& it : d_ptr->pickedPoints) { | |
| pts.push_back(it.point); | |
| } | |
| return pts; | |
| } | |
| std::vector<SbVec3f> CurveOnMeshHandler::getPoints() const | |
| { | |
| std::vector<SbVec3f> pts; | |
| for (auto it = d_ptr->cutLines.begin(); it != d_ptr->cutLines.end(); ++it) { | |
| std::vector<SbVec3f> segm = d_ptr->convert(*it); | |
| pts.insert(pts.end(), segm.begin(), segm.end()); | |
| } | |
| return pts; | |
| } | |
| Handle(Geom_BSplineCurve) CurveOnMeshHandler::approximateSpline(const std::vector<SbVec3f>& points) | |
| { | |
| TColgp_Array1OfPnt pnts(1, points.size()); | |
| Standard_Integer index = 1; | |
| for (const auto& it : points) { | |
| float x, y, z; | |
| it.getValue(x, y, z); | |
| pnts(index++) = gp_Pnt(x, y, z); | |
| } | |
| try { | |
| // GeomAPI_PointsToBSpline fit(pnts, 1, 2, GeomAbs_C0, 1.0e-3); | |
| // GeomAPI_PointsToBSpline fit(pnts, d_ptr->par.weight1, d_ptr->par.weight2, | |
| // d_ptr->par.weight3, | |
| // d_ptr->par.maxDegree, d_ptr->par.cont, d_ptr->par.tol3d); | |
| GeomAPI_PointsToBSpline fit(pnts, 1, d_ptr->par.maxDegree, d_ptr->par.cont, d_ptr->par.tol3d); | |
| Handle(Geom_BSplineCurve) spline = fit.Curve(); | |
| return spline; | |
| } | |
| catch (...) { | |
| return Handle(Geom_BSplineCurve)(); | |
| } | |
| } | |
| void CurveOnMeshHandler::approximateEdge(const TopoDS_Edge& edge, double tolerance) | |
| { | |
| BRepMesh_IncrementalMesh(edge, tolerance); | |
| TopLoc_Location loc; | |
| Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D(edge, loc); | |
| if (!aPoly.IsNull()) { | |
| int numNodes = aPoly->NbNodes(); | |
| const TColgp_Array1OfPnt& aNodes = aPoly->Nodes(); | |
| std::vector<SbVec3f> pts; | |
| pts.reserve(numNodes); | |
| for (int i = aNodes.Lower(); i <= aNodes.Upper(); i++) { | |
| const gp_Pnt& p = aNodes.Value(i); | |
| pts.emplace_back( | |
| static_cast<float>(p.X()), | |
| static_cast<float>(p.Y()), | |
| static_cast<float>(p.Z()) | |
| ); | |
| } | |
| d_ptr->curve->setPoints(pts); | |
| } | |
| } | |
| void CurveOnMeshHandler::displaySpline(const Handle(Geom_BSplineCurve) & spline) | |
| { | |
| if (d_ptr->viewer) { | |
| double u = spline->FirstParameter(); | |
| double v = spline->LastParameter(); | |
| BRepBuilderAPI_MakeEdge mkBuilder(spline, u, v); | |
| TopoDS_Edge edge = mkBuilder.Edge(); | |
| Gui::View3DInventorViewer* view3d = d_ptr->viewer->getViewer(); | |
| App::Document* doc = view3d->getDocument()->getDocument(); | |
| doc->openTransaction("Add spline"); | |
| Part::Feature* part = doc->addObject<Part::Feature>("Spline"); | |
| part->Shape.setValue(edge); | |
| doc->commitTransaction(); | |
| } | |
| } | |
| bool CurveOnMeshHandler::makePolyline(const std::vector<SbVec3f>& points, TopoDS_Wire& wire) | |
| { | |
| BRepBuilderAPI_MakePolygon mkPoly; | |
| for (const auto& it : points) { | |
| float x, y, z; | |
| it.getValue(x, y, z); | |
| mkPoly.Add(gp_Pnt(x, y, z)); | |
| } | |
| if (mkPoly.IsDone()) { | |
| wire = mkPoly.Wire(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void CurveOnMeshHandler::displayPolyline(const TopoDS_Wire& wire) | |
| { | |
| if (d_ptr->viewer) { | |
| Gui::View3DInventorViewer* view3d = d_ptr->viewer->getViewer(); | |
| App::Document* doc = view3d->getDocument()->getDocument(); | |
| doc->openTransaction("Add polyline"); | |
| Part::Feature* part = doc->addObject<Part::Feature>("Polyline"); | |
| part->Shape.setValue(wire); | |
| doc->commitTransaction(); | |
| } | |
| } | |
| bool CurveOnMeshHandler::tryCloseWire(const SbVec3f& p) const | |
| { | |
| if (d_ptr->pickedPoints.size() >= 3) { | |
| Private::PickedPoint first = d_ptr->pickedPoints.front(); | |
| // if the distance of the first and last points is small enough (~1mm) | |
| // the curve can be closed. | |
| float len = (first.point - p).length(); | |
| if (len < d_ptr->distance) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void CurveOnMeshHandler::closeWire() | |
| { | |
| Private::PickedPoint pick = d_ptr->pickedPoints.front(); | |
| if (d_ptr->projectLineOnMesh(pick)) { | |
| d_ptr->curve->setPoints(getPoints()); | |
| d_ptr->wireClosed = true; | |
| } | |
| } | |
| void CurveOnMeshHandler::Private::vertexCallback(void* ud, SoEventCallback* cb) | |
| { | |
| Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(cb->getUserData()); | |
| const SoEvent* ev = cb->getEvent(); | |
| if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) { | |
| // set as handled | |
| cb->setHandled(); | |
| const SoMouseButtonEvent* mbe = static_cast<const SoMouseButtonEvent*>(ev); | |
| if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 | |
| && mbe->getState() == SoButtonEvent::DOWN) { | |
| const SoPickedPoint* pp = cb->getPickedPoint(); | |
| if (pp) { | |
| CurveOnMeshHandler* self = static_cast<CurveOnMeshHandler*>(ud); | |
| if (!self->d_ptr->wireClosed) { | |
| Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(pp->getPath()); | |
| if (vp && vp->isDerivedFrom<MeshGui::ViewProviderMesh>()) { | |
| MeshGui::ViewProviderMesh* mesh = static_cast<MeshGui::ViewProviderMesh*>(vp); | |
| const SoDetail* detail = pp->getDetail(); | |
| if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) { | |
| // get the mesh and build a grid | |
| if (!self->d_ptr->mesh) { | |
| self->d_ptr->mesh = mesh; | |
| self->d_ptr->createGrid(); | |
| } | |
| else if (self->d_ptr->mesh != mesh) { | |
| Gui::getMainWindow()->statusBar()->showMessage( | |
| tr("Wrong mesh selected") | |
| ); | |
| return; | |
| } | |
| const SbVec3f& p = pp->getPoint(); | |
| const SbVec3f& n = pp->getNormal(); | |
| Private::PickedPoint pick; | |
| pick.facet = static_cast<const SoFaceDetail*>(detail)->getFaceIndex(); | |
| pick.point = p; | |
| pick.normal = n; | |
| if (self->d_ptr->pickedPoints.empty()) { | |
| self->d_ptr->pickedPoints.push_back(pick); | |
| self->d_ptr->curve->addVertex(p); | |
| } | |
| else { | |
| // check to auto-complete the curve | |
| if (self->tryCloseWire(p)) { | |
| self->closeWire(); | |
| } | |
| else if (self->d_ptr->projectLineOnMesh(pick)) { | |
| self->d_ptr->curve->setPoints(self->getPoints()); | |
| self->d_ptr->pickedPoints.push_back(pick); | |
| self->d_ptr->curve->addVertex(p); | |
| } | |
| } | |
| } | |
| } | |
| // try to 'complete' the curve | |
| else if (vp && vp->isDerivedFrom<ViewProviderCurveOnMesh>()) { | |
| const SbVec3f& p = pp->getPoint(); | |
| if (self->tryCloseWire(p)) { | |
| self->closeWire(); | |
| } | |
| } | |
| } | |
| } | |
| else { | |
| Gui::getMainWindow()->statusBar()->showMessage(tr("No point was selected")); | |
| } | |
| } | |
| else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 | |
| && mbe->getState() == SoButtonEvent::UP) { | |
| CurveOnMeshHandler* self = static_cast<CurveOnMeshHandler*>(ud); | |
| QTimer::singleShot(100, self, &CurveOnMeshHandler::onContextMenu); | |
| } | |
| } | |
| } | |
| void CurveOnMeshHandler::recomputeDocument() | |
| { | |
| if (d_ptr->viewer) { | |
| Gui::View3DInventorViewer* view3d = d_ptr->viewer->getViewer(); | |
| App::Document* doc = view3d->getDocument()->getDocument(); | |
| doc->recompute(); | |
| } | |
| } | |