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:
@@ -88,4 +88,6 @@ PyMODINIT_FUNC initPath()
|
||||
Path::Area ::init();
|
||||
Path::FeatureArea ::init();
|
||||
Path::FeatureAreaPython ::init();
|
||||
Path::FeatureAreaView ::init();
|
||||
Path::FeatureAreaViewPython ::init();
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include <App/Application.h>
|
||||
|
||||
#include <Mod/Part/App/OCCError.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include <Mod/Part/App/TopoShapePy.h>
|
||||
#include <TopoDS.hxx>
|
||||
@@ -57,7 +58,45 @@
|
||||
#include "Path.h"
|
||||
#include "FeaturePath.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 {
|
||||
class Module : public Py::ExtensionModule<Module>
|
||||
@@ -79,6 +118,22 @@ public:
|
||||
add_varargs_method("fromShape",&Module::fromShape,
|
||||
"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
|
||||
}
|
||||
|
||||
@@ -261,7 +316,119 @@ private:
|
||||
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,¶ms,&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()
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <BRepLib.hxx>
|
||||
#include <BRep_Builder.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) {
|
||||
if(s1.IsEqual(s2)) return true;
|
||||
TopoDS_Builder builder;
|
||||
TopoDS_Compound 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,
|
||||
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;
|
||||
int skipped = 0;
|
||||
@@ -193,7 +196,7 @@ int Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf,
|
||||
else if(to_edges) {
|
||||
for (TopExp_Explorer it(wire, TopAbs_EDGE); it.More(); it.Next())
|
||||
add(_areaOpen,BRepBuilderAPI_MakeWire(
|
||||
TopoDS::Edge(it.Current())).Wire(),trsf,deflection);
|
||||
TopoDS::Edge(it.Current())).Wire(),trsf,deflection,true);
|
||||
}else
|
||||
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.m_curves.splice(area.m_curves.end(),_area.m_curves);
|
||||
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,
|
||||
const gp_Trsf *trsf, double deflection)
|
||||
const gp_Trsf *trsf, double deflection, bool to_edges)
|
||||
{
|
||||
CCurve ccurve;
|
||||
BRepTools_WireExplorer xp(trsf?TopoDS::Wire(
|
||||
@@ -239,26 +242,32 @@ void Area::add(CArea &area, const TopoDS_Wire& wire,
|
||||
switch (curve.GetType()) {
|
||||
case GeomAbs_Line: {
|
||||
ccurve.append(CVertex(Point(p.X(),p.Y())));
|
||||
if(to_edges) {
|
||||
area.append(ccurve);
|
||||
ccurve.m_vertices.pop_front();
|
||||
}
|
||||
break;
|
||||
} case GeomAbs_Circle:{
|
||||
double first = curve.FirstParameter();
|
||||
double last = curve.LastParameter();
|
||||
gp_Circ circle = curve.Circle();
|
||||
gp_Ax1 axis = circle.Axis();
|
||||
int dir = axis.Direction().Z()<0?-1:1;
|
||||
if(reversed) dir = -dir;
|
||||
gp_Pnt loc = axis.Location();
|
||||
if(fabs(first-last)>M_PI) {
|
||||
// Split arc(circle) larger than half circle. This is
|
||||
// translated from PathUtil code. Not sure why it is
|
||||
// needed.
|
||||
gp_Pnt mid = curve.Value((last-first)*0.5+first);
|
||||
ccurve.append(CVertex(dir,Point(mid.X(),mid.Y()),
|
||||
if(!to_edges) {
|
||||
double first = curve.FirstParameter();
|
||||
double last = curve.LastParameter();
|
||||
gp_Circ circle = curve.Circle();
|
||||
gp_Ax1 axis = circle.Axis();
|
||||
int dir = axis.Direction().Z()<0?-1:1;
|
||||
if(reversed) dir = -dir;
|
||||
gp_Pnt loc = axis.Location();
|
||||
if(fabs(first-last)>M_PI) {
|
||||
// Split arc(circle) larger than half circle. Because gcode
|
||||
// can't handle full circle?
|
||||
gp_Pnt mid = curve.Value((last-first)*0.5+first);
|
||||
ccurve.append(CVertex(dir,Point(mid.X(),mid.Y()),
|
||||
Point(loc.X(),loc.Y())));
|
||||
}
|
||||
ccurve.append(CVertex(dir,Point(p.X(),p.Y()),
|
||||
Point(loc.X(),loc.Y())));
|
||||
break;
|
||||
}
|
||||
ccurve.append(CVertex(dir,Point(p.X(),p.Y()),
|
||||
Point(loc.X(),loc.Y())));
|
||||
break;
|
||||
//fall through
|
||||
} default: {
|
||||
// Discretize all other type of curves
|
||||
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++) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
ccurve.append(CVertex(Point(pt.X(),pt.Y())));
|
||||
if(to_edges) {
|
||||
area.append(ccurve);
|
||||
ccurve.m_vertices.pop_front();
|
||||
}
|
||||
}
|
||||
}else
|
||||
Standard_Failure::Raise("Curve discretization failed");
|
||||
}}
|
||||
}
|
||||
if(BRep_Tool::IsClosed(wire) && !ccurve.IsClosed()) {
|
||||
Base::Console().Warning("ccurve not closed\n");
|
||||
ccurve.append(ccurve.m_vertices.front());
|
||||
if(!to_edges) {
|
||||
if(BRep_Tool::IsClosed(wire) && !ccurve.IsClosed()) {
|
||||
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) {
|
||||
#define AREA_SRC_OP(_param) op
|
||||
PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE);
|
||||
Q_UNUSED(Operation);
|
||||
#define AREA_CONVERT_OP \
|
||||
ClipperLib::ClipType 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;
|
||||
for(TopExp_Explorer it(shape, TopAbs_SOLID);it.More();) {
|
||||
haveSolid = true;
|
||||
@@ -313,7 +350,7 @@ void Area::add(const TopoDS_Shape &shape,short op) {
|
||||
myHaveSolid = haveSolid;
|
||||
|
||||
clean();
|
||||
if(myShapes.empty())
|
||||
if(op!=OperationCompound && myShapes.empty())
|
||||
op = OperationUnion;
|
||||
myShapes.push_back(Shape(op,shape));
|
||||
}
|
||||
@@ -330,7 +367,7 @@ void Area::setParams(const AreaParams ¶ms) {
|
||||
}
|
||||
|
||||
void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
|
||||
if(!myHaveFace) {
|
||||
if(myParams.Fill==FillAuto && !myHaveFace) {
|
||||
TopExp_Explorer it(shape, TopAbs_FACE);
|
||||
myHaveFace = it.More();
|
||||
}
|
||||
@@ -342,7 +379,7 @@ void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
|
||||
CArea areaOpen;
|
||||
mySkippedShapes += add(area,shape,&myTrsf,myParams.Deflection,plane,
|
||||
myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen,
|
||||
myParams.OpenMode==OpenModeEdges,myParams.Reorder);
|
||||
myParams.OpenMode==OpenModeEdges,myParams.Reorient);
|
||||
if(areaOpen.m_curves.size()) {
|
||||
if(&area == myArea.get() || myParams.OpenMode == OpenModeNone)
|
||||
myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves);
|
||||
@@ -359,7 +396,7 @@ void Area::build() {
|
||||
if(myArea || mySections.size()) return;
|
||||
|
||||
if(myShapes.empty())
|
||||
throw Base::ValueError("Null shape");
|
||||
throw Base::ValueError("no shape added");
|
||||
|
||||
#define AREA_SRC(_param) myParams.PARAM_FNAME(_param)
|
||||
PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL);
|
||||
@@ -368,24 +405,41 @@ void Area::build() {
|
||||
myShapePlane.Nullify();
|
||||
for(const Shape &s : myShapes) {
|
||||
bool haveShape = false;
|
||||
bool done = false;
|
||||
TopoDS_Shape shapePlane;
|
||||
gp_Trsf trsf;
|
||||
gp_Ax3 pos;
|
||||
#define AREA_CHECK_PLANE(_type) \
|
||||
shapePlane.Nullify();\
|
||||
for(TopExp_Explorer it(s.shape, TopAbs_##_type); it.More(); it.Next()) {\
|
||||
haveShape = true;\
|
||||
BRepLib_FindSurface planeFinder(it.Current(),-1,Standard_True);\
|
||||
if (!planeFinder.Found())\
|
||||
continue;\
|
||||
myShapePlane = it.Current();\
|
||||
myTrsf.SetTransformation(GeomAdaptor_Surface(\
|
||||
planeFinder.Surface()).Plane().Position());\
|
||||
break;\
|
||||
shapePlane = it.Current();\
|
||||
pos = GeomAdaptor_Surface(planeFinder.Surface()).Plane().Position();\
|
||||
trsf.SetTransformation(pos);\
|
||||
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;
|
||||
|
||||
//Try to find a plane by iterating through shapes, prefer plane paralell with XY0
|
||||
AREA_CHECK_PLANE(FACE)
|
||||
AREA_CHECK_PLANE(WIRE)
|
||||
AREA_CHECK_PLANE(EDGE)
|
||||
}
|
||||
|
||||
if(myShapePlane.IsNull())
|
||||
throw Base::ValueError("shapes are not planar");
|
||||
}
|
||||
@@ -412,19 +466,13 @@ void Area::build() {
|
||||
|
||||
int error = 0;
|
||||
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);
|
||||
if((count-1)*myParams.Stepdown < zMax-zMin)
|
||||
++count;
|
||||
}
|
||||
for(int i=0;i<count;++i,zMax-=myParams.Stepdown) {
|
||||
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));
|
||||
BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax);
|
||||
const TopoDS_Shape &face = mkFace.Face();
|
||||
@@ -501,14 +549,27 @@ void Area::build() {
|
||||
mySkippedShapes = 0;
|
||||
short op = OperationUnion;
|
||||
bool pending = false;
|
||||
bool explode = myParams.Explode;
|
||||
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)
|
||||
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
|
||||
pending = false;
|
||||
PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE);
|
||||
myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill);
|
||||
areaClip.m_curves.clear();
|
||||
if(areaClip.m_curves.size()) {
|
||||
if(op == OperationCompound)
|
||||
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;
|
||||
}
|
||||
addToBuild(op==OperationUnion?*myArea:areaClip,s.shape);
|
||||
@@ -521,17 +582,106 @@ void Area::build() {
|
||||
if(pending){
|
||||
if(myParams.OpenMode!=OpenModeNone)
|
||||
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);
|
||||
myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill);
|
||||
if(op == OperationCompound)
|
||||
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);
|
||||
|
||||
//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(...) {
|
||||
clean();
|
||||
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) {
|
||||
gp_Trsf trsf(myTrsf.Inverted());
|
||||
bool bFill;
|
||||
@@ -559,14 +709,17 @@ TopoDS_Shape Area::toShape(CArea &area, short fill) {
|
||||
#define AREA_SECTION(_op,_index,...) do {\
|
||||
if(mySections.size()) {\
|
||||
if(_index>=(int)mySections.size())\
|
||||
throw Base::ValueError("index out of bound");\
|
||||
return TopoDS_Shape();\
|
||||
TopLoc_Location loc(myTrsf.Inverted());\
|
||||
if(_index<0) {\
|
||||
BRep_Builder builder;\
|
||||
TopoDS_Compound compound;\
|
||||
builder.MakeCompound(compound);\
|
||||
for(shared_ptr<Area> area : mySections)\
|
||||
builder.Add(compound,area->_op(-1, ## __VA_ARGS__).Moved(loc));\
|
||||
for(shared_ptr<Area> area : mySections){\
|
||||
const TopoDS_Shape &s = area->_op(-1, ## __VA_ARGS__);\
|
||||
if(s.IsNull()) continue;\
|
||||
builder.Add(compound,s.Moved(loc));\
|
||||
}\
|
||||
return compound;\
|
||||
}\
|
||||
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) {
|
||||
if(myParams.Thicken)
|
||||
area->Thicken(myParams.ToolRadius);
|
||||
const TopoDS_Shape &shape = toShape(*area,fill);
|
||||
builder.Add(compound,toShape(*area,fill));
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
build();
|
||||
CAreaConfig conf(myParams);
|
||||
CAreaPocketParams params(
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound compound;
|
||||
builder.MakeCompound(compound);
|
||||
|
||||
for(const CCurve &c : area.m_curves) {
|
||||
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(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());
|
||||
const TopoDS_Wire &wire = toShape(c,trsf);
|
||||
if(!wire.IsNull())
|
||||
builder.Add(compound,wire);
|
||||
}
|
||||
|
||||
if(fill) {
|
||||
if(!compound.IsNull() && fill) {
|
||||
try{
|
||||
Part::FaceMakerBullseye mkFace;
|
||||
if(trsf)
|
||||
@@ -888,3 +1051,186 @@ TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf) {
|
||||
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 ¢er, 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");
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,19 @@
|
||||
#ifndef PATH_AREA_H
|
||||
#define PATH_AREA_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <TopoDS.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
#include <gp_GTrsf.hxx>
|
||||
|
||||
#include "Path.h"
|
||||
#include "AreaParams.h"
|
||||
|
||||
class CArea;
|
||||
class CCurve;
|
||||
|
||||
namespace Path
|
||||
{
|
||||
@@ -152,8 +157,12 @@ public:
|
||||
/** Set a working plane
|
||||
*
|
||||
* 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
|
||||
* the child shapes are all colinear edges
|
||||
* individual children faces, wires or edges. By right, we should create a
|
||||
* 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
|
||||
*/
|
||||
@@ -177,13 +186,13 @@ public:
|
||||
* If more than one offset is requested, a compound shape is return
|
||||
* 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
|
||||
*
|
||||
* 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 */
|
||||
@@ -204,8 +213,11 @@ public:
|
||||
*/
|
||||
void clean(bool deleteShapes=false);
|
||||
|
||||
/** Get the combined shape */
|
||||
TopoDS_Shape getShape(int index);
|
||||
/** Get the combined shape
|
||||
* \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 */
|
||||
std::size_t getSectionCount() {
|
||||
@@ -219,10 +231,30 @@ public:
|
||||
* \arg \c wire: input wire object
|
||||
* \arg \c trsf: optional transform matrix to transform the wire shape into
|
||||
* 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,
|
||||
const gp_Trsf *trsf=NULL, double deflection=0.01);
|
||||
static void add(CArea &area, const TopoDS_Wire &wire, const gp_Trsf *trsf=NULL,
|
||||
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
|
||||
*
|
||||
@@ -236,7 +268,7 @@ public:
|
||||
* \arg \c areaOpen: for collecting open curves. If not supplied, open
|
||||
* curves are added to \c area
|
||||
* \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
|
||||
* if \c plane is supplied
|
||||
@@ -244,7 +276,7 @@ public:
|
||||
static int add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf=NULL,
|
||||
double deflection=0.01,const TopoDS_Shape *plane = NULL,
|
||||
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
|
||||
*
|
||||
@@ -256,7 +288,48 @@ public:
|
||||
static TopoDS_Shape toShape(const CArea &area, bool fill,
|
||||
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);
|
||||
|
||||
/** 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
|
||||
|
||||
@@ -44,20 +44,32 @@
|
||||
((enum2,clip_fill,ClipFill,0,\
|
||||
"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 */
|
||||
#define AREA_PARAMS_BASE \
|
||||
((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)))\
|
||||
((enum,coplanar,Coplanar,2,"Specifies the way to check coplanar.\n"\
|
||||
"'Force' will discard non coplaner shapes, 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."))\
|
||||
((enum,open_mode,OpenMode,0,"Specify how to handle open wires.\n"\
|
||||
"'None' means combin without openeration.\n"\
|
||||
"'Edges' means separate to edges before Union.\n"\
|
||||
"ClipperLib seems to have an urge to close open wires.",(None)(Union)(Edges)))\
|
||||
AREA_PARAMS_CLIPPER_FILL \
|
||||
((double,deflection,Deflection,0.01,"Deflection for non circular curve discretization"))
|
||||
((enum,coplanar,Coplanar,2,\
|
||||
"Specifies the way to check coplanar. 'Force' will discard non coplaner shapes,\n"\
|
||||
"but 'Check' only gives warning.",(None)(Check)(Force)))\
|
||||
((bool,reorient,Reorient,true,\
|
||||
"Re-orient closed wires in wire only shapes so that inner wires become holes."))\
|
||||
((bool,explode,Explode,false,\
|
||||
"If true, Area will explode the first shape into disconnected open edges, \n"\
|
||||
"with all curves discretized, so that later operations like 'Difference' \n"\
|
||||
"behave like wire cutting. Without exploding, 'Difference' in ClipperLib\n"\
|
||||
"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 */
|
||||
#define AREA_PARAMS_CAREA \
|
||||
@@ -72,7 +84,8 @@
|
||||
((short,min_arc_points,MinArcPoints,4,"Minimum segments for arc discretization"))\
|
||||
((short,max_arc_points,MaxArcPoints,100,"Maximum segments for arc discretization"))\
|
||||
((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
|
||||
*
|
||||
@@ -91,8 +104,10 @@
|
||||
|
||||
/** Operation code */
|
||||
#define AREA_PARAMS_OPCODE \
|
||||
((enum2,op,Operation,0,"Boolean operation",\
|
||||
(Union)(Difference)(Intersection)(Xor),(ClipperLib::ClipType,ClipperLib::ct)))
|
||||
((enum,op,Operation,0,\
|
||||
"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 */
|
||||
#define AREA_PARAMS_OFFSET \
|
||||
@@ -124,6 +139,36 @@
|
||||
((double,round_precision,RoundPreceision,0.0,\
|
||||
"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*/
|
||||
#define AREA_PARAMS_AREA \
|
||||
AREA_PARAMS_BASE \
|
||||
|
||||
@@ -58,6 +58,11 @@ any operation</UserDocu>
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="sortWires" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu></UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getParams">
|
||||
<Documentation>
|
||||
<UserDocu>Get current algorithm parameters as a dictionary.</UserDocu>
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include <Mod/Part/App/OCCError.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)
|
||||
#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"
|
||||
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 {
|
||||
@@ -141,12 +151,42 @@ PyObject* AreaPy::getShape(PyObject *args, PyObject *keywds)
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds,"|hO",kwlist,&pcObj))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
if(PyObject_IsTrue(pcObj))
|
||||
getAreaPtr()->clean();
|
||||
return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape(index)));
|
||||
if(PyObject_IsTrue(pcObj))
|
||||
getAreaPtr()->clean();
|
||||
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)
|
||||
@@ -205,13 +245,10 @@ PyObject* AreaPy::makeOffset(PyObject *args, PyObject *keywds)
|
||||
&index,PARAM_REF(PARAM_FARG,AREA_PARAMS_OFFSET)))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
//Expand the variable as function call arguments
|
||||
TopoDS_Shape resultShape = getAreaPtr()->makeOffset(index,
|
||||
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET));
|
||||
return Py::new_reference_to(Part::shape2pyshape(resultShape));
|
||||
}
|
||||
PY_CATCH_OCC;
|
||||
//Expand the variable as function call arguments
|
||||
TopoDS_Shape resultShape = getAreaPtr()->makeOffset(index,
|
||||
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET));
|
||||
return Py::new_reference_to(Part::shape2pyshape(resultShape));
|
||||
}
|
||||
|
||||
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)))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
TopoDS_Shape resultShape = getAreaPtr()->makePocket(index,
|
||||
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET));
|
||||
return Py::new_reference_to(Part::shape2pyshape(resultShape));
|
||||
}
|
||||
PY_CATCH_OCC;
|
||||
TopoDS_Shape resultShape = getAreaPtr()->makePocket(index,
|
||||
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET));
|
||||
return Py::new_reference_to(Part::shape2pyshape(resultShape));
|
||||
}
|
||||
|
||||
|
||||
PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL};
|
||||
|
||||
@@ -41,6 +41,7 @@ PROPERTY_SOURCE(Path::FeatureArea, Part::Feature)
|
||||
PARAM_ENUM_STRING_DECLARE(static const char *Enums,AREA_PARAMS_ALL)
|
||||
|
||||
FeatureArea::FeatureArea()
|
||||
:myBuild(false)
|
||||
{
|
||||
ADD_PROPERTY(Sources,(0));
|
||||
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)
|
||||
{
|
||||
std::vector<App::DocumentObject*> links = Sources.getValues();
|
||||
@@ -97,6 +106,17 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void)
|
||||
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
|
||||
{
|
||||
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 {
|
||||
/// @cond DOXERR
|
||||
PROPERTY_SOURCE_TEMPLATE(Path::FeatureAreaPython, Path::FeatureArea)
|
||||
PROPERTY_SOURCE_TEMPLATE(Path::FeatureAreaViewPython, Path::FeatureAreaView)
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -40,12 +40,13 @@ class PathExport FeatureArea : public Part::Feature
|
||||
PROPERTY_HEADER(Path::FeatureArea);
|
||||
|
||||
public:
|
||||
Area myArea;
|
||||
|
||||
/// Constructor
|
||||
FeatureArea(void);
|
||||
virtual ~FeatureArea();
|
||||
|
||||
Area &getArea();
|
||||
std::list<TopoDS_Shape> getShapes();
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
virtual const char* getViewProviderName(void) const {
|
||||
return "PathGui::ViewProviderArea";
|
||||
@@ -58,10 +59,36 @@ public:
|
||||
Part::PropertyPartShape WorkPlane;
|
||||
|
||||
PARAM_PROP_DECLARE(AREA_PARAMS_ALL)
|
||||
|
||||
private:
|
||||
bool myBuild;
|
||||
Area myArea;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ PyObject* FeatureAreaPy::getArea(PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return new AreaPy(new Area(getFeatureAreaPtr()->myArea));
|
||||
return new AreaPy(new Area(getFeatureAreaPtr()->getArea()));
|
||||
}
|
||||
|
||||
PyObject* FeatureAreaPy::setParams(PyObject *args, PyObject *keywds)
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Zheng, Lei <realthunder.dev@gmail.com>
|
||||
*/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
@@ -32,20 +34,17 @@
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <TopoDS_Vertex.hxx>
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <gp_Lin.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepAdaptor_CompCurve.hxx>
|
||||
#include <BRepAdaptor_HCompCurve.hxx>
|
||||
#include <Approx_Curve3d.hxx>
|
||||
#include <BRepAdaptor_HCurve.hxx>
|
||||
#include <Standard_Failure.hxx>
|
||||
#include <Standard_Version.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
|
||||
#include "FeatureArea.h"
|
||||
|
||||
using namespace Path;
|
||||
|
||||
@@ -54,79 +53,39 @@ PROPERTY_SOURCE(Path::FeatureShape, Path::Feature)
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
short FeatureShape::mustExecute(void) const
|
||||
{
|
||||
return Path::Feature::mustExecute();
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *FeatureShape::execute(void)
|
||||
{
|
||||
TopoDS_Shape shape = Shape.getValue();
|
||||
if (!shape.IsNull()) {
|
||||
if (shape.ShapeType() == TopAbs_WIRE) {
|
||||
Path::Toolpath result;
|
||||
bool first = true;
|
||||
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);
|
||||
}
|
||||
Toolpath path;
|
||||
std::vector<App::DocumentObject*> links = Sources.getValues();
|
||||
if (links.empty()) {
|
||||
Path.setValue(path);
|
||||
return new App::DocumentObjectExecReturn("No shapes linked");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
/*
|
||||
* Copyright (c) 2017 Zheng, Lei <realthunder.dev@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PATH_FeaturePathShape_H
|
||||
@@ -26,14 +29,13 @@
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/GeoFeature.h>
|
||||
#include <App/PropertyFile.h>
|
||||
#include <App/PropertyGeo.h>
|
||||
#include <App/FeaturePython.h>
|
||||
#include "Mod/Part/App/PropertyTopoShape.h"
|
||||
|
||||
#include "Path.h"
|
||||
#include "PropertyPath.h"
|
||||
#include "FeaturePath.h"
|
||||
#include "Area.h"
|
||||
|
||||
namespace Path
|
||||
{
|
||||
@@ -47,12 +49,14 @@ public:
|
||||
FeatureShape(void);
|
||||
virtual ~FeatureShape();
|
||||
|
||||
Part::PropertyPartShape Shape;
|
||||
// Part::PropertyPartShape Shape;
|
||||
App::PropertyLinkList Sources;
|
||||
App::PropertyVector StartPoint;
|
||||
PARAM_PROP_DECLARE(AREA_PARAMS_PATH)
|
||||
|
||||
//@{
|
||||
/// recalculate the feature
|
||||
virtual App::DocumentObjectExecReturn *execute(void);
|
||||
virtual short mustExecute(void) const;
|
||||
//@}
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
|
||||
@@ -1,95 +1,95 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
/***************************************************************************
|
||||
|
||||
* Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 *
|
||||
|
||||
* *
|
||||
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
|
||||
* *
|
||||
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
|
||||
* License as published by the Free Software Foundation; either *
|
||||
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
|
||||
* *
|
||||
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
|
||||
|
||||
#ifndef PATH_Path_H
|
||||
#define PATH_Path_H
|
||||
|
||||
#include "Command.h"
|
||||
//#include "Mod/Robot/App/kdl_cp/path_composite.hpp"
|
||||
//#include "Mod/Robot/App/kdl_cp/frames_io.hpp"
|
||||
#include <Base/Persistence.h>
|
||||
#include <Base/Vector3D.h>
|
||||
|
||||
namespace Path
|
||||
{
|
||||
|
||||
/** The representation of a CNC Toolpath */
|
||||
|
||||
class PathExport Toolpath : public Base::Persistence
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
public:
|
||||
Toolpath();
|
||||
Toolpath(const Toolpath&);
|
||||
~Toolpath();
|
||||
|
||||
Toolpath &operator=(const Toolpath&);
|
||||
|
||||
// from base class
|
||||
virtual unsigned int getMemSize (void) const;
|
||||
virtual void Save (Base::Writer &/*writer*/) const;
|
||||
virtual void Restore(Base::XMLReader &/*reader*/);
|
||||
void SaveDocFile (Base::Writer &writer) const;
|
||||
void RestoreDocFile(Base::Reader &reader);
|
||||
|
||||
// interface
|
||||
void clear(void); // clears the internal data
|
||||
void addCommand(const Command &Cmd); // adds a command at the end
|
||||
void insertCommand(const Command &Cmd, int); // inserts a command
|
||||
void deleteCommand(int); // deletes a command
|
||||
double getLength(void); // return the Length (mm) of the Path
|
||||
void recalculate(void); // recalculates the points
|
||||
void setFromGCode(const std::string); // sets the path from the contents of the given GCode string
|
||||
std::string toGCode(void) const; // gets a gcode string representation from the Path
|
||||
|
||||
// shortcut functions
|
||||
unsigned int getSize(void) const{return vpcCommands.size();}
|
||||
const std::vector<Command*> &getCommands(void)const{return vpcCommands;}
|
||||
const Command &getCommand(unsigned int pos)const {return *vpcCommands[pos];}
|
||||
|
||||
protected:
|
||||
std::vector<Command*> vpcCommands;
|
||||
//KDL::Path_Composite *pcPath;
|
||||
|
||||
/*
|
||||
inline KDL::Frame toFrame(const Base::Placement &To){
|
||||
return KDL::Frame(KDL::Rotation::Quaternion(To.getRotation()[0],
|
||||
To.getRotation()[1],
|
||||
To.getRotation()[2],
|
||||
To.getRotation()[3]),
|
||||
KDL::Vector(To.getPosition()[0],
|
||||
To.getPosition()[1],
|
||||
To.getPosition()[2]));
|
||||
}
|
||||
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
|
||||
* GNU Library General Public License for more details. *
|
||||
|
||||
* *
|
||||
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
|
||||
* *
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef PATH_Path_H
|
||||
|
||||
#define PATH_Path_H
|
||||
|
||||
|
||||
#include "Command.h"
|
||||
//#include "Mod/Robot/App/kdl_cp/path_composite.hpp"
|
||||
|
||||
//#include "Mod/Robot/App/kdl_cp/frames_io.hpp"
|
||||
#include <Base/Persistence.h>
|
||||
|
||||
#include <Base/Vector3D.h>
|
||||
|
||||
|
||||
|
||||
namespace Path
|
||||
|
||||
{
|
||||
|
||||
|
||||
|
||||
/** The representation of a CNC Toolpath */
|
||||
|
||||
|
||||
|
||||
class PathExport Toolpath : public Base::Persistence
|
||||
|
||||
{
|
||||
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Toolpath();
|
||||
|
||||
Toolpath(const Toolpath&);
|
||||
|
||||
~Toolpath();
|
||||
|
||||
|
||||
|
||||
Toolpath &operator=(const Toolpath&);
|
||||
|
||||
|
||||
|
||||
// from base class
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@ PyMODINIT_FUNC initPathGui()
|
||||
PathGui::ViewProviderPathPython ::init();
|
||||
PathGui::ViewProviderArea ::init();
|
||||
PathGui::ViewProviderAreaPython ::init();
|
||||
PathGui::ViewProviderAreaView ::init();
|
||||
PathGui::ViewProviderAreaViewPython ::init();
|
||||
|
||||
// add resources and reloads the translators
|
||||
loadPathResource();
|
||||
|
||||
@@ -68,39 +68,55 @@ void CmdPathArea::activated(int iMsg)
|
||||
Q_UNUSED(iMsg);
|
||||
std::list<std::string> cmds;
|
||||
std::ostringstream sources;
|
||||
std::string areaName;
|
||||
bool addView = true;
|
||||
for(const Gui::SelectionObject &selObj :
|
||||
getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId()))
|
||||
{
|
||||
const Part::Feature *pcObj = static_cast<const Part::Feature*>(selObj.getObject());
|
||||
const std::vector<std::string> &subnames = selObj.getSubNames();
|
||||
if(addView && areaName.size()) addView = false;
|
||||
|
||||
if(subnames.empty()) {
|
||||
if(addView && pcObj->getTypeId().isDerivedFrom(Path::FeatureArea::getClassTypeId()))
|
||||
areaName = pcObj->getNameInDocument();
|
||||
sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ",";
|
||||
continue;
|
||||
}
|
||||
for(const std::string &name : subnames) {
|
||||
if(!name.compare(0,4,"Face") &&
|
||||
!name.compare(0,4,"Edge"))
|
||||
{
|
||||
if(name.compare(0,4,"Face") && name.compare(0,4,"Edge")) {
|
||||
Base::Console().Error("Selected shape is not 2D\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int index = atoi(name.substr(4).c_str());
|
||||
|
||||
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 = FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ".Shape." <<
|
||||
name.substr(0,4) << "s[" << index-1 << ']';
|
||||
"').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 << ",";
|
||||
}
|
||||
}
|
||||
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");
|
||||
openCommand("Create Path Area");
|
||||
doCommand(Doc,"import PathCommands");
|
||||
for(const std::string &cmd : cmds)
|
||||
doCommand(Doc,cmd.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) {
|
||||
if(!name.compare(0,4,"Face") &&
|
||||
!name.compare(0,4,"Edge"))
|
||||
{
|
||||
if(name.compare(0,4,"Face") && name.compare(0,4,"Edge")) {
|
||||
Base::Console().Error("Selected shape is not 2D\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int index = atoi(name.substr(4).c_str());
|
||||
|
||||
std::ostringstream subname;
|
||||
subname << planeSubname << '.' << name.substr(0,4) << "s[" << index-1 << ']';
|
||||
subname << planeSubname << ",'" << name << "','Wires'";
|
||||
planeSubname = subname.str();
|
||||
}
|
||||
}
|
||||
@@ -195,10 +206,10 @@ void CmdPathAreaWorkplane::activated(int iMsg)
|
||||
}
|
||||
|
||||
openCommand("Select Workplane for Path Area");
|
||||
doCommand(Doc,"FreeCAD.activeDocument().%s.WorkPlane = FreeCAD.activeDocument().%s",
|
||||
areaName.c_str(),planeSubname.c_str());
|
||||
doCommand(Doc,"import PathCommands");
|
||||
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 = False",planeName.c_str());
|
||||
commitCommand();
|
||||
updateActive();
|
||||
}
|
||||
@@ -284,24 +295,48 @@ CmdPathShape::CmdPathShape()
|
||||
void CmdPathShape::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
std::vector<Gui::SelectionSingleton::SelObj> Sel = getSelection().getSelection();
|
||||
if (Sel.size() == 1) {
|
||||
if (Sel[0].pObject->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
|
||||
Part::Feature *pcPartObject = static_cast<Part::Feature*>(Sel[0].pObject);
|
||||
std::string FeatName = getUniqueObjectName("PathShape");
|
||||
openCommand("Create Path Compound");
|
||||
doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureShape','%s')",FeatName.c_str());
|
||||
doCommand(Doc,"FreeCAD.activeDocument().%s.Shape = FreeCAD.activeDocument().%s.Shape.copy()",FeatName.c_str(),pcPartObject->getNameInDocument());
|
||||
commitCommand();
|
||||
updateActive();
|
||||
} else {
|
||||
Base::Console().Error("Exactly one shape object must be selected\n");
|
||||
return;
|
||||
std::list<std::string> cmds;
|
||||
std::ostringstream sources;
|
||||
for(const Gui::SelectionObject &selObj :
|
||||
getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId()))
|
||||
{
|
||||
const Part::Feature *pcObj = static_cast<const Part::Feature*>(selObj.getObject());
|
||||
const std::vector<std::string> &subnames = selObj.getSubNames();
|
||||
if(subnames.empty()) {
|
||||
sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ",";
|
||||
continue;
|
||||
}
|
||||
for(const std::string &name : subnames) {
|
||||
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)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<file>icons/Path-Toolpath.svg</file>
|
||||
<file>icons/Path-ToolTable.svg</file>
|
||||
<file>icons/Path-Area.svg</file>
|
||||
<file>icons/Path-Area-View.svg</file>
|
||||
<file>icons/Path-Area-Workplane.svg</file>
|
||||
<file>icons/preferences-path.svg</file>
|
||||
<file>panels/ContourEdit.ui</file>
|
||||
|
||||
657
src/Mod/Path/Gui/Resources/icons/Path-Area-View.svg
Normal file
657
src/Mod/Path/Gui/Resources/icons/Path-Area-View.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 24 KiB |
@@ -115,11 +115,83 @@ bool ViewProviderArea::onDelete(const std::vector<std::string> &)
|
||||
|
||||
// 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 {
|
||||
/// @cond DOXERR
|
||||
PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderAreaPython, PathGui::ViewProviderArea)
|
||||
PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderAreaViewPython, PathGui::ViewProviderAreaView)
|
||||
/// @endcond
|
||||
|
||||
// explicit template instantiation
|
||||
template class PathGuiExport ViewProviderPythonFeatureT<PathGui::ViewProviderArea>;
|
||||
template class PathGuiExport ViewProviderPythonFeatureT<PathGui::ViewProviderAreaView>;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,29 @@ public:
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -26,8 +26,11 @@
|
||||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include "ViewProviderPathShape.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 PathGui;
|
||||
@@ -38,3 +41,74 @@ QIcon ViewProviderPathShape::getIcon() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,19 @@ class PathGuiExport ViewProviderPathShape: public ViewProviderPath
|
||||
PROPERTY_HEADER(PathGui::ViewProviderPathShape);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -81,12 +81,12 @@ class PathWorkbench (Workbench):
|
||||
# build commands list
|
||||
projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"]
|
||||
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"]
|
||||
threedopcmdlist = ["Path_Surfacing"]
|
||||
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
|
||||
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",]
|
||||
#remotecmdlist = ["Path_Remote"]
|
||||
|
||||
|
||||
@@ -78,3 +78,28 @@ class _CommandSelectLoop:
|
||||
if FreeCAD.GuiUp:
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -73,6 +73,58 @@ Point CArea::NearestPoint(const Point& p)const
|
||||
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)
|
||||
{
|
||||
for(std::list<CCurve>::iterator It = m_curves.begin(); It != m_curves.end(); It++)
|
||||
|
||||
@@ -85,6 +85,8 @@ public:
|
||||
void CurveIntersections(const CCurve& curve, std::list<Point> &pts)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
|
||||
#define CAREA_PARAM_DECLARE(_type,_name) \
|
||||
static _type get_##_name();\
|
||||
|
||||
@@ -468,16 +468,23 @@ void CArea::Offset(double inwards_value)
|
||||
|
||||
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++)
|
||||
{
|
||||
const CCurve &curve = *It;
|
||||
bool closed = curve.IsClosed();
|
||||
if(type == ptClip && !closed)
|
||||
continue;
|
||||
if(!closed) {
|
||||
if(type == ptClip){
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TPolygon p;
|
||||
MakePoly(curve, p, false);
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user