From 5f648aa2c1dcddbb20ca2df29158fc5072594a7a Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Wed, 25 Mar 2020 15:05:08 -0500 Subject: [PATCH 01/12] remove sleep calls, change some for loops to list comp, move isPointOnLine to base vector --- src/Base/Vector3D.cpp | 20 ++++++++ src/Base/Vector3D.h | 3 ++ src/Base/VectorPy.xml | 7 +++ src/Base/VectorPyImp.cpp | 26 ++++++++++ src/Mod/Path/PathScripts/PathSurface.py | 65 ++++++++++--------------- 5 files changed, 82 insertions(+), 39 deletions(-) diff --git a/src/Base/Vector3D.cpp b/src/Base/Vector3D.cpp index 0d0729013f..a5d34a7259 100644 --- a/src/Base/Vector3D.cpp +++ b/src/Base/Vector3D.cpp @@ -192,6 +192,26 @@ Vector3<_Precision> Vector3<_Precision>::Cross(const Vector3<_Precision>& rcVct) return cVctRes; } +template +bool Vector3<_Precision>::IsOnLine (const Vector3<_Precision>& startVct, const Vector3<_Precision>& endVct) const +{ + Vector3<_Precision> vectorAB = endVct - startVct; + Vector3<_Precision> vectorAC = *this - startVct; + Vector3<_Precision> crossproduct = vectorAB.Cross(vectorAC); + _Precision dotproduct = vectorAB.Dot(vectorAC); + + if (crossproduct.Length() > traits_type::epsilon()) + return false; + + if (dotproduct < 0) + return false; + + if (dotproduct > vectorAB.Length() * vectorAB.Length()) + return false; + + return true; +} + template bool Vector3<_Precision>::operator != (const Vector3<_Precision>& rcVct) const { diff --git a/src/Base/Vector3D.h b/src/Base/Vector3D.h index 4b0f4a414d..b0f7e2ebbb 100644 --- a/src/Base/Vector3D.h +++ b/src/Base/Vector3D.h @@ -133,6 +133,9 @@ public: bool operator == (const Vector3<_Precision>& rcVct) const; //@} + /// Check if Vector is on a line + bool IsOnLine (const Vector3<_Precision>& startVct, const Vector3<_Precision>& endVct) const; + /** @name Modification */ //@{ void ScaleX (_Precision f); diff --git a/src/Base/VectorPy.xml b/src/Base/VectorPy.xml index 517eff03fd..2f5a8486f0 100644 --- a/src/Base/VectorPy.xml +++ b/src/Base/VectorPy.xml @@ -74,6 +74,13 @@ + + + isOnLine(Vector, Vector) + checks if Vector is on a line + + + getAngle(Vector) diff --git a/src/Base/VectorPyImp.cpp b/src/Base/VectorPyImp.cpp index 712315dcb1..96328aab97 100644 --- a/src/Base/VectorPyImp.cpp +++ b/src/Base/VectorPyImp.cpp @@ -435,6 +435,32 @@ PyObject* VectorPy::cross(PyObject *args) return new VectorPy(v); } +PyObject* VectorPy::isOnLine(PyObject *args) +{ + PyObject *start, *end; + if (!PyArg_ParseTuple(args, "OO",&start, &end)) + return 0; + if (!PyObject_TypeCheck(start, &(VectorPy::Type))) { + PyErr_SetString(PyExc_TypeError, "First arg must be Vector"); + return 0; + } + if (!PyObject_TypeCheck(end, &(VectorPy::Type))) { + PyErr_SetString(PyExc_TypeError, "Second arg must be Vector"); + return 0; + } + + VectorPy* start_vec = static_cast(start); + VectorPy* end_vec = static_cast(end); + + VectorPy::PointerType this_ptr = reinterpret_cast(_pcTwinPointer); + VectorPy::PointerType start_ptr = reinterpret_cast(start_vec->_pcTwinPointer); + VectorPy::PointerType end_ptr = reinterpret_cast(end_vec->_pcTwinPointer); + + Py::Boolean result = this_ptr->IsOnLine(*start_ptr, *end_ptr); + + return Py::new_reference_to(result); +} + PyObject* VectorPy::getAngle(PyObject *args) { PyObject *obj; diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index df07fe881e..792928a4ba 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -365,6 +365,9 @@ class ObjectSurface(PathOp.ObjectOp): def opExecute(self, obj): '''opExecute(obj) ... process surface operation''' PathLog.track() + import cProfile + pr = cProfile.Profile() + pr.enable() self.modelSTLs = list() self.safeSTLs = list() @@ -530,7 +533,7 @@ class ObjectSurface(PathOp.ObjectOp): PathLog.info('Working on Model.Group[{}]: {}'.format(m, Mdl.Label)) # make stock-model-voidShapes STL model for avoidance detection on transitions self._makeSafeSTL(JOB, obj, m, FACES[m], VOIDS[m]) - time.sleep(0.2) + #time.sleep(0.2) # Process model/faces - OCL objects must be ready CMDS.extend(self._processCutAreas(JOB, obj, m, FACES[m], VOIDS[m])) @@ -603,6 +606,8 @@ class ObjectSurface(PathOp.ObjectOp): del self.deflection execTime = time.time() - startTime + pr.disable() + pr.dump_stats("/mnt/files/profile.cprof") PathLog.info('Operation time: {} sec.'.format(execTime)) return True @@ -1008,7 +1013,7 @@ class ObjectSurface(PathOp.ObjectOp): except Exception as eee: PathLog.error(str(eee)) cont = False - time.sleep(0.2) + #time.sleep(0.2) if cont: csFaceShape = self._getShapeSlice(baseEnv) @@ -1486,7 +1491,7 @@ class ObjectSurface(PathOp.ObjectOp): fuseShapes.append(adjStckWst) else: PathLog.warning('Path transitions might not avoid the model. Verify paths.') - time.sleep(0.3) + #time.sleep(0.3) else: # If boundbox is Job.Stock, add hidden pad under stock as base plate @@ -1523,7 +1528,7 @@ class ObjectSurface(PathOp.ObjectOp): LinearDeflection=obj.LinearDeflection.Value, AngularDeflection=obj.AngularDeflection.Value, Relative=False) - time.sleep(0.2) + #time.sleep(0.2) stl = ocl.STLSurf() for f in meshFuse.Facets: p = f.Points[0] @@ -2003,7 +2008,7 @@ class ObjectSurface(PathOp.ObjectOp): for v in range(1, lenOS): nxt = OS[v + 1] if optimize is True: - iPOL = self.isPointOnLine(prev, nxt, pnt) + iPOL = prev.isOnLine(nxt, pnt) if iPOL is True: pnt = nxt else: @@ -2057,7 +2062,7 @@ class ObjectSurface(PathOp.ObjectOp): ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (first / middle point) - iC = self.isPointOnLine(sp, ep, cp) + iC = sp.isOnLine(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -2163,7 +2168,7 @@ class ObjectSurface(PathOp.ObjectOp): cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (start point of segment) ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point - iC = self.isPointOnLine(sp, ep, cp) + iC = sp.isOnLine(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -2452,7 +2457,7 @@ class ObjectSurface(PathOp.ObjectOp): return ARCS def _planarDropCutScan(self, pdc, A, B): - PNTS = list() + #PNTS = list() (x1, y1) = A (x2, y2) = B path = ocl.Path() # create an empty path object @@ -2463,8 +2468,9 @@ class ObjectSurface(PathOp.ObjectOp): pdc.setPath(path) pdc.run() # run dropcutter algorithm on path CLP = pdc.getCLPoints() - for p in CLP: - PNTS.append(FreeCAD.Vector(p.x, p.y, p.z)) + PNTS = [FreeCAD.Vector(p.x, p.y, p.z) for p in CLP] + #for p in CLP: + # PNTS.append(FreeCAD.Vector(p.x, p.y, p.z)) return PNTS # pdc.getCLPoints() def _planarCircularDropCutScan(self, pdc, Arc, cMode): @@ -2601,7 +2607,7 @@ class ObjectSurface(PathOp.ObjectOp): # Process point if optimize is True: - iPOL = self.isPointOnLine(prev, nxt, pnt) + iPOL = prev.isOnLine(nxt, pnt) if iPOL is True: onLine = True else: @@ -2893,7 +2899,7 @@ class ObjectSurface(PathOp.ObjectOp): # Process point if prcs is True: if optimize is True: - iPOL = self.isPointOnLine(prev, nxt, pnt) + iPOL = prev.isOnLine(nxt, pnt) if iPOL is True: onLine = True else: @@ -3308,7 +3314,7 @@ class ObjectSurface(PathOp.ObjectOp): prevDepth = layDep lCnt += 1 # increment layer count PathLog.debug("--Layer " + str(lCnt) + ": " + str(len(advances)) + " OCL scans and gcode in " + str(time.time() - t_before) + " s") - time.sleep(0.2) + #time.sleep(0.2) # Eol return commands @@ -3467,7 +3473,7 @@ class ObjectSurface(PathOp.ObjectOp): self.holdPoint = ocl.Point(float("inf"), float("inf"), float("inf")) if self.onHold is False: - if not optimize or not self.isPointOnLine(FreeCAD.Vector(prev.x, prev.y, prev.z), FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): + if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLine(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed})) # elif i == lastCLP: # output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed})) @@ -3588,7 +3594,7 @@ class ObjectSurface(PathOp.ObjectOp): else: optimize = False - if not optimize or not self.isPointOnLine(FreeCAD.Vector(prev.x, prev.y, prev.z), FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): + if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLine(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'F': self.horizFeed})) # Rotate point data @@ -3606,26 +3612,6 @@ class ObjectSurface(PathOp.ObjectOp): return output - # Support functions for both dropcutter and waterline operations - def isPointOnLine(self, strtPnt, endPnt, pointP): - '''isPointOnLine(strtPnt, endPnt, pointP) ... Determine if a given point is on the line defined by start and end points.''' - tolerance = 1e-6 - vectorAB = endPnt - strtPnt - vectorAC = pointP - strtPnt - crossproduct = vectorAB.cross(vectorAC) - dotproduct = vectorAB.dot(vectorAC) - - if crossproduct.Length > tolerance: - return False - - if dotproduct < 0: - return False - - if dotproduct > vectorAB.Length * vectorAB.Length: - return False - - return True - def holdStopCmds(self, obj, zMax, pd, p2, txt): '''holdStopCmds(obj, zMax, pd, p2, txt) ... Gcode commands to be executed at beginning of hold.''' cmds = [] @@ -3754,10 +3740,11 @@ class ObjectSurface(PathOp.ObjectOp): A = (p1.x, p1.y) B = (p2.x, p2.y) LINE = self._planarDropCutScan(pdc, A, B) - zMax = LINE[0].z - for p in LINE: - if p.z > zMax: - zMax = p.z + zMax = max([obj.z for obj in LINE]) + #zMax = LINE[0].z + #for p in LINE: + # if p.z > zMax: + # zMax = p.z if minDep is not None: if zMax < minDep: zMax = minDep From 651c383925f802a49c0b63b7c2750f39373b23cd Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Thu, 26 Mar 2020 19:02:25 -0500 Subject: [PATCH 02/12] changed some for loops to list comprehensions, added openmp for area to make two loops parallel, changed a fuse to a compound --- src/Mod/Path/App/Area.cpp | 69 ++++++++++++++++++------- src/Mod/Path/App/CMakeLists.txt | 3 +- src/Mod/Path/PathScripts/PathSurface.py | 63 +++++----------------- 3 files changed, 65 insertions(+), 70 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index fe552ecbd9..84d735df33 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -30,6 +30,7 @@ # include "boost_fix/intrusive/detail/memory_util.hpp" # include "boost_fix/container/detail/memory_util.hpp" # endif +# include # include # include # include @@ -1290,8 +1291,10 @@ int Area::project(TopoDS_Shape &shape_out, showShape(joiner.comp,"pre_project"); - Area area(params); + auto new_params = params; + Area area(new_params); area.myParams.SectionCount = 0; + area.myParams.Offset = 0.0; area.myParams.PocketMode = 0; area.myParams.Explode = false; @@ -1302,8 +1305,7 @@ int Area::project(TopoDS_Shape &shape_out, area.myParams.Coplanar = CoplanarNone; area.myProjecting = true; area.add(joiner.comp, OperationUnion); - const TopoDS_Shape &shape = area.getShape(); - + const TopoDS_Shape shape = area.getShape(); area.myParams.dump("project"); showShape(shape,"projected"); @@ -1478,16 +1480,29 @@ std::vector > Area::makeSections( area->setPlane(face.Moved(locInverse)); if(project) { - for(const auto &s : projectedShapes) { - gp_Trsf t; - t.SetTranslation(gp_Vec(0,0,-d)); - TopLoc_Location wloc(t); - area->add(s.shape.Moved(wloc).Moved(locInverse),s.op); + #pragma omp parallel + { + int thread_count = omp_get_num_threads(); + int thread_num = omp_get_thread_num(); + size_t chunk_size= projectedShapes.size() / thread_count; + auto begin = projectedShapes.begin(); + std::advance(begin, thread_num * chunk_size); + auto end = begin; + if(thread_num == thread_count - 1) // last thread iterates the remaining sequence + end = projectedShapes.end(); + else + std::advance(end, chunk_size); + #pragma omp barrier + for(auto s = begin; s != end; ++s) { + gp_Trsf t; + t.SetTranslation(gp_Vec(0,0,-d)); + TopLoc_Location wloc(t); + area->add(s->shape.Moved(wloc).Moved(locInverse),s->op); + } } sections.push_back(area); break; } - for(auto it=myShapes.begin();it!=myShapes.end();++it) { const auto &s = *it; BRep_Builder builder; @@ -1600,18 +1615,32 @@ std::list Area::getProjectedShapes(const gp_Trsf &trsf, bool invers TopLoc_Location loc(trsf); TopLoc_Location locInverse(loc.Inverted()); - mySkippedShapes = 0; - for(auto &s : myShapes) { - TopoDS_Shape out; - int skipped = Area::project(out,s.shape.Moved(loc),&myParams); - if(skipped < 0) { - ++mySkippedShapes; - continue; - }else - mySkippedShapes += skipped; - if(!out.IsNull()) - ret.emplace_back(s.op,inverse?out.Moved(locInverse):out); + #pragma omp parallel + { + int thread_count = omp_get_num_threads(); + int thread_num = omp_get_thread_num(); + size_t chunk_size= myShapes.size() / thread_count; + auto begin = myShapes.begin(); + std::advance(begin, thread_num * chunk_size); + auto end = begin; + if(thread_num == thread_count - 1) // last thread iterates the remaining sequence + end = myShapes.end(); + else + std::advance(end, chunk_size); + #pragma omp barrier + for(auto s = begin; s != end; ++s){ + TopoDS_Shape out; + int skipped = Area::project(out,s->shape.Moved(loc),&myParams); + if(skipped < 0) { + ++mySkippedShapes; + continue; + }else + mySkippedShapes += skipped; + if(!out.IsNull()) + ret.emplace_back(s->op,inverse?out.Moved(locInverse):out); + } } + if(mySkippedShapes) AREA_WARN("skipped " << mySkippedShapes << " sub shapes during projection"); return ret; diff --git a/src/Mod/Path/App/CMakeLists.txt b/src/Mod/Path/App/CMakeLists.txt index 2cee9a3a3a..ec50f473ab 100644 --- a/src/Mod/Path/App/CMakeLists.txt +++ b/src/Mod/Path/App/CMakeLists.txt @@ -118,8 +118,9 @@ SOURCE_GROUP("Module" FILES ${Mod_SRCS}) # SOURCE_GROUP("KDL" FILES ${KDL_SRCS} ${KDL_HPPS} ${UTIL_SRCS} ${UTIL_HPPS} ) #endif(WIN32) +find_package(OpenMP REQUIRED) add_library(Path SHARED ${Path_SRCS}) -target_link_libraries(Path ${Path_LIBS}) +target_link_libraries(Path ${Path_LIBS} OpenMP::OpenMP_CXX) if(FREECAD_USE_PCH) add_definitions(-D_PreComp_) diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 792928a4ba..7514da410e 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -365,9 +365,9 @@ class ObjectSurface(PathOp.ObjectOp): def opExecute(self, obj): '''opExecute(obj) ... process surface operation''' PathLog.track() - import cProfile - pr = cProfile.Profile() - pr.enable() + #import cProfile + #pr = cProfile.Profile() + #pr.enable() self.modelSTLs = list() self.safeSTLs = list() @@ -606,8 +606,8 @@ class ObjectSurface(PathOp.ObjectOp): del self.deflection execTime = time.time() - startTime - pr.disable() - pr.dump_stats("/mnt/files/profile.cprof") + #pr.disable() + #pr.dump_stats("/mnt/files/profile.cprof") PathLog.info('Operation time: {} sec.'.format(execTime)) return True @@ -1492,7 +1492,6 @@ class ObjectSurface(PathOp.ObjectOp): else: PathLog.warning('Path transitions might not avoid the model. Verify paths.') #time.sleep(0.3) - else: # If boundbox is Job.Stock, add hidden pad under stock as base plate toolDiam = self.cutter.getDiameter() @@ -1511,11 +1510,7 @@ class ObjectSurface(PathOp.ObjectOp): voidEnv = PathUtils.getEnvelope(partshape=voidComp, depthparams=self.depthParams) # Produces .Shape fuseShapes.append(voidEnv) - f0 = fuseShapes.pop(0) - if len(fuseShapes) > 0: - fused = f0.fuse(fuseShapes) - else: - fused = f0 + fused = Part.makeCompound(fuseShapes) if self.showDebugObjects is True: T = FreeCAD.ActiveDocument.addObject('Part::Feature', 'safeSTLShape') @@ -2585,44 +2580,14 @@ class ObjectSurface(PathOp.ObjectOp): return GCODE def _planarSinglepassProcess(self, obj, PNTS): - output = [] - optimize = obj.OptimizeLinearPaths - lenPNTS = len(PNTS) - lstIdx = lenPNTS - 1 - lop = None - onLine = False - - # Initialize first three points - nxt = None - pnt = PNTS[0] - prev = FreeCAD.Vector(-442064564.6, 258539656553.27, 3538553425.847) - - # Add temp end point - PNTS.append(FreeCAD.Vector(-4895747464.6, -25855763553.2, 35865763425)) - - # Begin processing ocl points list into gcode - for i in range(0, lenPNTS): - # Calculate next point for consideration with current point - nxt = PNTS[i + 1] - - # Process point - if optimize is True: - iPOL = prev.isOnLine(nxt, pnt) - if iPOL is True: - onLine = True - else: - onLine = False - output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed})) - else: - output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed})) - - # Rotate point data - if onLine is False: - prev = pnt - pnt = nxt - # Efor - - temp = PNTS.pop() # Remove temp end point + if obj.OptimizeLinearPaths: + # first item will be compared to the last point, but I think that should work + output = [Path.Command('G1', {'X': PNTS[i].x, 'Y': PNTS[i].y, 'Z': PNTS[i].z, 'F': self.horizFeed}) + for i in range(0, len(PNTS) - 1) + if not PNTS[i].isOnLine(PNTS[i -1],PNTS[i + 1])] + output.append(Path.Command('G1', {'X': PNTS[-1].x, 'Y': PNTS[-1].y, 'Z': PNTS[-1].z, 'F': self.horizFeed})) + else: + output = [Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}) for pnt in PNTS] return output From 3f36f558535447956187ba1e09b1aec8471836a7 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Sun, 29 Mar 2020 18:44:43 -0500 Subject: [PATCH 03/12] change from openmp to std::thread --- src/Mod/Path/App/Area.cpp | 126 ++++++++++++++++++++++---------- src/Mod/Path/App/CMakeLists.txt | 5 +- 2 files changed, 90 insertions(+), 41 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 84d735df33..a27db4bb88 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -30,7 +30,6 @@ # include "boost_fix/intrusive/detail/memory_util.hpp" # include "boost_fix/container/detail/memory_util.hpp" # endif -# include # include # include # include @@ -85,6 +84,10 @@ # include #endif +// TODO: remove omp and console +#include +#include +#include #include #include @@ -1480,29 +1483,51 @@ std::vector > Area::makeSections( area->setPlane(face.Moved(locInverse)); if(project) { - #pragma omp parallel - { - int thread_count = omp_get_num_threads(); - int thread_num = omp_get_thread_num(); - size_t chunk_size= projectedShapes.size() / thread_count; - auto begin = projectedShapes.begin(); - std::advance(begin, thread_num * chunk_size); - auto end = begin; - if(thread_num == thread_count - 1) // last thread iterates the remaining sequence - end = projectedShapes.end(); - else - std::advance(end, chunk_size); - #pragma omp barrier - for(auto s = begin; s != end; ++s) { + typedef std::list container; + typedef container::iterator iter; + std::mutex m; + + unsigned int n = std::thread::hardware_concurrency(); + Base::Console().Error("makeSections Threads: %d Shapes: %d\n", n, projectedShapes.size()); + auto worker = [&m, &d, &area, &locInverse] (iter begin, iter end) { + Base::Console().Error("In worker (# items: %d)\n", std::distance(begin, end)); + for (auto it = begin; it != end; it++) { gp_Trsf t; t.SetTranslation(gp_Vec(0,0,-d)); TopLoc_Location wloc(t); - area->add(s->shape.Moved(wloc).Moved(locInverse),s->op); + { + std::lock_guard lockGuard(m); + area->add(it->shape.Moved(wloc).Moved(locInverse),it->op); + } } + }; + if(projectedShapes.size() < n) + { + worker(projectedShapes.begin(), projectedShapes.end()); + } else { + std::vector threads(n); + const size_t chunk_size = projectedShapes.size() / n; + + size_t thread_num = 1; + for(auto it = std::begin(threads); it != std::end(threads) -1; ++it) + { + auto begin = projectedShapes.begin(); + std::advance(begin, thread_num * chunk_size); + auto end = begin; + if(thread_num == n - 1) // last thread iterates the remaining sequence + end = projectedShapes.end(); + else + std::advance(end, chunk_size); + *it = std::thread(worker, begin, end); + thread_num++; + } + for (auto&& i : threads) + i.join(); } sections.push_back(area); break; } + for(auto it=myShapes.begin();it!=myShapes.end();++it) { const auto &s = *it; BRep_Builder builder; @@ -1615,32 +1640,55 @@ std::list Area::getProjectedShapes(const gp_Trsf &trsf, bool invers TopLoc_Location loc(trsf); TopLoc_Location locInverse(loc.Inverted()); - #pragma omp parallel - { - int thread_count = omp_get_num_threads(); - int thread_num = omp_get_thread_num(); - size_t chunk_size= myShapes.size() / thread_count; - auto begin = myShapes.begin(); - std::advance(begin, thread_num * chunk_size); - auto end = begin; - if(thread_num == thread_count - 1) // last thread iterates the remaining sequence - end = myShapes.end(); - else - std::advance(end, chunk_size); - #pragma omp barrier - for(auto s = begin; s != end; ++s){ + typedef std::list container; + typedef container::const_iterator iter; + std::mutex m; + int mySkippedShapes = 0; + + unsigned int n = std::thread::hardware_concurrency(); + Base::Console().Error("getProjectedShapes Threads: %d Shapes: %d\n", n, myShapes.size()); + auto worker = [&m, &inverse, &loc, &ret, &locInverse, &mySkippedShapes] (iter begin,iter end, const AreaParams& myParams) { + Base::Console().Error("In worker (# items: %d)\n", std::distance(begin, end)); + for (auto it = begin; it != end; it++) { TopoDS_Shape out; - int skipped = Area::project(out,s->shape.Moved(loc),&myParams); - if(skipped < 0) { - ++mySkippedShapes; - continue; - }else - mySkippedShapes += skipped; - if(!out.IsNull()) - ret.emplace_back(s->op,inverse?out.Moved(locInverse):out); + int skipped = Area::project(out,it->shape.Moved(loc), &myParams); + { + std::lock_guard lockGuard(m); + if(skipped < 0) { + ++mySkippedShapes; + continue; + }else + mySkippedShapes += skipped; + if(!out.IsNull()) + ret.emplace_back(it->op,inverse?out.Moved(locInverse):out); + } + } + }; + if(myShapes.size() < n) + { + worker(myShapes.begin(), myShapes.end(), myParams); + } else { + std::vector threads(n); + const size_t chunk_size = myShapes.size() / n; + + size_t thread_num = 1; + for(auto it = std::begin(threads); it != std::end(threads) -1; ++it) + { + auto begin = myShapes.begin(); + std::advance(begin, thread_num * chunk_size); + auto end = begin; + if(thread_num == n - 1) // last thread iterates the remaining sequence + end = myShapes.end(); + else + std::advance(end, chunk_size); + *it = std::thread(worker, begin, end, myParams); + thread_num++; + } + for (auto&& i : threads) + i.join(); } - + if(mySkippedShapes) AREA_WARN("skipped " << mySkippedShapes << " sub shapes during projection"); return ret; diff --git a/src/Mod/Path/App/CMakeLists.txt b/src/Mod/Path/App/CMakeLists.txt index ec50f473ab..69a260d61b 100644 --- a/src/Mod/Path/App/CMakeLists.txt +++ b/src/Mod/Path/App/CMakeLists.txt @@ -118,9 +118,10 @@ SOURCE_GROUP("Module" FILES ${Mod_SRCS}) # SOURCE_GROUP("KDL" FILES ${KDL_SRCS} ${KDL_HPPS} ${UTIL_SRCS} ${UTIL_HPPS} ) #endif(WIN32) -find_package(OpenMP REQUIRED) +find_package(Threads REQUIRED) + add_library(Path SHARED ${Path_SRCS}) -target_link_libraries(Path ${Path_LIBS} OpenMP::OpenMP_CXX) +target_link_libraries(Path ${Path_LIBS} Threads::Threads) if(FREECAD_USE_PCH) add_definitions(-D_PreComp_) From fbc266c1587012956e1cc7e51285227ae578fc76 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Mon, 30 Mar 2020 11:18:36 -0500 Subject: [PATCH 04/12] remove parallel processing, replace mesh generation with getting facets --- src/Mod/Path/App/AppPathPy.cpp | 48 ++++++++++ src/Mod/Path/App/Area.cpp | 117 ++++-------------------- src/Mod/Path/App/CMakeLists.txt | 6 +- src/Mod/Path/PathScripts/PathSurface.py | 84 ++++++++++------- 4 files changed, 122 insertions(+), 133 deletions(-) diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index 381ef87ef6..bc34de944c 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ #include #include #include +#include #include "CommandPy.h" #include "PathPy.h" @@ -103,6 +105,9 @@ public: Module() : Py::ExtensionModule("Path") { + add_varargs_method("getFacets",&Module::getFacets, + "getFacets(shape): simplified mesh generation" + ); add_varargs_method("write",&Module::write, "write(object,filename): Exports a given path object to a GCode file" ); @@ -142,6 +147,49 @@ public: private: + Py::Object getFacets(const Py::Tuple& args) + { + PyObject *shape; + PyObject *list = PyList_New(0); + if (!PyArg_ParseTuple(args.ptr(), "O", &shape)) + throw Py::Exception(); + auto theShape = static_cast(shape)->getTopoShapePtr()->getShape(); + for(TopExp_Explorer ex(theShape, TopAbs_FACE); ex.More(); ex.Next()) + { + TopoDS_Face currentFace = TopoDS::Face(ex.Current()); + TopLoc_Location loc; + Handle(Poly_Triangulation) facets = BRep_Tool::Triangulation(currentFace, loc); + const TopAbs_Orientation anOrientation = currentFace.Orientation(); + bool flip = (anOrientation == TopAbs_REVERSED); + if(!facets.IsNull()){ + auto nodes = facets->Nodes(); + auto triangles = facets->Triangles(); + for(int i = 1; i <= triangles.Length(); i++){ + Standard_Integer n1,n2,n3; + triangles[i].Get(n1, n2, n3); + gp_Pnt p1 = nodes[n1]; + gp_Pnt p2 = nodes[n2]; + gp_Pnt p3 = nodes[n3]; + p1.Transform(loc.Transformation()); + p2.Transform(loc.Transformation()); + p3.Transform(loc.Transformation()); + PyObject *t1 = PyTuple_Pack(3, PyFloat_FromDouble(p1.X()), PyFloat_FromDouble(p1.Y()), PyFloat_FromDouble(p1.Z())); + PyObject *t2 = PyTuple_Pack(3, PyFloat_FromDouble(p2.X()), PyFloat_FromDouble(p2.Y()), PyFloat_FromDouble(p2.Z())); + PyObject *t3 = PyTuple_Pack(3, PyFloat_FromDouble(p3.X()), PyFloat_FromDouble(p3.Y()), PyFloat_FromDouble(p3.Z())); + PyObject *points; + if(flip) + { + points = PyTuple_Pack(3, t2, t1, t3); + } else { + points = PyTuple_Pack(3, t1, t2, t3); + } + PyList_Append(list, points); + } + } + } + return Py::asObject(list); + } + Py::Object write(const Py::Tuple& args) { char* Name; diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index a27db4bb88..ea61c9049a 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -84,10 +84,6 @@ # include #endif -// TODO: remove omp and console -#include -#include -#include #include #include @@ -1294,10 +1290,8 @@ int Area::project(TopoDS_Shape &shape_out, showShape(joiner.comp,"pre_project"); - auto new_params = params; - Area area(new_params); + Area area(params); area.myParams.SectionCount = 0; - area.myParams.Offset = 0.0; area.myParams.PocketMode = 0; area.myParams.Explode = false; @@ -1308,7 +1302,8 @@ int Area::project(TopoDS_Shape &shape_out, area.myParams.Coplanar = CoplanarNone; area.myProjecting = true; area.add(joiner.comp, OperationUnion); - const TopoDS_Shape shape = area.getShape(); + const TopoDS_Shape &shape = area.getShape(); + area.myParams.dump("project"); showShape(shape,"projected"); @@ -1483,46 +1478,11 @@ std::vector > Area::makeSections( area->setPlane(face.Moved(locInverse)); if(project) { - typedef std::list container; - typedef container::iterator iter; - std::mutex m; - - unsigned int n = std::thread::hardware_concurrency(); - Base::Console().Error("makeSections Threads: %d Shapes: %d\n", n, projectedShapes.size()); - auto worker = [&m, &d, &area, &locInverse] (iter begin, iter end) { - Base::Console().Error("In worker (# items: %d)\n", std::distance(begin, end)); - for (auto it = begin; it != end; it++) { - gp_Trsf t; - t.SetTranslation(gp_Vec(0,0,-d)); - TopLoc_Location wloc(t); - { - std::lock_guard lockGuard(m); - area->add(it->shape.Moved(wloc).Moved(locInverse),it->op); - } - } - }; - if(projectedShapes.size() < n) - { - worker(projectedShapes.begin(), projectedShapes.end()); - } else { - std::vector threads(n); - const size_t chunk_size = projectedShapes.size() / n; - - size_t thread_num = 1; - for(auto it = std::begin(threads); it != std::end(threads) -1; ++it) - { - auto begin = projectedShapes.begin(); - std::advance(begin, thread_num * chunk_size); - auto end = begin; - if(thread_num == n - 1) // last thread iterates the remaining sequence - end = projectedShapes.end(); - else - std::advance(end, chunk_size); - *it = std::thread(worker, begin, end); - thread_num++; - } - for (auto&& i : threads) - i.join(); + for(const auto &s : projectedShapes) { + gp_Trsf t; + t.SetTranslation(gp_Vec(0,0,-d)); + TopLoc_Location wloc(t); + area->add(s.shape.Moved(wloc).Moved(locInverse),s.op); } sections.push_back(area); break; @@ -1640,55 +1600,18 @@ std::list Area::getProjectedShapes(const gp_Trsf &trsf, bool invers TopLoc_Location loc(trsf); TopLoc_Location locInverse(loc.Inverted()); - typedef std::list container; - typedef container::const_iterator iter; - std::mutex m; - int mySkippedShapes = 0; - - unsigned int n = std::thread::hardware_concurrency(); - Base::Console().Error("getProjectedShapes Threads: %d Shapes: %d\n", n, myShapes.size()); - auto worker = [&m, &inverse, &loc, &ret, &locInverse, &mySkippedShapes] (iter begin,iter end, const AreaParams& myParams) { - Base::Console().Error("In worker (# items: %d)\n", std::distance(begin, end)); - for (auto it = begin; it != end; it++) { - TopoDS_Shape out; - int skipped = Area::project(out,it->shape.Moved(loc), &myParams); - { - std::lock_guard lockGuard(m); - if(skipped < 0) { - ++mySkippedShapes; - continue; - }else - mySkippedShapes += skipped; - if(!out.IsNull()) - ret.emplace_back(it->op,inverse?out.Moved(locInverse):out); - } - - } - }; - if(myShapes.size() < n) - { - worker(myShapes.begin(), myShapes.end(), myParams); - } else { - std::vector threads(n); - const size_t chunk_size = myShapes.size() / n; - - size_t thread_num = 1; - for(auto it = std::begin(threads); it != std::end(threads) -1; ++it) - { - auto begin = myShapes.begin(); - std::advance(begin, thread_num * chunk_size); - auto end = begin; - if(thread_num == n - 1) // last thread iterates the remaining sequence - end = myShapes.end(); - else - std::advance(end, chunk_size); - *it = std::thread(worker, begin, end, myParams); - thread_num++; - } - for (auto&& i : threads) - i.join(); + mySkippedShapes = 0; + for(auto &s : myShapes) { + TopoDS_Shape out; + int skipped = Area::project(out,s.shape.Moved(loc),&myParams); + if(skipped < 0) { + ++mySkippedShapes; + continue; + }else + mySkippedShapes += skipped; + if(!out.IsNull()) + ret.emplace_back(s.op,inverse?out.Moved(locInverse):out); } - if(mySkippedShapes) AREA_WARN("skipped " << mySkippedShapes << " sub shapes during projection"); return ret; @@ -3531,4 +3454,4 @@ const AreaStaticParams &Area::getDefaultParams() { #if defined(__clang__) # pragma clang diagnostic pop -#endif +#endif \ No newline at end of file diff --git a/src/Mod/Path/App/CMakeLists.txt b/src/Mod/Path/App/CMakeLists.txt index 69a260d61b..72cc976129 100644 --- a/src/Mod/Path/App/CMakeLists.txt +++ b/src/Mod/Path/App/CMakeLists.txt @@ -118,10 +118,8 @@ SOURCE_GROUP("Module" FILES ${Mod_SRCS}) # SOURCE_GROUP("KDL" FILES ${KDL_SRCS} ${KDL_HPPS} ${UTIL_SRCS} ${UTIL_HPPS} ) #endif(WIN32) -find_package(Threads REQUIRED) - add_library(Path SHARED ${Path_SRCS}) -target_link_libraries(Path ${Path_LIBS} Threads::Threads) +target_link_libraries(Path ${Path_LIBS}) if(FREECAD_USE_PCH) add_definitions(-D_PreComp_) @@ -133,4 +131,4 @@ endif(FREECAD_USE_PCH) SET_BIN_DIR(Path Path /Mod/Path) SET_PYTHON_PREFIX_SUFFIX(Path) -INSTALL(TARGETS Path DESTINATION ${CMAKE_INSTALL_LIBDIR}) +INSTALL(TARGETS Path DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 7514da410e..d2f9682fa4 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -365,9 +365,11 @@ class ObjectSurface(PathOp.ObjectOp): def opExecute(self, obj): '''opExecute(obj) ... process surface operation''' PathLog.track() - #import cProfile - #pr = cProfile.Profile() - #pr.enable() + ######## DEBUG + import cProfile + pr = cProfile.Profile() + pr.enable() + ######## self.modelSTLs = list() self.safeSTLs = list() @@ -606,8 +608,10 @@ class ObjectSurface(PathOp.ObjectOp): del self.deflection execTime = time.time() - startTime - #pr.disable() - #pr.dump_stats("/mnt/files/profile.cprof") + ####### DEBUG + pr.disable() + pr.dump_stats("/mnt/files/profile.cprof") + ############# PathLog.info('Operation time: {} sec.'.format(execTime)) return True @@ -1421,26 +1425,49 @@ class ObjectSurface(PathOp.ObjectOp): # PathLog.debug(f" -self.modelTypes[{m}] == 'M'") if self.modelTypes[m] == 'M': - mesh = M.Mesh + #TODO: get this to work with new mesher + facets = M.Mesh.Facets else: # base.Shape.tessellate(0.05) # 0.5 original value # mesh = MeshPart.meshFromShape(base.Shape, Deflection=self.deflection) - mesh = MeshPart.meshFromShape(Shape=M.Shape, - LinearDeflection=obj.LinearDeflection.Value, - AngularDeflection=obj.AngularDeflection.Value, - Relative=False) + #facets = MeshPart.meshFromShape(Shape=M.Shape, + # LinearDeflection=obj.LinearDeflection.Value, + # AngularDeflection=obj.AngularDeflection.Value, + # Relative=False) + facets = Path.getFacets(M.Shape) + + stl = ocl.STLSurf() + if self.modelSTLs[m] is True: stl = ocl.STLSurf() - for f in mesh.Facets: - p = f.Points[0] - q = f.Points[1] - r = f.Points[2] - t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), - ocl.Point(q[0], q[1], q[2]), - ocl.Point(r[0], r[1], r[2])) - stl.addTriangle(t) - self.modelSTLs[m] = stl + + if obj.Algorithm == 'OCL Dropcutter': + for tri in facets: + #p = tri.Points[0] + #q = tri.Points[1] + #r = tri.Points[2] + #t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), + # ocl.Point(q[0], q[1], q[2]), + # ocl.Point(r[0], r[1], r[2])) + t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]), + ocl.Point(tri[1][0], tri[1][1], tri[1][2]), + ocl.Point(tri[2][0], tri[2][1], tri[2][2])) + stl.addTriangle(t) + self.modelSTLs[m] = stl + elif obj.Algorithm == 'OCL Waterline': + for tri in facets: + #p = f.Points[0] + #q = f.Points[1] + #r = f.Points[2] + #t = ocl.Triangle(ocl.Point(p[0], p[1], p[2] + obj.DepthOffset.Value), + # ocl.Point(q[0], q[1], q[2] + obj.DepthOffset.Value), + # ocl.Point(r[0], r[1], r[2] + obj.DepthOffset.Value)) + t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2] + obj.DepthOffset.Value), + ocl.Point(tri[1][0], tri[1][1], tri[1][2] + obj.DepthOffset.Value), + ocl.Point(tri[2][0], tri[2][1], tri[2][2] + obj.DepthOffset.Value)) + stl.addTriangle(t) + self.modelSTLs[m] = stl return def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes): @@ -1518,20 +1545,13 @@ class ObjectSurface(PathOp.ObjectOp): T.purgeTouched() self.tempGroup.addObject(T) - # Extract mesh from fusion - meshFuse = MeshPart.meshFromShape(Shape=fused, - LinearDeflection=obj.LinearDeflection.Value, - AngularDeflection=obj.AngularDeflection.Value, - Relative=False) - #time.sleep(0.2) + facets = Path.getFacets(fused) + stl = ocl.STLSurf() - for f in meshFuse.Facets: - p = f.Points[0] - q = f.Points[1] - r = f.Points[2] - t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), - ocl.Point(q[0], q[1], q[2]), - ocl.Point(r[0], r[1], r[2])) + for tri in facets: + t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]), + ocl.Point(tri[1][0], tri[1][1], tri[1][2]), + ocl.Point(tri[2][0], tri[2][1], tri[2][2])) stl.addTriangle(t) self.safeSTLs[mdlIdx] = stl From 948ec03492f0e0c32422a49b8a739bb5a5be2356 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Mon, 30 Mar 2020 11:31:59 -0500 Subject: [PATCH 05/12] visual studio doesn't like indexing facets with [, changed to ( --- src/Mod/Path/App/AppPathPy.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index bc34de944c..5948028288 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -166,10 +166,10 @@ private: auto triangles = facets->Triangles(); for(int i = 1; i <= triangles.Length(); i++){ Standard_Integer n1,n2,n3; - triangles[i].Get(n1, n2, n3); - gp_Pnt p1 = nodes[n1]; - gp_Pnt p2 = nodes[n2]; - gp_Pnt p3 = nodes[n3]; + triangles(i).Get(n1, n2, n3); + gp_Pnt p1 = nodes(n1); + gp_Pnt p2 = nodes(n2); + gp_Pnt p3 = nodes(n3); p1.Transform(loc.Transformation()); p2.Transform(loc.Transformation()); p3.Transform(loc.Transformation()); From 54022ea630653e38395aab10720ed78918433854 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Mon, 30 Mar 2020 14:18:27 -0500 Subject: [PATCH 06/12] remove debug profiling from surface --- src/Mod/Path/PathScripts/PathSurface.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index d2f9682fa4..2755925d70 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -365,11 +365,6 @@ class ObjectSurface(PathOp.ObjectOp): def opExecute(self, obj): '''opExecute(obj) ... process surface operation''' PathLog.track() - ######## DEBUG - import cProfile - pr = cProfile.Profile() - pr.enable() - ######## self.modelSTLs = list() self.safeSTLs = list() @@ -608,10 +603,6 @@ class ObjectSurface(PathOp.ObjectOp): del self.deflection execTime = time.time() - startTime - ####### DEBUG - pr.disable() - pr.dump_stats("/mnt/files/profile.cprof") - ############# PathLog.info('Operation time: {} sec.'.format(execTime)) return True From 0cbf56886a5f2f9423457c5ae4d2adf8baa843c4 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Mon, 30 Mar 2020 14:28:53 -0500 Subject: [PATCH 07/12] clean up commented old code --- src/Mod/Path/App/AppPathPy.cpp | 1 - src/Mod/Path/PathScripts/PathSurface.py | 28 ++----------------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index 5948028288..4a5306d41a 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -54,7 +54,6 @@ #include #include #include -#include #include "CommandPy.h" #include "PathPy.h" diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 2755925d70..5e4dbf67fe 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -1416,15 +1416,9 @@ class ObjectSurface(PathOp.ObjectOp): # PathLog.debug(f" -self.modelTypes[{m}] == 'M'") if self.modelTypes[m] == 'M': - #TODO: get this to work with new mesher - facets = M.Mesh.Facets + #TODO: test if this works + facets = M.Mesh.Facets.Points else: - # base.Shape.tessellate(0.05) # 0.5 original value - # mesh = MeshPart.meshFromShape(base.Shape, Deflection=self.deflection) - #facets = MeshPart.meshFromShape(Shape=M.Shape, - # LinearDeflection=obj.LinearDeflection.Value, - # AngularDeflection=obj.AngularDeflection.Value, - # Relative=False) facets = Path.getFacets(M.Shape) stl = ocl.STLSurf() @@ -1435,12 +1429,6 @@ class ObjectSurface(PathOp.ObjectOp): if obj.Algorithm == 'OCL Dropcutter': for tri in facets: - #p = tri.Points[0] - #q = tri.Points[1] - #r = tri.Points[2] - #t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), - # ocl.Point(q[0], q[1], q[2]), - # ocl.Point(r[0], r[1], r[2])) t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]), ocl.Point(tri[1][0], tri[1][1], tri[1][2]), ocl.Point(tri[2][0], tri[2][1], tri[2][2])) @@ -1448,12 +1436,6 @@ class ObjectSurface(PathOp.ObjectOp): self.modelSTLs[m] = stl elif obj.Algorithm == 'OCL Waterline': for tri in facets: - #p = f.Points[0] - #q = f.Points[1] - #r = f.Points[2] - #t = ocl.Triangle(ocl.Point(p[0], p[1], p[2] + obj.DepthOffset.Value), - # ocl.Point(q[0], q[1], q[2] + obj.DepthOffset.Value), - # ocl.Point(r[0], r[1], r[2] + obj.DepthOffset.Value)) t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2] + obj.DepthOffset.Value), ocl.Point(tri[1][0], tri[1][1], tri[1][2] + obj.DepthOffset.Value), ocl.Point(tri[2][0], tri[2][1], tri[2][2] + obj.DepthOffset.Value)) @@ -2475,8 +2457,6 @@ class ObjectSurface(PathOp.ObjectOp): pdc.run() # run dropcutter algorithm on path CLP = pdc.getCLPoints() PNTS = [FreeCAD.Vector(p.x, p.y, p.z) for p in CLP] - #for p in CLP: - # PNTS.append(FreeCAD.Vector(p.x, p.y, p.z)) return PNTS # pdc.getCLPoints() def _planarCircularDropCutScan(self, pdc, Arc, cMode): @@ -3717,10 +3697,6 @@ class ObjectSurface(PathOp.ObjectOp): B = (p2.x, p2.y) LINE = self._planarDropCutScan(pdc, A, B) zMax = max([obj.z for obj in LINE]) - #zMax = LINE[0].z - #for p in LINE: - # if p.z > zMax: - # zMax = p.z if minDep is not None: if zMax < minDep: zMax = minDep From bab00360c5ca0b08b21c87133a34fe14d0db6e47 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Tue, 31 Mar 2020 08:52:57 -0500 Subject: [PATCH 08/12] Area.cpp and CMakeLists.txt had extranious line ending --- src/Mod/Path/App/Area.cpp | 2 +- src/Mod/Path/App/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index ea61c9049a..fe552ecbd9 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -3454,4 +3454,4 @@ const AreaStaticParams &Area::getDefaultParams() { #if defined(__clang__) # pragma clang diagnostic pop -#endif \ No newline at end of file +#endif diff --git a/src/Mod/Path/App/CMakeLists.txt b/src/Mod/Path/App/CMakeLists.txt index 72cc976129..2cee9a3a3a 100644 --- a/src/Mod/Path/App/CMakeLists.txt +++ b/src/Mod/Path/App/CMakeLists.txt @@ -131,4 +131,4 @@ endif(FREECAD_USE_PCH) SET_BIN_DIR(Path Path /Mod/Path) SET_PYTHON_PREFIX_SUFFIX(Path) -INSTALL(TARGETS Path DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file +INSTALL(TARGETS Path DESTINATION ${CMAKE_INSTALL_LIBDIR}) From 2b8cd1be42547182f276ea1576697d4fc85cced1 Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Tue, 31 Mar 2020 12:22:08 -0500 Subject: [PATCH 09/12] add changes to Waterline --- src/Mod/Path/PathScripts/PathSurface.py | 25 +++----- src/Mod/Path/PathScripts/PathWaterline.py | 71 ++++++----------------- 2 files changed, 24 insertions(+), 72 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 5e4dbf67fe..33e508d88b 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -1419,28 +1419,17 @@ class ObjectSurface(PathOp.ObjectOp): #TODO: test if this works facets = M.Mesh.Facets.Points else: - facets = Path.getFacets(M.Shape) - - stl = ocl.STLSurf() - + facets = Path.getFacets(M.Shape) if self.modelSTLs[m] is True: stl = ocl.STLSurf() - if obj.Algorithm == 'OCL Dropcutter': - for tri in facets: - t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]), - ocl.Point(tri[1][0], tri[1][1], tri[1][2]), - ocl.Point(tri[2][0], tri[2][1], tri[2][2])) - stl.addTriangle(t) - self.modelSTLs[m] = stl - elif obj.Algorithm == 'OCL Waterline': - for tri in facets: - t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2] + obj.DepthOffset.Value), - ocl.Point(tri[1][0], tri[1][1], tri[1][2] + obj.DepthOffset.Value), - ocl.Point(tri[2][0], tri[2][1], tri[2][2] + obj.DepthOffset.Value)) - stl.addTriangle(t) - self.modelSTLs[m] = stl + for tri in facets: + t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]), + ocl.Point(tri[1][0], tri[1][1], tri[1][2]), + ocl.Point(tri[2][0], tri[2][1], tri[2][2])) + stl.addTriangle(t) + self.modelSTLs[m] = stl return def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes): diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 8ff606d6f1..969c8c7478 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -1404,24 +1404,18 @@ class ObjectWaterline(PathOp.ObjectOp): # PathLog.debug(f" -self.modelTypes[{m}] == 'M'") if self.modelTypes[m] == 'M': - mesh = M.Mesh + #TODO: test if this works + facets = M.Mesh.Facets.Points else: - # base.Shape.tessellate(0.05) # 0.5 original value - mesh = MeshPart.meshFromShape(Shape=M.Shape, - LinearDeflection=obj.LinearDeflection.Value, - AngularDeflection=obj.AngularDeflection.Value, - Relative=False) + facets = Path.getFacets(M.Shape) if self.modelSTLs[m] is True: stl = ocl.STLSurf() - for f in mesh.Facets: - p = f.Points[0] - q = f.Points[1] - r = f.Points[2] - t = ocl.Triangle(ocl.Point(p[0], p[1], p[2] + obj.DepthOffset.Value), - ocl.Point(q[0], q[1], q[2] + obj.DepthOffset.Value), - ocl.Point(r[0], r[1], r[2] + obj.DepthOffset.Value)) + for tri in facets: + t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2] + obj.DepthOffset.Value), + ocl.Point(tri[1][0], tri[1][1], tri[1][2] + obj.DepthOffset.Value), + ocl.Point(tri[2][0], tri[2][1], tri[2][2] + obj.DepthOffset.Value)) stl.addTriangle(t) self.modelSTLs[m] = stl return @@ -1494,11 +1488,7 @@ class ObjectWaterline(PathOp.ObjectOp): voidEnv = PathUtils.getEnvelope(partshape=voidComp, depthparams=self.depthParams) # Produces .Shape fuseShapes.append(voidEnv) - f0 = fuseShapes.pop(0) - if len(fuseShapes) > 0: - fused = f0.fuse(fuseShapes) - else: - fused = f0 + fused = Part.makeCompound(fuseShapes) if self.showDebugObjects is True: T = FreeCAD.ActiveDocument.addObject('Part::Feature', 'safeSTLShape') @@ -1506,20 +1496,13 @@ class ObjectWaterline(PathOp.ObjectOp): T.purgeTouched() self.tempGroup.addObject(T) - # Extract mesh from fusion - meshFuse = MeshPart.meshFromShape(Shape=fused, - LinearDeflection=obj.LinearDeflection.Value, - AngularDeflection=obj.AngularDeflection.Value, - Relative=False) - # time.sleep(0.2) + facets = Path.getFacets(fused) + stl = ocl.STLSurf() - for f in meshFuse.Facets: - p = f.Points[0] - q = f.Points[1] - r = f.Points[2] - t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), - ocl.Point(q[0], q[1], q[2]), - ocl.Point(r[0], r[1], r[2])) + for tri in facets: + t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]), + ocl.Point(tri[1][0], tri[1][1], tri[1][2]), + ocl.Point(tri[2][0], tri[2][1], tri[2][2])) stl.addTriangle(t) self.safeSTLs[mdlIdx] = stl @@ -1800,7 +1783,7 @@ class ObjectWaterline(PathOp.ObjectOp): ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (first / middle point) - iC = self.isPointOnLine(sp, ep, cp) + iC = sp.isOnLine(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -1906,7 +1889,7 @@ class ObjectWaterline(PathOp.ObjectOp): cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (start point of segment) ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point - iC = self.isPointOnLine(sp, ep, cp) + iC = sp.isOnLine(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -2713,7 +2696,7 @@ class ObjectWaterline(PathOp.ObjectOp): else: optimize = False - if not optimize or not self.isPointOnLine(FreeCAD.Vector(prev.x, prev.y, prev.z), FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): + if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLine(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'F': self.horizFeed})) # Rotate point data @@ -3341,26 +3324,6 @@ class ObjectWaterline(PathOp.ObjectOp): return (useOfst, usePat, clearLastLayer) - # Support functions for both dropcutter and waterline operations - def isPointOnLine(self, strtPnt, endPnt, pointP): - '''isPointOnLine(strtPnt, endPnt, pointP) ... Determine if a given point is on the line defined by start and end points.''' - tolerance = 1e-6 - vectorAB = endPnt - strtPnt - vectorAC = pointP - strtPnt - crossproduct = vectorAB.cross(vectorAC) - dotproduct = vectorAB.dot(vectorAC) - - if crossproduct.Length > tolerance: - return False - - if dotproduct < 0: - return False - - if dotproduct > vectorAB.Length * vectorAB.Length: - return False - - return True - def resetOpVariables(self, all=True): '''resetOpVariables() ... Reset class variables used for instance of operation.''' self.holdPoint = None From db959f1f09d261992d271a19bbc1c18648c5dc9a Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Wed, 1 Apr 2020 11:21:41 -0500 Subject: [PATCH 10/12] added check that facets are valid in getFacets --- src/Mod/Path/App/AppPathPy.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index 4a5306d41a..dc1d5204b5 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -172,17 +172,20 @@ private: p1.Transform(loc.Transformation()); p2.Transform(loc.Transformation()); p3.Transform(loc.Transformation()); - PyObject *t1 = PyTuple_Pack(3, PyFloat_FromDouble(p1.X()), PyFloat_FromDouble(p1.Y()), PyFloat_FromDouble(p1.Z())); - PyObject *t2 = PyTuple_Pack(3, PyFloat_FromDouble(p2.X()), PyFloat_FromDouble(p2.Y()), PyFloat_FromDouble(p2.Z())); - PyObject *t3 = PyTuple_Pack(3, PyFloat_FromDouble(p3.X()), PyFloat_FromDouble(p3.Y()), PyFloat_FromDouble(p3.Z())); - PyObject *points; - if(flip) - { - points = PyTuple_Pack(3, t2, t1, t3); - } else { - points = PyTuple_Pack(3, t1, t2, t3); + // TODO: verify if tolerence should be hard coded + if (!p1.IsEqual(p2, 0.01) && !p2.IsEqual(p3, 0.01) && !p3.IsEqual(p1, 0.01)) { + PyObject *t1 = PyTuple_Pack(3, PyFloat_FromDouble(p1.X()), PyFloat_FromDouble(p1.Y()), PyFloat_FromDouble(p1.Z())); + PyObject *t2 = PyTuple_Pack(3, PyFloat_FromDouble(p2.X()), PyFloat_FromDouble(p2.Y()), PyFloat_FromDouble(p2.Z())); + PyObject *t3 = PyTuple_Pack(3, PyFloat_FromDouble(p3.X()), PyFloat_FromDouble(p3.Y()), PyFloat_FromDouble(p3.Z())); + PyObject *points; + if(flip) + { + points = PyTuple_Pack(3, t2, t1, t3); + } else { + points = PyTuple_Pack(3, t1, t2, t3); + } + PyList_Append(list, points); } - PyList_Append(list, points); } } } From b631c5600add6beb44268fe26b60680d6fc5d69c Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Wed, 1 Apr 2020 13:26:57 -0500 Subject: [PATCH 11/12] move getFacets from Path to Part --- src/Mod/Part/App/AppPartPy.cpp | 48 ++++++++++++++++++++++++ src/Mod/Path/App/AppPathPy.cpp | 50 ------------------------- src/Mod/Path/PathScripts/PathSurface.py | 4 +- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index 748735779c..da506351e5 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -290,6 +290,9 @@ public: add_varargs_method("show",&Module::show, "show(shape,[string]) -- Add the shape to the active document or create one if no document exists." ); + add_varargs_method("getFacets",&Module::getFacets, + "getFacets(shape): simplified mesh generation" + ); add_varargs_method("makeCompound",&Module::makeCompound, "makeCompound(list) -- Create a compound out of a list of shapes." ); @@ -720,6 +723,51 @@ private: return Py::None(); } + Py::Object getFacets(const Py::Tuple& args) + { + PyObject *shape; + PyObject *list = PyList_New(0); + if (!PyArg_ParseTuple(args.ptr(), "O", &shape)) + throw Py::Exception(); + auto theShape = static_cast(shape)->getTopoShapePtr()->getShape(); + for(TopExp_Explorer ex(theShape, TopAbs_FACE); ex.More(); ex.Next()) + { + TopoDS_Face currentFace = TopoDS::Face(ex.Current()); + TopLoc_Location loc; + Handle(Poly_Triangulation) facets = BRep_Tool::Triangulation(currentFace, loc); + const TopAbs_Orientation anOrientation = currentFace.Orientation(); + bool flip = (anOrientation == TopAbs_REVERSED); + if(!facets.IsNull()){ + auto nodes = facets->Nodes(); + auto triangles = facets->Triangles(); + for(int i = 1; i <= triangles.Length(); i++){ + Standard_Integer n1,n2,n3; + triangles(i).Get(n1, n2, n3); + gp_Pnt p1 = nodes(n1); + gp_Pnt p2 = nodes(n2); + gp_Pnt p3 = nodes(n3); + p1.Transform(loc.Transformation()); + p2.Transform(loc.Transformation()); + p3.Transform(loc.Transformation()); + // TODO: verify if tolerence should be hard coded + if (!p1.IsEqual(p2, 0.01) && !p2.IsEqual(p3, 0.01) && !p3.IsEqual(p1, 0.01)) { + PyObject *t1 = PyTuple_Pack(3, PyFloat_FromDouble(p1.X()), PyFloat_FromDouble(p1.Y()), PyFloat_FromDouble(p1.Z())); + PyObject *t2 = PyTuple_Pack(3, PyFloat_FromDouble(p2.X()), PyFloat_FromDouble(p2.Y()), PyFloat_FromDouble(p2.Z())); + PyObject *t3 = PyTuple_Pack(3, PyFloat_FromDouble(p3.X()), PyFloat_FromDouble(p3.Y()), PyFloat_FromDouble(p3.Z())); + PyObject *points; + if(flip) + { + points = PyTuple_Pack(3, t2, t1, t3); + } else { + points = PyTuple_Pack(3, t1, t2, t3); + } + PyList_Append(list, points); + } + } + } + } + return Py::asObject(list); + } Py::Object makeCompound(const Py::Tuple& args) { PyObject *pcObj; diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index dc1d5204b5..381ef87ef6 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -104,9 +103,6 @@ public: Module() : Py::ExtensionModule("Path") { - add_varargs_method("getFacets",&Module::getFacets, - "getFacets(shape): simplified mesh generation" - ); add_varargs_method("write",&Module::write, "write(object,filename): Exports a given path object to a GCode file" ); @@ -146,52 +142,6 @@ public: private: - Py::Object getFacets(const Py::Tuple& args) - { - PyObject *shape; - PyObject *list = PyList_New(0); - if (!PyArg_ParseTuple(args.ptr(), "O", &shape)) - throw Py::Exception(); - auto theShape = static_cast(shape)->getTopoShapePtr()->getShape(); - for(TopExp_Explorer ex(theShape, TopAbs_FACE); ex.More(); ex.Next()) - { - TopoDS_Face currentFace = TopoDS::Face(ex.Current()); - TopLoc_Location loc; - Handle(Poly_Triangulation) facets = BRep_Tool::Triangulation(currentFace, loc); - const TopAbs_Orientation anOrientation = currentFace.Orientation(); - bool flip = (anOrientation == TopAbs_REVERSED); - if(!facets.IsNull()){ - auto nodes = facets->Nodes(); - auto triangles = facets->Triangles(); - for(int i = 1; i <= triangles.Length(); i++){ - Standard_Integer n1,n2,n3; - triangles(i).Get(n1, n2, n3); - gp_Pnt p1 = nodes(n1); - gp_Pnt p2 = nodes(n2); - gp_Pnt p3 = nodes(n3); - p1.Transform(loc.Transformation()); - p2.Transform(loc.Transformation()); - p3.Transform(loc.Transformation()); - // TODO: verify if tolerence should be hard coded - if (!p1.IsEqual(p2, 0.01) && !p2.IsEqual(p3, 0.01) && !p3.IsEqual(p1, 0.01)) { - PyObject *t1 = PyTuple_Pack(3, PyFloat_FromDouble(p1.X()), PyFloat_FromDouble(p1.Y()), PyFloat_FromDouble(p1.Z())); - PyObject *t2 = PyTuple_Pack(3, PyFloat_FromDouble(p2.X()), PyFloat_FromDouble(p2.Y()), PyFloat_FromDouble(p2.Z())); - PyObject *t3 = PyTuple_Pack(3, PyFloat_FromDouble(p3.X()), PyFloat_FromDouble(p3.Y()), PyFloat_FromDouble(p3.Z())); - PyObject *points; - if(flip) - { - points = PyTuple_Pack(3, t2, t1, t3); - } else { - points = PyTuple_Pack(3, t1, t2, t3); - } - PyList_Append(list, points); - } - } - } - } - return Py::asObject(list); - } - Py::Object write(const Py::Tuple& args) { char* Name; diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 33e508d88b..8663b05b95 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -1419,7 +1419,7 @@ class ObjectSurface(PathOp.ObjectOp): #TODO: test if this works facets = M.Mesh.Facets.Points else: - facets = Path.getFacets(M.Shape) + facets = Part.getFacets(M.Shape) if self.modelSTLs[m] is True: stl = ocl.STLSurf() @@ -1507,7 +1507,7 @@ class ObjectSurface(PathOp.ObjectOp): T.purgeTouched() self.tempGroup.addObject(T) - facets = Path.getFacets(fused) + facets = Part.getFacets(fused) stl = ocl.STLSurf() for tri in facets: From 5ecbc89f30f9a792b9a114c23214da565845858a Mon Sep 17 00:00:00 2001 From: Eric Trombly Date: Mon, 6 Apr 2020 06:34:30 -0500 Subject: [PATCH 12/12] change isOnLine to isOnLineSegment --- src/Base/Vector3D.cpp | 4 ++-- src/Base/Vector3D.h | 4 ++-- src/Base/VectorPy.xml | 6 +++--- src/Base/VectorPyImp.cpp | 4 ++-- src/Mod/Path/PathScripts/PathSurface.py | 14 +++++++------- src/Mod/Path/PathScripts/PathWaterline.py | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Base/Vector3D.cpp b/src/Base/Vector3D.cpp index a5d34a7259..a0c67d690d 100644 --- a/src/Base/Vector3D.cpp +++ b/src/Base/Vector3D.cpp @@ -193,7 +193,7 @@ Vector3<_Precision> Vector3<_Precision>::Cross(const Vector3<_Precision>& rcVct) } template -bool Vector3<_Precision>::IsOnLine (const Vector3<_Precision>& startVct, const Vector3<_Precision>& endVct) const +bool Vector3<_Precision>::IsOnLineSegment (const Vector3<_Precision>& startVct, const Vector3<_Precision>& endVct) const { Vector3<_Precision> vectorAB = endVct - startVct; Vector3<_Precision> vectorAC = *this - startVct; @@ -206,7 +206,7 @@ bool Vector3<_Precision>::IsOnLine (const Vector3<_Precision>& startVct, const V if (dotproduct < 0) return false; - if (dotproduct > vectorAB.Length() * vectorAB.Length()) + if (dotproduct > vectorAB.Sqr()) return false; return true; diff --git a/src/Base/Vector3D.h b/src/Base/Vector3D.h index b0f7e2ebbb..13651fc49a 100644 --- a/src/Base/Vector3D.h +++ b/src/Base/Vector3D.h @@ -133,8 +133,8 @@ public: bool operator == (const Vector3<_Precision>& rcVct) const; //@} - /// Check if Vector is on a line - bool IsOnLine (const Vector3<_Precision>& startVct, const Vector3<_Precision>& endVct) const; + /// Check if Vector is on a line segment + bool IsOnLineSegment (const Vector3<_Precision>& startVct, const Vector3<_Precision>& endVct) const; /** @name Modification */ //@{ diff --git a/src/Base/VectorPy.xml b/src/Base/VectorPy.xml index 2f5a8486f0..cd1ce5b710 100644 --- a/src/Base/VectorPy.xml +++ b/src/Base/VectorPy.xml @@ -74,10 +74,10 @@ - + - isOnLine(Vector, Vector) - checks if Vector is on a line + isOnLineSegment(Vector, Vector) + checks if Vector is on a line segment diff --git a/src/Base/VectorPyImp.cpp b/src/Base/VectorPyImp.cpp index 96328aab97..778e9907f4 100644 --- a/src/Base/VectorPyImp.cpp +++ b/src/Base/VectorPyImp.cpp @@ -435,7 +435,7 @@ PyObject* VectorPy::cross(PyObject *args) return new VectorPy(v); } -PyObject* VectorPy::isOnLine(PyObject *args) +PyObject* VectorPy::isOnLineSegment(PyObject *args) { PyObject *start, *end; if (!PyArg_ParseTuple(args, "OO",&start, &end)) @@ -456,7 +456,7 @@ PyObject* VectorPy::isOnLine(PyObject *args) VectorPy::PointerType start_ptr = reinterpret_cast(start_vec->_pcTwinPointer); VectorPy::PointerType end_ptr = reinterpret_cast(end_vec->_pcTwinPointer); - Py::Boolean result = this_ptr->IsOnLine(*start_ptr, *end_ptr); + Py::Boolean result = this_ptr->IsOnLineSegment(*start_ptr, *end_ptr); return Py::new_reference_to(result); } diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 8663b05b95..9e63b6f359 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -1985,7 +1985,7 @@ class ObjectSurface(PathOp.ObjectOp): for v in range(1, lenOS): nxt = OS[v + 1] if optimize is True: - iPOL = prev.isOnLine(nxt, pnt) + iPOL = prev.isOnLineSegment(nxt, pnt) if iPOL is True: pnt = nxt else: @@ -2039,7 +2039,7 @@ class ObjectSurface(PathOp.ObjectOp): ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (first / middle point) - iC = sp.isOnLine(ep, cp) + iC = sp.isOnLineSegment(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -2145,7 +2145,7 @@ class ObjectSurface(PathOp.ObjectOp): cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (start point of segment) ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point - iC = sp.isOnLine(ep, cp) + iC = sp.isOnLineSegment(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -2564,7 +2564,7 @@ class ObjectSurface(PathOp.ObjectOp): # first item will be compared to the last point, but I think that should work output = [Path.Command('G1', {'X': PNTS[i].x, 'Y': PNTS[i].y, 'Z': PNTS[i].z, 'F': self.horizFeed}) for i in range(0, len(PNTS) - 1) - if not PNTS[i].isOnLine(PNTS[i -1],PNTS[i + 1])] + if not PNTS[i].isOnLineSegment(PNTS[i -1],PNTS[i + 1])] output.append(Path.Command('G1', {'X': PNTS[-1].x, 'Y': PNTS[-1].y, 'Z': PNTS[-1].z, 'F': self.horizFeed})) else: output = [Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}) for pnt in PNTS] @@ -2844,7 +2844,7 @@ class ObjectSurface(PathOp.ObjectOp): # Process point if prcs is True: if optimize is True: - iPOL = prev.isOnLine(nxt, pnt) + iPOL = prev.isOnLineSegment(nxt, pnt) if iPOL is True: onLine = True else: @@ -3418,7 +3418,7 @@ class ObjectSurface(PathOp.ObjectOp): self.holdPoint = ocl.Point(float("inf"), float("inf"), float("inf")) if self.onHold is False: - if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLine(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): + if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLineSegment(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed})) # elif i == lastCLP: # output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed})) @@ -3539,7 +3539,7 @@ class ObjectSurface(PathOp.ObjectOp): else: optimize = False - if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLine(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): + if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLineSegment(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'F': self.horizFeed})) # Rotate point data diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 969c8c7478..0362680580 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -1783,7 +1783,7 @@ class ObjectWaterline(PathOp.ObjectOp): ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (first / middle point) - iC = sp.isOnLine(ep, cp) + iC = sp.isOnLineSegment(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -1889,7 +1889,7 @@ class ObjectWaterline(PathOp.ObjectOp): cp = FreeCAD.Vector(v1[0], v1[1], 0.0) # check point (start point of segment) ep = FreeCAD.Vector(v2[0], v2[1], 0.0) # end point - iC = sp.isOnLine(ep, cp) + iC = sp.isOnLineSegment(ep, cp) if iC is True: inLine.append('BRK') chkGap = True @@ -2696,7 +2696,7 @@ class ObjectWaterline(PathOp.ObjectOp): else: optimize = False - if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLine(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): + if not optimize or not FreeCAD.Vector(prev.x, prev.y, prev.z).isOnLineSegment(FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)): output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'F': self.horizFeed})) # Rotate point data