diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index b57317bd4a..d26bd7fff0 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -75,6 +75,7 @@ #include #include #include +#include #include #include "Area.h" @@ -527,6 +528,181 @@ std::shared_ptr Area::getClearedArea(double tipDiameter, double diameter) return clearedArea; } +class ClearedAreaSegmentVisitor : public PathSegmentVisitor +{ +private: + CArea pathSegments; + double maxZ; + double diameter; + + void line(const Base::Vector3d &last, const Base::Vector3d &next) + { + if (last.z <= maxZ && next.z <= maxZ) { + CCurve curve; + curve.append(CVertex{{last.x, last.y}}); + curve.append(CVertex{{next.x, next.y}}); + pathSegments.append(curve); + count++; + } else { + // printf("SKIP!"); + } + } +public: + int count = 0; + + ClearedAreaSegmentVisitor(double maxZ, double diameter) : maxZ(maxZ), diameter(diameter) + { + } + + CArea getClearedArea() + { + CArea result{pathSegments}; + result.Thicken(diameter / 2); + return result; + } + + void g0(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override + { + (void)id; + // printf("g0 [ "); + processPt(last); + // printf("] "); + processPts(pts); + // printf("_ "); + processPt(next); + + line(last, next); + // printf("\n"); + } + + void g1(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override + { + (void)id; + // printf("g1 [ "); + processPt(last); + // printf("] "); + processPts(pts); + // printf("_ "); + processPt(next); + + line(last, next); + // printf("\n"); + } + + void g23(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, const Base::Vector3d ¢er) override + { + (void)id; + (void)center; + // printf("g23 [ "); + processPt(last); + // printf("] "); + processPts(pts); + // printf("_ "); + processPt(next); + + // TODO do I need to add last and next to the list??? + // TODO rounding resolution of the function that calls this is too big + Base::Vector3d prev = last; + for (Base::Vector3d p : pts) { + line(prev, p); + prev = p; + } + line(prev, next); + // printf("\n"); + } + + void g8x(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, + const std::deque &p, const std::deque &q) override + { + (void)id; + (void)q; // always within the bounds of p + printf("g8x UNHANDLED\n"); + // processPt(last); + // processPts(pts); + // processPts(p); + // processPt(next); + (void)last; + (void)pts; + (void)p; + (void)next; + } + void g38(int id, const Base::Vector3d &last, const Base::Vector3d &next) override + { + (void)id; + printf("g38 UNHANDLED\n"); + // processPt(last); + // processPt(next); + (void)last; + (void)next; + } + +private: + void processPts(const std::deque &pts) { + for (std::deque::const_iterator it=pts.begin(); pts.end() != it; ++it) { + processPt(*it); + } + } + + void processPt(const Base::Vector3d &pt) { + // printf("(%7.3f, %7.3f, %7.3f) ", pt.x, pt.y, pt.z); + } +}; + +std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax) { + build(); +#define AREA_MY(_param) myParams.PARAM_FNAME(_param) + PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); + PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_CLIPPER_FILL); + (void)SubjectFill; + (void)ClipFill; + + // Do not fit arcs after these offsets; it introduces unnecessary approximation error, and all off + // those arcs will be converted back to segments again for clipper differencing in getRestArea anyway + CAreaConfig conf(myParams, /*no_fit_arcs*/ true); + + const double roundPrecision = myParams.Accuracy; + const double buffer = 2 * roundPrecision; + (void)buffer; + (void)JoinType; + (void)EndType; + + Base::Vector3d pos = Base::Vector3d(0, 0, zmax + 1); + printf("getClearedAreaFromPath(path, diameter=%g, zmax=%g:\n", diameter, zmax); + // printf("Gcode:\n"); + for (auto c : path->getCommands()) { + // printf("\t%s ", c->Name.c_str()); + for (std::map::iterator i = c->Parameters.begin(); i != c->Parameters.end(); ++i) { + // printf("%s%g ", i->first.c_str(), i->second); + } + // printf("\n"); + } + + printf("\n"); + printf("GCode walker:\n"); + ClearedAreaSegmentVisitor visitor(zmax, diameter); + PathSegmentWalker walker(*path); + walker.walk(visitor, Base::Vector3d(0, 0, zmax + 1)); + printf("Count: %d\n", visitor.count); + printf("\n"); + printf("\n"); + + + std::shared_ptr clearedArea = make_shared(&myParams); + //clearedArea->myTrsf = myTrsf; + clearedArea->myTrsf = {}; + if (visitor.count > 0) { + //gp_Trsf trsf(myTrsf.Inverted()); + TopoDS_Shape clearedAreaShape = Area::toShape(visitor.getClearedArea(), false/*, &trsf*/); + clearedArea->add(clearedAreaShape, OperationCompound); + clearedArea->build(); + } else { + clearedArea->myArea = std::make_unique(); + clearedArea->myAreaOpen = std::make_unique(); + } + + return clearedArea; +} + std::shared_ptr Area::getRestArea(std::vector> clearedAreas, double diameter) { build(); PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); @@ -538,6 +714,8 @@ std::shared_ptr Area::getRestArea(std::vector> clear // transform all clearedAreas into our workplane Area clearedAreasInPlane(&myParams); clearedAreasInPlane.myArea.reset(new CArea()); + printf("getRestArea\n"); + printf("\n"); for (std::shared_ptr clearedArea : clearedAreas) { gp_Trsf trsf = clearedArea->myTrsf; trsf.Invert(); diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index 5b192ae08a..3dc769dc9a 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -243,6 +243,7 @@ public: const TopoDS_Shape& plane = TopoDS_Shape()); std::shared_ptr getClearedArea(double tipDiameter, double diameter); + std::shared_ptr getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax); std::shared_ptr getRestArea(std::vector> clearedAreas, double diameter); TopoDS_Shape toTopoShape(); diff --git a/src/Mod/Path/App/AreaPy.xml b/src/Mod/Path/App/AreaPy.xml index a38a84aec1..029b245c21 100644 --- a/src/Mod/Path/App/AreaPy.xml +++ b/src/Mod/Path/App/AreaPy.xml @@ -67,6 +67,11 @@ same algorithm + + + + + diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index 817aed0e37..ec6c8d66e8 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -27,6 +27,7 @@ #include // inclusion of the generated files (generated out of AreaPy.xml) +#include "PathPy.h" #include "AreaPy.h" #include "AreaPy.cpp" @@ -152,6 +153,11 @@ static const PyMethodDef areaOverrides[] = { "getClearedArea(tipDiameter, diameter):\n" "Gets the area cleared when a tool maximally clears this area. This method assumes a tool tip diameter 'tipDiameter' traces the full area, and that (perhaps at a different height on the tool) this clears a different region with tool diameter 'diameter'.\n", }, + { + "getClearedAreaFromPath",nullptr,0, + "getClearedAreaFromPath(path, diameter, zmax):\n" + "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax.\n", + }, { "getRestArea",nullptr,0, "getRestArea(clearedAreas, diameter):\n" @@ -415,6 +421,24 @@ PyObject* AreaPy::getClearedArea(PyObject *args) } PY_CATCH_OCC } +PyObject* AreaPy::getClearedAreaFromPath(PyObject *args) +{ + PY_TRY { + PyObject *pyPath; + double diameter, zmax; + if (!PyArg_ParseTuple(args, "Odd", &pyPath, &diameter, &zmax)) + return nullptr; + if (!PyObject_TypeCheck(pyPath, &(PathPy::Type))) { + PyErr_SetString(PyExc_TypeError, "path must be of type PathPy"); + return nullptr; + } + const PathPy *path = static_cast(pyPath); + std::shared_ptr clearedArea = getAreaPtr()->getClearedAreaFromPath(path->getToolpathPtr(), diameter, zmax); + auto pyClearedArea = Py::asObject(new AreaPy(new Area(*clearedArea, true))); + return Py::new_reference_to(pyClearedArea); + } PY_CATCH_OCC +} + PyObject* AreaPy::getRestArea(PyObject *args) { PY_TRY { diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index 8e329ae4b5..77862274c2 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -272,7 +272,23 @@ class ObjectOp(PathOp.ObjectOp): restSections = [] for section in sections: z = section.getShape().BoundBox.ZMin - sectionClearedAreas = [a for a in clearedAreas if a.getShape().BoundBox.ZMax <= z] + # sectionClearedAreas = [a for a in clearedAreas if a.getShape().BoundBox.ZMax <= z] + sectionClearedAreas = [] + for op in self.job.Operations.Group: + print(op.Name) + if self in [x.Proxy for x in [op] + op.OutListRecursive if hasattr(x, "Proxy")]: + print("found self") + break + if hasattr(op, "Active") and op.Active and op.Path: + tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) + diameter = tool.Diameter.getValueAs("mm") + sectionClearedAreas.append(area.getClearedAreaFromPath(op.Path, diameter, z+0.001)) + debugZ = -1.5 + if debugZ -.1 < z and z < debugZ + .1: + debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) + debugObj.Label = "Debug_{}_{}".format(debugZ, op.Label) + debugObj.Shape = sectionClearedAreas[-1].getShape() + pass restSection = section.getRestArea(sectionClearedAreas, self.tool.Diameter.getValueAs("mm")) restSections.append(restSection) sections = restSections