From 8edd584b68b1be25ad8356bc8edacb453efd2a87 Mon Sep 17 00:00:00 2001 From: kreso-t Date: Fri, 7 Sep 2018 18:26:06 +0200 Subject: [PATCH] Path: Adaptive - keep tool down ratio option + bugfixes --- src/Mod/Path/PathScripts/PathAdaptive.py | 7 + src/Mod/Path/PathScripts/PathAdaptiveGui.py | 17 ++ src/Mod/Path/libarea/Adaptive.cpp | 179 +++++++++++++------- src/Mod/Path/libarea/Adaptive.hpp | 6 +- src/Mod/Path/libarea/PythonStuff.cpp | 1 + src/Mod/Path/libarea/pyarea.cpp | 1 + 6 files changed, 144 insertions(+), 67 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathAdaptive.py b/src/Mod/Path/PathScripts/PathAdaptive.py index 766c3a4d1b..6cb16253c5 100644 --- a/src/Mod/Path/PathScripts/PathAdaptive.py +++ b/src/Mod/Path/PathScripts/PathAdaptive.py @@ -236,7 +236,10 @@ def Execute(op,obj): opType = area.AdaptiveOperationType.ProfilingInside + keepToolDownRatio=3.0 + if hasattr(obj, 'KeepToolDownRatio'): keepToolDownRatio = float(obj.KeepToolDownRatio) # put here all properties that influence calculation of adaptive base paths, + inputStateObject = { "tool": float(op.tool.Diameter), "tolerance": float(obj.Tolerance), @@ -247,6 +250,7 @@ def Execute(op,obj): "operationType": obj.OperationType, "side": obj.Side, "forceInsideOut" : obj.ForceInsideOut, + "keepToolDownRatio": keepToolDownRatio, "stockToLeave": float(obj.StockToLeave) } @@ -277,6 +281,7 @@ def Execute(op,obj): a2d.stepOverFactor = 0.01*obj.StepOver a2d.toolDiameter = float(op.tool.Diameter) a2d.helixRampDiameter = helixDiameter + a2d.keepToolDownDistRatio = keepToolDownRatio a2d.stockToLeave =float(obj.StockToLeave) a2d.tolerance = float(obj.Tolerance) a2d.forceInsideOut = obj.ForceInsideOut @@ -329,6 +334,7 @@ class PathAdaptive(PathOp.ObjectOp): obj.addProperty("App::PropertyFloat", "Tolerance", "Adaptive", "Influences accuracy and performance") obj.addProperty("App::PropertyPercent", "StepOver", "Adaptive", "Percent of cutter diameter to step over on each pass") obj.addProperty("App::PropertyDistance", "LiftDistance", "Adaptive", "Lift distance for rapid moves") + obj.addProperty("App::PropertyDistance", "KeepToolDownRatio", "Adaptive", "Max length of keep tool down path compared to direct distance between points") obj.addProperty("App::PropertyDistance", "StockToLeave", "Adaptive", "How much stock to leave (i.e. for finishing operation)") # obj.addProperty("App::PropertyBool", "ProcessHoles", "Adaptive","Process holes as well as the face outline") @@ -366,6 +372,7 @@ class PathAdaptive(PathOp.ObjectOp): obj.AdaptiveInputState ="" obj.AdaptiveOutputState = "" obj.StockToLeave= 0 + obj.KeepToolDownRatio=3.0 def opExecute(self, obj): '''opExecute(obj) ... called whenever the receiver needs to be recalculated. diff --git a/src/Mod/Path/PathScripts/PathAdaptiveGui.py b/src/Mod/Path/PathScripts/PathAdaptiveGui.py index 18f61c781e..f15fcf1635 100644 --- a/src/Mod/Path/PathScripts/PathAdaptiveGui.py +++ b/src/Mod/Path/PathScripts/PathAdaptiveGui.py @@ -83,6 +83,15 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): form.LiftDistance.setToolTip("How much to lift the tool up during the rapid repositioning moves (used when no obstacles)") formLayout.addRow(QtGui.QLabel("Lift Distance"),form.LiftDistance) + #KeepToolDownRatio + form.KeepToolDownRatio = QtGui.QDoubleSpinBox() + form.KeepToolDownRatio.setMinimum(1.0) + form.KeepToolDownRatio.setMaximum(10) + form.KeepToolDownRatio.setSingleStep(1) + form.KeepToolDownRatio.setValue(3.0) + form.KeepToolDownRatio.setToolTip("Max length of keep tool down path compared to direct distance between points") + formLayout.addRow(QtGui.QLabel("Keep Tool Down Ratio"),form.KeepToolDownRatio) + #stock to leave form.StockToLeave = QtGui.QDoubleSpinBox() form.StockToLeave.setMinimum(0.0) @@ -124,6 +133,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.HelixAngle.valueChanged) signals.append(self.form.HelixDiameterLimit.valueChanged) signals.append(self.form.LiftDistance.valueChanged) + signals.append(self.form.KeepToolDownRatio.valueChanged) signals.append(self.form.StockToLeave.valueChanged) # signals.append(self.form.ProcessHoles.stateChanged) @@ -139,6 +149,9 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.form.HelixAngle.setValue(obj.HelixAngle) self.form.HelixDiameterLimit.setValue(obj.HelixDiameterLimit) self.form.LiftDistance.setValue(obj.LiftDistance) + if hasattr(obj, 'KeepToolDownRatio'): + self.form.KeepToolDownRatio.setValue(obj.KeepToolDownRatio) + if hasattr(obj, 'StockToLeave'): self.form.StockToLeave.setValue(obj.StockToLeave) @@ -163,6 +176,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): obj.HelixAngle = self.form.HelixAngle.value() obj.HelixDiameterLimit = self.form.HelixDiameterLimit.value() obj.LiftDistance = self.form.LiftDistance.value() + + if hasattr(obj, 'KeepToolDownRatio'): + obj.KeepToolDownRatio = self.form.KeepToolDownRatio.value() + if hasattr(obj, 'StockToLeave'): obj.StockToLeave = self.form.StockToLeave.value() diff --git a/src/Mod/Path/libarea/Adaptive.cpp b/src/Mod/Path/libarea/Adaptive.cpp index de4c903dba..5eb8bab0b9 100644 --- a/src/Mod/Path/libarea/Adaptive.cpp +++ b/src/Mod/Path/libarea/Adaptive.cpp @@ -412,6 +412,23 @@ namespace AdaptivePath { return res.size()>1; } + + // finds interim points not neccesarily on the same keeptooldown linking path + bool FindClosestClearPoints(const Paths & paths,IntPoint p1,IntPoint p2, double distanceLmit, IntPoint &interim1,IntPoint &interim2) { + size_t clpPathIndex; + size_t clpSegmentIndex1; + double clpParameter1; + size_t clpSegmentIndex2; + double clpParameter2; + double limitSqrd = distanceLmit*distanceLmit; + double distSqrd=DistancePointToPathsSqrd(paths,p1,interim1,clpPathIndex,clpSegmentIndex1,clpParameter1); + if(distSqrd>limitSqrd) return false; // too far + // find second point + distSqrd=DistancePointToPathsSqrd(paths,p2,interim2,clpPathIndex,clpSegmentIndex2,clpParameter2); + if(distSqrd>limitSqrd) return false; // still too far + return true; + } + bool PopPathWithClosestPoint(Paths & paths /*closest path is removed from collection */ ,IntPoint p1, Path & result) { @@ -1038,10 +1055,10 @@ namespace AdaptivePath { if(stockToLeave>NTOL) { clipof.Clear(); clipof.AddPaths(inputPaths,JoinType::jtRound,EndType::etClosedPolygon); - if(opType==OperationType::otClearingOutside || opType==OperationType::otProfilingOutside) - clipof.Execute(inputPaths,stockToLeave*scaleFactor); - else - clipof.Execute(inputPaths,-stockToLeave*scaleFactor); + // if(opType==OperationType::otClearingOutside || opType==OperationType::otProfilingOutside) + // clipof.Execute(inputPaths,stockToLeave*scaleFactor); + // else + clipof.Execute(inputPaths,-stockToLeave*scaleFactor); } else { // fix for clipper glitches clipof.Clear(); @@ -1145,7 +1162,7 @@ namespace AdaptivePath { // prepare stock boundary overshooted paths clipof.Clear(); clipof.AddPaths(stockInputPaths,JoinType::jtSquare,EndType::etClosedPolygon); - double overshootDistance =4*toolRadiusScaled ; + double overshootDistance =4*toolRadiusScaled + stockToLeave*scaleFactor ; if(forceInsideOut) overshootDistance=0; Paths stockOvershoot; clipof.Execute(stockOvershoot,overshootDistance); @@ -1156,30 +1173,7 @@ namespace AdaptivePath { // add stock paths, with overshooting for(auto p : stockOvershoot) inputPaths.push_back(p); } else if(opType==OperationType::otClearingInside) { - // check if there are open paths, and try to close it through overshooted stock boundary - - // Paths openInputPaths; // open paths will be removed by simplify - // Paths closedInputPaths; - // for(const auto & pth:inputPaths) { - // if(DistanceSqrd(pth.front(),pth.back())>SAME_POINT_TOL_SQRD_SCALED) { - // openInputPaths.push_back(pth); - // } else { - // closedInputPaths.push_back(pth); - // } - // } - // if(openInputPaths.size()==1) { - // cout << "SINGLE OPEN PATH MODE" << endl; - // Path & op = openInputPaths.front(); - // Path connect; - // FindPathBetweenClosestPoints(stockOvershoot,op.back(),op.front(),scaleFactor*100000,connect); - // Paths cl; - // cout << op; - // cout << connect; - // cl.push_back(op); - // cl.push_back(connect); - // ConnectPaths(cl,inputPaths); - // for(auto &p: closedInputPaths) inputPaths.push_back(p); - // } + // potential TODO: check if there are open paths, and try to close it through overshooted stock boundary } SimplifyPolygons(inputPaths); @@ -1398,12 +1392,12 @@ namespace AdaptivePath { /** * returns true if path is clear from obstacles */ - bool Adaptive2d::IsClearPath(const Path &tp,const Paths & cleared) { + bool Adaptive2d::IsClearPath(const Path &tp,const Paths & cleared, double safetyDistanceScaled) { Clipper clip; ClipperOffset clipof; clipof.AddPath(tp,JoinType::jtRound,EndType::etOpenRound); Paths toolShape; - clipof.Execute(toolShape,toolRadiusScaled-2); + clipof.Execute(toolShape,toolRadiusScaled+safetyDistanceScaled); clip.AddPaths(toolShape,PolyType::ptSubject,true); clip.AddPaths(cleared,PolyType::ptClip,true); Paths crossing; @@ -1412,7 +1406,7 @@ namespace AdaptivePath { for(auto &p : crossing) { collisionArea += fabs(Area(p)); } - return collisionArea <= optimalCutAreaPD*RESOLUTION_FACTOR/2; + return collisionArea < optimalCutAreaPD*RESOLUTION_FACTOR/10+1; } bool Adaptive2d::IsAllowedToCutTrough(const IntPoint &p1,const IntPoint &p2,const Paths & cleared, const Paths & toolBoundPaths) { @@ -1445,7 +1439,7 @@ namespace AdaptivePath { return true; } - void Adaptive2d::AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared, const Paths & toolBoundPaths, bool close) { + void Adaptive2d::AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared, const Paths & toolBoundPaths) { if(passToolPath.size()<1) return; IntPoint nextPoint(passToolPath[0]); @@ -1458,7 +1452,7 @@ namespace AdaptivePath { //first we try to cut through the linking move for short distances bool linkFound = false; double linkDistance = sqrt(DistanceSqrd(lastPoint,nextPoint)); - if(linkDistance<4*toolRadiusScaled && IsAllowedToCutTrough(lastPoint,nextPoint,cleared,toolBoundPaths)) { + if(linkDistance0) output.AdaptivePaths.push_back(cutPath); } @@ -1761,7 +1793,7 @@ namespace AdaptivePath { #ifdef DEV_MODE clock_t start_clock=clock(); #endif - + Paths clearedBeforePass; /******************************* * LOOP - PASSES *******************************/ @@ -1791,7 +1823,7 @@ namespace AdaptivePath { size_t clpPathIndex; size_t clpSegmentIndex; double clpParamter; - Paths clearedBeforePass = cleared; + clearedBeforePass = cleared; /******************************* * LOOP - POINTS *******************************/ @@ -2022,14 +2054,14 @@ namespace AdaptivePath { /* FINISHING PASS */ /**********************************/ + Paths finishingPaths; clipof.Clear(); clipof.AddPaths(boundPaths,JoinType::jtRound,EndType::etClosedPolygon); - Paths finishingPaths; clipof.Execute(finishingPaths,-toolRadiusScaled); IntPoint lastPoint = toolPos; Path finShiftedPath; - + bool allCutsAllowed=true; while(PopPathWithClosestPoint(finishingPaths,lastPoint,finShiftedPath)) { // skip finishing passes outside the stock boundary - make no sense to cut where is no material bool allPointsOutside=true; @@ -2048,25 +2080,40 @@ namespace AdaptivePath { for(auto & pt:finShiftedPath) { progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor)); } - Path cleaned; - CleanPath(finShiftedPath,cleaned,FINISHING_CLEAN_PATH_TOLERANCE); - AppendToolPath(progressPaths,output,cleaned,cleared,toolBoundPaths,true); - // expand cleared for finishing path - clipof.Clear(); - clipof.AddPath(cleaned,JoinType::jtRound,EndType::etOpenRound); - Paths toolCoverPoly; - clipof.Execute(toolCoverPoly,toolRadiusScaled+1); - clip.Clear(); - clip.AddPaths(cleared,PolyType::ptSubject,true); - clip.AddPaths(toolCoverPoly,PolyType::ptClip,true); - clip.Execute(ClipType::ctUnion,cleared); - CleanPolygons(cleared); + if(!finShiftedPath.empty()) finShiftedPath << finShiftedPath.front(); // make sure its closed - if(finShiftedPath.size()>0) { - lastPoint.X = finShiftedPath.back().X; - lastPoint.Y = finShiftedPath.back().Y; + Path finCleaned; + CleanPath(finShiftedPath,finCleaned,FINISHING_CLEAN_PATH_TOLERANCE); + + //safety check for finishing paths + + for(size_t i=1;i Execute(const DPaths &stockPaths, const DPaths &paths, std::function progressCallbackFn); @@ -100,8 +100,8 @@ namespace AdaptivePath { bool FindEntryPointOutside(TPaths &progressPaths,const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/, IntPoint &entryPoint /*output*/, IntPoint & toolPos, DoublePoint & toolDir); double CalcCutArea(Clipper & clip,const IntPoint &toolPos, const IntPoint &newToolPos, const Paths &cleared_paths, bool preventConvetionalMode=true); - void AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared,const Paths & toolBoundPaths, bool close=false); - bool IsClearPath(const Path & path,const Paths & cleared); + void AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared,const Paths & toolBoundPaths); + bool IsClearPath(const Path & path,const Paths & cleared, double safetyDistanceScaled=0); bool IsAllowedToCutTrough(const IntPoint &p1,const IntPoint &p2,const Paths & cleared,const Paths & toolBoundPaths); friend class EngagePoint; // for CalcCutArea diff --git a/src/Mod/Path/libarea/PythonStuff.cpp b/src/Mod/Path/libarea/PythonStuff.cpp index f399abd92d..ce6811bd2b 100644 --- a/src/Mod/Path/libarea/PythonStuff.cpp +++ b/src/Mod/Path/libarea/PythonStuff.cpp @@ -530,6 +530,7 @@ BOOST_PYTHON_MODULE(area) { .def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut) //.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) .def_readwrite("tolerance", &Adaptive2d::tolerance) + .def_readwrite("keepToolDownDistRatio", &Adaptive2d::keepToolDownDistRatio) .def_readwrite("opType", &Adaptive2d::opType); diff --git a/src/Mod/Path/libarea/pyarea.cpp b/src/Mod/Path/libarea/pyarea.cpp index 9abf5c3b97..3f29e78644 100644 --- a/src/Mod/Path/libarea/pyarea.cpp +++ b/src/Mod/Path/libarea/pyarea.cpp @@ -391,6 +391,7 @@ void init_pyarea(py::module &m){ .def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut) //.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) .def_readwrite("tolerance", &Adaptive2d::tolerance) + .def_readwrite("keepToolDownDistRatio", &Adaptive2d::keepToolDownDistRatio) .def_readwrite("opType", &Adaptive2d::opType); }