Path: added Path.fromShapes and Path.sortWires

* Path.fromShapes can now convert any number of shapes to Path with
optimzied travel distances. It internally uses Path.sortWires to
minimize travel distances, and also sort wires by its Z height in case
of sectioned wires.

* The above python function is impelmented in Path::Area class.

* Path::FeatureShape is rewrote to take advantage of these two
functions.

* Add Path::FeatureAreaView to partially display a Path::FeatureArea's
sections.
This commit is contained in:
Zheng, Lei 2017-01-27 17:13:16 +08:00
parent 79a261e868
commit a3f46a40e9
26 changed files with 2093 additions and 378 deletions

View File

@ -88,4 +88,6 @@ PyMODINIT_FUNC initPath()
Path::Area ::init(); Path::Area ::init();
Path::FeatureArea ::init(); Path::FeatureArea ::init();
Path::FeatureAreaPython ::init(); Path::FeatureAreaPython ::init();
Path::FeatureAreaView ::init();
Path::FeatureAreaViewPython ::init();
} }

View File

@ -37,6 +37,7 @@
#include <App/DocumentObjectPy.h> #include <App/DocumentObjectPy.h>
#include <App/Application.h> #include <App/Application.h>
#include <Mod/Part/App/OCCError.h>
#include <Mod/Part/App/TopoShape.h> #include <Mod/Part/App/TopoShape.h>
#include <Mod/Part/App/TopoShapePy.h> #include <Mod/Part/App/TopoShapePy.h>
#include <TopoDS.hxx> #include <TopoDS.hxx>
@ -57,7 +58,45 @@
#include "Path.h" #include "Path.h"
#include "FeaturePath.h" #include "FeaturePath.h"
#include "FeaturePathCompound.h" #include "FeaturePathCompound.h"
#include "Area.h"
#define PATH_CATCH catch (Standard_Failure &e) \
{ \
std::string str; \
Standard_CString msg = e.GetMessageString(); \
str += typeid(e).name(); \
str += " "; \
if (msg) {str += msg;} \
else {str += "No OCCT Exception Message";} \
Base::Console().Error(str.c_str()); \
PyErr_SetString(Part::PartExceptionOCCError,str.c_str()); \
} \
catch(Base::Exception &e) \
{ \
std::string str; \
str += "FreeCAD exception thrown ("; \
str += e.what(); \
str += ")"; \
e.ReportException(); \
PyErr_SetString(Base::BaseExceptionFreeCADError,str.c_str());\
} \
catch(std::exception &e) \
{ \
std::string str; \
str += "STL exception thrown ("; \
str += e.what(); \
str += ")"; \
Base::Console().Error(str.c_str()); \
PyErr_SetString(Base::BaseExceptionFreeCADError,str.c_str());\
} \
catch(const char *e) \
{ \
PyErr_SetString(Base::BaseExceptionFreeCADError,e); \
} throw Py::Exception();
namespace Part {
extern PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape);
}
namespace Path { namespace Path {
class Module : public Py::ExtensionModule<Module> class Module : public Py::ExtensionModule<Module>
@ -79,6 +118,22 @@ public:
add_varargs_method("fromShape",&Module::fromShape, add_varargs_method("fromShape",&Module::fromShape,
"fromShape(Shape): Returns a Path object from a Part Shape" "fromShape(Shape): Returns a Path object from a Part Shape"
); );
add_keyword_method("fromShapes",&Module::fromShapes,
"fromShapes(shapes, sort=True, start=Vector(), " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_PATH) ")\n"
"\nReturns a Path object from a list of shapes\n"
"\n* shapes: input list of shapes.\n"
"\n* start (Vector()): optional start position.\n"
PARAM_PY_DOC(ARG, AREA_PARAMS_PATH)
);
add_keyword_method("sortWires",&Module::sortWires,
"sortWires(shapes, start=Vector(), params=None, " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT_WIRES) ")\n"
"\nReturns (wires,end), where 'wires' is sorted accross Z value and with optimized travel distance,\n"
"and 'end' is the ending position of the whole wires\n"
"\n* shapes: input shape list\n"
"\n* start (Vector()): optional start position.\n"
"\n* params (None): optional dictionary for configuring Path.Area internally used to sort the wires.\n"
PARAM_PY_DOC(ARG, AREA_PARAMS_SORT_WIRES)
);
initialize("This module is the Path module."); // register with Python initialize("This module is the Path module."); // register with Python
} }
@ -261,7 +316,119 @@ private:
throw Py::RuntimeError(e.what()); throw Py::RuntimeError(e.what());
} }
} }
Py::Object fromShapes(const Py::Tuple& args, const Py::Dict &kwds)
{
PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_PATH)
PyObject *pShapes=NULL;
PyObject *start=NULL;
static char* kwd_list[] = {"shapes", "start",
PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_PATH), NULL};
if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(),
"O|O!" PARAM_PY_KWDS(AREA_PARAMS_PATH), kwd_list,
&pShapes, &(Base::VectorPy::Type), &start,
PARAM_REF(PARAM_FARG,AREA_PARAMS_PATH)))
throw Py::Exception();
std::list<TopoDS_Shape> shapes;
if (PyObject_TypeCheck(pShapes, &(Part::TopoShapePy::Type)))
shapes.push_back(static_cast<Part::TopoShapePy*>(pShapes)->getTopoShapePtr()->getShape());
else if (PyObject_TypeCheck(pShapes, &(PyList_Type)) ||
PyObject_TypeCheck(pShapes, &(PyTuple_Type)))
{
Py::Sequence shapeSeq(pShapes);
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
PyObject* item = (*it).ptr();
if(!PyObject_TypeCheck(item, &(Part::TopoShapePy::Type))) {
PyErr_SetString(PyExc_TypeError, "non-shape object in sequence");
throw Py::Exception();
}
shapes.push_back(static_cast<Part::TopoShapePy*>(item)->getTopoShapePtr()->getShape());
}
}
gp_Pnt pstart;
if(start) {
Base::Vector3d vec = static_cast<Base::VectorPy*>(start)->value();
pstart.SetCoord(vec.x, vec.y, vec.z);
}
try {
std::unique_ptr<Toolpath> path(new Toolpath);
Area::toPath(*path,shapes,&pstart,PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_PATH));
return Py::asObject(new PathPy(path.release()));
} PATH_CATCH
}
Py::Object sortWires(const Py::Tuple& args, const Py::Dict &kwds)
{
AreaParams params;
PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SORT_WIRES)
PyObject *pShapes=NULL;
PyObject *start=NULL;
PyObject *pParams=NULL;
static char* kwd_list[] = {"shapes", "start", "params",
PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT_WIRES), NULL};
if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(),
"O|O!O!" PARAM_PY_KWDS(AREA_PARAMS_SORT_WIRES), kwd_list,
&pShapes, &(Base::VectorPy::Type), &start, &PyDict_Type, &pParams,
PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT_WIRES)))
throw Py::Exception();
std::list<TopoDS_Shape> shapes;
if (PyObject_TypeCheck(pShapes, &(Part::TopoShapePy::Type)))
shapes.push_back(static_cast<Part::TopoShapePy*>(pShapes)->getTopoShapePtr()->getShape());
else if (PyObject_TypeCheck(pShapes, &(PyList_Type)) ||
PyObject_TypeCheck(pShapes, &(PyTuple_Type))) {
Py::Sequence shapeSeq(pShapes);
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
PyObject* item = (*it).ptr();
if(!PyObject_TypeCheck(item, &(Part::TopoShapePy::Type))) {
PyErr_SetString(PyExc_TypeError, "non-shape object in sequence");
throw Py::Exception();
}
shapes.push_back(static_cast<Part::TopoShapePy*>(item)->getTopoShapePtr()->getShape());
}
}
if(pParams) {
static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL};
PARAM_PY_DECLARE(PARAM_FNAME,AREA_PARAMS_CONF);
#define AREA_SET(_param) \
PARAM_FNAME(_param) = \
PARAM_TYPED(PARAM_PY_CAST_,_param)(params.PARAM_FNAME(_param));
PARAM_FOREACH(AREA_SET,AREA_PARAMS_CONF)
if (!PyArg_ParseTupleAndKeywords(NULL, pParams,
"|" PARAM_PY_KWDS(AREA_PARAMS_CONF), kwlist,
PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF)))
throw Py::Exception();
#define AREA_GET(_param) \
params.PARAM_FNAME(_param) = \
PARAM_TYPED(PARAM_CAST_PY_,_param)(PARAM_FNAME(_param));
PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF)
}
gp_Pnt pstart,pend;
if(start) {
Base::Vector3d vec = static_cast<Base::VectorPy*>(start)->value();
pstart.SetCoord(vec.x, vec.y, vec.z);
}
try {
std::list<TopoDS_Shape> wires = Area::sortWires(shapes,&params,&pstart,
&pend, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT_WIRES));
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);
PyTuple_SetItem(ret,0,list);
PyTuple_SetItem(ret,1,new Base::VectorPy(
Base::Vector3d(pend.X(),pend.Y(),pend.Z())));
return Py::asObject(ret);
} PATH_CATCH
}
}; };
PyObject* initModule() PyObject* initModule()

View File

@ -24,6 +24,8 @@
#ifndef _PreComp_ #ifndef _PreComp_
#endif #endif
#include <boost/range/adaptor/reversed.hpp>
#include <BRepLib.hxx> #include <BRepLib.hxx>
#include <BRep_Builder.hxx> #include <BRep_Builder.hxx>
#include <BRep_Tool.hxx> #include <BRep_Tool.hxx>
@ -150,6 +152,7 @@ void Area::setPlane(const TopoDS_Shape &shape) {
} }
bool Area::isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2) { bool Area::isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2) {
if(s1.IsEqual(s2)) return true;
TopoDS_Builder builder; TopoDS_Builder builder;
TopoDS_Compound comp; TopoDS_Compound comp;
builder.MakeCompound(comp); builder.MakeCompound(comp);
@ -161,7 +164,7 @@ bool Area::isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2) {
int Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf, int Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf,
double deflection, const TopoDS_Shape *plane, bool force_coplanar, double deflection, const TopoDS_Shape *plane, bool force_coplanar,
CArea *areaOpen, bool to_edges, bool reorder) CArea *areaOpen, bool to_edges, bool reorient)
{ {
bool haveShape = false; bool haveShape = false;
int skipped = 0; int skipped = 0;
@ -193,7 +196,7 @@ int Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf,
else if(to_edges) { else if(to_edges) {
for (TopExp_Explorer it(wire, TopAbs_EDGE); it.More(); it.Next()) for (TopExp_Explorer it(wire, TopAbs_EDGE); it.More(); it.Next())
add(_areaOpen,BRepBuilderAPI_MakeWire( add(_areaOpen,BRepBuilderAPI_MakeWire(
TopoDS::Edge(it.Current())).Wire(),trsf,deflection); TopoDS::Edge(it.Current())).Wire(),trsf,deflection,true);
}else }else
add(_areaOpen,wire,trsf,deflection); add(_areaOpen,wire,trsf,deflection);
} }
@ -210,7 +213,7 @@ int Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf,
} }
} }
if(reorder) if(reorient)
_area.Reorder(); _area.Reorder();
area.m_curves.splice(area.m_curves.end(),_area.m_curves); area.m_curves.splice(area.m_curves.end(),_area.m_curves);
if(areaOpen) if(areaOpen)
@ -221,7 +224,7 @@ int Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf,
} }
void Area::add(CArea &area, const TopoDS_Wire& wire, void Area::add(CArea &area, const TopoDS_Wire& wire,
const gp_Trsf *trsf, double deflection) const gp_Trsf *trsf, double deflection, bool to_edges)
{ {
CCurve ccurve; CCurve ccurve;
BRepTools_WireExplorer xp(trsf?TopoDS::Wire( BRepTools_WireExplorer xp(trsf?TopoDS::Wire(
@ -239,26 +242,32 @@ void Area::add(CArea &area, const TopoDS_Wire& wire,
switch (curve.GetType()) { switch (curve.GetType()) {
case GeomAbs_Line: { case GeomAbs_Line: {
ccurve.append(CVertex(Point(p.X(),p.Y()))); ccurve.append(CVertex(Point(p.X(),p.Y())));
if(to_edges) {
area.append(ccurve);
ccurve.m_vertices.pop_front();
}
break; break;
} case GeomAbs_Circle:{ } case GeomAbs_Circle:{
double first = curve.FirstParameter(); if(!to_edges) {
double last = curve.LastParameter(); double first = curve.FirstParameter();
gp_Circ circle = curve.Circle(); double last = curve.LastParameter();
gp_Ax1 axis = circle.Axis(); gp_Circ circle = curve.Circle();
int dir = axis.Direction().Z()<0?-1:1; gp_Ax1 axis = circle.Axis();
if(reversed) dir = -dir; int dir = axis.Direction().Z()<0?-1:1;
gp_Pnt loc = axis.Location(); if(reversed) dir = -dir;
if(fabs(first-last)>M_PI) { gp_Pnt loc = axis.Location();
// Split arc(circle) larger than half circle. This is if(fabs(first-last)>M_PI) {
// translated from PathUtil code. Not sure why it is // Split arc(circle) larger than half circle. Because gcode
// needed. // can't handle full circle?
gp_Pnt mid = curve.Value((last-first)*0.5+first); gp_Pnt mid = curve.Value((last-first)*0.5+first);
ccurve.append(CVertex(dir,Point(mid.X(),mid.Y()), ccurve.append(CVertex(dir,Point(mid.X(),mid.Y()),
Point(loc.X(),loc.Y())));
}
ccurve.append(CVertex(dir,Point(p.X(),p.Y()),
Point(loc.X(),loc.Y()))); Point(loc.X(),loc.Y())));
break;
} }
ccurve.append(CVertex(dir,Point(p.X(),p.Y()), //fall through
Point(loc.X(),loc.Y())));
break;
} default: { } default: {
// Discretize all other type of curves // Discretize all other type of curves
GCPnts_UniformDeflection discretizer(curve, deflection, GCPnts_UniformDeflection discretizer(curve, deflection,
@ -268,16 +277,22 @@ void Area::add(CArea &area, const TopoDS_Wire& wire,
for (int i=1; i<=nbPoints; i++) { for (int i=1; i<=nbPoints; i++) {
gp_Pnt pt = discretizer.Value (i); gp_Pnt pt = discretizer.Value (i);
ccurve.append(CVertex(Point(pt.X(),pt.Y()))); ccurve.append(CVertex(Point(pt.X(),pt.Y())));
if(to_edges) {
area.append(ccurve);
ccurve.m_vertices.pop_front();
}
} }
}else }else
Standard_Failure::Raise("Curve discretization failed"); Standard_Failure::Raise("Curve discretization failed");
}} }}
} }
if(BRep_Tool::IsClosed(wire) && !ccurve.IsClosed()) { if(!to_edges) {
Base::Console().Warning("ccurve not closed\n"); if(BRep_Tool::IsClosed(wire) && !ccurve.IsClosed()) {
ccurve.append(ccurve.m_vertices.front()); Base::Console().Warning("ccurve not closed\n");
ccurve.append(ccurve.m_vertices.front());
}
area.append(ccurve);
} }
area.append(ccurve);
} }
@ -296,10 +311,32 @@ void Area::clean(bool deleteShapes) {
} }
void Area::add(const TopoDS_Shape &shape,short op) { void Area::add(const TopoDS_Shape &shape,short op) {
#define AREA_SRC_OP(_param) op #define AREA_CONVERT_OP \
PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); ClipperLib::ClipType Operation;\
Q_UNUSED(Operation); switch(op){\
case OperationUnion:\
Operation = ClipperLib::ctUnion;\
break;\
case OperationDifference:\
Operation = ClipperLib::ctDifference;\
break;\
case OperationIntersection:\
Operation = ClipperLib::ctIntersection;\
break;\
case OperationXor:\
Operation = ClipperLib::ctXor;\
break;\
default:\
throw Base::ValueError("invalid Operation");\
}
if(shape.IsNull())
throw Base::ValueError("null shape");
if(op!=OperationCompound) {
AREA_CONVERT_OP;
Q_UNUSED(Operation);
}
bool haveSolid = false; bool haveSolid = false;
for(TopExp_Explorer it(shape, TopAbs_SOLID);it.More();) { for(TopExp_Explorer it(shape, TopAbs_SOLID);it.More();) {
haveSolid = true; haveSolid = true;
@ -313,7 +350,7 @@ void Area::add(const TopoDS_Shape &shape,short op) {
myHaveSolid = haveSolid; myHaveSolid = haveSolid;
clean(); clean();
if(myShapes.empty()) if(op!=OperationCompound && myShapes.empty())
op = OperationUnion; op = OperationUnion;
myShapes.push_back(Shape(op,shape)); myShapes.push_back(Shape(op,shape));
} }
@ -330,7 +367,7 @@ void Area::setParams(const AreaParams &params) {
} }
void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) { void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
if(!myHaveFace) { if(myParams.Fill==FillAuto && !myHaveFace) {
TopExp_Explorer it(shape, TopAbs_FACE); TopExp_Explorer it(shape, TopAbs_FACE);
myHaveFace = it.More(); myHaveFace = it.More();
} }
@ -342,7 +379,7 @@ void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
CArea areaOpen; CArea areaOpen;
mySkippedShapes += add(area,shape,&myTrsf,myParams.Deflection,plane, mySkippedShapes += add(area,shape,&myTrsf,myParams.Deflection,plane,
myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen, myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen,
myParams.OpenMode==OpenModeEdges,myParams.Reorder); myParams.OpenMode==OpenModeEdges,myParams.Reorient);
if(areaOpen.m_curves.size()) { if(areaOpen.m_curves.size()) {
if(&area == myArea.get() || myParams.OpenMode == OpenModeNone) if(&area == myArea.get() || myParams.OpenMode == OpenModeNone)
myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves); myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves);
@ -359,7 +396,7 @@ void Area::build() {
if(myArea || mySections.size()) return; if(myArea || mySections.size()) return;
if(myShapes.empty()) if(myShapes.empty())
throw Base::ValueError("Null shape"); throw Base::ValueError("no shape added");
#define AREA_SRC(_param) myParams.PARAM_FNAME(_param) #define AREA_SRC(_param) myParams.PARAM_FNAME(_param)
PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL); PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL);
@ -368,24 +405,41 @@ void Area::build() {
myShapePlane.Nullify(); myShapePlane.Nullify();
for(const Shape &s : myShapes) { for(const Shape &s : myShapes) {
bool haveShape = false; bool haveShape = false;
bool done = false;
TopoDS_Shape shapePlane;
gp_Trsf trsf;
gp_Ax3 pos;
#define AREA_CHECK_PLANE(_type) \ #define AREA_CHECK_PLANE(_type) \
shapePlane.Nullify();\
for(TopExp_Explorer it(s.shape, TopAbs_##_type); it.More(); it.Next()) {\ for(TopExp_Explorer it(s.shape, TopAbs_##_type); it.More(); it.Next()) {\
haveShape = true;\ haveShape = true;\
BRepLib_FindSurface planeFinder(it.Current(),-1,Standard_True);\ BRepLib_FindSurface planeFinder(it.Current(),-1,Standard_True);\
if (!planeFinder.Found())\ if (!planeFinder.Found())\
continue;\ continue;\
myShapePlane = it.Current();\ shapePlane = it.Current();\
myTrsf.SetTransformation(GeomAdaptor_Surface(\ pos = GeomAdaptor_Surface(planeFinder.Surface()).Plane().Position();\
planeFinder.Surface()).Plane().Position());\ trsf.SetTransformation(pos);\
break;\ gp_Dir dir(pos.Direction());\
if(fabs(dir.X())<Precision::Confusion() &&\
fabs(dir.Y())<Precision::Confusion()) {\
myShapePlane = shapePlane;\
myTrsf = trsf;\
done = true;\
break;\
}\
if(myShapePlane.IsNull()) {\
myShapePlane = shapePlane;\
myTrsf = trsf;\
}\
}\ }\
if(!myShapePlane.IsNull()) break;\ if(done) break;\
if(haveShape) continue; if(haveShape) continue;
//Try to find a plane by iterating through shapes, prefer plane paralell with XY0
AREA_CHECK_PLANE(FACE) AREA_CHECK_PLANE(FACE)
AREA_CHECK_PLANE(WIRE) AREA_CHECK_PLANE(WIRE)
AREA_CHECK_PLANE(EDGE) AREA_CHECK_PLANE(EDGE)
} }
if(myShapePlane.IsNull()) if(myShapePlane.IsNull())
throw Base::ValueError("shapes are not planar"); throw Base::ValueError("shapes are not planar");
} }
@ -412,19 +466,13 @@ void Area::build() {
int error = 0; int error = 0;
int count = myParams.SectionCount; int count = myParams.SectionCount;
if(count<0 || count*myParams.Stepdown > zMax-zMin) if(count<0 || count*myParams.Stepdown > zMax-zMin) {
count = ceil((zMax-zMin)/myParams.Stepdown); count = ceil((zMax-zMin)/myParams.Stepdown);
if((count-1)*myParams.Stepdown < zMax-zMin)
++count;
}
for(int i=0;i<count;++i,zMax-=myParams.Stepdown) { for(int i=0;i<count;++i,zMax-=myParams.Stepdown) {
if(zMax < zMin) zMax = zMin; if(zMax < zMin) zMax = zMin;
// gp_Pnt p1(xMax,yMin,zMax);
// gp_Pnt p2(xMax,yMax,zMax);
// gp_Pnt p3(xMin,yMax,zMax);
// gp_Pnt p4(xMin,yMin,zMax);
// mkWire.Add(BRepBuilderAPI_MakeEdge(p1,p2).Edge());
// mkWire.Add(BRepBuilderAPI_MakeEdge(p2,p3).Edge());
// mkWire.Add(BRepBuilderAPI_MakeEdge(p3,p4).Edge());
// mkWire.Add(BRepBuilderAPI_MakeEdge(p4,p1).Edge());
gp_Pln pln(gp_Pnt(0,0,zMax),gp_Dir(0,0,1)); gp_Pln pln(gp_Pnt(0,0,zMax),gp_Dir(0,0,1));
BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax); BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax);
const TopoDS_Shape &face = mkFace.Face(); const TopoDS_Shape &face = mkFace.Face();
@ -501,14 +549,27 @@ void Area::build() {
mySkippedShapes = 0; mySkippedShapes = 0;
short op = OperationUnion; short op = OperationUnion;
bool pending = false; bool pending = false;
bool explode = myParams.Explode;
for(const Shape &s : myShapes) { for(const Shape &s : myShapes) {
if(op!=s.op) { if(explode) {
explode = false;
for (TopExp_Explorer it(s.shape, TopAbs_EDGE); it.More(); it.Next())
add(*myArea,BRepBuilderAPI_MakeWire(
TopoDS::Edge(it.Current())).Wire(),&myTrsf,myParams.Deflection,true);
continue;
}else if(op!=s.op) {
if(myParams.OpenMode!=OpenModeNone) if(myParams.OpenMode!=OpenModeNone)
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
pending = false; pending = false;
PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); if(areaClip.m_curves.size()) {
myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill); if(op == OperationCompound)
areaClip.m_curves.clear(); myArea->m_curves.splice(myArea->m_curves.end(),areaClip.m_curves);
else{
AREA_CONVERT_OP;
myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill);
areaClip.m_curves.clear();
}
}
op=s.op; op=s.op;
} }
addToBuild(op==OperationUnion?*myArea:areaClip,s.shape); addToBuild(op==OperationUnion?*myArea:areaClip,s.shape);
@ -521,17 +582,106 @@ void Area::build() {
if(pending){ if(pending){
if(myParams.OpenMode!=OpenModeNone) if(myParams.OpenMode!=OpenModeNone)
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); if(op == OperationCompound)
myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill); myArea->m_curves.splice(myArea->m_curves.end(),areaClip.m_curves);
else{
AREA_CONVERT_OP;
myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill);
}
} }
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
//Reassemble wires after explode
if(myParams.Explode) {
std::list<TopoDS_Edge> edges;
gp_Trsf trsf(myTrsf.Inverted());
for(const auto &c : myArea->m_curves) {
TopoDS_Wire wire = toShape(c,&trsf);
if(wire.IsNull()) continue;
TopExp_Explorer it(wire, TopAbs_EDGE);
edges.push_back(TopoDS::Edge(it.Current()));
}
Area area(&myParams);
area.myParams.Explode = false;
area.myParams.Coplanar = CoplanarNone;
area.myWorkPlane = myWorkPlane.IsNull()?myShapePlane:myWorkPlane;
area.myTrsf = myTrsf;
while(edges.size()) {
BRepBuilderAPI_MakeWire mkWire;
for(const auto &e : Part::sort_Edges(myParams.Tolerance,edges))
mkWire.Add(TopoDS::Edge(e));
area.add(mkWire.Wire(),OperationCompound);
}
area.build();
myArea = std::move(area.myArea);
}
}catch(...) { }catch(...) {
clean(); clean();
throw; throw;
} }
} }
list<TopoDS_Shape> Area::sortWires(int index, int count, const gp_Pnt *pstart,
gp_Pnt *_pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_MIN_DIST))
{
std::list<TopoDS_Shape> wires;
build();
gp_Pnt pend,pt;
if(pstart) pt = *pstart;
pt.Transform(TopLoc_Location(myTrsf));
if(mySections.size()) {
if(index>=(int)mySections.size())
throw Base::ValueError("index out of bound");
TopLoc_Location loc(myTrsf.Inverted());
if(index<0) {
index = 0;
count = mySections.size();
}
if(count<=0 || count>(int)mySections.size())
count = mySections.size();
for(int i=index;i<count;++i) {
const std::list<TopoDS_Shape> ws =
mySections[i]->sortWires(-1,0,&pt,&pend,
PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST));
for(auto &wire : ws)
wires.push_back(wire.Moved(loc));
pt = pend;
}
if(_pend)
*_pend = pend.Transformed(loc);
return wires;
}
if(!myArea || myArea->m_curves.empty()) return wires;
CArea area(*myArea);
Point p(pt.X(),pt.Y());
area.ChangeStartToNearest(&p,
PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST));
gp_Trsf trsf(myTrsf.Inverted());
for(const CCurve &c : area.m_curves) {
const TopoDS_Wire &wire = toShape(c,&trsf);
if(wire.IsNull()) continue;
wires.push_back(toShape(c,&trsf));
}
if(_pend) {
gp_Pnt pend = pt;
if(area.m_curves.size() &&
area.m_curves.back().m_vertices.size())
{
const Point &pt = area.m_curves.back().m_vertices.back().m_p;
pend.SetCoord(pt.x,pt.y,0.0);
}
*_pend = pend.Transformed(TopLoc_Location(trsf));
}
return wires;
}
TopoDS_Shape Area::toShape(CArea &area, short fill) { TopoDS_Shape Area::toShape(CArea &area, short fill) {
gp_Trsf trsf(myTrsf.Inverted()); gp_Trsf trsf(myTrsf.Inverted());
bool bFill; bool bFill;
@ -559,14 +709,17 @@ TopoDS_Shape Area::toShape(CArea &area, short fill) {
#define AREA_SECTION(_op,_index,...) do {\ #define AREA_SECTION(_op,_index,...) do {\
if(mySections.size()) {\ if(mySections.size()) {\
if(_index>=(int)mySections.size())\ if(_index>=(int)mySections.size())\
throw Base::ValueError("index out of bound");\ return TopoDS_Shape();\
TopLoc_Location loc(myTrsf.Inverted());\ TopLoc_Location loc(myTrsf.Inverted());\
if(_index<0) {\ if(_index<0) {\
BRep_Builder builder;\ BRep_Builder builder;\
TopoDS_Compound compound;\ TopoDS_Compound compound;\
builder.MakeCompound(compound);\ builder.MakeCompound(compound);\
for(shared_ptr<Area> area : mySections)\ for(shared_ptr<Area> area : mySections){\
builder.Add(compound,area->_op(-1, ## __VA_ARGS__).Moved(loc));\ const TopoDS_Shape &s = area->_op(-1, ## __VA_ARGS__);\
if(s.IsNull()) continue;\
builder.Add(compound,s.Moved(loc));\
}\
return compound;\ return compound;\
}\ }\
return mySections[_index]->_op(-1, ## __VA_ARGS__).Moved(loc);\ return mySections[_index]->_op(-1, ## __VA_ARGS__).Moved(loc);\
@ -637,6 +790,7 @@ TopoDS_Shape Area::getShape(int index) {
for(shared_ptr<CArea> area : areas) { for(shared_ptr<CArea> area : areas) {
if(myParams.Thicken) if(myParams.Thicken)
area->Thicken(myParams.ToolRadius); area->Thicken(myParams.ToolRadius);
const TopoDS_Shape &shape = toShape(*area,fill);
builder.Add(compound,toShape(*area,fill)); builder.Add(compound,toShape(*area,fill));
} }
builder.Add(compound,areaPocket.makePocket( builder.Add(compound,areaPocket.makePocket(
@ -781,7 +935,6 @@ TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_POCKE
throw Base::ValueError("unknown poket mode"); throw Base::ValueError("unknown poket mode");
} }
build();
CAreaConfig conf(myParams); CAreaConfig conf(myParams);
CAreaPocketParams params( CAreaPocketParams params(
tool_radius,extra_offset,stepover,from_center,pm,zig_angle); tool_radius,extra_offset,stepover,from_center,pm,zig_angle);
@ -802,75 +955,85 @@ static inline bool IsLeft(const gp_Pnt &a, const gp_Pnt &b, const gp_Pnt &c) {
return ((b.X() - a.X())*(c.Y() - a.Y()) - (b.Y() - a.Y())*(c.X() - a.X())) > 0; return ((b.X() - a.X())*(c.Y() - a.Y()) - (b.Y() - a.Y())*(c.X() - a.X())) > 0;
} }
TopoDS_Wire Area::toShape(const CCurve &c, const gp_Trsf *trsf) {
BRepBuilderAPI_MakeWire mkWire;
gp_Pnt pstart,pt;
bool first = true;
for(const CVertex &v : c.m_vertices){
if(first){
first = false;
pstart = pt = gp_Pnt(v.m_p.x,v.m_p.y,0);
continue;
}
gp_Pnt pnext(v.m_p.x,v.m_p.y,0);
if(pnext.Distance(pt)<Precision::Confusion())
continue;
if(v.m_type == 0) {
mkWire.Add(BRepBuilderAPI_MakeEdge(pt,pnext).Edge());
} else {
gp_Pnt center(v.m_c.x,v.m_c.y,0);
double r = center.Distance(pt);
double r2 = center.Distance(pnext);
if(fabs(r-r2) > Precision::Confusion()) {
double d = pt.Distance(pnext);
double q = sqrt(r*r - d*d*0.25);
double x = (pt.X()+pnext.X())*0.5;
double y = (pt.Y()+pnext.Y())*0.5;
double dx = q*(pt.Y()-pnext.Y())/d;
double dy = q*(pnext.X()-pt.X())/d;
gp_Pnt newCenter(x + dx, y + dy,0);
if(IsLeft(pt,pnext,center) != IsLeft(pt,pnext,newCenter)) {
newCenter.SetX(x - dx);
newCenter.SetY(y - dy);
}
Base::Console().Warning(
"Arc correction: %lf,%lf, center(%lf,%lf)->(%lf,%lf)\n",
r,r2,center.X(),center.Y(),newCenter.X(),newCenter.Y());
center = newCenter;
}
gp_Ax2 axis(center, gp_Dir(0,0,v.m_type));
mkWire.Add(BRepBuilderAPI_MakeEdge(gp_Circ(axis,r),pt,pnext).Edge());
}
pt = pnext;
}
if(!mkWire.IsDone())
return TopoDS_Wire();
if(c.IsClosed() && !BRep_Tool::IsClosed(mkWire.Wire())){
// This should never happen after changing libarea's
// Point::tolerance to be the same as Precision::Confusion().
// Just leave it here in case.
BRepAdaptor_Curve curve(mkWire.Edge());
gp_Pnt p1(curve.Value(curve.FirstParameter()));
gp_Pnt p2(curve.Value(curve.LastParameter()));
std::stringstream str;
str<< "warning: patch open wire type " <<
c.m_vertices.back().m_type << endl <<
'(' << p1.X() << ',' << p1.Y() << ')' << endl <<
'(' << p2.X() << ',' << p2.Y() << ')' << endl <<
'(' << pt.X() << ',' << pt.Y() << ')' << endl <<
'(' << pstart.X() << ',' <<pstart.Y() <<')' <<endl;
Base::Console().Warning(str.str().c_str());
mkWire.Add(BRepBuilderAPI_MakeEdge(pt,pstart).Edge());
}
if(trsf)
return TopoDS::Wire(mkWire.Wire().Moved(TopLoc_Location(*trsf)));
else
return mkWire.Wire();
}
TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf) { TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf) {
BRep_Builder builder; BRep_Builder builder;
TopoDS_Compound compound; TopoDS_Compound compound;
builder.MakeCompound(compound); builder.MakeCompound(compound);
for(const CCurve &c : area.m_curves) { for(const CCurve &c : area.m_curves) {
BRepBuilderAPI_MakeWire mkWire; const TopoDS_Wire &wire = toShape(c,trsf);
gp_Pnt pstart,pt; if(!wire.IsNull())
bool first = true; builder.Add(compound,wire);
for(const CVertex &v : c.m_vertices){
if(first){
first = false;
pstart = pt = gp_Pnt(v.m_p.x,v.m_p.y,0);
continue;
}
gp_Pnt pnext(v.m_p.x,v.m_p.y,0);
if(v.m_type == 0) {
mkWire.Add(BRepBuilderAPI_MakeEdge(pt,pnext).Edge());
} else {
gp_Pnt center(v.m_c.x,v.m_c.y,0);
double r = center.Distance(pt);
double r2 = center.Distance(pnext);
if(fabs(r-r2) > Precision::Confusion()) {
double d = pt.Distance(pnext);
double q = sqrt(r*r - d*d*0.25);
double x = (pt.X()+pnext.X())*0.5;
double y = (pt.Y()+pnext.Y())*0.5;
double dx = q*(pt.Y()-pnext.Y())/d;
double dy = q*(pnext.X()-pt.X())/d;
gp_Pnt newCenter(x + dx, y + dy,0);
if(IsLeft(pt,pnext,center) != IsLeft(pt,pnext,newCenter)) {
newCenter.SetX(x - dx);
newCenter.SetY(y - dy);
}
Base::Console().Warning(
"Arc correction: %lf,%lf, center(%lf,%lf)->(%lf,%lf)\n",
r,r2,center.X(),center.Y(),newCenter.X(),newCenter.Y());
center = newCenter;
}
gp_Ax2 axis(center, gp_Dir(0,0,v.m_type));
mkWire.Add(BRepBuilderAPI_MakeEdge(gp_Circ(axis,r),pt,pnext).Edge());
}
pt = pnext;
}
if(c.IsClosed() && !BRep_Tool::IsClosed(mkWire.Wire())){
// This should never happen after changing libarea's
// Point::tolerance to be the same as Precision::Confusion().
// Just leave it here in case.
BRepAdaptor_Curve curve(mkWire.Edge());
gp_Pnt p1(curve.Value(curve.FirstParameter()));
gp_Pnt p2(curve.Value(curve.LastParameter()));
std::stringstream str;
str<< "warning: patch open wire" <<
c.m_vertices.back().m_type << endl <<
'(' << p1.X() << ',' << p1.Y() << ')' << endl <<
'(' << p2.X() << ',' << p2.Y() << ')' << endl <<
'(' << pt.X() << ',' << pt.Y() << ')' << endl <<
'(' << pstart.X() << ',' <<pstart.Y() <<')' <<endl;
Base::Console().Warning(str.str().c_str());
mkWire.Add(BRepBuilderAPI_MakeEdge(pt,pstart).Edge());
}
if(trsf)
builder.Add(compound,mkWire.Wire().Moved(TopLoc_Location(*trsf)));
else
builder.Add(compound,mkWire.Wire());
} }
if(fill) { if(!compound.IsNull() && fill) {
try{ try{
Part::FaceMakerBullseye mkFace; Part::FaceMakerBullseye mkFace;
if(trsf) if(trsf)
@ -888,3 +1051,186 @@ TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf) {
return compound; return compound;
} }
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_WIRES))
{
std::list<TopoDS_Shape> wires;
//Heristic sorting by shape's vertex Z value. For performance's sake, we don't
//perform any planar checking here
std::multimap<double,TopoDS_Shape> shape_map;
for (auto &shape : shapes) {
std::list<TopoDS_Shape> subshapes;
if(!explode)
subshapes.push_back(shape);
else{
bool haveShape=false;
for(TopExp_Explorer it(shape,TopAbs_WIRE);it.More();it.Next()) {
haveShape=true;
subshapes.push_back(it.Current());
}
if(!haveShape) {
for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next())
subshapes.push_back(it.Current());
}
}
//Order the shapes by its vertex Z value.
for(auto &s : subshapes) {
bool first=true;
double z=0.0;
for(TopExp_Explorer it(s,TopAbs_VERTEX);it.More();) {
gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(it.Current()));
if(first || z < p.Z()) {
first = false;
z = p.Z();
}
if(!top_z) break;
}
shape_map.insert(std::pair<double,TopoDS_Shape>(z,s));
}
}
if(!shape_map.size())
return wires;
Area area(params);
//We'll do planar checking here, so disable Area planar check
area.myParams.Coplanar = Area::CoplanarNone;
gp_Pnt pstart,pend;
if(_pstart) pstart = *_pstart;
TopoDS_Shape plane = shape_map.rbegin()->second;
area.setPlane(plane);
for(auto &item : boost::adaptors::reverse(shape_map)) {
//do planar checking, and sort wires grouped by plane
if(!Area::isCoplanar(plane,item.second)) {
wires.splice(wires.end(),area.sortWires(
-1,0,&pstart,&pend, PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST)));
pstart = pend;
area.clean(true);
plane = item.second;
area.setPlane(plane);
}
area.add(item.second,Area::OperationCompound);
}
wires.splice(wires.end(),area.sortWires(
-1,0,&pstart,&pend, PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST)));
if(_pend) *_pend = pend;
return wires;
}
static void addCommand(Toolpath &path, const gp_Pnt &p,
bool g0=false, double g0height=0.0, double clearance=0.0)
{
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();
path.addCommand(cmd);
}
static void addCommand(Toolpath &path,
const gp_Pnt &pstart, const gp_Pnt &pend,
const gp_Pnt &center, bool clockwise)
{
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();
path.addCommand(cmd);
}
void Area::toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
const gp_Pnt *pstart, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_PATH))
{
std::list<TopoDS_Shape> wires;
if(sort)
wires = sortWires(shapes,NULL,pstart);
else{
for(auto &shape : shapes) {
if (shape.IsNull())
continue;
bool haveShape=false;
for(TopExp_Explorer it(shape,TopAbs_WIRE);it.More();it.Next()) {
haveShape=true;
wires.push_back(it.Current());
}
if(haveShape) continue;
for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next())
wires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(it.Current())).Wire());
}
}
if(threshold < Precision::Confusion())
threshold = Precision::Confusion();
gp_Pnt plast,p;
if(pstart) plast = *pstart;
bool first = true;
for(const TopoDS_Shape &wire : wires) {
BRepTools_WireExplorer xp(TopoDS::Wire(wire));
p = BRep_Tool::Pnt(xp.CurrentVertex());
if(first||(p.Z()>=plast.Z()&&p.Distance(plast)>threshold))
addCommand(path,p,true,height,clearance);
else
addCommand(path,p);
plast = p;
first = false;
for(;xp.More();xp.Next(),plast=p) {
BRepAdaptor_Curve curve(xp.Current());
bool reversed = (xp.Current().Orientation()==TopAbs_REVERSED);
p = curve.Value(reversed?curve.FirstParameter():curve.LastParameter());
switch (curve.GetType()) {
case GeomAbs_Line: {
addCommand(path,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;
if(reversed) clockwise = !clockwise;
gp_Pnt center = axis.Location();
if(fabs(first-last)>M_PI) {
// Split arc(circle) larger than half circle.
gp_Pnt mid = curve.Value((last-first)*0.5+first);
addCommand(path,plast,mid,center,clockwise);
plast = mid;
}
addCommand(path,plast,p,center,clockwise);
break;
} default: {
// Discretize all other type of curves
GCPnts_UniformDeflection discretizer(curve, deflection,
curve.FirstParameter(), curve.LastParameter());
if (discretizer.IsDone () && discretizer.NbPoints () > 0) {
int nbPoints = discretizer.NbPoints ();
for (int i=1; i<=nbPoints; i++) {
gp_Pnt pt = discretizer.Value (i);
addCommand(path,pt);
}
}else
Standard_Failure::Raise("Curve discretization failed");
}}
}
}
}

View File

@ -23,14 +23,19 @@
#ifndef PATH_AREA_H #ifndef PATH_AREA_H
#define PATH_AREA_H #define PATH_AREA_H
#include <memory>
#include <vector>
#include <list>
#include <TopoDS.hxx> #include <TopoDS.hxx>
#include <gp_Pln.hxx> #include <gp_Pln.hxx>
#include <gp_Circ.hxx> #include <gp_Circ.hxx>
#include <gp_GTrsf.hxx> #include <gp_GTrsf.hxx>
#include "Path.h"
#include "AreaParams.h" #include "AreaParams.h"
class CArea; class CArea;
class CCurve;
namespace Path namespace Path
{ {
@ -152,8 +157,12 @@ public:
/** Set a working plane /** Set a working plane
* *
* If no working plane are set, Area will try to find a working plane from * If no working plane are set, Area will try to find a working plane from
* all the added children shapes. The purpose of this function is in case * individual children faces, wires or edges. By right, we should create a
* the child shapes are all colinear edges * compound of all shapes and then findplane on it. However, because we
* supports solid, and also because OCC may hang for a long time if
* something goes a bit off, we opt to find plane on each individual shape.
* If you intend to pass individual edges, you must supply a workplane shape
* manually
* *
* \arg \c shape: a shape defining a working plane * \arg \c shape: a shape defining a working plane
*/ */
@ -177,13 +186,13 @@ public:
* If more than one offset is requested, a compound shape is return * If more than one offset is requested, a compound shape is return
* containing all offset shapes as wires regardless of \c Fill setting. * containing all offset shapes as wires regardless of \c Fill setting.
*/ */
TopoDS_Shape makeOffset(int index, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_OFFSET)); TopoDS_Shape makeOffset(int index=-1, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_OFFSET));
/** Make a pocket of the combined shape /** Make a pocket of the combined shape
* *
* See #AREA_PARAMS_POCKET for description of the arguments. * See #AREA_PARAMS_POCKET for description of the arguments.
*/ */
TopoDS_Shape makePocket(int index, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_POCKET)); TopoDS_Shape makePocket(int index=-1, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_POCKET));
/** Config this Area object */ /** Config this Area object */
@ -204,8 +213,11 @@ public:
*/ */
void clean(bool deleteShapes=false); void clean(bool deleteShapes=false);
/** Get the combined shape */ /** Get the combined shape
TopoDS_Shape getShape(int index); * \arg \c index: index of the section, -1 for all sections. No effect on
* non-sectioned area.
*/
TopoDS_Shape getShape(int index=-1);
/** Return the number of sections */ /** Return the number of sections */
std::size_t getSectionCount() { std::size_t getSectionCount() {
@ -219,10 +231,30 @@ public:
* \arg \c wire: input wire object * \arg \c wire: input wire object
* \arg \c trsf: optional transform matrix to transform the wire shape into * \arg \c trsf: optional transform matrix to transform the wire shape into
* XY0 plane. * XY0 plane.
* \arg \c deflection: for defecting non circular curves * \arg \c deflection: for discretizing non circular curves
* \arg \c to_edges: if true, discretize all curves, and insert as open
* line segments
* */ * */
static void add(CArea &area, const TopoDS_Wire &wire, static void add(CArea &area, const TopoDS_Wire &wire, const gp_Trsf *trsf=NULL,
const gp_Trsf *trsf=NULL, double deflection=0.01); double deflection=0.01, bool to_edges=false);
/** Output a list or sorted wire with minimize traval distance
*
* \arg \c index: index of the section, -1 for all sections. No effect on
* non-sectioned area.
* \arg \c count: number of the sections to return, <=0 for all sections
* after \c index. No effect on non-sectioned area.
* \arg \c pstart: optional start point
* \arg \c pend: optional output containing the ending point of the returned
* wires
*
* See #AREA_PARAMS_MIN_DIST for other arguments
*
* \return sorted wires
* */
std::list<TopoDS_Shape> sortWires(int index=-1, int count=0,
const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL,
PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_MIN_DIST));
/** Add a OCC generic shape to CArea /** Add a OCC generic shape to CArea
* *
@ -236,7 +268,7 @@ public:
* \arg \c areaOpen: for collecting open curves. If not supplied, open * \arg \c areaOpen: for collecting open curves. If not supplied, open
* curves are added to \c area * curves are added to \c area
* \arg \c to_edges: separate open wires to individual edges * \arg \c to_edges: separate open wires to individual edges
* \arg \c reorder: reorder closed wires for wire only shape * \arg \c reorient: reorient closed wires for wire only shape
* *
* \return Returns the number of non coplaner. Planar testing only happens * \return Returns the number of non coplaner. Planar testing only happens
* if \c plane is supplied * if \c plane is supplied
@ -244,7 +276,7 @@ public:
static int add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf=NULL, static int add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf=NULL,
double deflection=0.01,const TopoDS_Shape *plane = NULL, double deflection=0.01,const TopoDS_Shape *plane = NULL,
bool force_coplanar=true, CArea *areaOpen=NULL, bool to_edges=false, bool force_coplanar=true, CArea *areaOpen=NULL, bool to_edges=false,
bool reorder=true); bool reorient=true);
/** Convert curves in CArea into an OCC shape /** Convert curves in CArea into an OCC shape
* *
@ -256,7 +288,48 @@ public:
static TopoDS_Shape toShape(const CArea &area, bool fill, static TopoDS_Shape toShape(const CArea &area, bool fill,
const gp_Trsf *trsf=NULL); const gp_Trsf *trsf=NULL);
/** Convert a single curve into an OCC wire
*
* \arg \c curve: input curve object
* \arg \c trsf: optional transform matrix to transform the shape back into
* its original position.
* */
static TopoDS_Wire toShape(const CCurve &curve, const gp_Trsf *trsf=NULL);
/** Check if two OCC shape is coplanar */
static bool isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2); static bool isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2);
/** Group shapes by their plane, and return a list of sorted wires
*
* The output wires is ordered by its occupied plane, and sorted to
* minimize traval distance
*
* \arg \c shapes: input list of shapes.
* \arg \c params: optional Area parameters for the Area object internally
* used for sorting
* \arg \c pstart: optional start point
* \arg \c pend: optional output containing the ending point of the returned
* maybe broken if the algorithm see fits.
*
* See #AREA_PARAMS_SORT_WIRES for other arguments
*
* \return sorted wires
*/
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_WIRES));
/** Convert a list of wires to gcode
*
* \arg \c path: output toolpath
* \arg \c shapes: input list of shapes
* \arg \c pstart: output start point,
*
* See #AREA_PARAMS_PATH for other arguments
*/
static void toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
const gp_Pnt *pstart=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH));
}; };
} //namespace Path } //namespace Path

View File

@ -44,20 +44,32 @@
((enum2,clip_fill,ClipFill,0,\ ((enum2,clip_fill,ClipFill,0,\
"ClipperLib clip fill type. \nSee https://goo.gl/5pYQQP",AREA_CLIPPER_FILL_TYPE)) "ClipperLib clip fill type. \nSee https://goo.gl/5pYQQP",AREA_CLIPPER_FILL_TYPE))
/** Deflection parameter */
#define AREA_PARAMS_DEFLECTION \
((double,deflection,Deflection,0.01,\
"Deflection for non circular curve discretization. It also also used for\n"\
"discretizing circular wires when you 'Explode' the shape for wire operations"))
/** Base parameters */ /** Base parameters */
#define AREA_PARAMS_BASE \ #define AREA_PARAMS_BASE \
((enum,fill,Fill,2,"Fill the output wires to make a face. \n"\ ((enum,fill,Fill,2,"Fill the output wires to make a face. \n"\
"Auto means make a face if any of the children has a face.",(None)(Face)(Auto)))\ "Auto means make a face if any of the children has a face.",(None)(Face)(Auto)))\
((enum,coplanar,Coplanar,2,"Specifies the way to check coplanar.\n"\ ((enum,coplanar,Coplanar,2,\
"'Force' will discard non coplaner shapes, but 'Check' only gives warning.",\ "Specifies the way to check coplanar. 'Force' will discard non coplaner shapes,\n"\
(None)(Check)(Force)))\ "but 'Check' only gives warning.",(None)(Check)(Force)))\
((bool,reorder,Reorder,true,"Re-orient closed wires in wire only shapes so that inner wires become holes."))\ ((bool,reorient,Reorient,true,\
((enum,open_mode,OpenMode,0,"Specify how to handle open wires.\n"\ "Re-orient closed wires in wire only shapes so that inner wires become holes."))\
"'None' means combin without openeration.\n"\ ((bool,explode,Explode,false,\
"'Edges' means separate to edges before Union.\n"\ "If true, Area will explode the first shape into disconnected open edges, \n"\
"ClipperLib seems to have an urge to close open wires.",(None)(Union)(Edges)))\ "with all curves discretized, so that later operations like 'Difference' \n"\
AREA_PARAMS_CLIPPER_FILL \ "behave like wire cutting. Without exploding, 'Difference' in ClipperLib\n"\
((double,deflection,Deflection,0.01,"Deflection for non circular curve discretization")) "behave like face cutting."))\
((enum,open_mode,OpenMode,0,\
"Specify how to handle open wires. 'None' means combin without openeration.\n"\
"'Edges' means separate to edges before Union. ClipperLib seems to have an.\n"\
"urge to close open wires.",(None)(Union)(Edges)))\
AREA_PARAMS_DEFLECTION \
AREA_PARAMS_CLIPPER_FILL
/** libarea algorithm option parameters */ /** libarea algorithm option parameters */
#define AREA_PARAMS_CAREA \ #define AREA_PARAMS_CAREA \
@ -72,7 +84,8 @@
((short,min_arc_points,MinArcPoints,4,"Minimum segments for arc discretization"))\ ((short,min_arc_points,MinArcPoints,4,"Minimum segments for arc discretization"))\
((short,max_arc_points,MaxArcPoints,100,"Maximum segments for arc discretization"))\ ((short,max_arc_points,MaxArcPoints,100,"Maximum segments for arc discretization"))\
((double,clipper_scale,ClipperScale,10000.0,\ ((double,clipper_scale,ClipperScale,10000.0,\
"ClipperLib operate on intergers. This is the scale factor to \nconvert floating points.")) "ClipperLib operate on intergers. This is the scale factor to convert\n"\
"floating points."))
/** Pocket parameters /** Pocket parameters
* *
@ -91,8 +104,10 @@
/** Operation code */ /** Operation code */
#define AREA_PARAMS_OPCODE \ #define AREA_PARAMS_OPCODE \
((enum2,op,Operation,0,"Boolean operation",\ ((enum,op,Operation,0,\
(Union)(Difference)(Intersection)(Xor),(ClipperLib::ClipType,ClipperLib::ct))) "Boolean operation. For the first four operation, see https://goo.gl/Gj8RUu.\n"\
"'Compound' means no operation, normal used to do Area.sortWires().",\
(Union)(Difference)(Intersection)(Xor)(Compound)))
/** Offset parameters */ /** Offset parameters */
#define AREA_PARAMS_OFFSET \ #define AREA_PARAMS_OFFSET \
@ -124,6 +139,36 @@
((double,round_precision,RoundPreceision,0.0,\ ((double,round_precision,RoundPreceision,0.0,\
"Round joint precision. If =0, it defaults to Accuracy. \nSee https://goo.gl/4odfQh")) "Round joint precision. If =0, it defaults to Accuracy. \nSee https://goo.gl/4odfQh"))
/** Area path generation parameters */
#define AREA_PARAMS_PATH \
((bool, sort, SortShape, true, \
"Whether to sort the shapes to optimize travel distance. You can customize wire\n"\
"sorting by calling sortWires() manually."))\
((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."))\
((double, height, RetractHeight, 0.0,"Tool retraction absolute height"))\
((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"))\
AREA_PARAMS_DEFLECTION
#define AREA_PARAMS_MIN_DIST \
((double, min_dist, MinDistance, 1.0, \
"minimum distance for the generate new wires. Wires maybe broken if the\n"\
"algorithm see fits."))\
/** Area wire sorting parameters */
#define AREA_PARAMS_SORT_WIRES \
((bool, explode, Explode, true,\
"If ture, the input shape will be exploded into wires before doing planar checking.\n"\
"Otherwise, and whole shape is considered to be in one plane without checking."))\
((bool, top_z, TopZ, false, \
"If ture, the planes is ordered by the first the shapes first vertex Z value.\n"\
"Otherwise, by the highest Z of all of its vertexes."))\
AREA_PARAMS_MIN_DIST
/** Group of all Area configuration parameters except CArea's*/ /** Group of all Area configuration parameters except CArea's*/
#define AREA_PARAMS_AREA \ #define AREA_PARAMS_AREA \
AREA_PARAMS_BASE \ AREA_PARAMS_BASE \

View File

@ -58,6 +58,11 @@ any operation</UserDocu>
</UserDocu> </UserDocu>
</Documentation> </Documentation>
</Methode> </Methode>
<Methode Name="sortWires" Keyword="true">
<Documentation>
<UserDocu></UserDocu>
</Documentation>
</Methode>
<Methode Name="getParams"> <Methode Name="getParams">
<Documentation> <Documentation>
<UserDocu>Get current algorithm parameters as a dictionary.</UserDocu> <UserDocu>Get current algorithm parameters as a dictionary.</UserDocu>

View File

@ -22,10 +22,10 @@
#include "PreCompiled.h" #include "PreCompiled.h"
#include <Mod/Part/App/OCCError.h>
#include <Mod/Part/App/TopoShapePy.h> #include <Mod/Part/App/TopoShapePy.h>
#include <Base/VectorPy.h>
#include "Mod/Path/App/Area.h" #include "Area.h"
// inclusion of the generated files (generated out of AreaPy.xml) // inclusion of the generated files (generated out of AreaPy.xml)
#include "AreaPy.h" #include "AreaPy.h"
@ -77,6 +77,16 @@ static const AreaDoc myDocs[] = {
"\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n" "\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n"
PARAM_PY_DOC(ARG,AREA_PARAMS_POCKET), PARAM_PY_DOC(ARG,AREA_PARAMS_POCKET),
}, },
{
"sortWires",
"sortWires(index=-1, count=0, start=Vector()" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_MIN_DIST) "):\n"
"Returns a tuple (wires,end): sorted wires with minimized travel distance, and the endpoint of the wires.\n"
"\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n"
"\n* count (0): the number of sections to return. <=0 means all sections starting from index.\n"
"\n* start (Vector()): a vector specifies the start point.\n"
PARAM_PY_DOC(ARG,AREA_PARAMS_MIN_DIST),
},
}; };
struct AreaPyDoc { struct AreaPyDoc {
@ -141,12 +151,42 @@ PyObject* AreaPy::getShape(PyObject *args, PyObject *keywds)
if (!PyArg_ParseTupleAndKeywords(args, keywds,"|hO",kwlist,&pcObj)) if (!PyArg_ParseTupleAndKeywords(args, keywds,"|hO",kwlist,&pcObj))
return 0; return 0;
try { if(PyObject_IsTrue(pcObj))
if(PyObject_IsTrue(pcObj)) getAreaPtr()->clean();
getAreaPtr()->clean(); return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape(index)));
return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape(index))); }
PyObject* AreaPy::sortWires(PyObject *args, PyObject *keywds){
PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_MIN_DIST)
short index = -1;
short count = 0;
PyObject *start = NULL;
static char *kwlist[] = {"index","count","start",
PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_MIN_DIST), NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds,
"|hhO!" PARAM_PY_KWDS(AREA_PARAMS_MIN_DIST),
kwlist,&index,&count,&(Base::VectorPy::Type),&start,
PARAM_REF(PARAM_FARG,AREA_PARAMS_MIN_DIST)))
return 0;
gp_Pnt pstart,pend;
if(start) {
Base::Vector3d vec = static_cast<Base::VectorPy*>(start)->value();
pstart.SetCoord(vec.x, vec.y, vec.z);
} }
PY_CATCH_OCC; std::list<TopoDS_Shape> wires = getAreaPtr()->sortWires(
index,count,&pstart,&pend,
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST));
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);
PyTuple_SetItem(ret,0,list);
PyTuple_SetItem(ret,1,new Base::VectorPy(
Base::Vector3d(pend.X(),pend.Y(),pend.Z())));
return ret;
} }
PyObject* AreaPy::add(PyObject *args, PyObject *keywds) PyObject* AreaPy::add(PyObject *args, PyObject *keywds)
@ -205,13 +245,10 @@ PyObject* AreaPy::makeOffset(PyObject *args, PyObject *keywds)
&index,PARAM_REF(PARAM_FARG,AREA_PARAMS_OFFSET))) &index,PARAM_REF(PARAM_FARG,AREA_PARAMS_OFFSET)))
return 0; return 0;
try { //Expand the variable as function call arguments
//Expand the variable as function call arguments TopoDS_Shape resultShape = getAreaPtr()->makeOffset(index,
TopoDS_Shape resultShape = getAreaPtr()->makeOffset(index, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET));
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET)); return Py::new_reference_to(Part::shape2pyshape(resultShape));
return Py::new_reference_to(Part::shape2pyshape(resultShape));
}
PY_CATCH_OCC;
} }
PyObject* AreaPy::makePocket(PyObject *args, PyObject *keywds) PyObject* AreaPy::makePocket(PyObject *args, PyObject *keywds)
@ -228,15 +265,11 @@ PyObject* AreaPy::makePocket(PyObject *args, PyObject *keywds)
&index,PARAM_REF(PARAM_FARG,AREA_PARAMS_POCKET))) &index,PARAM_REF(PARAM_FARG,AREA_PARAMS_POCKET)))
return 0; return 0;
try { TopoDS_Shape resultShape = getAreaPtr()->makePocket(index,
TopoDS_Shape resultShape = getAreaPtr()->makePocket(index, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET));
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET)); return Py::new_reference_to(Part::shape2pyshape(resultShape));
return Py::new_reference_to(Part::shape2pyshape(resultShape));
}
PY_CATCH_OCC;
} }
PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds) PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds)
{ {
static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL}; static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL};

View File

@ -41,6 +41,7 @@ PROPERTY_SOURCE(Path::FeatureArea, Part::Feature)
PARAM_ENUM_STRING_DECLARE(static const char *Enums,AREA_PARAMS_ALL) PARAM_ENUM_STRING_DECLARE(static const char *Enums,AREA_PARAMS_ALL)
FeatureArea::FeatureArea() FeatureArea::FeatureArea()
:myBuild(false)
{ {
ADD_PROPERTY(Sources,(0)); ADD_PROPERTY(Sources,(0));
ADD_PROPERTY(WorkPlane,(TopoDS_Shape())); ADD_PROPERTY(WorkPlane,(TopoDS_Shape()));
@ -62,6 +63,14 @@ FeatureArea::~FeatureArea()
{ {
} }
Area &FeatureArea::getArea() {
if(!myBuild) {
myBuild = true;
execute();
}
return myArea;
}
App::DocumentObjectExecReturn *FeatureArea::execute(void) App::DocumentObjectExecReturn *FeatureArea::execute(void)
{ {
std::vector<App::DocumentObject*> links = Sources.getValues(); std::vector<App::DocumentObject*> links = Sources.getValues();
@ -97,6 +106,17 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void)
return Part::Feature::execute(); return Part::Feature::execute();
} }
std::list<TopoDS_Shape> FeatureArea::getShapes() {
std::list<TopoDS_Shape> shapes;
Area &area = getArea();
if(area.getSectionCount()) {
for(int i=0;i<(int)area.getSectionCount();++i)
shapes.push_back(area.getShape(i));
}else
shapes.push_back(area.getShape());
return shapes;
}
short FeatureArea::mustExecute(void) const short FeatureArea::mustExecute(void) const
{ {
if (Sources.isTouched()) if (Sources.isTouched())
@ -119,14 +139,85 @@ PyObject *FeatureArea::getPyObject()
} }
// Python Area feature --------------------------------------------------------- // FeatureAreaView -------------------------------------------------------------
//
PROPERTY_SOURCE(Path::FeatureAreaView, Part::Feature)
FeatureAreaView::FeatureAreaView()
{
ADD_PROPERTY(Source,(0));
ADD_PROPERTY_TYPE(SectionIndex,(0),"Section",App::Prop_None,"The start index of the section to show");
ADD_PROPERTY_TYPE(SectionCount,(1),"Section",App::Prop_None,"Number of sections to show");
}
std::list<TopoDS_Shape> FeatureAreaView::getShapes() {
std::list<TopoDS_Shape> shapes;
App::DocumentObject* pObj = Source.getValue();
if (!pObj) return shapes;
if(!pObj->isDerivedFrom(FeatureArea::getClassTypeId()))
return shapes;
Area &area = static_cast<FeatureArea*>(pObj)->getArea();
int index=SectionIndex.getValue(),count=SectionCount.getValue();
if(index<0) {
index = 0;
count = area.getSectionCount();
}
if(index >= (int)area.getSectionCount())
return shapes;
if(count<=0)
count = (int)area.getSectionCount();
if(count==1)
shapes.push_back(area.getShape(index));
else{
count += index;
if(count>(int)area.getSectionCount())
count = area.getSectionCount();
for(int i=index;i<count;++i)
shapes.push_back(area.getShape(i));
}
return shapes;
}
App::DocumentObjectExecReturn *FeatureAreaView::execute(void)
{
App::DocumentObject* pObj = Source.getValue();
if (!pObj)
return new App::DocumentObjectExecReturn("No shape linked");
if(!pObj->isDerivedFrom(FeatureArea::getClassTypeId()))
return new App::DocumentObjectExecReturn("Linked object is not a FeatureArea");
std::list<TopoDS_Shape> shapes = getShapes();
if(shapes.empty())
Shape.setValue(TopoDS_Shape());
else if(shapes.size()==1) {
Shape.setValue(shapes.front());
}else{
BRep_Builder builder;
TopoDS_Compound compound;
builder.MakeCompound(compound);
for(auto &shape : shapes)
builder.Add(compound,shape);
Shape.setValue(compound);
}
return Part::Feature::execute();
}
// Python feature ---------------------------------------------------------
namespace App { namespace App {
/// @cond DOXERR /// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(Path::FeatureAreaPython, Path::FeatureArea) PROPERTY_SOURCE_TEMPLATE(Path::FeatureAreaPython, Path::FeatureArea)
PROPERTY_SOURCE_TEMPLATE(Path::FeatureAreaViewPython, Path::FeatureAreaView)
template<> const char* Path::FeatureAreaPython::getViewProviderName(void) const { template<> const char* Path::FeatureAreaPython::getViewProviderName(void) const {
return "PathGui::ViewProviderArea"; return "PathGui::ViewProviderAreaPython";
}
template<> const char* Path::FeatureAreaViewPython::getViewProviderName(void) const {
return "PathGui::ViewProviderAreaViewPython";
} }
/// @endcond /// @endcond

View File

@ -40,12 +40,13 @@ class PathExport FeatureArea : public Part::Feature
PROPERTY_HEADER(Path::FeatureArea); PROPERTY_HEADER(Path::FeatureArea);
public: public:
Area myArea;
/// Constructor /// Constructor
FeatureArea(void); FeatureArea(void);
virtual ~FeatureArea(); virtual ~FeatureArea();
Area &getArea();
std::list<TopoDS_Shape> getShapes();
/// returns the type name of the ViewProvider /// returns the type name of the ViewProvider
virtual const char* getViewProviderName(void) const { virtual const char* getViewProviderName(void) const {
return "PathGui::ViewProviderArea"; return "PathGui::ViewProviderArea";
@ -58,10 +59,36 @@ public:
Part::PropertyPartShape WorkPlane; Part::PropertyPartShape WorkPlane;
PARAM_PROP_DECLARE(AREA_PARAMS_ALL) PARAM_PROP_DECLARE(AREA_PARAMS_ALL)
private:
bool myBuild;
Area myArea;
}; };
typedef App::FeaturePythonT<FeatureArea> FeatureAreaPython; typedef App::FeaturePythonT<FeatureArea> FeatureAreaPython;
class PathExport FeatureAreaView : public Part::Feature
{
PROPERTY_HEADER(Path::FeatureAreaView);
public:
/// Constructor
FeatureAreaView(void);
std::list<TopoDS_Shape> getShapes();
virtual const char* getViewProviderName(void) const {
return "PathGui::ViewProviderAreaView";
}
virtual App::DocumentObjectExecReturn *execute(void);
App::PropertyLink Source;
App::PropertyInteger SectionIndex;
App::PropertyInteger SectionCount;
};
typedef App::FeaturePythonT<FeatureAreaView> FeatureAreaViewPython;
} //namespace Path } //namespace Path

View File

@ -46,7 +46,7 @@ PyObject* FeatureAreaPy::getArea(PyObject *args)
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, ""))
return NULL; return NULL;
return new AreaPy(new Area(getFeatureAreaPtr()->myArea)); return new AreaPy(new Area(getFeatureAreaPtr()->getArea()));
} }
PyObject* FeatureAreaPy::setParams(PyObject *args, PyObject *keywds) PyObject* FeatureAreaPy::setParams(PyObject *args, PyObject *keywds)

View File

@ -19,7 +19,9 @@
* Suite 330, Boston, MA 02111-1307, USA * * Suite 330, Boston, MA 02111-1307, USA *
* * * *
***************************************************************************/ ***************************************************************************/
/*
* Copyright (c) 2017 Zheng, Lei <realthunder.dev@gmail.com>
*/
#include "PreCompiled.h" #include "PreCompiled.h"
@ -32,20 +34,17 @@
#include <App/DocumentObjectPy.h> #include <App/DocumentObjectPy.h>
#include <Base/Placement.h> #include <Base/Placement.h>
#include <Mod/Part/App/TopoShape.h> #include <Mod/Part/App/TopoShape.h>
#include <Mod/Part/App/PartFeature.h>
#include <TopoDS.hxx> #include <TopoDS.hxx>
#include <TopoDS_Shape.hxx> #include <TopoDS_Shape.hxx>
#include <TopoDS_Edge.hxx> #include <TopoDS_Edge.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopExp_Explorer.hxx> #include <TopExp_Explorer.hxx>
#include <gp_Lin.hxx> #include <Standard_Failure.hxx>
#include <BRep_Tool.hxx> #include <Standard_Version.hxx>
#include <BRepAdaptor_CompCurve.hxx> #include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepAdaptor_HCompCurve.hxx>
#include <Approx_Curve3d.hxx>
#include <BRepAdaptor_HCurve.hxx>
#include "FeatureArea.h"
using namespace Path; using namespace Path;
@ -54,79 +53,39 @@ PROPERTY_SOURCE(Path::FeatureShape, Path::Feature)
FeatureShape::FeatureShape() FeatureShape::FeatureShape()
{ {
ADD_PROPERTY_TYPE(Shape,(TopoDS_Shape()),"Path",App::Prop_None,"The shape data of this feature"); ADD_PROPERTY(Sources,(0));
ADD_PROPERTY_TYPE(StartPoint,(Base::Vector3d()),"Path",App::Prop_None,"Path start position");
PARAM_PROP_ADD("Path",AREA_PARAMS_PATH);
} }
FeatureShape::~FeatureShape() FeatureShape::~FeatureShape()
{ {
} }
short FeatureShape::mustExecute(void) const
{
return Path::Feature::mustExecute();
}
App::DocumentObjectExecReturn *FeatureShape::execute(void) App::DocumentObjectExecReturn *FeatureShape::execute(void)
{ {
TopoDS_Shape shape = Shape.getValue(); Toolpath path;
if (!shape.IsNull()) { std::vector<App::DocumentObject*> links = Sources.getValues();
if (shape.ShapeType() == TopAbs_WIRE) { if (links.empty()) {
Path::Toolpath result; Path.setValue(path);
bool first = true; return new App::DocumentObjectExecReturn("No shapes linked");
Base::Placement last;
TopExp_Explorer ExpEdges (shape,TopAbs_EDGE);
while (ExpEdges.More()) {
const TopoDS_Edge& edge = TopoDS::Edge(ExpEdges.Current());
TopExp_Explorer ExpVerts(edge,TopAbs_VERTEX);
bool vfirst = true;
while (ExpVerts.More()) {
const TopoDS_Vertex& vert = TopoDS::Vertex(ExpVerts.Current());
gp_Pnt pnt = BRep_Tool::Pnt(vert);
Base::Placement tpl;
tpl.setPosition(Base::Vector3d(pnt.X(),pnt.Y(),pnt.Z()));
if (first) {
// add first point as a G0 move
Path::Command cmd;
std::ostringstream ctxt;
ctxt << "G0 X" << tpl.getPosition().x << " Y" << tpl.getPosition().y << " Z" << tpl.getPosition().z;
cmd.setFromGCode(ctxt.str());
result.addCommand(cmd);
first = false;
vfirst = false;
} else {
if (vfirst)
vfirst = false;
else {
Path::Command cmd;
cmd.setFromPlacement(tpl);
// write arc data if needed
BRepAdaptor_Curve adapt(edge);
if (adapt.GetType() == GeomAbs_Circle) {
gp_Circ circ = adapt.Circle();
gp_Pnt c = circ.Location();
bool clockwise = false;
gp_Dir n = circ.Axis().Direction();
if (n.Z() < 0)
clockwise = true;
Base::Vector3d center = Base::Vector3d(c.X(),c.Y(),c.Z());
// center coords must be relative to last point
center -= last.getPosition();
cmd.setCenter(center,clockwise);
}
result.addCommand(cmd);
}
}
ExpVerts.Next();
last = tpl;
}
ExpEdges.Next();
}
Path.setValue(result);
}
} }
const Base::Vector3d &v = StartPoint.getValue();
gp_Pnt pstart(v.x,v.y,v.z);
std::list<TopoDS_Shape> shapes;
for (std::vector<App::DocumentObject*>::iterator it = links.begin(); it != links.end(); ++it) {
if (!(*it && (*it)->isDerivedFrom(Part::Feature::getClassTypeId())))
continue;
const TopoDS_Shape &shape = static_cast<Part::Feature*>(*it)->Shape.getShape().getShape();
if (shape.IsNull())
continue;
shapes.push_back(shape);
}
Area::toPath(path,shapes,&pstart,PARAM_PROP_ARGS(AREA_PARAMS_PATH));
Path.setValue(path);
return App::DocumentObject::StdReturn; return App::DocumentObject::StdReturn;
} }

View File

@ -19,6 +19,9 @@
* Suite 330, Boston, MA 02111-1307, USA * * Suite 330, Boston, MA 02111-1307, USA *
* * * *
***************************************************************************/ ***************************************************************************/
/*
* Copyright (c) 2017 Zheng, Lei <realthunder.dev@gmail.com>
*/
#ifndef PATH_FeaturePathShape_H #ifndef PATH_FeaturePathShape_H
@ -26,14 +29,13 @@
#include <App/DocumentObject.h> #include <App/DocumentObject.h>
#include <App/GeoFeature.h> #include <App/GeoFeature.h>
#include <App/PropertyFile.h>
#include <App/PropertyGeo.h> #include <App/PropertyGeo.h>
#include <App/FeaturePython.h> #include <App/FeaturePython.h>
#include "Mod/Part/App/PropertyTopoShape.h" #include "Mod/Part/App/PropertyTopoShape.h"
#include "Path.h"
#include "PropertyPath.h" #include "PropertyPath.h"
#include "FeaturePath.h" #include "FeaturePath.h"
#include "Area.h"
namespace Path namespace Path
{ {
@ -47,12 +49,14 @@ public:
FeatureShape(void); FeatureShape(void);
virtual ~FeatureShape(); virtual ~FeatureShape();
Part::PropertyPartShape Shape; // Part::PropertyPartShape Shape;
App::PropertyLinkList Sources;
App::PropertyVector StartPoint;
PARAM_PROP_DECLARE(AREA_PARAMS_PATH)
//@{ //@{
/// recalculate the feature /// recalculate the feature
virtual App::DocumentObjectExecReturn *execute(void); virtual App::DocumentObjectExecReturn *execute(void);
virtual short mustExecute(void) const;
//@} //@}
/// returns the type name of the ViewProvider /// returns the type name of the ViewProvider

View File

@ -1,95 +1,95 @@
/*************************************************************************** /***************************************************************************
* Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 *
* * * *
* This file is part of the FreeCAD CAx development system. * * This file is part of the FreeCAD CAx development system. *
* * * *
* This library is free software; you can redistribute it and/or * * This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public * * modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either * * License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. * * version 2 of the License, or (at your option) any later version. *
* * * *
* This library is distributed in the hope that it will be useful, * * This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. * * GNU Library General Public License for more details. *
* * * *
* You should have received a copy of the GNU Library General Public * * You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, * * License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, * * write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA * * Suite 330, Boston, MA 02111-1307, USA *
* * * *
***************************************************************************/ ***************************************************************************/
#ifndef PATH_Path_H
#define PATH_Path_H
#include "Command.h"
#ifndef PATH_Path_H //#include "Mod/Robot/App/kdl_cp/path_composite.hpp"
#define PATH_Path_H //#include "Mod/Robot/App/kdl_cp/frames_io.hpp"
#include <Base/Persistence.h>
#include "Command.h" #include <Base/Vector3D.h>
//#include "Mod/Robot/App/kdl_cp/path_composite.hpp"
//#include "Mod/Robot/App/kdl_cp/frames_io.hpp" namespace Path
#include <Base/Persistence.h> {
#include <Base/Vector3D.h>
/** The representation of a CNC Toolpath */
namespace Path
{ class PathExport Toolpath : public Base::Persistence
{
/** The representation of a CNC Toolpath */ TYPESYSTEM_HEADER();
class PathExport Toolpath : public Base::Persistence public:
{ Toolpath();
TYPESYSTEM_HEADER(); Toolpath(const Toolpath&);
~Toolpath();
public:
Toolpath(); Toolpath &operator=(const Toolpath&);
Toolpath(const Toolpath&);
~Toolpath(); // from base class
virtual unsigned int getMemSize (void) const;
Toolpath &operator=(const Toolpath&); virtual void Save (Base::Writer &/*writer*/) const;
virtual void Restore(Base::XMLReader &/*reader*/);
// from base class void SaveDocFile (Base::Writer &writer) const;
virtual unsigned int getMemSize (void) const; void RestoreDocFile(Base::Reader &reader);
virtual void Save (Base::Writer &/*writer*/) const;
virtual void Restore(Base::XMLReader &/*reader*/); // interface
void SaveDocFile (Base::Writer &writer) const; void clear(void); // clears the internal data
void RestoreDocFile(Base::Reader &reader); void addCommand(const Command &Cmd); // adds a command at the end
void insertCommand(const Command &Cmd, int); // inserts a command
// interface void deleteCommand(int); // deletes a command
void clear(void); // clears the internal data double getLength(void); // return the Length (mm) of the Path
void addCommand(const Command &Cmd); // adds a command at the end void recalculate(void); // recalculates the points
void insertCommand(const Command &Cmd, int); // inserts a command void setFromGCode(const std::string); // sets the path from the contents of the given GCode string
void deleteCommand(int); // deletes a command std::string toGCode(void) const; // gets a gcode string representation from the Path
double getLength(void); // return the Length (mm) of the Path
void recalculate(void); // recalculates the points // shortcut functions
void setFromGCode(const std::string); // sets the path from the contents of the given GCode string unsigned int getSize(void) const{return vpcCommands.size();}
std::string toGCode(void) const; // gets a gcode string representation from the Path const std::vector<Command*> &getCommands(void)const{return vpcCommands;}
const Command &getCommand(unsigned int pos)const {return *vpcCommands[pos];}
// shortcut functions
unsigned int getSize(void) const{return vpcCommands.size();} protected:
const std::vector<Command*> &getCommands(void)const{return vpcCommands;} std::vector<Command*> vpcCommands;
const Command &getCommand(unsigned int pos)const {return *vpcCommands[pos];} //KDL::Path_Composite *pcPath;
protected: /*
std::vector<Command*> vpcCommands; inline KDL::Frame toFrame(const Base::Placement &To){
//KDL::Path_Composite *pcPath; return KDL::Frame(KDL::Rotation::Quaternion(To.getRotation()[0],
To.getRotation()[1],
/* To.getRotation()[2],
inline KDL::Frame toFrame(const Base::Placement &To){ To.getRotation()[3]),
return KDL::Frame(KDL::Rotation::Quaternion(To.getRotation()[0], KDL::Vector(To.getPosition()[0],
To.getRotation()[1], To.getPosition()[1],
To.getRotation()[2], To.getPosition()[2]));
To.getRotation()[3]), }
KDL::Vector(To.getPosition()[0], inline Base::Placement toPlacement(const KDL::Frame &To){
To.getPosition()[1], double x,y,z,w;
To.getPosition()[2])); To.M.GetQuaternion(x,y,z,w);
} return Base::Placement(Base::Vector3d(To.p[0],To.p[1],To.p[2]),Base::Rotation(x,y,z,w));
inline Base::Placement toPlacement(const KDL::Frame &To){ } */
double x,y,z,w; };
To.M.GetQuaternion(x,y,z,w);
return Base::Placement(Base::Vector3d(To.p[0],To.p[1],To.p[2]),Base::Rotation(x,y,z,w)); } //namespace Path
} */
};
#endif // PATH_Path_H
} //namespace Path
#endif // PATH_Path_H

View File

@ -80,6 +80,8 @@ PyMODINIT_FUNC initPathGui()
PathGui::ViewProviderPathPython ::init(); PathGui::ViewProviderPathPython ::init();
PathGui::ViewProviderArea ::init(); PathGui::ViewProviderArea ::init();
PathGui::ViewProviderAreaPython ::init(); PathGui::ViewProviderAreaPython ::init();
PathGui::ViewProviderAreaView ::init();
PathGui::ViewProviderAreaViewPython ::init();
// add resources and reloads the translators // add resources and reloads the translators
loadPathResource(); loadPathResource();

View File

@ -68,39 +68,55 @@ void CmdPathArea::activated(int iMsg)
Q_UNUSED(iMsg); Q_UNUSED(iMsg);
std::list<std::string> cmds; std::list<std::string> cmds;
std::ostringstream sources; std::ostringstream sources;
std::string areaName;
bool addView = true;
for(const Gui::SelectionObject &selObj : for(const Gui::SelectionObject &selObj :
getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId())) getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId()))
{ {
const Part::Feature *pcObj = static_cast<const Part::Feature*>(selObj.getObject()); const Part::Feature *pcObj = static_cast<const Part::Feature*>(selObj.getObject());
const std::vector<std::string> &subnames = selObj.getSubNames(); const std::vector<std::string> &subnames = selObj.getSubNames();
if(addView && areaName.size()) addView = false;
if(subnames.empty()) { if(subnames.empty()) {
if(addView && pcObj->getTypeId().isDerivedFrom(Path::FeatureArea::getClassTypeId()))
areaName = pcObj->getNameInDocument();
sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ","; sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ",";
continue; continue;
} }
for(const std::string &name : subnames) { for(const std::string &name : subnames) {
if(!name.compare(0,4,"Face") && if(name.compare(0,4,"Face") && name.compare(0,4,"Edge")) {
!name.compare(0,4,"Edge"))
{
Base::Console().Error("Selected shape is not 2D\n"); Base::Console().Error("Selected shape is not 2D\n");
return; return;
} }
int index = atoi(name.substr(4).c_str());
std::ostringstream subname; std::ostringstream subname;
subname << pcObj->getNameInDocument() << '_' << name; subname << pcObj->getNameInDocument() << '_' << name;
std::string sub_fname = getUniqueObjectName(subname.str().c_str()); std::string sub_fname = getUniqueObjectName(subname.str().c_str());
std::ostringstream cmd; std::ostringstream cmd;
cmd << "FreeCAD.activeDocument().addObject('Part::Feature','" << sub_fname << cmd << "FreeCAD.activeDocument().addObject('Part::Feature','" << sub_fname <<
"').Shape = FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ".Shape." << "').Shape = PathCommands.findShape(FreeCAD.activeDocument()." <<
name.substr(0,4) << "s[" << index-1 << ']'; pcObj->getNameInDocument() << ".Shape,'" << name << "'";
if(!name.compare(0,4,"Edge"))
cmd << ",'Wires'";
cmd << ')';
cmds.push_back(cmd.str()); cmds.push_back(cmd.str());
sources << "FreeCAD.activeDocument()." << sub_fname << ","; sources << "FreeCAD.activeDocument()." << sub_fname << ",";
} }
} }
if(addView && areaName.size()) {
std::string FeatName = getUniqueObjectName("FeatureAreaView");
openCommand("Create Path Area View");
doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureAreaView','%s')",FeatName.c_str());
doCommand(Doc,"FreeCAD.activeDocument().%s.Source = FreeCAD.activeDocument().%s",
FeatName.c_str(),areaName.c_str());
commitCommand();
updateActive();
return;
}
std::string FeatName = getUniqueObjectName("FeatureArea"); std::string FeatName = getUniqueObjectName("FeatureArea");
openCommand("Create Path Area"); openCommand("Create Path Area");
doCommand(Doc,"import PathCommands");
for(const std::string &cmd : cmds) for(const std::string &cmd : cmds)
doCommand(Doc,cmd.c_str()); doCommand(Doc,cmd.c_str());
doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureArea','%s')",FeatName.c_str()); doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureArea','%s')",FeatName.c_str());
@ -171,17 +187,12 @@ void CmdPathAreaWorkplane::activated(int iMsg)
} }
for(const std::string &name : subnames) { for(const std::string &name : subnames) {
if(!name.compare(0,4,"Face") && if(name.compare(0,4,"Face") && name.compare(0,4,"Edge")) {
!name.compare(0,4,"Edge"))
{
Base::Console().Error("Selected shape is not 2D\n"); Base::Console().Error("Selected shape is not 2D\n");
return; return;
} }
int index = atoi(name.substr(4).c_str());
std::ostringstream subname; std::ostringstream subname;
subname << planeSubname << '.' << name.substr(0,4) << "s[" << index-1 << ']'; subname << planeSubname << ",'" << name << "','Wires'";
planeSubname = subname.str(); planeSubname = subname.str();
} }
} }
@ -195,10 +206,10 @@ void CmdPathAreaWorkplane::activated(int iMsg)
} }
openCommand("Select Workplane for Path Area"); openCommand("Select Workplane for Path Area");
doCommand(Doc,"FreeCAD.activeDocument().%s.WorkPlane = FreeCAD.activeDocument().%s", doCommand(Doc,"import PathCommands");
areaName.c_str(),planeSubname.c_str()); doCommand(Doc,"FreeCAD.activeDocument().%s.WorkPlane = PathCommands.findShape("
"FreeCAD.activeDocument().%s)", areaName.c_str(),planeSubname.c_str());
doCommand(Doc,"FreeCAD.activeDocument().%s.ViewObject.Visibility = True",areaName.c_str()); doCommand(Doc,"FreeCAD.activeDocument().%s.ViewObject.Visibility = True",areaName.c_str());
// doCommand(Doc,"FreeCAD.activeDocument().%s.ViewObject.Visibility = False",planeName.c_str());
commitCommand(); commitCommand();
updateActive(); updateActive();
} }
@ -284,24 +295,48 @@ CmdPathShape::CmdPathShape()
void CmdPathShape::activated(int iMsg) void CmdPathShape::activated(int iMsg)
{ {
Q_UNUSED(iMsg); Q_UNUSED(iMsg);
std::vector<Gui::SelectionSingleton::SelObj> Sel = getSelection().getSelection(); std::list<std::string> cmds;
if (Sel.size() == 1) { std::ostringstream sources;
if (Sel[0].pObject->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { for(const Gui::SelectionObject &selObj :
Part::Feature *pcPartObject = static_cast<Part::Feature*>(Sel[0].pObject); getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId()))
std::string FeatName = getUniqueObjectName("PathShape"); {
openCommand("Create Path Compound"); const Part::Feature *pcObj = static_cast<const Part::Feature*>(selObj.getObject());
doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureShape','%s')",FeatName.c_str()); const std::vector<std::string> &subnames = selObj.getSubNames();
doCommand(Doc,"FreeCAD.activeDocument().%s.Shape = FreeCAD.activeDocument().%s.Shape.copy()",FeatName.c_str(),pcPartObject->getNameInDocument()); if(subnames.empty()) {
commitCommand(); sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ",";
updateActive(); continue;
} else { }
Base::Console().Error("Exactly one shape object must be selected\n"); for(const std::string &name : subnames) {
return; if(name.compare(0,4,"Face") && name.compare(0,4,"Edge")) {
Base::Console().Warning("Ignored shape %s %s\n",
pcObj->getNameInDocument(), name.c_str());
continue;
}
std::ostringstream subname;
subname << pcObj->getNameInDocument() << '_' << name;
std::string sub_fname = getUniqueObjectName(subname.str().c_str());
std::ostringstream cmd;
cmd << "FreeCAD.activeDocument().addObject('Part::Feature','" << sub_fname <<
"').Shape = PathCommands.findShape(FreeCAD.activeDocument()." <<
pcObj->getNameInDocument() << ".Shape,'" << name << "'";
if(!name.compare(0,4,"Edge"))
cmd << ",'Wires'";
cmd << ')';
cmds.push_back(cmd.str());
sources << "FreeCAD.activeDocument()." << sub_fname << ",";
} }
} else {
Base::Console().Error("Exactly one shape object must be selected\n");
return;
} }
std::string FeatName = getUniqueObjectName("PathShape");
openCommand("Create Path Shape");
doCommand(Doc,"import PathCommands");
for(const std::string &cmd : cmds)
doCommand(Doc,cmd.c_str());
doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureShape','%s')",FeatName.c_str());
doCommand(Doc,"FreeCAD.activeDocument().%s.Sources = [ %s ]",FeatName.c_str(),sources.str().c_str());
commitCommand();
updateActive();
} }
bool CmdPathShape::isActive(void) bool CmdPathShape::isActive(void)

View File

@ -47,6 +47,7 @@
<file>icons/Path-Toolpath.svg</file> <file>icons/Path-Toolpath.svg</file>
<file>icons/Path-ToolTable.svg</file> <file>icons/Path-ToolTable.svg</file>
<file>icons/Path-Area.svg</file> <file>icons/Path-Area.svg</file>
<file>icons/Path-Area-View.svg</file>
<file>icons/Path-Area-Workplane.svg</file> <file>icons/Path-Area-Workplane.svg</file>
<file>icons/preferences-path.svg</file> <file>icons/preferences-path.svg</file>
<file>panels/ContourEdit.ui</file> <file>panels/ContourEdit.ui</file>

View File

@ -0,0 +1,657 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="Path-Area-View.svg">
<defs
id="defs2818">
<linearGradient
inkscape:collect="always"
id="linearGradient4066">
<stop
style="stop-color:#4e9a06;stop-opacity:1"
offset="0"
id="stop4068" />
<stop
style="stop-color:#8ae234;stop-opacity:1"
offset="1"
id="stop4070" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4022">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop4024" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop4026" />
</linearGradient>
<linearGradient
id="linearGradient4513">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517" />
</linearGradient>
<linearGradient
id="linearGradient3681">
<stop
id="stop3697"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3687"
x1="37.89756"
y1="41.087898"
x2="4.0605712"
y2="40.168594"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3695"
x1="37.894287"
y1="40.484772"
x2="59.811455"
y2="43.558987"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
id="linearGradient3681-3">
<stop
id="stop3697-3"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685-4" />
</linearGradient>
<linearGradient
y2="43.558987"
x2="59.811455"
y1="40.484772"
x1="37.894287"
gradientTransform="translate(-37.00068,-20.487365)"
gradientUnits="userSpaceOnUse"
id="linearGradient3608"
xlink:href="#linearGradient3681-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-2" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-4" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538"
xlink:href="#linearGradient4513-2"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538-6"
xlink:href="#linearGradient4513-1"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-7" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-5" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3069"
xlink:href="#linearGradient4513-1-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-6" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3102"
xlink:href="#linearGradient4513-1-2"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4513-1"
id="radialGradient3132"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.39497909,0,0,1.1841158,64.952609,-7.0541574)"
cx="32.151962"
cy="27.950663"
fx="32.151962"
fy="27.950663"
r="23.634638" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4022"
id="linearGradient4028"
x1="36.538239"
y1="45.928013"
x2="36.804447"
y2="23.332739"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4066"
id="linearGradient4072"
x1="41.610756"
y1="59.853931"
x2="32.101425"
y2="10.99928"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4031"
id="linearGradient4055"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(71.494719,-3.1982556)"
x1="30.000002"
y1="14"
x2="36"
y2="54.227272" />
<linearGradient
id="linearGradient4031">
<stop
id="stop4033"
offset="0"
style="stop-color:#d3d7cf;stop-opacity:1" />
<stop
id="stop4035"
offset="1"
style="stop-color:#888a85;stop-opacity:1" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.8890873"
inkscape:cx="-7.4813451"
inkscape:cy="10.659623"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1375"
inkscape:window-height="876"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid3234"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title>Path-FaceProfile</dc:title>
<dc:date>2016-01-19</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="color:#000000;fill:url(#linearGradient4028);fill-opacity:1;fill-rule:nonzero;stroke:#0b1521;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 33,21 17,45 41,49 53,33 49,23 z"
id="rect3083"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="color:#000000;fill:none;stroke:#172a04;stroke-width:8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50,13 39,11 c -4,-1 -5.171573,-1.4142136 -8,0 -2,1 -3,2 -5,5 L 7,46 c -3,4 -1,7 3,8 l 28,4 c 5,1 9,-2 12,-6 l 4,-5"
id="rect3083-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
style="color:#000000;fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 33.965909,23.113636 20.349931,43.571429 40.140486,46.839294 50.710729,32.691101 47.57301,24.842851 z"
id="rect3083-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="color:#000000;fill:none;stroke:url(#linearGradient4072);stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50,13 39,11 c -4,-1 -5.171573,-1.414214 -8,0 -2,1 -3,2 -5,5 L 7,46 c -3,4 -1,7 3,8 l 28,4 c 5,1 9,-2 12,-6 l 4,-5"
id="rect3083-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
style="color:#000000;fill:none;stroke:#8ae234;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50.064283,12 38.517882,9.919647 c -4,-1.0000001 -5.171573,-1.4142141 -8,0 -2,1 -3,2 -5,5 l -19.0000003,30 c -2.9999999,4 -1,7 2.9999998,8 l 28.0000005,4 c 5,1 9,-2 12,-6 L 53,46"
id="rect3083-0-6-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
id="path3599"
d="m 26.202906,1.6813301 c -9.673905,0 -20.9601314,6.2397496 -25.79708379,18.7192689 C 5.2427746,29.76025 14.916679,36 26.202906,36 37.489143,36 47.163048,29.76025 52,20.400599 47.163048,7.9210797 35.876821,1.6813301 26.202906,1.6813301 Z m 0,31.1987859 c -9.673905,0 -17.7354897,-6.23975 -19.3478099,-12.479517 C 8.4674163,14.160848 16.529001,7.9210797 26.202906,7.9210797 c 9.673915,0 17.7355,6.2397683 19.34782,12.4795193 -1.61232,6.239767 -9.673905,12.479517 -19.34782,12.479517 z m 0,-21.839152 c -1.007692,0 -1.914621,0.194993 -2.821549,0.438734 1.662698,0.731222 2.821549,2.339912 2.821549,4.241091 0,2.583655 -2.166544,4.67981 -4.836953,4.67981 -1.965006,0 -3.627715,-1.121208 -4.383483,-2.729881 -0.251923,0.877467 -0.453469,1.754917 -0.453469,2.729881 0,5.167306 4.333107,9.359651 9.673905,9.359651 5.340808,0 9.673915,-4.192345 9.673915,-9.359651 0,-5.167292 -4.333107,-9.359635 -9.673915,-9.359635 z"
inkscape:connector-curvature="0" />
<path
style="opacity:0.84799972;fill:#ffffff;stroke:#ed1521;stroke-width:0.18025407;stroke-opacity:1"
d="M 24.513492,32.743549 C 20.158618,32.279756 16.320657,30.874437 13.101873,28.565038 10.565128,26.744986 8.0555748,23.645238 7.240207,21.324834 6.9310922,20.445146 6.9319596,20.410993 7.289147,19.39655 c 1.39318,-3.956736 5.894877,-8.025327 10.97742,-9.9212893 2.388118,-0.8908503 4.189049,-1.243432 6.954324,-1.3615029 3.700832,-0.1580165 6.748099,0.3948804 10.068295,1.8267941 4.509958,1.9450261 8.541022,5.8276231 9.860413,9.4972421 l 0.343034,0.954079 -0.406823,1.087884 c -1.903008,5.08883 -8.045412,9.656313 -14.648854,10.892867 -1.533211,0.287106 -4.793511,0.491266 -5.923465,0.370925 z m 3.944714,-3.168696 c 3.268532,-0.752696 6.040924,-3.250309 7.009108,-6.314414 0.74188,-2.347903 0.609807,-4.448295 -0.422862,-6.724951 -1.727573,-3.808655 -5.799065,-6.000195 -10.098702,-5.435771 -0.713226,0.09362 -1.36028,0.238742 -1.437897,0.322477 -0.07761,0.08374 0.205724,0.373163 0.629646,0.643172 2.40184,1.529797 2.621108,4.930323 0.448249,6.951712 -2.087088,1.941597 -5.735326,1.528636 -7.161292,-0.810623 -0.190194,-0.312008 -0.397714,-0.56753 -0.461157,-0.567826 -0.184558,-8.66e-4 -0.490195,1.808374 -0.485585,2.874447 0.0097,2.22833 0.762415,4.123494 2.376571,5.983168 2.345137,2.701835 6.063739,3.893862 9.603921,3.078609 z"
id="path3630"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -115,11 +115,83 @@ bool ViewProviderArea::onDelete(const std::vector<std::string> &)
// Python object ----------------------------------------------------------------------- // Python object -----------------------------------------------------------------------
PROPERTY_SOURCE(PathGui::ViewProviderAreaView, PartGui::ViewProviderPlaneParametric)
ViewProviderAreaView::ViewProviderAreaView()
{
sPixmap = "Path-Area-View.svg";
}
ViewProviderAreaView::~ViewProviderAreaView()
{
}
std::vector<App::DocumentObject*> ViewProviderAreaView::claimChildren(void) const
{
std::vector<App::DocumentObject*> ret;
Path::FeatureAreaView* feature = static_cast<Path::FeatureAreaView*>(getObject());
if(feature->Source.getValue())
ret.push_back(feature->Source.getValue());
return ret;
}
bool ViewProviderAreaView::canDragObjects() const
{
return true;
}
bool ViewProviderAreaView::canDragObject(App::DocumentObject* obj) const
{
return obj && obj->getTypeId().isDerivedFrom(Path::FeatureArea::getClassTypeId());
}
void ViewProviderAreaView::dragObject(App::DocumentObject* )
{
Path::FeatureAreaView* feature = static_cast<Path::FeatureAreaView*>(getObject());
feature->Source.setValue(NULL);
}
bool ViewProviderAreaView::canDropObjects() const
{
return true;
}
bool ViewProviderAreaView::canDropObject(App::DocumentObject* obj) const
{
return canDragObject(obj);
}
void ViewProviderAreaView::dropObject(App::DocumentObject* obj)
{
Path::FeatureAreaView* feature = static_cast<Path::FeatureAreaView*>(getObject());
feature->Source.setValue(obj);
}
void ViewProviderAreaView::updateData(const App::Property* prop)
{
PartGui::ViewProviderPlaneParametric::updateData(prop);
if (prop->getTypeId() == App::PropertyLink::getClassTypeId())
Gui::Application::Instance->hideViewProvider(
static_cast<const App::PropertyLink*>(prop)->getValue());
}
bool ViewProviderAreaView::onDelete(const std::vector<std::string> &)
{
Path::FeatureAreaView* feature = static_cast<Path::FeatureAreaView*>(getObject());
Gui::Application::Instance->showViewProvider(feature->Source.getValue());
return true;
}
// Python object -----------------------------------------------------------------------
namespace Gui { namespace Gui {
/// @cond DOXERR /// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderAreaPython, PathGui::ViewProviderArea) PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderAreaPython, PathGui::ViewProviderArea)
PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderAreaViewPython, PathGui::ViewProviderAreaView)
/// @endcond /// @endcond
// explicit template instantiation // explicit template instantiation
template class PathGuiExport ViewProviderPythonFeatureT<PathGui::ViewProviderArea>; template class PathGuiExport ViewProviderPythonFeatureT<PathGui::ViewProviderArea>;
template class PathGuiExport ViewProviderPythonFeatureT<PathGui::ViewProviderAreaView>;
} }

View File

@ -54,6 +54,29 @@ public:
typedef Gui::ViewProviderPythonFeatureT<ViewProviderArea> ViewProviderAreaPython; typedef Gui::ViewProviderPythonFeatureT<ViewProviderArea> ViewProviderAreaPython;
class PathGuiExport ViewProviderAreaView : public PartGui::ViewProviderPlaneParametric
{
PROPERTY_HEADER(PathGui::ViewProviderAreaView);
public:
ViewProviderAreaView();
virtual ~ViewProviderAreaView();
virtual std::vector<App::DocumentObject*> claimChildren(void) const;
virtual void updateData(const App::Property*);
virtual bool onDelete(const std::vector<std::string> &);
/// drag and drop
virtual bool canDragObjects() const;
virtual bool canDragObject(App::DocumentObject*) const;
virtual void dragObject(App::DocumentObject*);
virtual bool canDropObjects() const;
virtual bool canDropObject(App::DocumentObject*) const;
virtual void dropObject(App::DocumentObject*);
};
typedef Gui::ViewProviderPythonFeatureT<ViewProviderAreaView> ViewProviderAreaViewPython;
} //namespace PathGui } //namespace PathGui

View File

@ -26,8 +26,11 @@
#ifndef _PreComp_ #ifndef _PreComp_
#endif #endif
#include "ViewProviderPathShape.h"
#include <Gui/BitmapFactory.h> #include <Gui/BitmapFactory.h>
#include <Gui/Application.h>
#include <Mod/Part/App/PartFeature.h>
#include <Mod/Path/App/FeaturePathShape.h>
#include "ViewProviderPathShape.h"
using namespace Gui; using namespace Gui;
using namespace PathGui; using namespace PathGui;
@ -38,3 +41,74 @@ QIcon ViewProviderPathShape::getIcon() const
{ {
return Gui::BitmapFactory().pixmap("Path-Shape"); return Gui::BitmapFactory().pixmap("Path-Shape");
} }
std::vector<App::DocumentObject*> ViewProviderPathShape::claimChildren(void) const
{
return std::vector<App::DocumentObject*>(
static_cast<Path::FeatureShape*>(getObject())->Sources.getValues());
}
bool ViewProviderPathShape::canDragObjects() const
{
return true;
}
bool ViewProviderPathShape::canDragObject(App::DocumentObject* obj) const
{
return obj && obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId());
}
void ViewProviderPathShape::dragObject(App::DocumentObject* obj)
{
Path::FeatureShape *feature = static_cast<Path::FeatureShape*>(getObject());
std::vector<App::DocumentObject*> sources = feature->Sources.getValues();
for (std::vector<App::DocumentObject*>::iterator it = sources.begin(); it != sources.end(); ++it) {
if (*it == obj) {
sources.erase(it);
feature->Sources.setValues(sources);
break;
}
}
}
bool ViewProviderPathShape::canDropObjects() const
{
return true;
}
bool ViewProviderPathShape::canDropObject(App::DocumentObject* obj) const
{
return canDragObject(obj);
}
void ViewProviderPathShape::dropObject(App::DocumentObject* obj)
{
Path::FeatureShape *feature = static_cast<Path::FeatureShape*>(getObject());
std::vector<App::DocumentObject*> sources = feature->Sources.getValues();
sources.push_back(obj);
feature->Sources.setValues(sources);
}
void ViewProviderPathShape::updateData(const App::Property* prop)
{
PathGui::ViewProviderPath::updateData(prop);
if (prop->getTypeId() == App::PropertyLinkList::getClassTypeId()) {
std::vector<App::DocumentObject*> pShapes = static_cast<const App::PropertyLinkList*>(prop)->getValues();
for (std::vector<App::DocumentObject*>::iterator it = pShapes.begin(); it != pShapes.end(); ++it) {
if (*it)
Gui::Application::Instance->hideViewProvider(*it);
}
}
}
bool ViewProviderPathShape::onDelete(const std::vector<std::string> &)
{
// get the input shapes
Path::FeatureShape *feature = static_cast<Path::FeatureShape*>(getObject());
std::vector<App::DocumentObject*> pShapes =feature->Sources.getValues();
for (std::vector<App::DocumentObject*>::iterator it = pShapes.begin(); it != pShapes.end(); ++it) {
if (*it)
Gui::Application::Instance->showViewProvider(*it);
}
return true;
}

View File

@ -34,6 +34,19 @@ class PathGuiExport ViewProviderPathShape: public ViewProviderPath
PROPERTY_HEADER(PathGui::ViewProviderPathShape); PROPERTY_HEADER(PathGui::ViewProviderPathShape);
public: public:
/// grouping handling
virtual std::vector<App::DocumentObject*> claimChildren(void) const;
virtual void updateData(const App::Property*);
virtual bool onDelete(const std::vector<std::string> &);
/// drag and drop
virtual bool canDragObjects() const;
virtual bool canDragObject(App::DocumentObject*) const;
virtual void dragObject(App::DocumentObject*);
virtual bool canDropObjects() const;
virtual bool canDropObject(App::DocumentObject*) const;
virtual void dropObject(App::DocumentObject*);
QIcon getIcon(void) const; QIcon getIcon(void) const;
}; };

View File

@ -81,12 +81,12 @@ class PathWorkbench (Workbench):
# build commands list # build commands list
projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"] projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"]
toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"] toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"]
prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_ToolLenOffset", "Path_Comment", "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_FromShape"] prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_ToolLenOffset", "Path_Comment", "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_Shape"]
twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace", "Path_Helix"] twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace", "Path_Helix"]
threedopcmdlist = ["Path_Surfacing"] threedopcmdlist = ["Path_Surfacing"]
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ] modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"] dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"]
extracmdlist = ["Path_SelectLoop", "Path_Area", "Path_Area_Workplane"] extracmdlist = ["Path_SelectLoop", "Path_Shape", "Path_Area", "Path_Area_Workplane"]
#modcmdmore = ["Path_Hop",] #modcmdmore = ["Path_Hop",]
#remotecmdlist = ["Path_Remote"] #remotecmdlist = ["Path_Remote"]

View File

@ -78,3 +78,28 @@ class _CommandSelectLoop:
if FreeCAD.GuiUp: if FreeCAD.GuiUp:
FreeCADGui.addCommand('Path_SelectLoop',_CommandSelectLoop()) FreeCADGui.addCommand('Path_SelectLoop',_CommandSelectLoop())
def findShape(shape,subname=None,subtype=None):
'''To find a higher oder shape containing the subshape with subname.
E.g. to find the wire containing 'Edge1' in shape,
findShape(shape,'Edge1','Wires')
'''
if not subname:
return shape
ret = shape.getElement(subname)
if not subtype or not ret or ret.isNull():
return ret;
if subname.startswith('Face'):
tp = 'Faces'
elif subname.startswith('Edge'):
tp = 'Edges'
elif subname.startswith('Vertex'):
tp = 'Vertex'
else:
return ret
for obj in getattr(shape,subtype):
for sobj in getattr(obj,tp):
if sobj.isEqual(ret):
return obj
return ret

View File

@ -73,6 +73,58 @@ Point CArea::NearestPoint(const Point& p)const
return best_point; return best_point;
} }
void CArea::ChangeStartToNearest(const Point *point, double min_dist) {
for(std::list<CCurve>::const_iterator It=m_curves.begin(),ItNext=It;
It != m_curves.end(); It=ItNext)
{
++ItNext;
if(It->m_vertices.size()<=1)
m_curves.erase(It);
}
if(m_curves.empty()) return;
std::list<CCurve> curves;
Point p;
if(point) p =*point;
if(min_dist < Point::tolerance)
min_dist = Point::tolerance;
while(m_curves.size()) {
std::list<CCurve>::iterator It=m_curves.begin();
std::list<CCurve>::iterator ItBest=It++;
Point best_point = ItBest->NearestPoint(p);
double best_dist = p.dist(best_point);
for(; It != m_curves.end(); ++It)
{
const CCurve& curve = *It;
Point near_point = curve.NearestPoint(p);
double dist = near_point.dist(p);
if(dist < best_dist)
{
best_dist = dist;
best_point = near_point;
ItBest = It;
}
}
if(ItBest->IsClosed()) {
ItBest->ChangeStart(best_point);
}else if(ItBest->m_vertices.back().m_p.dist(best_point)<=min_dist) {
ItBest->Reverse();
}else if(ItBest->m_vertices.front().m_p.dist(best_point)>min_dist) {
ItBest->Break(best_point);
m_curves.push_back(*ItBest);
m_curves.back().ChangeEnd(best_point);
ItBest->ChangeStart(best_point);
}
curves.splice(curves.end(),m_curves,ItBest);
p = curves.back().m_vertices.back().m_p;
}
m_curves.splice(m_curves.end(),curves);
}
void CArea::GetBox(CBox2D &box) void CArea::GetBox(CBox2D &box)
{ {
for(std::list<CCurve>::iterator It = m_curves.begin(); It != m_curves.end(); It++) for(std::list<CCurve>::iterator It = m_curves.begin(); It != m_curves.end(); It++)

View File

@ -85,6 +85,8 @@ public:
void CurveIntersections(const CCurve& curve, std::list<Point> &pts)const; void CurveIntersections(const CCurve& curve, std::list<Point> &pts)const;
void InsideCurves(const CCurve& curve, std::list<CCurve> &curves_inside)const; void InsideCurves(const CCurve& curve, std::list<CCurve> &curves_inside)const;
void ChangeStartToNearest(const Point *point=NULL, double min_dist=1.0);
//Avoid outside direct accessing static member variable because of Windows DLL issue //Avoid outside direct accessing static member variable because of Windows DLL issue
#define CAREA_PARAM_DECLARE(_type,_name) \ #define CAREA_PARAM_DECLARE(_type,_name) \
static _type get_##_name();\ static _type get_##_name();\

View File

@ -468,16 +468,23 @@ void CArea::Offset(double inwards_value)
void CArea::PopulateClipper(Clipper &c, PolyType type) const void CArea::PopulateClipper(Clipper &c, PolyType type) const
{ {
int skipped = 0;
for (std::list<CCurve>::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) for (std::list<CCurve>::const_iterator It = m_curves.begin(); It != m_curves.end(); It++)
{ {
const CCurve &curve = *It; const CCurve &curve = *It;
bool closed = curve.IsClosed(); bool closed = curve.IsClosed();
if(type == ptClip && !closed) if(!closed) {
continue; if(type == ptClip){
++skipped;
continue;
}
}
TPolygon p; TPolygon p;
MakePoly(curve, p, false); MakePoly(curve, p, false);
c.AddPath(p, type, closed); c.AddPath(p, type, closed);
} }
if(skipped)
std::cout << "libarea: warning skipped " << skipped << " open wires" << std::endl;
} }
void CArea::Clip(ClipType op, const CArea *a, void CArea::Clip(ClipType op, const CArea *a,