Path.Area: support arc plane selection

This commit is contained in:
Zheng, Lei
2017-03-12 10:39:34 +08:00
committed by wmayer
parent 80850e176d
commit 796a8f3cd5
4 changed files with 233 additions and 66 deletions

View File

@@ -127,11 +127,15 @@ public:
"\n* <key>: any key supported by Path.Area, see Path.Area.getParamDesc() for description"
);
add_keyword_method("sortWires",&Module::sortWires,
"sortWires(shapes, start=Vector(), " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT) ", key=value...)\n"
"sortWires(shapes, start=Vector(), "
PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_ARC_PLANE)
PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT) ", key=value...)\n"
"\nReturns (wires,end), where 'wires' is sorted across Z value and with optimized travel distance,\n"
"and 'end' is the ending position of the whole wires\n"
"and 'end' is the ending position of the whole wires. If arc_plane==1, it returns (wires,end,arc_plane),\n"
"where arc_plane is the found plane if any, or unchanged.\n"
"\n* shapes: input shape list\n"
"\n* start (Vector()): optional start position.\n"
PARAM_PY_DOC(ARG, AREA_PARAMS_ARC_PLANE)
PARAM_PY_DOC(ARG, AREA_PARAMS_SORT)
"\n* <key>: any key supported by Path.Area, see Path.Area.getParamDesc() for description"
);
@@ -373,16 +377,22 @@ private:
Py::Object sortWires(const Py::Tuple& args, const Py::Dict &kwds)
{
PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_ARC_PLANE)
PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SORT)
PARAM_PY_DECLARE_INIT(PARAM_FNAME,AREA_PARAMS_CONF)
PyObject *pShapes=NULL;
PyObject *start=NULL;
static char* kwd_list[] = {"shapes", "start",
PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_ARC_PLANE),
PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT),
PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF), NULL};
if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(),
"O|O!" PARAM_PY_KWDS(AREA_PARAMS_SORT) PARAM_PY_KWDS(AREA_PARAMS_CONF),
"O|O!"
PARAM_PY_KWDS(AREA_PARAMS_ARC_PLANE)
PARAM_PY_KWDS(AREA_PARAMS_SORT)
PARAM_PY_KWDS(AREA_PARAMS_CONF),
kwd_list, &pShapes, &(Base::VectorPy::Type), &start,
PARAM_REF(PARAM_FARG,AREA_PARAMS_ARC_PLANE),
PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT),
PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF)))
throw Py::Exception();
@@ -413,16 +423,19 @@ private:
}
try {
bool need_arc_plane = arc_plane==Area::ArcPlaneAuto;
std::list<TopoDS_Shape> wires = Area::sortWires(shapes,&params,&pstart,
&pend, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT));
&pend, &arc_plane, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT));
PyObject *list = PyList_New(0);
for(auto &wire : wires)
PyList_Append(list,Py::new_reference_to(
Part::shape2pyshape(TopoDS::Wire(wire))));
PyObject *ret = PyTuple_New(2);
PyObject *ret = PyTuple_New(need_arc_plane?3:2);
PyTuple_SetItem(ret,0,list);
PyTuple_SetItem(ret,1,new Base::VectorPy(
Base::Vector3d(pend.X(),pend.Y(),pend.Z())));
if(need_arc_plane)
PyTuple_SetItem(ret,2,PyInt_FromLong(arc_plane));
return Py::asObject(ret);
} PATH_CATCH
}

View File

@@ -262,19 +262,19 @@ void Area::addWire(CArea &area, const TopoDS_Wire& wire,
double first = curve.FirstParameter();
double last = curve.LastParameter();
gp_Circ circle = curve.Circle();
gp_Ax1 axis = circle.Axis();
int dir = axis.Direction().Z()<0?-1:1;
if(reversed) dir = -dir;
gp_Pnt loc = axis.Location();
gp_Dir dir = circle.Axis().Direction();
gp_Pnt center = circle.Location();
int type = dir.Z()<0?-1:1;
if(reversed) type = -type;
if(fabs(first-last)>M_PI) {
// Split arc(circle) larger than half circle. Because gcode
// can't handle full circle?
gp_Pnt mid = curve.Value((last-first)*0.5+first);
ccurve.append(CVertex(dir,Point(mid.X(),mid.Y()),
Point(loc.X(),loc.Y())));
ccurve.append(CVertex(type,Point(mid.X(),mid.Y()),
Point(center.X(),center.Y())));
}
ccurve.append(CVertex(dir,Point(p.X(),p.Y()),
Point(loc.X(),loc.Y())));
ccurve.append(CVertex(type,Point(p.X(),p.Y()),
Point(center.X(),center.Y())));
break;
}
//fall through
@@ -494,14 +494,18 @@ struct FindPlane {
if (!finder.Found())
return;
// NOTE: It seemed that FindSurface disregardge shape's transformation,
// so we have to transformed the found plane manually
gp_Ax3 pos = GeomAdaptor_Surface(finder.Surface()).Plane().Position().Transformed(
shape.Location().Transformation());
gp_Ax3 pos = GeomAdaptor_Surface(finder.Surface()).Plane().Position();
// It seemed that FindSurface disregard shape's transformation,
// so we have to transformed the found plane manually, or is it??
// pos.Transform(shape.Location().Transformation());
//force plane to be right handed
// We only use right hand coordinate, hence gp_Ax2 instead of gp_Ax3
// This means that no matter what the work plane face oriented, we
// will treat it as face upward in a right hand coordinate system.
if(!pos.Direct())
pos = gp_Ax3(pos.Ax2());
gp_Dir dir(pos.Direction());
trsf.SetTransformation(pos);
@@ -1178,7 +1182,7 @@ TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_POCKE
Stepover = -stepover;
return makeOffset(index,PARAM_FIELDS(PARAM_FNAME,AREA_PARAMS_OFFSET));
}case Area::PocketModeZigZagOffset:
pm = ZigZagThenSingleOffsetPocketMode;
pm = ZigZagThenSingleOffsetPocketMode;
break;
default:
throw Base::ValueError("unknown poket mode");
@@ -1530,24 +1534,79 @@ struct ShapeInfo{
struct ShapeInfoBuilder {
std::list<ShapeInfo> &myList;
gp_Trsf &myTrsf;
short *myArcPlane;
bool &myArcPlaneFound;
ShapeInfoBuilder(std::list<ShapeInfo> &list)
:myList(list)
ShapeInfoBuilder(bool &plane_found, short *arc_plane, gp_Trsf &trsf, std::list<ShapeInfo> &list)
:myList(list) ,myTrsf(trsf) ,myArcPlane(arc_plane), myArcPlaneFound(plane_found)
{}
void operator()(const TopoDS_Shape &shape, int) {
void operator()(const TopoDS_Shape &shape, int type) {
BRepLib_FindSurface finder(shape,-1,Standard_True);
if(finder.Found())
myList.push_back(ShapeInfo(finder,shape));
else
if(!finder.Found()) {
myList.push_back(ShapeInfo(shape));
return;
}
myList.push_back(ShapeInfo(finder,shape));
if(myArcPlane==NULL || *myArcPlane==Area::ArcPlaneNone || myArcPlaneFound)
return;
if(type == TopAbs_EDGE) {
BRepAdaptor_Curve curve(TopoDS::Edge(shape));
if(curve.GetType()!=GeomAbs_Circle) return;
}else{
for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) {
BRepAdaptor_Curve curve(TopoDS::Edge(it.Current()));
if(curve.GetType()==GeomAbs_Circle) goto NEXT;
}
return;
}
NEXT:
gp_Ax3 pos = myList.back().myPln.Position();
if(!pos.Direct()) pos = gp_Ax3(pos.Ax2());
const gp_Dir &dir = pos.Direction();
gp_Ax3 dstPos;
switch(*myArcPlane) {
case Area::ArcPlaneAuto: {
bool x0 = fabs(dir.X())<Precision::Confusion();
bool y0 = fabs(dir.Y())<Precision::Confusion();
bool z0 = fabs(dir.Z())<Precision::Confusion();
if(x0&&y0)
*myArcPlane = Area::ArcPlaneXY;
else if(x0&&z0)
*myArcPlane = Area::ArcPlaneZX;
else if(z0&&y0)
*myArcPlane = Area::ArcPlaneYZ;
else {
*myArcPlane = Area::ArcPlaneXY;
dstPos = gp_Ax3(pos.Location(),gp_Dir(0,0,1));
break;
}
myArcPlaneFound = true;
return;
}case Area::ArcPlaneXY:
dstPos = gp_Ax3(pos.Location(),gp_Dir(0,0,1));
break;
case Area::ArcPlaneZX:
dstPos = gp_Ax3(pos.Location(),gp_Dir(0,1,0));
break;
case Area::ArcPlaneYZ:
dstPos = gp_Ax3(pos.Location(),gp_Dir(1,0,0));
break;
default:
return;
}
myTrsf.SetTransformation(pos,dstPos);
myArcPlaneFound = true;
return;
}
};
std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
const AreaParams *params, const gp_Pnt *_pstart, gp_Pnt *_pend,
PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT))
short *arc_plane, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT))
{
std::list<TopoDS_Shape> wires;
@@ -1575,6 +1634,9 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
#define SORT_WIRE_TIME(_msg) \
TIME_PRINT(t1,"sortWires "<< _msg)
gp_Trsf trsf;
bool arcPlaneFound = false;
if(sort_mode == SortMode3D) {
for(auto &shape : shapes)
shape_list.push_back(ShapeInfo(shape));
@@ -1582,13 +1644,14 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
//first pass, find plane of each shape
for(auto &shape : shapes) {
//explode the shape
foreachSubshape(shape,ShapeInfoBuilder(shape_list));
foreachSubshape(shape,ShapeInfoBuilder(
arcPlaneFound,arc_plane,trsf,shape_list));
}
if(shape_list.empty())
return wires;
SORT_WIRE_TIME("plan finding");
SORT_WIRE_TIME("plane finding");
}
Bnd_Box bounds;
@@ -1603,6 +1666,10 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
//Second stage, group shape by its plane, and find overall boundary
for(auto itNext=shape_list.begin(),it=itNext;it!=shape_list.end();it=itNext) {
++itNext;
if(arcPlaneFound) {
it->myShape.Move(trsf);
if(it->myPlanar) it->myPln.Transform(trsf);
}
if(use_bound)
BRepBndLib::Add(it->myShape, bounds, Standard_False);
if(!it->myPlanar) continue;
@@ -1694,28 +1761,54 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
return std::move(wires);
}
static void addCommand(Toolpath &path, const gp_Pnt &p,
bool g0=false, double g0height=0.0, double clearance=0.0)
static inline void addParameter(Command &cmd, const char *name,
double last, double next, bool relative=false)
{
double d = next-last;
if(fabs(d)<Precision::Confusion()) return;
cmd.Parameters[name] = relative?d:next;
}
static inline void addCommand(Toolpath &path, const gp_Pnt &last,
const gp_Pnt &next, const char *name = "G1") {
Command cmd;
cmd.Name = g0?"G0":"G1";
if(g0 && fabs(g0height)>Precision::Confusion()) {
cmd.Parameters["Z"] = g0height;
path.addCommand(cmd);
cmd.Parameters["X"] = p.X();
cmd.Parameters["Y"] = p.Y();
path.addCommand(cmd);
if(fabs(clearance)>Precision::Confusion()) {
cmd.Parameters["Z"] = p.Z()+clearance;
path.addCommand(cmd);
cmd.Name = "G1";
}
}else{
cmd.Parameters["X"] = p.X();
cmd.Parameters["Y"] = p.Y();
}
cmd.Parameters["Z"] = p.Z();
cmd.Name = name;
addParameter(cmd,"X",last.X(),next.X());
addParameter(cmd,"Y",last.Y(),next.Y());
addParameter(cmd,"Z",last.Z(),next.Z());
path.addCommand(cmd);
return;
}
typedef Standard_Real (gp_Pnt::*AxisGetter)() const;
typedef void (gp_Pnt::*AxisSetter)(Standard_Real);
static void addCommand(Toolpath &path,
gp_Pnt last, const gp_Pnt &next,
AxisGetter getter, AxisSetter setter,
double retraction, double clearance)
{
if(!getter || retraction-(last.*getter)() < Precision::Confusion()) {
addCommand(path,last,next,"G0");
return;
}
gp_Pnt pt(last);
(pt.*setter)(retraction);
addCommand(path,last,pt,"G0");
last = pt;
pt = next;
(pt.*setter)(retraction);
addCommand(path,last,pt,"G0");
if(clearance>Precision::Confusion() &&
clearance+(next.*getter)() < retraction)
{
last = pt;
pt = next;
(pt.*setter)((next.*getter)()+clearance);
addCommand(path,last,pt,"G0");
addCommand(path,pt,next);
}else
addCommand(path,pt,next,"G0");
}
static void addCommand(Toolpath &path,
@@ -1724,12 +1817,18 @@ static void addCommand(Toolpath &path,
{
Command cmd;
cmd.Name = clockwise?"G2":"G3";
cmd.Parameters["I"] = center.X()-pstart.X();
cmd.Parameters["J"] = center.Y()-pstart.Y();
cmd.Parameters["K"] = center.Z()-pstart.Z();
cmd.Parameters["X"] = pend.X();
cmd.Parameters["Y"] = pend.Y();
cmd.Parameters["Z"] = pend.Z();
addParameter(cmd,"I",pstart.X(),center.X(),true);
addParameter(cmd,"J",pstart.Y(),center.Y(),true);
addParameter(cmd,"K",pstart.Z(),center.Z(),true);
addParameter(cmd,"X",pstart.X(),pend.X());
addParameter(cmd,"Y",pstart.Y(),pend.Y());
addParameter(cmd,"Z",pstart.Z(),pend.Z());
path.addCommand(cmd);
}
static inline void addCommand(Toolpath &path, const char *name) {
Command cmd;
cmd.Name = name;
path.addCommand(cmd);
}
@@ -1742,10 +1841,42 @@ void Area::toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
AreaParams params;
if(_params) params =*_params;
wires = sortWires(shapes,&params,pstart,pend,
PARAM_REF(PARAM_FARG,AREA_PARAMS_ARC_PLANE),
PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT));
// absolute mode
addCommand(path,"G90");
if(arc_plane==ArcPlaneZX)
addCommand(path,"G18");
else if(arc_plane==ArcPlaneYZ)
addCommand(path,"G19");
threshold = fabs(threshold);
if(threshold < Precision::Confusion())
threshold = Precision::Confusion();
clearance = fabs(clearance);
AxisGetter getter = NULL;
AxisSetter setter = NULL;
retraction = fabs(retraction);
if(retraction>Precision::Confusion()) {
switch(retract_axis) {
case RetractAxisX:
getter = &gp_Pnt::X;
setter = &gp_Pnt::SetX;
break;
case RetractAxisY:
getter = &gp_Pnt::Y;
setter = &gp_Pnt::SetY;
break;
case RetractAxisZ:
getter = &gp_Pnt::Z;
setter = &gp_Pnt::SetZ;
break;
}
}
gp_Pnt plast,p;
if(pstart) plast = *pstart;
bool first = true;
@@ -1753,9 +1884,9 @@ void Area::toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
BRepTools_WireExplorer xp(TopoDS::Wire(wire));
p = BRep_Tool::Pnt(xp.CurrentVertex());
if(first||p.Distance(plast)>threshold)
addCommand(path,p,true,height,clearance);
addCommand(path,plast,p,getter,setter,retraction,clearance);
else
addCommand(path,p);
addCommand(path,plast,p);
plast = p;
first = false;
for(;xp.More();xp.Next(),plast=p) {
@@ -1773,27 +1904,33 @@ void Area::toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
if(reversed) {
for (int i=nbPoints-1; i>=1; --i) {
gp_Pnt pt = curve.Value(discretizer.Parameter(i));
addCommand(path,pt);
addCommand(path,plast,pt);
plast = pt;
}
}else{
for (int i=2; i<=nbPoints; i++) {
gp_Pnt pt = curve.Value(discretizer.Parameter(i));
addCommand(path,pt);
addCommand(path,plast,pt);
plast = pt;
}
}
break;
}
}
addCommand(path,p);
addCommand(path,plast,p);
break;
} case GeomAbs_Circle:{
double first = curve.FirstParameter();
double last = curve.LastParameter();
gp_Circ circle = curve.Circle();
gp_Ax1 axis = circle.Axis();
bool clockwise = axis.Direction().Z()<0;
const gp_Circ &circle = curve.Circle();
// Get the normal vector after trasform the circle to the XY0
// plane, in order to judge if the circle is facing upward or
// downward
const gp_Ax1 &axis = circle.Axis();
const gp_Dir &dir = axis.Direction().Transformed(curve.Trsf().Inverted());
bool clockwise = dir.Z()<0;
if(reversed) clockwise = !clockwise;
gp_Pnt center = axis.Location();
gp_Pnt center = circle.Location();
if(segmentation > Precision::Confusion()) {
GCPnts_UniformAbscissa discretizer(curve, segmentation,
curve.FirstParameter(), curve.LastParameter());
@@ -1832,12 +1969,14 @@ void Area::toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
if(reversed) {
for (int i=nbPoints-1; i>=1; --i) {
gp_Pnt pt = discretizer.Value (i);
addCommand(path,pt);
addCommand(path,plast,pt);
plast = pt;
}
}else{
for (int i=2; i<=nbPoints; i++) {
gp_Pnt pt = discretizer.Value (i);
addCommand(path,pt);
addCommand(path,plast,pt);
plast = pt;
}
}
}else

View File

@@ -440,6 +440,8 @@ public:
* used for sorting
* \arg \c pstart: optional start point
* \arg \c pend: optional output containing the ending point of the returned
* \arg \c arc_plane: optional arc plane selection, if given the found plane
* will be returned. See #AREA_PARAMS_ARC_PLANE for more details.
*
* See #AREA_PARAMS_SORT for other arguments
*
@@ -447,7 +449,8 @@ public:
*/
static std::list<TopoDS_Shape> sortWires(const std::list<TopoDS_Shape> &shapes,
const AreaParams *params = NULL, const gp_Pnt *pstart=NULL,
gp_Pnt *pend=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT));
gp_Pnt *pend=NULL, short *arc_plane = NULL,
PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT));
/** Convert a list of wires to gcode
*

View File

@@ -173,6 +173,15 @@
"minimum distance for the generated new wires. Wires maybe broken if the\n"\
"algorithm see fits. Set to zero to disable wire breaking.",App::PropertyLength))
/** Arc plane */
#define AREA_PARAMS_ARC_PLANE \
((enum, arc_plane, ArcPlane, 1, "Arc drawing plane, corresponding to G17, G18, and G19.\n"\
"If not 'None', the output wires will be transformed to align with the selected plane,\n"\
"and the corresponding GCode will be inserted.\n"\
"'Auto' means the plane is determined by the first encountered arc plane. If the found\n"\
"plane does not align to any GCode plane, XY plane is used.",\
(None)(Auto)(XY)(ZX)(YZ)))
/** Area wire sorting parameters */
#define AREA_PARAMS_SORT \
((enum, sort_mode, SortMode, 1, "Wire sorting mode to optimize travel distance.\n"\
@@ -185,12 +194,15 @@
/** Area path generation parameters */
#define AREA_PARAMS_PATH \
AREA_PARAMS_ARC_PLANE \
AREA_PARAMS_SORT \
((double, threshold, RetractThreshold, 0.0,\
"If two wire's end points are separated within this threshold, they are consider\n"\
"as connected. You may want to set this to the tool diameter to keep the tool down.",\
App::PropertyLength))\
((double, height, RetractHeight, 0.0,"Tool retraction absolute height",App::PropertyLength))\
((double, retraction, Retraction, 0.0,"Tool retraction absolute coordinate along retraction axis",\
App::PropertyLength))\
((enum, retract_axis, RetractAxis, 2,"Tool retraction axis",(X)(Y)(Z)))\
((double, clearance, Clearance, 0.0,\
"When return from last retraction, this gives the pause height relative to the Z\n"\
"value of the next move",App::PropertyLength))\