diff --git a/src/Mod/Mesh/App/Core/Approximation.cpp b/src/Mod/Mesh/App/Core/Approximation.cpp index 0c7521907b..85efdcbef1 100644 --- a/src/Mod/Mesh/App/Core/Approximation.cpp +++ b/src/Mod/Mesh/App/Core/Approximation.cpp @@ -906,7 +906,7 @@ float CylinderFit::Fit() return FLOAT_MAX; _bIsFitted = true; -#if 0 +#if 1 std::vector input; std::transform(_vPoints.begin(), _vPoints.end(), std::back_inserter(input), [](const Base::Vector3f& v) { return Wm4::Vector3d(v.x, v.y, v.z); }); diff --git a/src/Mod/Mesh/App/Core/Segmentation.cpp b/src/Mod/Mesh/App/Core/Segmentation.cpp index a77b80fbe3..fe8790dcde 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.cpp +++ b/src/Mod/Mesh/App/Core/Segmentation.cpp @@ -171,19 +171,23 @@ float PlaneSurfaceFit::GetDistanceToSurface(const Base::Vector3f& pnt) const return fitter->GetDistanceToPlane(pnt); } -Base::Vector3f PlaneSurfaceFit::Project(const Base::Vector3f& pt) const +std::vector PlaneSurfaceFit::Parameters() const { - Base::Vector3f prj(pt); - if (!fitter) { - prj.ProjectToPlane(basepoint, normal); - } - else { - Base::Vector3f base = fitter->GetBase(); - Base::Vector3f norm = fitter->GetNormal(); - prj.ProjectToPlane(base, norm); + Base::Vector3f base = basepoint; + Base::Vector3f norm = normal; + if (fitter) { + base = fitter->GetBase(); + norm = fitter->GetNormal(); } - return prj; + std::vector c; + c.push_back(base.x); + c.push_back(base.y); + c.push_back(base.z); + c.push_back(norm.x); + c.push_back(norm.y); + c.push_back(norm.z); + return c; } // -------------------------------------------------------- @@ -272,10 +276,26 @@ float CylinderSurfaceFit::GetDistanceToSurface(const Base::Vector3f& pnt) const return (dist - radius); } -Base::Vector3f CylinderSurfaceFit::Project(const Base::Vector3f& pt) const +std::vector CylinderSurfaceFit::Parameters() const { - //TODO - return pt; + Base::Vector3f base = basepoint; + Base::Vector3f norm = axis; + float radval = radius; + if (fitter) { + base = fitter->GetBase(); + norm = fitter->GetAxis(); + radval = fitter->GetRadius(); + } + + std::vector c; + c.push_back(base.x); + c.push_back(base.y); + c.push_back(base.z); + c.push_back(norm.x); + c.push_back(norm.y); + c.push_back(norm.z); + c.push_back(radval); + return c; } // -------------------------------------------------------- @@ -353,10 +373,21 @@ float SphereSurfaceFit::GetDistanceToSurface(const Base::Vector3f& pnt) const return (dist - radius); } -Base::Vector3f SphereSurfaceFit::Project(const Base::Vector3f& pt) const +std::vector SphereSurfaceFit::Parameters() const { - //TODO - return pt; + Base::Vector3f base = center; + float radval = radius; + if (fitter) { + base = fitter->GetCenter(); + radval = fitter->GetRadius(); + } + + std::vector c; + c.push_back(base.x); + c.push_back(base.y); + c.push_back(base.z); + c.push_back(radval); + return c; } // -------------------------------------------------------- @@ -410,15 +441,9 @@ void MeshDistanceGenericSurfaceFitSegment::AddFacet(const MeshFacet& face) fitter->AddTriangle(triangle); } -std::vector MeshDistanceGenericSurfaceFitSegment::Project(const std::vector& pts) const +std::vector MeshDistanceGenericSurfaceFitSegment::Parameters() const { - std::vector prj; - prj.reserve(pts.size()); - for (const auto it : pts) { - prj.push_back(fitter->Project(it)); - } - - return prj; + return fitter->Parameters(); } // -------------------------------------------------------- diff --git a/src/Mod/Mesh/App/Core/Segmentation.h b/src/Mod/Mesh/App/Core/Segmentation.h index 6b4d16132c..0a90070f6f 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.h +++ b/src/Mod/Mesh/App/Core/Segmentation.h @@ -99,7 +99,7 @@ public: virtual bool Done() const = 0; virtual float Fit() = 0; virtual float GetDistanceToSurface(const Base::Vector3f&) const = 0; - virtual Base::Vector3f Project(const Base::Vector3f&) const = 0; + virtual std::vector Parameters() const = 0; }; class MeshExport PlaneSurfaceFit : public AbstractSurfaceFit @@ -115,7 +115,7 @@ public: bool Done() const; float Fit(); float GetDistanceToSurface(const Base::Vector3f&) const; - Base::Vector3f Project(const Base::Vector3f&) const; + std::vector Parameters() const; private: Base::Vector3f basepoint; @@ -136,7 +136,7 @@ public: bool Done() const; float Fit(); float GetDistanceToSurface(const Base::Vector3f&) const; - Base::Vector3f Project(const Base::Vector3f&) const; + std::vector Parameters() const; private: Base::Vector3f basepoint; @@ -158,7 +158,7 @@ public: bool Done() const; float Fit(); float GetDistanceToSurface(const Base::Vector3f&) const; - Base::Vector3f Project(const Base::Vector3f&) const; + std::vector Parameters() const; private: Base::Vector3f center; @@ -177,7 +177,7 @@ public: void Initialize(unsigned long); bool TestInitialFacet(unsigned long) const; void AddFacet(const MeshFacet& rclFacet); - std::vector Project(const std::vector&) const; + std::vector Parameters() const; protected: AbstractSurfaceFit* fitter; diff --git a/src/Mod/ReverseEngineering/Gui/AppReverseEngineeringGui.cpp b/src/Mod/ReverseEngineering/Gui/AppReverseEngineeringGui.cpp index 5ae80af858..7317655970 100644 --- a/src/Mod/ReverseEngineering/Gui/AppReverseEngineeringGui.cpp +++ b/src/Mod/ReverseEngineering/Gui/AppReverseEngineeringGui.cpp @@ -27,6 +27,7 @@ #endif #include +#include #include #include #include "Workbench.h" @@ -74,6 +75,15 @@ PyMOD_INIT_FUNC(ReverseEngineeringGui) PyMOD_Return(0); } + // load dependent module + try { + Base::Interpreter().loadModule("MeshGui"); + } + catch(const Base::Exception& e) { + PyErr_SetString(PyExc_ImportError, e.what()); + PyMOD_Return(0); + } + PyObject* mod = ReverseEngineeringGui::initModule(); Base::Console().Log("Loading GUI of ReverseEngineering module... done\n"); diff --git a/src/Mod/ReverseEngineering/Gui/Command.cpp b/src/Mod/ReverseEngineering/Gui/Command.cpp index 840950e25f..43b4c6ccfb 100644 --- a/src/Mod/ReverseEngineering/Gui/Command.cpp +++ b/src/Mod/ReverseEngineering/Gui/Command.cpp @@ -34,11 +34,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -98,7 +100,7 @@ CmdApproxPlane::CmdApproxPlane() { sAppModule = "Reen"; sGroup = QT_TR_NOOP("Reverse Engineering"); - sMenuText = QT_TR_NOOP("Approximate plane..."); + sMenuText = QT_TR_NOOP("Plane..."); sToolTipText = QT_TR_NOOP("Approximate a plane"); sWhatsThis = "Reen_ApproxPlane"; sStatusTip = sToolTipText; @@ -192,6 +194,104 @@ bool CmdApproxPlane::isActive(void) return false; } +DEF_STD_CMD_A(CmdApproxCylinder) + +CmdApproxCylinder::CmdApproxCylinder() + : Command("Reen_ApproxCylinder") +{ + sAppModule = "Reen"; + sGroup = QT_TR_NOOP("Reverse Engineering"); + sMenuText = QT_TR_NOOP("Cylinder"); + sToolTipText = QT_TR_NOOP("Approximate a cylinder"); + sWhatsThis = "Reen_ApproxCylinder"; + sStatusTip = sToolTipText; +} + +void CmdApproxCylinder::activated(int) +{ + std::vector sel = getSelection().getObjectsOfType(); + openCommand("Fit cylinder"); + for (auto it : sel) { + const Mesh::MeshObject& mesh = it->Mesh.getValue(); + const MeshCore::MeshKernel& kernel = mesh.getKernel(); + MeshCore::CylinderFit fit; + fit.AddPoints(kernel.GetPoints()); + if (fit.Fit() < FLOAT_MAX) { + Base::Vector3f base = fit.GetBase(); + Base::Rotation rot; + rot.setValue(Base::Vector3d(0,0,1), Base::convertTo(fit.GetAxis())); + double q0, q1, q2, q3; + rot.getValue(q0, q1, q2, q3); + + std::stringstream str; + str << "from FreeCAD import Base" << std::endl; + str << "App.ActiveDocument.addObject('Part::Cylinder','Cylinder_fit')" << std::endl; + str << "App.ActiveDocument.ActiveObject.Radius = " << fit.GetRadius() << std::endl; + str << "App.ActiveDocument.ActiveObject.Placement = Base.Placement(" + << "Base.Vector(" << base.x << "," << base.y << "," << base.z << ")," + << "Base.Rotation(" << q0 << "," << q1 << "," << q2 << "," << q3 << "))" << std::endl; + + runCommand(Gui::Command::Doc, str.str().c_str()); + } + } + commitCommand(); + updateActive(); +} + +bool CmdApproxCylinder::isActive(void) +{ + if (getSelection().countObjectsOfType(Mesh::Feature::getClassTypeId()) > 0) + return true; + return false; +} + +DEF_STD_CMD_A(CmdApproxSphere) + +CmdApproxSphere::CmdApproxSphere() + : Command("Reen_ApproxSphere") +{ + sAppModule = "Reen"; + sGroup = QT_TR_NOOP("Reverse Engineering"); + sMenuText = QT_TR_NOOP("Sphere"); + sToolTipText = QT_TR_NOOP("Approximate a sphere"); + sWhatsThis = "Reen_ApproxSphere"; + sStatusTip = sToolTipText; +} + +void CmdApproxSphere::activated(int) +{ + std::vector sel = getSelection().getObjectsOfType(); + openCommand("Fit sphere"); + for (auto it : sel) { + const Mesh::MeshObject& mesh = it->Mesh.getValue(); + const MeshCore::MeshKernel& kernel = mesh.getKernel(); + MeshCore::SphereFit fit; + fit.AddPoints(kernel.GetPoints()); + if (fit.Fit() < FLOAT_MAX) { + Base::Vector3f base = fit.GetCenter(); + + std::stringstream str; + str << "from FreeCAD import Base" << std::endl; + str << "App.ActiveDocument.addObject('Part::Sphere','Sphere_fit')" << std::endl; + str << "App.ActiveDocument.ActiveObject.Radius = " << fit.GetRadius() << std::endl; + str << "App.ActiveDocument.ActiveObject.Placement = Base.Placement(" + << "Base.Vector(" << base.x << "," << base.y << "," << base.z << ")," + << "Base.Rotation(" << 1 << "," << 0 << "," << 0 << "," << 0 << "))" << std::endl; + + runCommand(Gui::Command::Doc, str.str().c_str()); + } + } + commitCommand(); + updateActive(); +} + +bool CmdApproxSphere::isActive(void) +{ + if (getSelection().countObjectsOfType(Mesh::Feature::getClassTypeId()) > 0) + return true; + return false; +} + DEF_STD_CMD_A(CmdSegmentation) CmdSegmentation::CmdSegmentation() @@ -199,7 +299,7 @@ CmdSegmentation::CmdSegmentation() { sAppModule = "Reen"; sGroup = QT_TR_NOOP("Reverse Engineering"); - sMenuText = QT_TR_NOOP("Create mesh segments..."); + sMenuText = QT_TR_NOOP("Mesh segmentation..."); sToolTipText = QT_TR_NOOP("Create mesh segments"); sWhatsThis = "Reen_Segmentation"; sStatusTip = sToolTipText; @@ -231,8 +331,8 @@ CmdMeshBoundary::CmdMeshBoundary() { sAppModule = "Reen"; sGroup = QT_TR_NOOP("Reverse Engineering"); - sMenuText = QT_TR_NOOP("Wire from mesh..."); - sToolTipText = QT_TR_NOOP("Create wire from mesh"); + sMenuText = QT_TR_NOOP("Wire from mesh boundary..."); + sToolTipText = QT_TR_NOOP("Create wire from mesh boundaries"); sWhatsThis = "Reen_Segmentation"; sStatusTip = sToolTipText; } @@ -252,6 +352,9 @@ void CmdMeshBoundary::activated(int) TopoDS_Compound compound; builder.MakeCompound(compound); + TopoDS_Shape shape; + std::vector wires; + for (auto bt = bounds.begin(); bt != bounds.end(); ++bt) { BRepBuilderAPI_MakePolygon mkPoly; for (std::vector::reverse_iterator it = bt->rbegin(); it != bt->rend(); ++it) { @@ -259,12 +362,24 @@ void CmdMeshBoundary::activated(int) } if (mkPoly.IsDone()) { builder.Add(compound, mkPoly.Wire()); + wires.push_back(mkPoly.Wire()); } } - Part::Feature* shapeFea = static_cast(document->addObject("Part::Feature", "Wires from mesh")); - shapeFea->Shape.setValue(compound); + try { + shape = Part::FaceMakerCheese::makeFace(wires); + } + catch (...) { + } + if (!shape.IsNull()) { + Part::Feature* shapeFea = static_cast(document->addObject("Part::Feature", "Face from mesh")); + shapeFea->Shape.setValue(shape); + } + else { + Part::Feature* shapeFea = static_cast(document->addObject("Part::Feature", "Wire from mesh")); + shapeFea->Shape.setValue(compound); + } } document->commitTransaction(); } @@ -366,6 +481,8 @@ void CreateReverseEngineeringCommands(void) Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager(); rcCmdMgr.addCommand(new CmdApproxSurface()); rcCmdMgr.addCommand(new CmdApproxPlane()); + rcCmdMgr.addCommand(new CmdApproxCylinder()); + rcCmdMgr.addCommand(new CmdApproxSphere()); rcCmdMgr.addCommand(new CmdSegmentation()); rcCmdMgr.addCommand(new CmdMeshBoundary()); rcCmdMgr.addCommand(new CmdPoissonReconstruction()); diff --git a/src/Mod/ReverseEngineering/Gui/Segmentation.cpp b/src/Mod/ReverseEngineering/Gui/Segmentation.cpp index 831f63023d..56d9f3c1b4 100644 --- a/src/Mod/ReverseEngineering/Gui/Segmentation.cpp +++ b/src/Mod/ReverseEngineering/Gui/Segmentation.cpp @@ -27,6 +27,8 @@ # include # include # include +# include +# include # include # include # include @@ -94,6 +96,7 @@ void Segmentation::accept() MeshCore::MeshCurvature meshCurv(kernel); meshCurv.ComputePerVertex(); + // First create segments by curavture to get the surface type std::vector segm; if (ui->groupBoxPln->isChecked()) { segm.emplace_back(new MeshCore::MeshCurvaturePlanarSegment @@ -101,20 +104,23 @@ void Segmentation::accept() } finder.FindSegments(segm); - // For each planar segment compute a plane and use this then for a more accurate 2nd segmentation std::vector segmSurf; for (std::vector::iterator it = segm.begin(); it != segm.end(); ++it) { const std::vector& data = (*it)->GetSegments(); - for (std::vector::const_iterator jt = data.begin(); jt != data.end(); ++jt) { - std::vector indexes = kernel.GetFacetPoints(*jt); - MeshCore::PlaneFit fit; - fit.AddPoints(kernel.GetPoints(indexes)); - if (fit.Fit() < FLOAT_MAX) { - Base::Vector3f base = fit.GetBase(); - Base::Vector3f axis = fit.GetNormal(); - MeshCore::AbstractSurfaceFit* fitter = new MeshCore::PlaneSurfaceFit(base, axis); - segmSurf.emplace_back(new MeshCore::MeshDistanceGenericSurfaceFitSegment - (fitter, kernel, ui->numPln->value(), ui->distToPln->value())); + + // For each planar segment compute a plane and use this then for a more accurate 2nd segmentation + if (strcmp((*it)->GetType(), "Plane") == 0) { + for (std::vector::const_iterator jt = data.begin(); jt != data.end(); ++jt) { + std::vector indexes = kernel.GetFacetPoints(*jt); + MeshCore::PlaneFit fit; + fit.AddPoints(kernel.GetPoints(indexes)); + if (fit.Fit() < FLOAT_MAX) { + Base::Vector3f base = fit.GetBase(); + Base::Vector3f axis = fit.GetNormal(); + MeshCore::AbstractSurfaceFit* fitter = new MeshCore::PlaneSurfaceFit(base, axis); + segmSurf.emplace_back(new MeshCore::MeshDistanceGenericSurfaceFitSegment + (fitter, kernel, ui->numPln->value(), ui->distToPln->value())); + } } } } @@ -140,6 +146,7 @@ void Segmentation::accept() std::shared_ptr genSegm = std::dynamic_pointer_cast (*it); + bool isPlanar = (strcmp(genSegm->GetType(), "Plane") == 0); for (std::vector::const_iterator jt = data.begin(); jt != data.end(); ++jt) { // reset flag for facets of segment algo.ResetFacetsFlag(*jt, MeshCore::MeshFacet::TMP0); @@ -159,33 +166,47 @@ void Segmentation::accept() std::list > bounds; algo.GetFacetBorders(*jt, bounds); - std::vector wires; - for (auto bt = bounds.begin(); bt != bounds.end(); ++bt) { - // project the points onto the surface - auto prj = genSegm->Project(*bt); - BRepBuilderAPI_MakePolygon mkPoly; - for (std::vector::reverse_iterator it = prj.rbegin(); it != prj.rend(); ++it) { - mkPoly.Add(gp_Pnt(it->x,it->y,it->z)); - } - if (mkPoly.IsDone()) { - wires.push_back(mkPoly.Wire()); - } - } + // Handle planar segments + if (isPlanar) { + std::vector par = genSegm->Parameters(); + gp_Pnt loc(par.at(0), par.at(1), par.at(2)); + gp_Dir dir(par.at(3), par.at(4), par.at(5)); - try { - TopoDS_Shape shape = Part::FaceMakerCheese::makeFace(wires); - if (!shape.IsNull()) { - builder.Add(compound, shape); + Handle(Geom_Plane) hPlane(new Geom_Plane(loc, dir)); + + std::vector wires; + for (auto bt = bounds.begin(); bt != bounds.end(); ++bt) { + // project the points onto the surface + std::vector polygon; + std::transform(bt->begin(), bt->end(), std::back_inserter(polygon), [&hPlane](const Base::Vector3f v) { + gp_Pnt p(v.x, v.y, v.z); + return GeomAPI_ProjectPointOnSurf(p, hPlane).NearestPoint(); + }); + + BRepBuilderAPI_MakePolygon mkPoly; + for (std::vector::reverse_iterator it = polygon.rbegin(); it != polygon.rend(); ++it) { + mkPoly.Add(*it); + } + if (mkPoly.IsDone()) { + wires.push_back(mkPoly.Wire()); + } } - else { + + try { + TopoDS_Shape shape = Part::FaceMakerCheese::makeFace(wires); + if (!shape.IsNull()) { + builder.Add(compound, shape); + } + else { + failures.push_back(feaSegm); + Base::Console().Warning("Failed to create face from %s\n", feaSegm->Label.getValue()); + } + } + catch (Standard_Failure&) { failures.push_back(feaSegm); - Base::Console().Warning("Failed to create face from %s\n", feaSegm->Label.getValue()); + Base::Console().Error("Fatal failure to create face from %s\n", feaSegm->Label.getValue()); } } - catch (Standard_Failure&) { - failures.push_back(feaSegm); - Base::Console().Error("Fatal failure to create face from %s\n", feaSegm->Label.getValue()); - } } } } diff --git a/src/Mod/ReverseEngineering/Gui/Workbench.cpp b/src/Mod/ReverseEngineering/Gui/Workbench.cpp index d5f5845f59..e2398d426f 100644 --- a/src/Mod/ReverseEngineering/Gui/Workbench.cpp +++ b/src/Mod/ReverseEngineering/Gui/Workbench.cpp @@ -54,9 +54,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const Gui::MenuItem* item = root->findItem("&Windows"); Gui::MenuItem* reen = new Gui::MenuItem; root->insertItem(item, reen); - reen->setCommand("&REEN"); - *reen << "Reen_ApproxPlane" - << "Reen_ApproxSurface"; + reen->setCommand("&Reverse Engineering"); Gui::MenuItem *reconstruct = new Gui::MenuItem(); reconstruct->setCommand("Surface reconstruction"); @@ -74,6 +72,15 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Reen_MeshBoundary"; *reen << segm; + Gui::MenuItem *approx = new Gui::MenuItem(); + approx->setCommand("Approximation"); + *approx << "Reen_ApproxPlane" + << "Reen_ApproxCylinder" + << "Reen_ApproxSphere" + << "Separator" + << "Reen_ApproxSurface"; + *reen << approx; + return root; }