diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 035dba767c..2af48e8250 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -530,8 +530,9 @@ struct WireJoiner { BRep_Builder builder; TopoDS_Compound comp; + gp_Dir dir; - WireJoiner() { + WireJoiner(const gp_Dir &dir = gp_Dir()):dir(dir) { builder.MakeCompound(comp); } @@ -554,11 +555,10 @@ struct WireJoiner { if(BRep_Tool::IsClosed(e)){ BRepBuilderAPI_MakeWire mkWire; mkWire.Add(e); - const TopoDS_Wire &wire = mkWire.Wire(); - if(bbox && Area::getWireDirection(wire)>0) - builder.Add(comp,wire.Reversed()); - else - builder.Add(comp,wire); + TopoDS_Wire wire = mkWire.Wire(); + if(bbox) + Area::setWireOrientation(wire,dir,true); + builder.Add(comp,wire); return; } gp_Pnt p1,p2; @@ -795,11 +795,9 @@ struct WireJoiner { else mkWire.Add(TopoDS::Edge(info.edge.Reversed())); } - const TopoDS_Wire &wire = mkWire.Wire(); - if(Area::getWireDirection(wire)>0) - builder.Add(comp,wire.Reversed()); - else - builder.Add(comp,wire); + TopoDS_Wire wire = mkWire.Wire(); + Area::setWireOrientation(wire,dir,true); + builder.Add(comp,wire); break; } } @@ -932,10 +930,11 @@ std::list Area::project(const TopoDS_Shape &solid) { TIME_INIT2(t,t1); Handle_HLRBRep_Algo brep_hlr = NULL; + gp_Dir dir(0,0,1); try { brep_hlr = new HLRBRep_Algo(); brep_hlr->Add(solid, 0); - HLRAlgo_Projector projector(gp_Ax2(gp_Pnt(),gp_Dir(0,0,1))); + HLRAlgo_Projector projector(gp_Ax2(gp_Pnt(),dir)); brep_hlr->Projector(projector); brep_hlr->Update(); brep_hlr->Hide(); @@ -943,7 +942,7 @@ std::list Area::project(const TopoDS_Shape &solid) AREA_ERR("error occurred while projecting shape"); } TIME_PRINT(t1,"HLRBrep_Algo"); - WireJoiner joiner; + WireJoiner joiner(dir); try { #define ADD_HLR_SHAPE(_name) \ shape = hlrToShape._name##Compound();\ @@ -1173,22 +1172,32 @@ std::vector > Area::makeSections( continue; } - if(myParams.Fill != FillNone) { - Part::FaceMakerBullseye mkFace; - mkFace.setPlane(pln); - for(const TopoDS_Wire &wire : wires) + // always try to make face to normalize wire orientation + Part::FaceMakerBullseye mkFace; + mkFace.setPlane(pln); + for(const TopoDS_Wire &wire : wires) { + if(BRep_Tool::IsClosed(wire)) mkFace.addWire(wire); - try { - mkFace.Build(); - if (mkFace.Shape().IsNull()) - AREA_WARN("FaceMakerBullseye return null shape on section"); - else { - builder.Add(comp,mkFace.Shape()); - continue; + } + try { + mkFace.Build(); + const TopoDS_Shape &shape = mkFace.Shape(); + if (shape.IsNull()) + AREA_WARN("FaceMakerBullseye return null shape on section"); + else { + for(auto it=wires.begin(),itNext=it;it!=wires.end();it=itNext) { + ++itNext; + if(BRep_Tool::IsClosed(*it)) + wires.erase(it); + } + for(TopExp_Explorer xp(shape,myParams.Fill==FillNone?TopAbs_WIRE:TopAbs_FACE); + xp.More();xp.Next()) + { + builder.Add(comp,xp.Current()); } - }catch (Base::Exception &e){ - AREA_WARN("FaceMakerBullseye failed on section: " << e.what()); } + }catch (Base::Exception &e){ + AREA_WARN("FaceMakerBullseye failed on section: " << e.what()); } for(const TopoDS_Wire &wire : wires) builder.Add(comp,wire); @@ -1903,12 +1912,8 @@ struct GetWires { info.wire = BRepBuilderAPI_MakeWire(TopoDS::Edge(shape)).Wire(); info.isClosed = BRep_Tool::IsClosed(info.wire); - if(info.isClosed && params.orientation != Area::OrientationNone){ - int dir =Area::getWireDirection(info.wire); - if((dir>0&¶ms.orientation==Area::OrientationCW) || - (dir<0&¶ms.orientation==Area::OrientationCCW)) - info.wire.Reverse(); - } + if(info.isClosed && params.orientation == Area::OrientationReversed) + info.wire.Reverse(); TIME_INIT(t); if(params.abscissa &myList; gp_Trsf &myTrsf; - short *myArcPlane; + short &myArcPlane; bool &myArcPlaneFound; ShapeParams &myParams; - ShapeInfoBuilder(bool &plane_found, short *arc_plane, gp_Trsf &trsf, + ShapeInfoBuilder(bool &plane_found, short &arc_plane, gp_Trsf &trsf, std::list &list, ShapeParams ¶ms) :myList(list) ,myTrsf(trsf) ,myArcPlane(arc_plane) ,myArcPlaneFound(plane_found), myParams(params) @@ -2269,9 +2274,9 @@ struct ShapeInfoBuilder { return; } myList.push_back(ShapeInfo(finder,shape,myParams)); - if(myArcPlane==NULL || myArcPlaneFound || - *myArcPlane==Area::ArcPlaneNone || - *myArcPlane==Area::ArcPlaneVariable) + if(myArcPlaneFound || + myArcPlane==Area::ArcPlaneNone || + myArcPlane==Area::ArcPlaneVariable) return; if(type == TopAbs_EDGE) { @@ -2295,19 +2300,19 @@ struct ShapeInfoBuilder { bool x0 = fabs(dir.X()) &wires; + const gp_Dir &dir; short orientation; short direction; - WireOrienter(std::list &ws, short o, short d) - :wires(ws),orientation(o),direction(d) + WireOrienter(std::list &ws, const gp_Dir &dir, short o, short d) + :wires(ws),dir(dir),orientation(o),direction(d) {} void operator()(const TopoDS_Shape &shape, int type) { @@ -2356,12 +2362,8 @@ struct WireOrienter { TopoDS_Shape &wire = wires.back(); if(BRep_Tool::IsClosed(wire)) { - if(orientation!=Area::OrientationNone) { - int dir = Area::getWireDirection(wire); - if((dir>0&&orientation==Area::OrientationCW) || - (dir<0&&orientation==Area::OrientationCCW)) - wire.Reverse(); - } + if(orientation==Area::OrientationReversed) + wire.Reverse(); }else if(direction!=Area::DirectionNone) { gp_Pnt p1,p2; getEndPoints(TopoDS::Wire(wire),p1,p2); @@ -2393,18 +2395,36 @@ struct WireOrienter { }; std::list Area::sortWires(const std::list &shapes, - const gp_Pnt *_pstart, gp_Pnt *_pend, short *arc_plane, + const gp_Pnt *_pstart, gp_Pnt *_pend, short *_parc_plane, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) { std::list wires; if(shapes.empty()) return wires; + short _arc_plane = ArcPlaneNone; + short &arc_plane = _parc_plane?*_parc_plane:_arc_plane; + if(sort_mode == SortModeNone) { + gp_Dir dir; + switch(arc_plane) { + case ArcPlaneYZ: + dir = gp_Dir(1,0,0); + break; + case ArcPlaneZX: + dir = gp_Dir(0,1,0); + break; + default: + if(arc_plane != ArcPlaneXY) + AREA_WARN("Sort mode 'None' without a given arc plane, using XY plane"); + arc_plane = ArcPlaneXY; + dir = gp_Dir(0,0,1); + break; + } for(auto &shape : shapes) { if(!shape.IsNull()) foreachSubshape(shape, - WireOrienter(wires,orientation,direction), TopAbs_WIRE); + WireOrienter(wires,dir,orientation,direction), TopAbs_WIRE); } return std::move(wires); } @@ -2627,29 +2647,24 @@ static inline void addGCode(Toolpath &path, const char *name) { path.addCommand(cmd); } -int Area::getWireDirection(const TopoDS_Shape &shape, const gp_Pln *pln) { - gp_Dir dir; - if(pln) - dir = pln->Axis().Direction(); - else{ - BRepLib_FindSurface finder(shape,-1,Standard_True); - if(!finder.Found()) return 0; - dir = GeomAdaptor_Surface(finder.Surface()).Plane().Axis().Direction(); - } - const TopoDS_Wire &wire = TopoDS::Wire(shape); +void Area::setWireOrientation(TopoDS_Wire &wire, const gp_Dir &dir, bool wire_ccw) { //make a test face BRepBuilderAPI_MakeFace mkFace(wire, /*onlyplane=*/Standard_True); - if(!mkFace.IsDone()) return 0; + if(!mkFace.IsDone()) { + AREA_WARN("setWireOrientation: failed to make test face"); + return; + } TopoDS_Face tmpFace = mkFace.Face(); //compare face surface normal with our plane's one BRepAdaptor_Surface surf(tmpFace); - bool normal_co = surf.Plane().Axis().Direction().Dot(dir) > 0; + bool ccw = surf.Plane().Axis().Direction().Dot(dir) > 0; //unlikely, but just in case OCC decided to reverse our wire for the face... take that into account! TopoDS_Iterator it(tmpFace, /*CumOri=*/Standard_False); - normal_co ^= it.Value().Orientation() != wire.Orientation(); + ccw ^= it.Value().Orientation() != wire.Orientation(); - return normal_co ? 1 : -1; + if(ccw != wire_ccw) + wire.Reverse(); } void Area::toPath(Toolpath &path, const std::list &shapes, diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index 172b71788d..c020deb789 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -446,7 +446,7 @@ public: const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH)); - static int getWireDirection(const TopoDS_Shape& wire, const gp_Pln *plane=0); + static void setWireOrientation(TopoDS_Wire& wire, const gp_Dir &dir, bool ccw); PARAM_ENUM_DECLARE(AREA_PARAMS_PATH) diff --git a/src/Mod/Path/App/AreaParams.h b/src/Mod/Path/App/AreaParams.h index 9ae8002bcc..362e4c640b 100644 --- a/src/Mod/Path/App/AreaParams.h +++ b/src/Mod/Path/App/AreaParams.h @@ -190,6 +190,11 @@ "arc encountered.",\ (None)(Auto)(XY)(ZX)(YZ)(Variable))) +#define AREA_PARAMS_ORIENTATION \ + ((enum, orientation, Orientation, 0, "Enforce loop orientation\n"\ + "'Normal' means CCW for outer wires when looking against the positive axis direction, \n"\ + "and CW for inner wires. 'Reversed' means the other way round", (Normal)(Reversed))) + /** Area wire sorting parameters */ #define AREA_PARAMS_SORT \ ((enum, sort_mode, SortMode, 1, "Wire sorting mode to optimize travel distance.\n"\ @@ -202,9 +207,7 @@ ((double, abscissa, SortAbscissa, 3.0, "Controls vertex sampling on wire for nearest point searching\n"\ "The sampling is dong using OCC GCPnts_UniformAbscissa",App::PropertyLength))\ ((short, nearest_k, NearestK, 3, "Nearest k sampling vertices are considered during sorting"))\ - ((enum, orientation, Orientation, 0, "Enforce loop orientation\n"\ - "Note the CW/CCW here specifies the outer wire orientation. For inner wires (holes), the\n"\ - "enforced orientation is reversed", (None)(CW)(CCW)))\ + AREA_PARAMS_ORIENTATION \ ((enum, direction, Direction, 0, "Enforce open path direction",\ (None)(XPositive)(XNegative)(YPositive)(YNegative)(ZPositive)(ZNegative)))