Path: Adaptive - feature to clear from outside stock bounday inwards
This commit is contained in:
@@ -160,35 +160,44 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter):
|
||||
lx=region["HelixCenterPoint"][0]
|
||||
ly=region["HelixCenterPoint"][1]
|
||||
|
||||
r = helixRadius - 0.01
|
||||
#helix ramp
|
||||
passDepth = (passStartDepth - passEndDepth)
|
||||
maxfi = passDepth / depthPerOneCircle * 2 * math.pi
|
||||
fi = 0
|
||||
offsetFi =-maxfi + startAngle-math.pi/16
|
||||
|
||||
helixStart = [region["HelixCenterPoint"][0] + r * math.cos(offsetFi), region["HelixCenterPoint"][1] + r * math.sin(offsetFi)]
|
||||
|
||||
op.commandlist.append(Path.Command("(helix to depth: %f)"%passEndDepth))
|
||||
#if step == 1:
|
||||
#rapid move to start point
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.ClearanceHeight.Value}))
|
||||
#rapid move to safe height
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.SafeHeight.Value}))
|
||||
#helix ramp
|
||||
if helixRadius>0.0001:
|
||||
r = helixRadius - 0.01
|
||||
maxfi = passDepth / depthPerOneCircle * 2 * math.pi
|
||||
fi = 0
|
||||
offsetFi =-maxfi + startAngle-math.pi/16
|
||||
|
||||
op.commandlist.append(Path.Command("G1", {
|
||||
"X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed}))
|
||||
helixStart = [region["HelixCenterPoint"][0] + r * math.cos(offsetFi), region["HelixCenterPoint"][1] + r * math.sin(offsetFi)]
|
||||
|
||||
op.commandlist.append(Path.Command("(helix to depth: %f)"%passEndDepth))
|
||||
#if step == 1:
|
||||
#rapid move to start point
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.ClearanceHeight.Value}))
|
||||
#rapid move to safe height
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.SafeHeight.Value}))
|
||||
|
||||
op.commandlist.append(Path.Command("G1", {
|
||||
"X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed}))
|
||||
|
||||
while fi<maxfi:
|
||||
x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi)
|
||||
y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi)
|
||||
z = passStartDepth - fi / maxfi * (passStartDepth - passEndDepth)
|
||||
op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.vertFeed}))
|
||||
lx=x
|
||||
ly=y
|
||||
fi=fi+math.pi/16
|
||||
else: # no helix entry
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": region["StartPoint"][0], "Y": region["StartPoint"][1], "Z": obj.ClearanceHeight.Value}))
|
||||
op.commandlist.append(Path.Command("G1", {
|
||||
"X":region["StartPoint"][0], "Y": region["StartPoint"][1], "Z": passEndDepth,"F": op.vertFeed}))
|
||||
|
||||
while fi<maxfi:
|
||||
x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi)
|
||||
y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi)
|
||||
z = passStartDepth - fi / maxfi * (passStartDepth - passEndDepth)
|
||||
op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.vertFeed}))
|
||||
lx=x
|
||||
ly=y
|
||||
fi=fi+math.pi/16
|
||||
op.commandlist.append(Path.Command("(adaptive - depth: %f)"%passEndDepth))
|
||||
#add adaptive paths
|
||||
for pth in region["AdaptivePaths"]:
|
||||
@@ -245,10 +254,8 @@ def Execute(op,obj):
|
||||
try:
|
||||
Console.PrintMessage("Tool diam: %f \n"%op.tool.Diameter)
|
||||
helixDiameter = min(op.tool.Diameter,1000.0 if obj.HelixDiameterLimit.Value==0.0 else obj.HelixDiameterLimit.Value )
|
||||
nestingLimit=0
|
||||
topZ=op.stock.Shape.BoundBox.ZMax
|
||||
|
||||
opType = area.AdaptiveOperationType.Clearing
|
||||
obj.Stopped = False
|
||||
obj.StopProcessing = False
|
||||
if obj.Tolerance<0.001: obj.Tolerance=0.001
|
||||
@@ -262,40 +269,45 @@ def Execute(op,obj):
|
||||
|
||||
pathArray=connectEdges(edges)
|
||||
|
||||
stockPaths = []
|
||||
if op.stock.StockType == "CreateCylinder":
|
||||
stockPaths.append([discretize(op.stock.Shape.Edges[0])])
|
||||
else:
|
||||
stockBB = op.stock.Shape.BoundBox
|
||||
v=[]
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMin,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMax,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMax,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
|
||||
stockPaths.append([v])
|
||||
|
||||
opType = area.AdaptiveOperationType.ClearingInside
|
||||
if obj.OperationType == "Clearing":
|
||||
if obj.Side == "Outside":
|
||||
if op.stock.StockType == "CreateCylinder":
|
||||
pathArray.append([discretize(op.stock.Shape.Edges[0])])
|
||||
else:
|
||||
stockBB = op.stock.Shape.BoundBox
|
||||
v=[]
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMin,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMax,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMax,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
|
||||
pathArray.append([v])
|
||||
if not obj.ProcessHoles: nestingLimit = 2
|
||||
elif not obj.ProcessHoles: nestingLimit = 1
|
||||
opType = area.AdaptiveOperationType.Clearing
|
||||
opType = area.AdaptiveOperationType.ClearingOutside
|
||||
else:
|
||||
opType = area.AdaptiveOperationType.ClearingInside
|
||||
else: # profiling
|
||||
if obj.Side == "Outside":
|
||||
opType = area.AdaptiveOperationType.ProfilingOutside
|
||||
else:
|
||||
opType = area.AdaptiveOperationType.ProfilingInside
|
||||
if not obj.ProcessHoles: nestingLimit = 1
|
||||
|
||||
path2d = convertTo2d(pathArray)
|
||||
stockPath2d = convertTo2d(stockPaths)
|
||||
|
||||
# put here all properties that influence calculation of adaptive base paths,
|
||||
inputStateObject = {
|
||||
"tool": float(op.tool.Diameter),
|
||||
"tolerance": float(obj.Tolerance),
|
||||
"geometry" : path2d,
|
||||
"stockGeometry": stockPath2d,
|
||||
"stepover" : float(obj.StepOver),
|
||||
"effectiveHelixDiameter": float(helixDiameter),
|
||||
"operationType": obj.OperationType,
|
||||
"side": obj.Side,
|
||||
"processHoles": obj.ProcessHoles,
|
||||
"forceInsideOut" : obj.ForceInsideOut,
|
||||
"stockToLeave": float(obj.StockToLeave)
|
||||
}
|
||||
|
||||
@@ -325,10 +337,10 @@ def Execute(op,obj):
|
||||
a2d.helixRampDiameter = helixDiameter
|
||||
a2d.stockToLeave =float(obj.StockToLeave)
|
||||
a2d.tolerance = float(obj.Tolerance)
|
||||
a2d.forceInsideOut = obj.ForceInsideOut
|
||||
a2d.opType = opType
|
||||
a2d.polyTreeNestingLimit = nestingLimit
|
||||
#EXECUTE
|
||||
results = a2d.Execute(path2d,progressFn)
|
||||
results = a2d.Execute(stockPath2d,path2d,progressFn)
|
||||
|
||||
#need to convert results to python object to be JSON serializable
|
||||
adaptiveResults = []
|
||||
@@ -376,7 +388,9 @@ class PathAdaptive(PathOp.ObjectOp):
|
||||
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", "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")
|
||||
# obj.addProperty("App::PropertyBool", "ProcessHoles", "Adaptive","Process holes as well as the face outline")
|
||||
|
||||
obj.addProperty("App::PropertyBool", "ForceInsideOut", "Adaptive","Force plunging into material inside and clearing towards the edges")
|
||||
obj.addProperty("App::PropertyBool", "Stopped",
|
||||
"Adaptive", "Stop processing")
|
||||
obj.setEditorMode('Stopped', 2) #hide this property
|
||||
@@ -401,7 +415,8 @@ class PathAdaptive(PathOp.ObjectOp):
|
||||
obj.Tolerance = 0.1
|
||||
obj.StepOver = 20
|
||||
obj.LiftDistance=1.0
|
||||
obj.ProcessHoles = True
|
||||
# obj.ProcessHoles = True
|
||||
obj.ForceInsideOut = True
|
||||
obj.Stopped = False
|
||||
obj.StopProcessing = False
|
||||
obj.HelixAngle = 5
|
||||
|
||||
@@ -92,10 +92,15 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
form.StockToLeave.setToolTip("How much material to leave (i.e. for finishing operation)")
|
||||
formLayout.addRow(QtGui.QLabel("Stock to Leave"),form.StockToLeave)
|
||||
|
||||
#process holes
|
||||
form.ProcessHoles = QtGui.QCheckBox()
|
||||
form.ProcessHoles.setChecked(True)
|
||||
formLayout.addRow(QtGui.QLabel("Process Holes"),form.ProcessHoles)
|
||||
# #process holes
|
||||
# form.ProcessHoles = QtGui.QCheckBox()
|
||||
# form.ProcessHoles.setChecked(True)
|
||||
# formLayout.addRow(QtGui.QLabel("Process Holes"),form.ProcessHoles)
|
||||
|
||||
#Force inside out
|
||||
form.ForceInsideOut = QtGui.QCheckBox()
|
||||
form.ForceInsideOut.setChecked(True)
|
||||
formLayout.addRow(QtGui.QLabel("Force Clearing Inside-Out"),form.ForceInsideOut)
|
||||
|
||||
layout.addLayout(formLayout)
|
||||
|
||||
@@ -121,7 +126,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
signals.append(self.form.LiftDistance.valueChanged)
|
||||
signals.append(self.form.StockToLeave.valueChanged)
|
||||
|
||||
signals.append(self.form.ProcessHoles.stateChanged)
|
||||
# signals.append(self.form.ProcessHoles.stateChanged)
|
||||
signals.append(self.form.ForceInsideOut.stateChanged)
|
||||
signals.append(self.form.StopButton.toggled)
|
||||
return signals
|
||||
|
||||
@@ -136,7 +142,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if hasattr(obj, 'StockToLeave'):
|
||||
self.form.StockToLeave.setValue(obj.StockToLeave)
|
||||
|
||||
self.form.ProcessHoles.setChecked(obj.ProcessHoles)
|
||||
# self.form.ProcessHoles.setChecked(obj.ProcessHoles)
|
||||
self.form.ForceInsideOut.setChecked(obj.ForceInsideOut)
|
||||
self.setupToolController(obj, self.form.ToolController)
|
||||
self.form.StopButton.setChecked(obj.Stopped)
|
||||
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
|
||||
@@ -159,7 +166,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if hasattr(obj, 'StockToLeave'):
|
||||
obj.StockToLeave = self.form.StockToLeave.value()
|
||||
|
||||
obj.ProcessHoles = self.form.ProcessHoles.isChecked()
|
||||
# obj.ProcessHoles = self.form.ProcessHoles.isChecked()
|
||||
obj.ForceInsideOut = self.form.ForceInsideOut.isChecked()
|
||||
obj.Stopped = self.form.StopButton.isChecked()
|
||||
if(obj.Stopped):
|
||||
self.form.StopButton.setChecked(False) #reset the button
|
||||
|
||||
@@ -793,7 +793,7 @@ namespace AdaptivePath {
|
||||
/****************************************
|
||||
// Adaptive2d - Execute
|
||||
*****************************************/
|
||||
std::list<AdaptiveOutput> Adaptive2d::Execute(const DPaths &paths, std::function<bool(TPaths)> progressCallbackFn) {
|
||||
std::list<AdaptiveOutput> Adaptive2d::Execute(const DPaths &stockPaths, const DPaths &paths, std::function<bool(TPaths)> progressCallbackFn) {
|
||||
//**********************************
|
||||
// Initializations
|
||||
// **********************************
|
||||
@@ -845,6 +845,7 @@ namespace AdaptivePath {
|
||||
// **********************
|
||||
// Convert input paths to clipper
|
||||
//************************
|
||||
inputPaths.clear();
|
||||
for(size_t i=0;i<paths.size();i++) {
|
||||
Path cpth;
|
||||
for(size_t j=0;j<paths[i].size();j++) {
|
||||
@@ -855,6 +856,21 @@ namespace AdaptivePath {
|
||||
}
|
||||
SimplifyPolygons(inputPaths);
|
||||
|
||||
//************************
|
||||
// convert stock paths
|
||||
// *************************
|
||||
stockInputPaths.clear();
|
||||
for(size_t i=0;i<stockPaths.size();i++) {
|
||||
Path cpth;
|
||||
for(size_t j=0;j<stockPaths[i].size();j++) {
|
||||
std::pair<double,double> pt = stockPaths[i][j];
|
||||
cpth.push_back(IntPoint(long(pt.first*scaleFactor),long(pt.second*scaleFactor)));
|
||||
}
|
||||
stockInputPaths.push_back(cpth);
|
||||
}
|
||||
SimplifyPolygons(stockInputPaths);
|
||||
|
||||
|
||||
if(stockToLeave>NTOL) {
|
||||
clipof.Clear();
|
||||
clipof.AddPaths(inputPaths,JoinType::jtRound,EndType::etClosedPolygon);
|
||||
@@ -874,7 +890,18 @@ namespace AdaptivePath {
|
||||
// Resolve hierarchy and run processing
|
||||
// ********************************
|
||||
|
||||
if(opType==OperationType::otClearing) {
|
||||
if(opType==OperationType::otClearingInside || opType==OperationType::otClearingOutside) {
|
||||
// add stock paths, with overshooting
|
||||
if(opType==OperationType::otClearingOutside) {
|
||||
clipof.Clear();
|
||||
clipof.AddPaths(stockInputPaths,JoinType::jtRound,EndType::etClosedPolygon);
|
||||
double overshootDistance =2*toolRadiusScaled + toolRadiusScaled*stepOverFactor;
|
||||
if(forceInsideOut) overshootDistance=0;
|
||||
Paths stockOvershoot;
|
||||
clipof.Execute(stockOvershoot,overshootDistance);
|
||||
ReversePaths(stockOvershoot);
|
||||
for(auto p : stockOvershoot) inputPaths.push_back(p);
|
||||
}
|
||||
clipof.Clear();
|
||||
clipof.AddPaths(inputPaths,JoinType::jtRound,EndType::etClosedPolygon);
|
||||
Paths paths;
|
||||
@@ -942,7 +969,10 @@ namespace AdaptivePath {
|
||||
return results;
|
||||
}
|
||||
|
||||
bool Adaptive2d::FindEntryPoint(const Paths & toolBoundPaths,const Paths &boundPaths, Paths &cleared /*output-initial cleard area by helix*/, IntPoint &entryPoint /*output*/) {
|
||||
bool Adaptive2d::FindEntryPoint(TPaths &progressPaths, const Paths & toolBoundPaths,const Paths &boundPaths,
|
||||
Paths &cleared /*output-initial cleard area by helix*/,
|
||||
IntPoint &entryPoint /*output*/,
|
||||
IntPoint & toolPos, DoublePoint & toolDir) {
|
||||
Paths incOffset;
|
||||
Paths lastValidOffset;
|
||||
Clipper clip;
|
||||
@@ -1018,8 +1048,66 @@ namespace AdaptivePath {
|
||||
}
|
||||
//DrawCircle(entryPoint,scaleFactor,10);
|
||||
if(!found) cerr<<"Start point not found!"<<endl;
|
||||
if(found) {
|
||||
// visualize/progress for helix
|
||||
clipof.Clear();
|
||||
Path hp;
|
||||
hp << entryPoint;
|
||||
clipof.AddPath(hp,JoinType::jtRound,EndType::etOpenRound);
|
||||
Paths hps;
|
||||
clipof.Execute(hps,helixRampRadiusScaled);
|
||||
AddPathsToProgress(progressPaths,hps);
|
||||
|
||||
|
||||
toolPos = IntPoint(entryPoint.X,entryPoint.Y - helixRampRadiusScaled);
|
||||
toolDir = DoublePoint(1.0,0.0);
|
||||
|
||||
}
|
||||
return found;
|
||||
}
|
||||
bool Adaptive2d::FindEntryPointOutside(TPaths &progressPaths, const Paths & toolBoundPaths,const Paths &boundPaths,
|
||||
Paths &cleared /*output-initial cleard area by helix*/,
|
||||
IntPoint &entryPoint /*output*/,
|
||||
IntPoint & toolPos, DoublePoint & toolDir) {
|
||||
Clipper clip;
|
||||
ClipperOffset clipof;
|
||||
// check if boundary shape to cut is outside the stock
|
||||
// clip.AddPaths(boundPaths,PolyType::ptSubject, true);
|
||||
// clip.AddPaths(stockInputPaths,PolyType::ptClip, true);
|
||||
// Paths outsidePaths;
|
||||
// clip.Execute(ClipType::ctDifference,outsidePaths);
|
||||
for(const auto & pth : toolBoundPaths) {
|
||||
for(size_t i=0;i<pth.size();i++) {
|
||||
IntPoint checkPoint = pth[i];
|
||||
IntPoint lastPoint = i>0 ? pth[i-1] : pth.back();
|
||||
// if point is outside the stock
|
||||
if(PointInPolygon(checkPoint,stockInputPaths.front())==0) {
|
||||
clipof.Clear();
|
||||
clipof.AddPaths(toolBoundPaths,JoinType::jtRound,EndType::etClosedPolygon);
|
||||
Paths sol2;
|
||||
clipof.Execute(sol2,toolRadiusScaled*stepOverFactor);
|
||||
clipof.Clear();
|
||||
clipof.AddPaths(sol2,JoinType::jtRound,EndType::etClosedLine);
|
||||
clipof.Execute(cleared,toolRadiusScaled);
|
||||
// trim cleared paths to stock boundary (only outside part remain)
|
||||
clip.Clear();
|
||||
clip.AddPaths(cleared,PolyType::ptSubject, true);
|
||||
clip.AddPaths(stockInputPaths,PolyType::ptClip, true);
|
||||
clip.Execute(ClipType::ctDifference,cleared);
|
||||
|
||||
AddPathsToProgress(progressPaths,cleared);
|
||||
entryPoint=checkPoint;
|
||||
toolPos = entryPoint;
|
||||
// find tool dir
|
||||
double len=sqrt(DistanceSqrd(lastPoint,checkPoint));
|
||||
toolDir = DoublePoint((checkPoint.X - lastPoint.X)/len,(checkPoint.Y - lastPoint.Y)/len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//AddPathsToProgress(progressPaths,outsidePaths);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if line from lastPoint to nextPoint is clear from obstacles
|
||||
@@ -1130,6 +1218,18 @@ namespace AdaptivePath {
|
||||
while(progressPaths.front().second.size()>0) progressPaths.front().second.pop_back();
|
||||
progressPaths.front().second.push_back(next);
|
||||
}
|
||||
|
||||
void Adaptive2d::AddPathsToProgress(TPaths &progressPaths,Paths paths) {
|
||||
for(const auto & pth :paths) {
|
||||
if(pth.size()>0) {
|
||||
progressPaths.push_back(TPath());
|
||||
for(const auto pt: pth)
|
||||
progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor));
|
||||
progressPaths.back().second.push_back(DPoint(double(pth.front().X)/scaleFactor,double(pth.front().Y)/scaleFactor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Adaptive2d::ProcessPolyNode(Paths & boundPaths, Paths & toolBoundPaths) {
|
||||
//cout << " Adaptive2d::ProcessPolyNode" << endl;
|
||||
Perf_ProcessPolyNode.Start();
|
||||
@@ -1142,10 +1242,21 @@ namespace AdaptivePath {
|
||||
//SimplifyPolygons(toolBoundPaths);
|
||||
CleanPolygons(toolBoundPaths);
|
||||
//SimplifyPolygons(boundPaths);
|
||||
CleanPolygons(toolBoundPaths);
|
||||
//CleanPolygons(toolBoundPaths);
|
||||
//AddPathsToProgress(progressPaths,toolBoundPaths);
|
||||
|
||||
IntPoint toolPos;
|
||||
DoublePoint toolDir;
|
||||
Paths cleared;
|
||||
if(!FindEntryPoint(toolBoundPaths, boundPaths, cleared, entryPoint)) return;
|
||||
bool outsideEntry = false;
|
||||
if(FindEntryPointOutside(progressPaths, toolBoundPaths, boundPaths, cleared, entryPoint, toolPos,toolDir)) {
|
||||
toolPos = IntPoint(entryPoint.X,entryPoint.Y);
|
||||
toolDir = DoublePoint(1.0,0.0);
|
||||
outsideEntry=true;
|
||||
} else {
|
||||
if(!FindEntryPoint(progressPaths, toolBoundPaths, boundPaths, cleared, entryPoint, toolPos,toolDir)) return;
|
||||
}
|
||||
|
||||
//cout << "Entry point:" << entryPoint << endl;
|
||||
Clipper clip;
|
||||
ClipperOffset clipof;
|
||||
@@ -1156,42 +1267,14 @@ namespace AdaptivePath {
|
||||
long stepScaled = long(RESOLUTION_FACTOR);
|
||||
IntPoint engagePoint;
|
||||
|
||||
IntPoint toolPos;
|
||||
DoublePoint toolDir;
|
||||
|
||||
IntPoint newToolPos;
|
||||
DoublePoint newToolDir;
|
||||
|
||||
// visualize/progress for boundPath
|
||||
for(const auto & pth :toolBoundPaths) {
|
||||
if(pth.size()>0) {
|
||||
progressPaths.push_back(TPath());
|
||||
for(const auto pt: pth)
|
||||
progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor));
|
||||
progressPaths.back().second.push_back(DPoint(double(pth.front().X)/scaleFactor,double(pth.front().Y)/scaleFactor));
|
||||
}
|
||||
}
|
||||
CheckReportProgress(progressPaths, true);
|
||||
|
||||
|
||||
// visualize/progress for helix
|
||||
clipof.Clear();
|
||||
Path hp;
|
||||
hp << entryPoint;
|
||||
clipof.AddPath(hp,JoinType::jtRound,EndType::etOpenRound);
|
||||
Paths hps;
|
||||
clipof.Execute(hps,helixRampRadiusScaled);
|
||||
progressPaths.push_back(TPath());
|
||||
|
||||
// show in progress cb
|
||||
for(auto & pt:hps[0]) {
|
||||
progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor));
|
||||
}
|
||||
CheckReportProgress(progressPaths);
|
||||
|
||||
// find the first tool position and direction
|
||||
toolPos = IntPoint(entryPoint.X,entryPoint.Y - helixRampRadiusScaled);
|
||||
toolDir = DoublePoint(1.0,0.0);
|
||||
output.StartPoint =DPoint(double(toolPos.X)/scaleFactor,double(toolPos.Y)/scaleFactor);
|
||||
IntPoint startPoint = toolPos;
|
||||
output.StartPoint =DPoint(double(startPoint.X)/scaleFactor,double(startPoint.Y)/scaleFactor);
|
||||
|
||||
bool firstEngagePoint=true;
|
||||
Path passToolPath; // to store pass toolpath
|
||||
@@ -1211,6 +1294,8 @@ namespace AdaptivePath {
|
||||
long over_cut_count =0;
|
||||
unclearLinkingMoveCount=0;
|
||||
//long engage_no_cut_count=0;
|
||||
double prevDistFromStart =0;
|
||||
bool prevDistTrend = false;
|
||||
|
||||
double perf_total_len=0;
|
||||
#ifdef DEV_MODE
|
||||
@@ -1247,7 +1332,7 @@ namespace AdaptivePath {
|
||||
*******************************/
|
||||
for(long point_index=0;point_index<POINTS_PER_PASS_LIMIT;point_index++) {
|
||||
if(stopProcessing) break;
|
||||
//cout<<"Pass:"<< pass << " Point:" << point_index;
|
||||
//cout<<"Pass:"<< pass << " Point:" << point_index << endl;
|
||||
total_points++;
|
||||
AverageDirection(gyro, toolDir);
|
||||
Perf_DistanceToBoundary.Start();
|
||||
@@ -1363,11 +1448,12 @@ namespace AdaptivePath {
|
||||
}
|
||||
|
||||
|
||||
if(firstEngagePoint) { // initial spiral shape need clearing in smaller intervals
|
||||
double distFromEntry = sqrt(DistanceSqrd(toolPos,entryPoint));
|
||||
double circ = distFromEntry * M_PI;
|
||||
//cout << (circ/(16*RESOLUTION_FACTOR)) << endl;
|
||||
if(toClearPath.size()>circ/(16*RESOLUTION_FACTOR)) {
|
||||
|
||||
// update cleared paths when trend of distance from start point changes sign (starts to get closer, or start to get farther)
|
||||
double distFromStart = sqrt(DistanceSqrd(toolPos,startPoint));
|
||||
bool distanceTrend = distFromStart > prevDistFromStart ? true : false;
|
||||
|
||||
if(distFromStart!=prevDistTrend) {
|
||||
Perf_ExpandCleared.Start();
|
||||
// expand cleared
|
||||
clipof.Clear();
|
||||
@@ -1381,8 +1467,10 @@ namespace AdaptivePath {
|
||||
CleanPolygons(cleared);
|
||||
toClearPath.clear();
|
||||
Perf_ExpandCleared.Stop();
|
||||
}
|
||||
}
|
||||
prevDistTrend = distanceTrend;
|
||||
prevDistFromStart = distFromStart;
|
||||
|
||||
|
||||
if(area>0) { // cut is ok - record it
|
||||
if(toClearPath.size()==0) toClearPath.push_back(toolPos);
|
||||
@@ -1463,6 +1551,14 @@ namespace AdaptivePath {
|
||||
IntPoint lastPoint;
|
||||
|
||||
for(auto & pth: finishingPaths) {
|
||||
// trim finishing passes outside the stock boundary - make no sense to cut where is no material
|
||||
Paths diff;
|
||||
clip.Clear();
|
||||
clip.AddPath(pth,PolyType::ptSubject,true);
|
||||
clip.AddPaths(stockInputPaths,PolyType::ptClip,true);
|
||||
clip.Execute(ClipType::ctDifference,diff);
|
||||
if(diff.size()>0) continue;
|
||||
|
||||
progressPaths.push_back(TPath());
|
||||
// show in progress cb
|
||||
for(auto & pt:pth) {
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace AdaptivePath {
|
||||
|
||||
enum MotionType { mtCutting = 0, mtLinkClear = 1, mtLinkNotClear = 2, mtLinkClearAtPrevPass = 3 };
|
||||
|
||||
enum OperationType { otClearing = 0, otProfilingInside = 1, otProfilingOutside = 2 };
|
||||
enum OperationType { otClearingInside = 0, otClearingOutside = 1, otProfilingInside = 2, otProfilingOutside = 3 };
|
||||
|
||||
typedef std::pair<double,double> DPoint;
|
||||
typedef std::vector<DPoint> DPath;
|
||||
@@ -58,12 +58,12 @@ namespace AdaptivePath {
|
||||
double toolDiameter=5;
|
||||
double helixRampDiameter=0;
|
||||
double stepOverFactor = 0.2;
|
||||
int polyTreeNestingLimit=0;
|
||||
double tolerance=0.1;
|
||||
double stockToLeave=0;
|
||||
OperationType opType = OperationType::otClearing;
|
||||
bool forceInsideOut = true;
|
||||
OperationType opType = OperationType::otClearingInside;
|
||||
|
||||
std::list<AdaptiveOutput> Execute(const DPaths &paths, std::function<bool(TPaths)> progressCallbackFn);
|
||||
std::list<AdaptiveOutput> Execute(const DPaths &stockPaths, const DPaths &paths, std::function<bool(TPaths)> progressCallbackFn);
|
||||
|
||||
#ifdef DEV_MODE
|
||||
/*for debugging*/
|
||||
@@ -75,7 +75,8 @@ namespace AdaptivePath {
|
||||
private:
|
||||
std::list<AdaptiveOutput> results;
|
||||
Paths inputPaths;
|
||||
|
||||
Paths stockInputPaths;
|
||||
int polyTreeNestingLimit=0;
|
||||
double scaleFactor=100;
|
||||
long toolRadiusScaled=10;
|
||||
long finishPassOffsetScaled=0;
|
||||
@@ -92,14 +93,17 @@ namespace AdaptivePath {
|
||||
Path toolGeometry; // tool geometry at coord 0,0, should not be modified
|
||||
|
||||
void ProcessPolyNode(Paths & boundPaths, Paths & toolBoundPaths);
|
||||
bool FindEntryPoint(const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/, IntPoint &entryPoint /*output*/);
|
||||
bool FindEntryPoint(TPaths &progressPaths,const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/,
|
||||
IntPoint &entryPoint /*output*/, IntPoint & toolPos, DoublePoint & toolDir);
|
||||
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);
|
||||
void AppendToolPath(AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared,const Paths & toolBoundPaths, bool close=false);
|
||||
bool CheckCollision(const IntPoint &lastPoint,const IntPoint &nextPoint,const Paths & cleared);
|
||||
friend class EngagePoint; // for CalcCutArea
|
||||
|
||||
void CheckReportProgress(TPaths &progressPaths,bool force=false);
|
||||
|
||||
void AddPathsToProgress(TPaths &progressPaths,Paths paths);
|
||||
private: // constants for fine tuning
|
||||
const bool preventConvetionalMode = true;
|
||||
const double RESOLUTION_FACTOR = 8.0;
|
||||
|
||||
@@ -295,8 +295,21 @@ double AreaGetArea(const CArea& a)
|
||||
|
||||
|
||||
// Adaptive2d.Execute wrapper
|
||||
bp::list AdaptiveExecute(AdaptivePath::Adaptive2d& ada,const boost::python::list &in_paths, boost::python::object progressCallbackFn) {
|
||||
bp::list AdaptiveExecute(AdaptivePath::Adaptive2d& ada,const boost::python::list &stock_paths, const boost::python::list &in_paths, boost::python::object progressCallbackFn) {
|
||||
bp::list out_list;
|
||||
|
||||
// convert stock paths
|
||||
AdaptivePath::DPaths stock_dpaths;
|
||||
for(bp::ssize_t i=0;i<bp::len(stock_paths);i++) {
|
||||
bp::list in_path=bp::extract<boost::python::list>(stock_paths[i]);
|
||||
AdaptivePath::DPath dpath;
|
||||
for(bp::ssize_t j=0;j<bp::len(in_path);j++) {
|
||||
bp::list in_point = bp::extract<bp::list>(in_path[j]);
|
||||
dpath.push_back(pair<double,double>(bp::extract<double>(in_point[0]),bp::extract<double>(in_point[1])));
|
||||
}
|
||||
stock_dpaths.push_back(dpath);
|
||||
}
|
||||
|
||||
// convert inputs
|
||||
AdaptivePath::DPaths dpaths;
|
||||
for(bp::ssize_t i=0;i<bp::len(in_paths);i++) {
|
||||
@@ -309,7 +322,7 @@ bp::list AdaptiveExecute(AdaptivePath::Adaptive2d& ada,const boost::python::list
|
||||
dpaths.push_back(dpath);
|
||||
}
|
||||
// Execute with callback
|
||||
std::list<AdaptivePath::AdaptiveOutput> result=ada.Execute(dpaths,[progressCallbackFn](AdaptivePath::TPaths tp)->bool {
|
||||
std::list<AdaptivePath::AdaptiveOutput> result=ada.Execute(stock_dpaths,dpaths,[progressCallbackFn](AdaptivePath::TPaths tp)->bool {
|
||||
bp::list out_paths;
|
||||
for(const auto & in_pair : tp) {
|
||||
bp::list path;
|
||||
@@ -495,7 +508,8 @@ BOOST_PYTHON_MODULE(area) {
|
||||
.value("LinkClearAtPrevPass", MotionType::mtLinkClearAtPrevPass);
|
||||
|
||||
bp::enum_<OperationType>("AdaptiveOperationType")
|
||||
.value("Clearing", OperationType::otClearing)
|
||||
.value("ClearingInside", OperationType::otClearingInside)
|
||||
.value("ClearingOutside", OperationType::otClearingOutside)
|
||||
.value("ProfilingInside", OperationType::otProfilingInside)
|
||||
.value("ProfilingOutside", OperationType::otProfilingOutside);
|
||||
|
||||
@@ -513,7 +527,8 @@ BOOST_PYTHON_MODULE(area) {
|
||||
.def_readwrite("toolDiameter", &Adaptive2d::toolDiameter)
|
||||
.def_readwrite("stockToLeave", &Adaptive2d::stockToLeave)
|
||||
.def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter)
|
||||
.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
|
||||
.def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut)
|
||||
//.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
|
||||
.def_readwrite("tolerance", &Adaptive2d::tolerance)
|
||||
.def_readwrite("opType", &Adaptive2d::opType);
|
||||
|
||||
|
||||
@@ -369,7 +369,8 @@ void init_pyarea(py::module &m){
|
||||
.value("LinkClearAtPrevPass", MotionType::mtLinkClearAtPrevPass);
|
||||
|
||||
py::enum_<OperationType>(m, "AdaptiveOperationType")
|
||||
.value("Clearing", OperationType::otClearing)
|
||||
.value("ClearingInside", OperationType::otClearingInside)
|
||||
.value("ClearingOutside", OperationType::otClearingOutside)
|
||||
.value("ProfilingInside", OperationType::otProfilingInside)
|
||||
.value("ProfilingOutside", OperationType::otProfilingOutside);
|
||||
|
||||
@@ -387,7 +388,8 @@ void init_pyarea(py::module &m){
|
||||
.def_readwrite("toolDiameter", &Adaptive2d::toolDiameter)
|
||||
.def_readwrite("stockToLeave", &Adaptive2d::stockToLeave)
|
||||
.def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter)
|
||||
.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
|
||||
.def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut)
|
||||
//.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
|
||||
.def_readwrite("tolerance", &Adaptive2d::tolerance)
|
||||
.def_readwrite("opType", &Adaptive2d::opType);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user