From e31a1b3da852f8a0106ac171051fbd165152fec1 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Tue, 16 Sep 2025 12:59:27 -0400 Subject: [PATCH] [CAM] WIP fix adaptive stepover bug, many debug statements remain --- src/Mod/CAM/Path/Op/Adaptive.py | 3 + src/Mod/CAM/libarea/Adaptive.cpp | 130 +++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 43 deletions(-) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 4d8fb39dce..0b1a104cc6 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -784,7 +784,10 @@ def ExecuteModelAware(op, obj): "orderCutsByRegion": obj.OrderCutsByRegion, } + import random + insideInputStateObject = { + "TODO TESTING": random.random(), "tool": op.tool.Diameter.Value, "tolerance": obj.Tolerance, "geometry": [ diff --git a/src/Mod/CAM/libarea/Adaptive.cpp b/src/Mod/CAM/libarea/Adaptive.cpp index 13497a6ff4..7f766db7cd 100644 --- a/src/Mod/CAM/libarea/Adaptive.cpp +++ b/src/Mod/CAM/libarea/Adaptive.cpp @@ -24,11 +24,13 @@ #include "Adaptive.hpp" #include +#include #include #include #include #include #include +#include namespace ClipperLib { @@ -1117,58 +1119,65 @@ public: void clear() { - angles.clear(); - areas.clear(); + m_min.reset(); + m_max.reset(); + } + bool bothSides() + { + return m_min && m_max && m_min->second < 0 && m_max->second >= 0; } // adds point keeping the incremental order of areas for interpolation to work correctly void addPoint(double area, double angle) { - std::size_t size = areas.size(); - if (size == 0 || area > areas[size - 1] + NTOL) { // first point or largest area point - areas.push_back(area); - angles.push_back(angle); - return; + if (!m_min) { + m_min = {angle, area}; } - - for (std::size_t i = 0; i < size; i++) { - if (area < areas[i] - NTOL && (i == 0 || area > areas[i - 1] + NTOL)) { - areas.insert(areas.begin() + i, area); - angles.insert(angles.begin() + i, angle); + else if (!m_max) { + m_max = {angle, area}; + if (m_min->second > m_max->second) { + auto tmp = m_min; + m_min = m_max; + m_max = tmp; } } + else if (bothSides()) { + if (area < 0) { + m_min = {angle, area}; + } + else { + m_max = {angle, area}; + } + } + else { + if (abs(m_min->second) > abs(m_max->second)) { + m_min.reset(); + } + else { + m_max.reset(); + } + addPoint(area, angle); + } } - double interpolateAngle(double targetArea) + double interpolateAngle(ofstream& fout) { - std::size_t size = areas.size(); - if (size < 2 || targetArea > areas[size - 1]) { - return MIN_ANGLE; // max engage angle - convenient value to initially measure cut area - } - if (targetArea < areas[0]) { - return MAX_ANGLE; // min engage angle + if (!m_min) { + return MIN_ANGLE; } - for (size_t i = 1; i < size; i++) { - // find 2 subsequent points where target area is between - if (areas[i - 1] <= targetArea && areas[i] > targetArea) { - // linear interpolation - double af = (targetArea - areas[i - 1]) / (areas[i] - areas[i - 1]); - double a = angles[i - 1] + af * (angles[i] - angles[i - 1]); - return a; - } + if (!m_max) { + return MAX_ANGLE; } - return MIN_ANGLE; + + fout << "(" << m_min->first << ", " << m_min->second << ") ~ (" << m_max->first << ", " + << m_max->second << ") "; + double p = (0 - m_min->second) / (m_max->second - m_min->second); + return m_min->first * (1 - p) + m_max->first * p; } double clampAngle(double angle) { - if (angle < MIN_ANGLE) { - return MIN_ANGLE; - } - if (angle > MAX_ANGLE) { - return MAX_ANGLE; - } - return angle; + return max(min(angle, MAX_ANGLE), MIN_ANGLE); } double getRandomAngle() @@ -1177,12 +1186,12 @@ public: } size_t getPointCount() { - return areas.size(); + return (m_min ? 1 : 0) + (m_max ? 1 : 0); } -private: - vector angles; - vector areas; +public: + std::optional> m_min; + std::optional> m_max; }; //*************************************** @@ -2771,6 +2780,9 @@ void Adaptive2d::AddPathToProgress(TPaths& progressPaths, const Path pth, Motion void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) { + ofstream fout("adaptive_debug.txt"); + fout << endl << endl << "----------------------" << endl; + fout << "Start ProcessPolyNode" << endl; Perf_ProcessPolyNode.Start(); current_region++; cout << "** Processing region: " << current_region << endl; @@ -2823,12 +2835,14 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) engageBounds.push_back(p); } outsideEntry = true; + fout << "Outside entry " << entryPoint << endl; } else { if (!FindEntryPoint(progressPaths, toolBoundPaths, boundPaths, cleared, entryPoint, toolPos, toolDir)) { Perf_ProcessPolyNode.Stop(); return; } + fout << "Helix entry " << entryPoint << endl; } EngagePoint engage(engageBounds); // engage point stepping instance @@ -2890,6 +2904,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) // LOOP - PASSES //******************************* for (long pass = 0; pass < PASSES_LIMIT; pass++) { + fout << "New pass! " << pass << endl; if (stopProcessing) { break; } @@ -2928,6 +2943,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) // LOOP - POINTS //******************************* for (long point_index = 0; point_index < POINTS_PER_PASS_LIMIT; point_index++) { + fout << endl << "Point " << point_index << endl; if (stopProcessing) { break; } @@ -2966,12 +2982,14 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) if (stepScaled < RESOLUTION_FACTOR) { stepScaled = long(RESOLUTION_FACTOR); } + fout << "\tstepScaled " << stepScaled << endl; //***************************** // ANGLE vs AREA ITERATIONS //***************************** double predictedAngle = averageDV(angleHistory); double maxError = AREA_ERROR_FACTOR * optimalCutAreaPD; + fout << "optimal area " << optimalCutAreaPD << " maxError " << maxError << endl; double area = 0; double areaPD = 0; interp.clear(); @@ -2981,34 +2999,51 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) double prev_error = __DBL_MAX__; for (iteration = 0; iteration < MAX_ITERATIONS; iteration++) { total_iterations++; + fout << "It " << iteration << " "; if (iteration == 0) { angle = predictedAngle; + fout << "case predicted "; } else if (iteration == 1) { angle = interp.MIN_ANGLE; // max engage + fout << "case minimum "; } - else if (iteration == 3) { - angle = interp.MAX_ANGLE; // min engage + else if (iteration == 2) { + if (interp.bothSides()) { + angle = interp.interpolateAngle(fout); + fout << "case interp "; + } + else { + angle = interp.MAX_ANGLE; // min engage + fout << "case maximum "; + } } else if (interp.getPointCount() < 2) { angle = interp.getRandomAngle(); + fout << "case random "; } else { - angle = interp.interpolateAngle(targetAreaPD); + angle = interp.interpolateAngle(fout); + fout << "case interp "; } + fout << "raw " << angle << " "; angle = interp.clampAngle(angle); + fout << "clamped " << angle << " "; newToolDir = rotate(toolDir, angle); newToolPos = IntPoint( long(toolPos.X + newToolDir.X * stepScaled), long(toolPos.Y + newToolDir.Y * stepScaled) ); + fout << "int pos " << newToolPos << " "; area = CalcCutArea(clip, toolPos, newToolPos, cleared); areaPD = area / double(stepScaled); // area per distance - interp.addPoint(areaPD, angle); + fout << "addPoint " << areaPD << " " << angle << " "; double error = areaPD - targetAreaPD; + interp.addPoint(error, angle); + fout << "areaPD " << areaPD << " error " << error << " "; // cout << " iter:" << iteration << " angle:" << angle << " area:" << areaPD // << " target:" << targetAreaPD << " error:" << error << " max:" << maxError // << endl; @@ -3017,14 +3052,18 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) if (angleHistory.size() > ANGLE_HISTORY_POINTS) { angleHistory.erase(angleHistory.begin()); } + fout << "small enough" << endl; break; } if (iteration > 5 && fabs(error - prev_error) < 0.001) { + fout << "no change" << endl; break; } if (iteration == MAX_ITERATIONS - 1) { + fout << "too many iterations!" << endl; total_exceeded++; } + fout << endl; prev_error = error; } Perf_PointIterations.Stop(); @@ -3046,6 +3085,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) long(toolPos.Y + newToolDir.Y * stepScaled) ); recalcArea = true; + fout << "\tRewrote tooldir/toolpos for boundary approach" << endl; } //********************************************** @@ -3061,6 +3101,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) long(toolPos.X + newToolDir.X * stepScaled), long(toolPos.Y + newToolDir.Y * stepScaled) ); + fout << "\tMoving tool back within boundary..." << endl; } if (rotateStep >= 180) { #ifdef DEV_MODE @@ -3076,6 +3117,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) // safety condition if (area > stepScaled * optimalCutAreaPD && areaPD > 2 * optimalCutAreaPD) { over_cut_count++; + fout << "\tCut area too big!!!" << endl; break; } @@ -3093,6 +3135,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) if (area > 0.5 * MIN_CUT_AREA_FACTOR * optimalCutAreaPD * RESOLUTION_FACTOR) { // cut is ok - record it + fout << "\tFinal cut acceptance" << endl; noCutDistance = 0; if (toClearPath.empty()) { toClearPath.push_back(toolPos); @@ -3148,6 +3191,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) break; } noCutDistance += stepScaled; + fout << "\tFailed to accept point??" << endl; } } /* end of points loop*/