Path: added Path.Area and Path.FeatureArea

This commit is contained in:
Zheng, Lei 2017-01-19 23:07:06 +08:00
parent 4a0e037893
commit 36423f24de
18 changed files with 3479 additions and 3 deletions

View File

@ -41,6 +41,8 @@
#include "PropertyTooltable.h"
#include "FeaturePathCompound.h"
#include "FeaturePathShape.h"
#include "AreaPy.h"
#include "FeatureArea.h"
namespace Path {
extern PyObject* initModule();
@ -49,15 +51,24 @@ extern PyObject* initModule();
/* Python entry */
PyMODINIT_FUNC initPath()
{
// load dependent module
try {
Base::Interpreter().runString("import Part");
}
catch(const Base::Exception& e) {
PyErr_SetString(PyExc_ImportError, e.what());
return;
}
PyObject* pathModule = Path::initModule();
Base::Console().Log("Loading Path module... done\n");
// Add Types to module
Base::Interpreter().addType(&Path::CommandPy ::Type, pathModule, "Command");
Base::Interpreter().addType(&Path::PathPy ::Type, pathModule, "Path");
Base::Interpreter().addType(&Path::ToolPy ::Type, pathModule, "Tool");
Base::Interpreter().addType(&Path::TooltablePy ::Type, pathModule, "Tooltable");
Base::Interpreter().addType(&Path::AreaPy ::Type, pathModule, "Area");
// NOTE: To finish the initialization of our own type objects we must
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
@ -74,4 +85,7 @@ PyMODINIT_FUNC initPath()
Path::FeatureCompoundPython ::init();
Path::FeatureShape ::init();
Path::FeatureShapePython ::init();
Path::Area ::init();
Path::FeatureArea ::init();
Path::FeatureAreaPython ::init();
}

592
src/Mod/Path/App/Area.cpp Normal file
View File

@ -0,0 +1,592 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <BRepLib.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <BRepBuilderAPI_FindPlane.hxx>
#include <BRepLib_FindSurface.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepTools.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <GeomAbs_JoinType.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <Standard_Failure.hxx>
#include <gp_Circ.hxx>
#include <gp_GTrsf.hxx>
#include <Standard_Version.hxx>
#include <GCPnts_UniformDeflection.hxx>
#include <Base/Exception.h>
#include <Base/Tools.h>
#include <Base/Console.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/Part/App/FaceMakerBullseye.h>
#include "Area.h"
#include "../libarea/Area.h"
using namespace Path;
CAreaParams::CAreaParams()
:PARAM_INIT(NAME,AREA_PARAMS_CAREA)
{}
AreaParams::AreaParams()
:PARAM_INIT(NAME,AREA_PARAMS_BASE)
{}
CAreaConfig::CAreaConfig(const CAreaParams &p, bool noFitArcs)
:params(p)
{
// Arc fitting is lossy. we shall reduce the number of unecessary fit
if(noFitArcs)
params.FitArcs=false;
#define AREA_CONF_SAVE_AND_APPLY(_param) \
PARAM_FNAME(_param) = BOOST_PP_CAT(CArea::get_,PARAM_FARG(_param))();\
BOOST_PP_CAT(CArea::set_,PARAM_FARG(_param))(params.PARAM_FNAME(_param));
PARAM_FOREACH(AREA_CONF_SAVE_AND_APPLY,AREA_PARAMS_CAREA)
}
CAreaConfig::~CAreaConfig() {
#define AREA_CONF_RESTORE(_param) \
BOOST_PP_CAT(CArea::set_,PARAM_FARG(_param))(PARAM_FNAME(_param));
PARAM_FOREACH(AREA_CONF_RESTORE,AREA_PARAMS_CAREA)
}
//////////////////////////////////////////////////////////////////////////////
TYPESYSTEM_SOURCE(Path::Area, Base::BaseClass);
Area::Area(const AreaParams *params)
:myArea(NULL)
,myAreaOpen(NULL)
,myHaveFace(false)
{
if(params)
setParams(*params);
}
Area::~Area() {
clean();
}
void Area::setPlane(const TopoDS_Shape &shape) {
myWorkPlane = shape;
}
void Area::add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf,
double deflection, CArea *areaOpen, bool to_edges, bool reorder)
{
bool haveShape = false;
for (TopExp_Explorer it(shape, TopAbs_FACE); it.More(); it.Next()) {
haveShape = true;
const TopoDS_Face &face = TopoDS::Face(it.Current());
for (TopExp_Explorer it(face, TopAbs_WIRE); it.More(); it.Next())
add(area,TopoDS::Wire(it.Current()),trsf,deflection);
}
if(haveShape) return;
CArea _area;
CArea _areaOpen;
for (TopExp_Explorer it(shape, TopAbs_WIRE); it.More(); it.Next()) {
haveShape = true;
const TopoDS_Wire &wire = TopoDS::Wire(it.Current());
if(BRep_Tool::IsClosed(wire))
add(_area,wire,trsf,deflection);
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);
}else
add(_areaOpen,wire,trsf,deflection);
}
if(!haveShape) {
for (TopExp_Explorer it(shape, TopAbs_EDGE); it.More(); it.Next()) {
add(_areaOpen,BRepBuilderAPI_MakeWire(
TopoDS::Edge(it.Current())).Wire(),trsf,deflection);
}
}
if(reorder)
_area.Reorder();
area.m_curves.splice(area.m_curves.end(),_area.m_curves);
if(areaOpen)
areaOpen->m_curves.splice(areaOpen->m_curves.end(),_areaOpen.m_curves);
else
area.m_curves.splice(area.m_curves.end(),_areaOpen.m_curves);
}
void Area::add(CArea &area, const TopoDS_Wire& wire,
const gp_Trsf *trsf, double deflection)
{
CCurve ccurve;
BRepTools_WireExplorer xp(trsf?TopoDS::Wire(
wire.Moved(TopLoc_Location(*trsf))):wire);
gp_Pnt p = BRep_Tool::Pnt(xp.CurrentVertex());
ccurve.append(CVertex(Point(p.X(),p.Y())));
for (;xp.More();xp.Next()) {
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: {
ccurve.append(CVertex(Point(p.X(),p.Y())));
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()),
Point(loc.X(),loc.Y())));
}
ccurve.append(CVertex(dir,Point(p.X(),p.Y()),
Point(loc.X(),loc.Y())));
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);
ccurve.append(CVertex(Point(pt.X(),pt.Y())));
}
}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());
}
area.append(ccurve);
}
void Area::clean(bool deleteShapes) {
myShape.Nullify();
delete myArea;
myArea = NULL;
delete myAreaOpen;
myAreaOpen = NULL;
if(deleteShapes)
myShapes.clear();
}
void Area::add(const TopoDS_Shape &shape,short op) {
#define AREA_SRC_OP(_v) op
PARAM_ENUM_CONVERT(AREA_SRC_OP,,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE);
TopExp_Explorer it(shape, TopAbs_SHELL);
if(it.More())
throw Base::ValueError("not a 2D shape");
clean();
if(myShapes.empty())
Operation = ClipperLib::ctUnion;
myShapes.push_back(Shape((short)Operation,shape));
}
void Area::setParams(const AreaParams &params) {
#define AREA_SRC2(_v) params._v
// Validate all enum type of parameters
PARAM_ENUM_CHECK(AREA_SRC2,PARAM_ENUM_EXCEPT,AREA_PARAMS_CONF);
if(params!=myParams)
clean();
myParams = params;
}
void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
if(!myHaveFace) {
TopExp_Explorer it(shape, TopAbs_FACE);
myHaveFace = it.More();
}
CArea areaOpen;
add(area,shape,&myTrsf,myParams.Deflection,&areaOpen,
myParams.OpenMode==OpenModeEdges,myParams.Reorder);
if(areaOpen.m_curves.size()) {
if(&area == myArea || myParams.OpenMode == OpenModeNone)
myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves);
else
Base::Console().Warning("open wires discarded in clipping shapes\n");
}
}
void Area::build() {
if(myArea) return;
if(myShapes.empty())
throw Base::ValueError("Null shape");
#define AREA_SRC(_v) myParams._v
PARAM_ENUM_CONVERT(AREA_SRC,,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL);
TopoDS_Builder builder;
TopoDS_Compound comp;
builder.MakeCompound(comp);
if(!myWorkPlane.IsNull())
builder.Add(comp,myWorkPlane);
else {
for(const Shape &s : myShapes)
builder.Add(comp, s.shape);
}
BRepLib_FindSurface planeFinder(comp,-1,Standard_True);
if (!planeFinder.Found())
throw Base::ValueError("shapes are not coplanar");
myTrsf.SetTransformation(GeomAdaptor_Surface(
planeFinder.Surface()).Plane().Position());
myArea = new CArea();
myAreaOpen = new CArea();
CAreaConfig conf(myParams);
CArea areaClip;
short op = ClipperLib::ctUnion;
bool pending = false;
for(const Shape &s : myShapes) {
if(op!=s.op) {
if(myParams.OpenMode!=OpenModeNone)
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
pending = false;
myArea->Clip((ClipperLib::ClipType)op,&areaClip,SubjectFill,ClipFill);
areaClip.m_curves.clear();
op=s.op;
}
addToBuild(op==ClipperLib::ctUnion?*myArea:areaClip,s.shape);
pending = true;
}
if(pending){
if(myParams.OpenMode!=OpenModeNone)
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
myArea->Clip((ClipperLib::ClipType)op,&areaClip,SubjectFill,ClipFill);
}
myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves);
}
TopoDS_Shape Area::toShape(CArea &area, short fill) {
gp_Trsf trsf(myTrsf.Inverted());
bool bFill;
switch(fill){
case Area::FillAuto:
bFill = myHaveFace;
break;
case Area::FillFace:
bFill = true;
break;
default:
bFill = false;
}
if(myParams.FitArcs) {
if(&area == myArea) {
CArea copy(area);
copy.FitArcs();
return toShape(copy,bFill,&trsf);
}
area.FitArcs();
}
return toShape(area,bFill,&trsf);
}
const TopoDS_Shape &Area::getShape() {
if(myShape.IsNull()) {
build();
CAreaConfig conf(myParams);
myShape = toShape(*myArea,myParams.Fill);
}
return myShape;
}
TopoDS_Shape Area::makeOffset(PARAM_ARGS(ARG,AREA_PARAMS_OFFSET)) {
std::list<TopoDS_Shape> shapes;
makeOffset(shapes,PARAM_FIELDS(ARG,AREA_PARAMS_OFFSET));
if(shapes.empty())
return TopoDS_Shape();
if(shapes.size()==1)
return shapes.front();
BRep_Builder builder;
TopoDS_Compound compound;
builder.MakeCompound(compound);
for(const TopoDS_Shape &s : shapes)
builder.Add(compound,s);
return compound;
}
void Area::makeOffset(std::list<TopoDS_Shape> &shapes,
PARAM_ARGS(ARG,AREA_PARAMS_OFFSET))
{
if(fabs(offset)<Precision::Confusion()){
shapes.push_back(getShape());
return;
}
build();
CAreaConfig conf(myParams);
if(myParams.Thicken) {
CArea area(*myArea);
area.Thicken(fabs(offset));
shapes.push_back(toShape(area,myParams.Fill));
return;
}
long count = 1;
if(extra_pass) {
if(fabs(stepover)<Precision::Confusion())
stepover = offset;
if(extra_pass > 0) {
count += extra_pass;
}else{
if(stepover>0 || offset>0)
throw Base::ValueError("invalid extra count");
// In this case, we loop until no outputs from clipper
count=-1;
}
}
PARAM_ENUM_CONVERT(AREA_SRC,,PARAM_ENUM_EXCEPT,AREA_PARAMS_OFFSET_CONF);
#ifdef AREA_OFFSET_ALGO
PARAM_ENUM_CONVERT(AREA_SRC,,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL);
#endif
for(int i=0;count<0||i<count;++i,offset+=stepover) {
CArea area;
CArea areaOpen;
#ifdef AREA_OFFSET_ALGO
if(myParams.Algo == Area::Algolibarea) {
for(const CCurve &c : myArea->m_curves) {
if(c.IsClosed())
area.append(c);
else
areaOpen.append(c);
}
}else
#endif
area = *myArea;
#ifdef AREA_OFFSET_ALGO
switch(myParams.Algo){
case Area::Algolibarea:
// libarea somehow fails offset without Reorder, but ClipperOffset
// works okay. Don't know why
area.Reorder();
area.Offset(-offset);
if(areaOpen.m_curves.size()) {
areaOpen.Thicken(offset);
area.Clip(ClipperLib::ctUnion,&areaOpen,SubjectFill,ClipFill);
}
break;
case Area::AlgoClipperOffset:
#endif
area.OffsetWithClipper(offset,JoinType,EndType,
myParams.MiterLimit,myParams.RoundPreceision);
#ifdef AREA_OFFSET_ALGO
break;
}
#endif
if(area.m_curves.empty())
return;
if(count == 1) {
shapes.push_back(toShape(area,myParams.Fill));
return;
}
shapes.push_back(toShape(area,Area::FillNone));
}
}
TopoDS_Shape Area::makePocket(PARAM_ARGS(ARG,AREA_PARAMS_POCKET)) {
if(tool_radius < Precision::Confusion())
throw Base::ValueError("tool radius too small");
if(stepover == 0.0)
stepover = tool_radius;
if(stepover < Precision::Confusion())
throw Base::ValueError("stepover too small");
if(mode == Area::PocketModeNone)
return TopoDS_Shape();
PocketMode pm;
switch(mode) {
case Area::PocketModeZigZag:
pm = ZigZagPocketMode;
break;
case Area::PocketModeSpiral:
pm = SpiralPocketMode;
break;
case Area::PocketModeOffset: {
PARAM_DECLARE_INIT(NAME,AREA_PARAMS_OFFSET);
Offset = -tool_radius-extra_offset;
ExtraPass = -1;
Stepover = -stepover;
return makeOffset(PARAM_FIELDS(NAME,AREA_PARAMS_OFFSET));
}case Area::PocketModeZigZagOffset:
pm = ZigZagThenSingleOffsetPocketMode;
break;
default:
throw Base::ValueError("unknown poket mode");
}
build();
CAreaConfig conf(myParams);
CAreaPocketParams params(
tool_radius,extra_offset,stepover,from_center,pm,zig_angle);
CArea in(*myArea),out;
// MakePcoketToolPath internally uses libarea Offset which somehow demands
// reorder before input, otherwise nothing is shown.
in.Reorder();
in.MakePocketToolpath(out.m_curves,params);
return toShape(out,FillNone);
}
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_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());
}
if(fill) {
try{
Part::FaceMakerBullseye mkFace;
if(trsf)
mkFace.setPlane(gp_Pln().Transformed(*trsf));
for(TopExp_Explorer it(compound, TopAbs_WIRE); it.More(); it.Next())
mkFace.addWire(TopoDS::Wire(it.Current()));
mkFace.Build();
if (mkFace.Shape().IsNull())
Base::Console().Warning("FaceMakerBullseye returns null shape\n");
return mkFace.Shape();
}catch (Base::Exception &e){
Base::Console().Warning("FaceMakerBullseye failed: %s\n", e.what());
}
}
return compound;
}

238
src/Mod/Path/App/Area.h Normal file
View File

@ -0,0 +1,238 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#ifndef PATH_AREA_H
#define PATH_AREA_H
#include <TopoDS.hxx>
#include <gp_Pln.hxx>
#include <gp_Circ.hxx>
#include <gp_GTrsf.hxx>
#include "AreaParams.h"
class CArea;
namespace Path
{
/** Store libarea algorithm configuration */
struct PathExport CAreaParams {
PARAM_DECLARE(NAME,AREA_PARAMS_CAREA)
CAreaParams();
};
/** Store all Area configurations */
struct PathExport AreaParams: CAreaParams {
PARAM_DECLARE(NAME,AREA_PARAMS_BASE)
PARAM_DECLARE(NAME,AREA_PARAMS_OFFSET_CONF)
bool operator==(const AreaParams &other) const {
#define AREA_COMPARE(_param) \
if(PARAM_FIELD(NAME,_param)!=other.PARAM_FIELD(NAME,_param)) return false;
PARAM_FOREACH(AREA_COMPARE,AREA_PARAMS_CONF)
return true;
}
bool operator!=(const AreaParams &other) const {
return !(*this == other);
}
AreaParams();
};
/** libarea configurator
*
* It is kind of troublesome with the fact that libarea uses static variables to
* config its algorithm. CAreaConfig makes it easy to safely customize libarea.
*/
struct PathExport CAreaConfig {
/** Stores current libarea settings */
PARAM_DECLARE(NAME,AREA_PARAMS_CAREA)
/** Stores user defined setting */
CAreaParams params;
/** The constructor automatically saves current setting and apply user defined ones
*
* \arg \c p user defined configurations
* \arg \c noFitArgs if true, will override and disable arc fitting. Because
* arc unfiting and fitting is lossy. And repeatedly perform these operation
* may cause shape deformation. So it is best to delay arc fitting until the
* final step*/
CAreaConfig(const CAreaParams &p, bool noFitArcs=true);
/** The destructor restores the setting, and thus exception safe. */
~CAreaConfig();
};
/** Base class for FreeCAD wrapping of libarea */
class PathExport Area: public Base::BaseClass {
TYPESYSTEM_HEADER();
protected:
struct Shape {
short op;
TopoDS_Shape shape;
Shape(short opCode, const TopoDS_Shape &s)
:op(opCode)
,shape(s)
{}
};
std::list<Shape> myShapes;
CArea *myArea;
CArea *myAreaOpen;
gp_Trsf myTrsf;
AreaParams myParams;
TopoDS_Shape myWorkPlane;
TopoDS_Shape myShape;
bool myHaveFace;
/** Called internally to combine children shapes for further processing */
void build();
/** Called by build() to add children shape
*
* Mainly for checking if there is any faces for auto fill*/
void addToBuild(CArea &area, const TopoDS_Shape &shape);
/** Called internally to obtain the combained children shapes */
TopoDS_Shape toShape(CArea &area, short fill);
public:
/** Declare all parameters defined in #AREA_PARAMS_ALL as member variable */
PARAM_ENUM_DECLARE(AREA_PARAMS_ALL)
public:
Area(const AreaParams *params = NULL);
virtual ~Area();
/** 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
*
* \arg \c shape: a shape defining a working plane
*/
void setPlane(const TopoDS_Shape &shape);
/** Add a child shape with given operation code
*
* No validation is done at this point. Exception will be thrown when asking
* for output shape, if any of the children shapes is not valid or not
* coplanar
*
* \arg \c shape: the child shape
* \arg \c op: operation code, see #AREA_PARAMS_OPCODE
*/
void add(const TopoDS_Shape &shape,PARAM_ARGS_DEF(ARG,AREA_PARAMS_OPCODE));
/** Generate an offset of the combined shape
*
* See #AREA_PARAMS_OFFSET for description of the arguments.
* 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(PARAM_ARGS_DEF(ARG,AREA_PARAMS_OFFSET));
/** Obtain a list of offset shapes of the combined shape,
*
* See #AREA_PARAMS_OFFSET for description of the arguments.
*/
void makeOffset(std::list<TopoDS_Shape> &shapes,
PARAM_ARGS_DEF(ARG,AREA_PARAMS_OFFSET));
/** Make a pocket of the combined shape
*
* See #AREA_PARAMS_POCKET for description of the arguments.
*/
TopoDS_Shape makePocket(PARAM_ARGS_DEF(ARG,AREA_PARAMS_POCKET));
/** Config this Area object */
void setParams(const AreaParams &params);
/** Get the current configuration */
const AreaParams &getParams() const {
return myParams;
}
/** Clean internal caches
*
* The combained shapes is cached internally to make other operation more
* efficient, such as makeOffset() and makePocket()
*
* \arg \c deleteShapes: if true, delete all children shapes.
*/
void clean(bool deleteShapes=false);
/** Get the combined shape */
const TopoDS_Shape &getShape();
/** Add a OCC wire shape to CArea
*
* \arg \c area: output converted curved object to here
* \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
* */
static void add(CArea &area, const TopoDS_Wire &wire,
const gp_Trsf *trsf=NULL,double deflection=0.01);
/** Add a OCC generic shape to CArea
*
* \arg \c area: output converted curved object to here
* \arg \c shape: input shape 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 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
* */
static void add(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf=NULL,
double deflection=0.01,CArea *areaOpen=NULL, bool to_edges=false, bool reorder=true);
/** Convert curves in CArea into an OCC shape
*
* \arg \c area: input area object
* \arg \c fill: if true, create a face object from the wires
* \arg \c trsf: optional transform matrix to transform the shape back into
* its original position.
* */
static TopoDS_Shape toShape(const CArea &area, bool fill,
const gp_Trsf *trsf=NULL);
};
} //namespace Path
#endif //PATH_AREA_H

View File

@ -0,0 +1,129 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#ifndef PATH_AreaParams_H
#define PATH_AreaParams_H
// deifne this to enable offset algo selection
// #define AREA_OFFSET_ALGO
/** \file
* Parameters definition for Path::Area and its companion
* See \ref ParamPage "here" for details of parameter definition.
*/
#include "ParamsHelper.h"
/** clipper fill type */
#define AREA_CLIPPER_FILL_TYPE \
(NonZero)(EvenOdd)(Positive)(Negative),(ClipperLib::PolyFillType,ClipperLib::pft)
/** Paramerters of clipper fill types */
#define AREA_PARAMS_CLIPPER_FILL \
((enum2,subject_fill,SubjectFill,0,\
"ClipperLib subject fill type. \nSee https://goo.gl/5pYQQP",AREA_CLIPPER_FILL_TYPE))\
((enum2,clip_fill,ClipFill,0,\
"ClipperLib clip fill type. \nSee https://goo.gl/5pYQQP",AREA_CLIPPER_FILL_TYPE))
/** 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)))\
((bool,reorder,Reorder,false,"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"))
/** libarea algorithm option parameters */
#define AREA_PARAMS_CAREA \
((double,tolerance,Tolerance,Precision::Confusion(),"Point coincidence tolerance"))\
((bool,fit_arcs,FitArcs,true,"Enable arc fitting"))\
((bool,clipper_simple,Simplify,false,\
"Simplify polygons after operation. See https://goo.gl/Mh9XK1"))\
((double,clipper_clean_distance,CleanDistance,0.0,\
"Clean polygon smaller than this distance. See https://goo.gl/jox3JY"))\
((double,accuracy,Accuracy,0.01,"Arc fitting accuracy"))\
((double,units,Unit,1.0,"Scaling factor for convertion to inch"))\
((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."))
/** Pocket parameters
*
* These parameters cooresponds to CAreaPocketParams in libarea
* */
#define AREA_PARAMS_POCKET \
((enum,mode,PocketMode,1,"Selects the pocket toolpath pattern",(None)(ZigZag)(Offset)(Spiral)(ZigZagOffset)))\
((double,tool_radius,ToolRadius,1.0,"Tool radius for pocketing"))\
((double,extra_offset,PocketExtraOffset,0.0,"Extra offset for pocketing"))\
((double,stepover,PocketStepover,0.0,"Cutter diameter to step over on each pass. If =0, use ToolRadius."))\
((bool,from_center,FromCenter,true,"Start pocketing from center"))\
((double,zig_angle,ZigAngle,45,"Zig angle in degree"))
/** Operation code */
#define AREA_PARAMS_OPCODE \
((enum2,op,Operation,0,"Boolean operation",\
(Union)(Difference)(Intersection)(Xor),(ClipperLib::ClipType,ClipperLib::ct)))
/** Offset parameters */
#define AREA_PARAMS_OFFSET \
((double,offset,Offset,0.0,"Offset value, positive for expansion, negative for shrinking"))\
((long,extra_pass,ExtraPass,0,"Number of extra offset pass to generate."))\
((double,stepover,Stepover,0.0,"Cutter diameter to step over on each pass. If =0, use Offset"))
#ifdef AREA_OFFSET_ALGO
# define AREA_PARAMS_OFFSET_ALGO \
((enum,algo,Algo,0,"Offset algorithm type",(Clipper)(libarea)))
#else
# define AREA_PARAMS_OFFSET_ALGO
#endif
/** Offset configuration parameters */
#define AREA_PARAMS_OFFSET_CONF \
AREA_PARAMS_OFFSET_ALGO \
((bool,thicken,Thicken,false,"Thicken the resulting wires with Offset"))\
((enum2,join_type,JoinType,0,"ClipperOffset join type. \nSee https://goo.gl/4odfQh",\
(Round)(Square)(Miter),(ClipperLib::JoinType,ClipperLib::jt)))\
((enum2,end_type,EndType,0,"\nClipperOffset end type. See https://goo.gl/tj7gkX",\
(OpenRound)(ClosedPolygon)(ClosedLine)(OpenSquare)(OpenButt),(ClipperLib::EndType,ClipperLib::et)))\
((double,miter_limit,MiterLimit,2.0,"Miter limit for joint type Miter. See https://goo.gl/K8xX9h"))\
((double,round_precision,RoundPreceision,0.0,\
"Round joint precision. If =0, it defaults to Accuracy. \nSee https://goo.gl/4odfQh"))
/** Group of all Area configuration parameters */
#define AREA_PARAMS_CONF \
AREA_PARAMS_CAREA \
AREA_PARAMS_BASE \
AREA_PARAMS_OFFSET_CONF
/** Group of all Area parameters */
#define AREA_PARAMS_ALL \
AREA_PARAMS_CONF \
AREA_PARAMS_OPCODE \
AREA_PARAMS_OFFSET \
AREA_PARAMS_POCKET
#endif //PATH_AreaParam_H

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="AreaPy"
Twin="Area"
TwinPointer="Area"
Include="Mod/Path/App/Area.h"
Namespace="Path"
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
<Documentation>
<Author Licence="LGPL" Name="Zheng, Lei" EMail="realthunder.dev@gmail.com" />
<UserDocu>FreeCAD python wrapper of libarea</UserDocu>
</Documentation>
<Methode Name="add" Keyword='true'>
<Documentation>
<UserDocu></UserDocu>
</Documentation>
</Methode>
<Methode Name="setPlane">
<Documentation>
<UserDocu>setPlane(shape): Set the working plane. The shape will not be used for
any operation</UserDocu>
</Documentation>
</Methode>
<Methode Name="toShape" Keyword='true'>
<Documentation>
<UserDocu>toShape(rebuild=False): Return the resulting shape</UserDocu>
</Documentation>
</Methode>
<Methode Name="makeOffset" Keyword='true'>
<Documentation>
<UserDocu></UserDocu>
</Documentation>
</Methode>
<Methode Name="makePocket" Keyword='true'>
<Documentation>
<UserDocu></UserDocu>
</Documentation>
</Methode>
<Methode Name="setParams" Keyword="true">
<Documentation>
<UserDocu></UserDocu>
</Documentation>
</Methode>
<Methode Name="getParamsDesc" Keyword="true">
<Documentation>
<UserDocu>getParamsDesc(as_string=True): Returns a list of supported parameters and their descriptions.\n
* as_string: if False, then return a dictionary of documents of all supported parameters.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getParams">
<Documentation>
<UserDocu>Get current algorithm parameters as a dictionary.</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@ -0,0 +1,292 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#include <Mod/Part/App/OCCError.h>
#include <Mod/Part/App/TopoShapePy.h>
#include "Mod/Path/App/Area.h"
// inclusion of the generated files (generated out of AreaPy.xml)
#include "AreaPy.h"
#include "AreaPy.cpp"
struct AreaDoc {
const char *name;
const char *doc;
};
/** Generate doc string from parameter definitions
* It will generate doc string and replace the one generated from xml
* */
static const AreaDoc myDocs[] = {
{
"setParams",
"setParam(key=value...): Set algorithm parameters. You can call getParamsDesc() to \n"
"get a list of supported parameters and their descriptions.\n"
PARAM_PY_DOC(NAME,AREA_PARAMS_CONF)
},
{
"add",
"add((shape...)," PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_OPCODE) "):\n"
"Add TopoShape(s) with given operation code\n"
PARAM_PY_DOC(ARG,AREA_PARAMS_OPCODE)
"\nThe first shape's wires will be fused together regardless of the op code given.\n"
"Subsequent shape's wire will be combined using the op code. All shape wires\n"
"shall be coplanar, and are used to determine a working plane for face making and\n"
"offseting. You can call setPlane() to supply a reference shape to determin the\n"
"working plane in case the added shapes are all colinear lines.\n",
},
{
"makeOffset",
"makeOffset(" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_OFFSET) "):\n"
"Make an 2D offset of the shape.\n"
PARAM_PY_DOC(ARG,AREA_PARAMS_OFFSET),
},
{
"makePocket",
"makePocket(" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_POCKET) "):\n"
"Generate pocket toolpath of the shape.\n"
PARAM_PY_DOC(ARG,AREA_PARAMS_POCKET),
},
};
struct AreaPyDoc {
AreaPyDoc() {
for(PyMethodDef &method : Path::AreaPy::Methods) {
if(!method.ml_name) continue;
for(const AreaDoc &doc : myDocs) {
if(std::strcmp(method.ml_name,doc.name)==0) {
method.ml_doc = doc.doc;
break;
}
}
}
}
};
static AreaPyDoc doc;
namespace Part {
extern Py::Object shape2pyshape(const TopoDS_Shape &shape);
}
using namespace Path;
// returns a string which represents the object e.g. when printed in python
std::string AreaPy::representation(void) const
{
std::stringstream str;
str << "<Area object at " << getAreaPtr() << ">";
return str.str();
}
PyObject *AreaPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
{
return new AreaPy(new Area);
}
// constructor method
int AreaPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
PyObject* AreaPy::setPlane(PyObject *args) {
PyObject *pcObj;
if (!PyArg_ParseTuple(args, "O!", &(Part::TopoShapePy::Type), &pcObj))
Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters");
#define GET_TOPOSHAPE(_p) static_cast<Part::TopoShapePy*>(_p)->getTopoShapePtr()->getShape()
getAreaPtr()->setPlane(GET_TOPOSHAPE(pcObj));
return Py_None;
}
PyObject* AreaPy::toShape(PyObject *args, PyObject *keywds)
{
PyObject *pcObj = Py_True;
static char *kwlist[] = {"rebuild", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds,"|O",kwlist,&pcObj))
Py_Error(Base::BaseExceptionFreeCADError, "This method accepts no argument");
try {
if(PyObject_IsTrue(pcObj))
getAreaPtr()->clean(true);
return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape()));
}
PY_CATCH_OCC;
}
PyObject* AreaPy::add(PyObject *args, PyObject *keywds)
{
PARAM_PY_DECLARE_INIT(ARG,AREA_PARAMS_OPCODE)
PyObject *pcObj;
static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_OPCODE), NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds,
"O|" PARAM_PY_KWDS(AREA_PARAMS_OPCODE),
kwlist,&pcObj,PARAM_REF(ARG,AREA_PARAMS_OPCODE)))
Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters");
if (PyObject_TypeCheck(pcObj, &(Part::TopoShapePy::Type))) {
getAreaPtr()->add(GET_TOPOSHAPE(pcObj),op);
return Py_None;
}
Py::Sequence shapeSeq(pcObj);
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");
return 0;
}
}
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it){
PyObject* item = (*it).ptr();
getAreaPtr()->add(GET_TOPOSHAPE(item),
PARAM_PY_FIELDS(ARG,AREA_PARAMS_OPCODE));
}
return Py_None;
}
PyObject* AreaPy::makeOffset(PyObject *args, PyObject *keywds)
{
//Generate a keyword string defined in the ARG field of OFFSET parameter list
static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_OFFSET), NULL};
//Declare variables defined in the ARG field of the OFFSET parameter list with
//initialization to defaults
PARAM_PY_DECLARE_INIT(ARG,AREA_PARAMS_OFFSET)
//Parse arguments to overwrite the defaults
if (!PyArg_ParseTupleAndKeywords(args, keywds,
"|" PARAM_PY_KWDS(AREA_PARAMS_OFFSET), kwlist,
PARAM_REF(ARG,AREA_PARAMS_OFFSET)))
Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters");
try {
//Expand the variable as function call arguments
TopoDS_Shape resultShape = getAreaPtr()->makeOffset(
PARAM_PY_FIELDS(ARG,AREA_PARAMS_OFFSET));
return Py::new_reference_to(Part::shape2pyshape(resultShape));
}
PY_CATCH_OCC;
}
PyObject* AreaPy::makePocket(PyObject *args, PyObject *keywds)
{
static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_POCKET), NULL};
PARAM_PY_DECLARE_INIT(ARG,AREA_PARAMS_POCKET)
if (!PyArg_ParseTupleAndKeywords(args, keywds,
"|" PARAM_PY_KWDS(AREA_PARAMS_POCKET), kwlist,
PARAM_REF(ARG,AREA_PARAMS_POCKET)))
Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters");
try {
TopoDS_Shape resultShape = getAreaPtr()->makePocket(
PARAM_PY_FIELDS(ARG,AREA_PARAMS_POCKET));
return Py::new_reference_to(Part::shape2pyshape(resultShape));
}
PY_CATCH_OCC;
}
PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds)
{
static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL};
//Declare variables defined in the NAME field of the CONF parameter list
PARAM_PY_DECLARE(NAME,AREA_PARAMS_CONF);
AreaParams params = getAreaPtr()->getParams();
#define AREA_SET(_param) \
PARAM_FNAME(_param) = \
PARAM_TYPED(PARAM_PY_CAST_,_param)(params.PARAM_FNAME(_param));
//populate the CONF variables with params
PARAM_FOREACH(AREA_SET,AREA_PARAMS_CONF)
//Parse arguments to overwrite CONF variables
if (!PyArg_ParseTupleAndKeywords(args, keywds,
"|" PARAM_PY_KWDS(AREA_PARAMS_CONF), kwlist,
PARAM_REF(NAME,AREA_PARAMS_CONF)))
Py_Error(Base::BaseExceptionFreeCADError,
"Wrong parameters, call getParamsDesc() to get supported params");
#define AREA_GET(_param) \
params.PARAM_FNAME(_param) = \
PARAM_TYPED(PARAM_CAST_PY_,_param)(PARAM_FNAME(_param));
//populate 'params' with the CONF variables
PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF)
getAreaPtr()->setParams(params);
return Py_None;
}
PyObject* AreaPy::getParams(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
Py_Error(Base::BaseExceptionFreeCADError, "This method accepts no argument");
const AreaParams &params =getAreaPtr()->getParams();
PyObject *dict = PyDict_New();
#define AREA_SRC(_v) params._v
PARAM_PY_DICT_SET_VALUE(dict,AREA_SRC,AREA_PARAMS_CONF)
return dict;
}
PyObject* AreaPy::getParamsDesc(PyObject *args, PyObject *keywds)
{
PyObject *pcObj = Py_True;
static char *kwlist[] = {"as_string", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds,"|O",kwlist,&pcObj))
Py_Error(Base::BaseExceptionFreeCADError, "This method accepts no argument");
if(PyObject_IsTrue(pcObj))
return PyString_FromString(PARAM_PY_DOC(NAME,AREA_PARAMS_CONF));
PyObject *dict = PyDict_New();
PARAM_PY_DICT_SET_DOC(dict,AREA_PARAMS_CONF)
return dict;
}
// custom attributes get/set
PyObject *AreaPy::getCustomAttributes(const char* /*attr*/) const
{
return 0;
}
int AreaPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@ -22,6 +22,7 @@ link_directories(${OCC_LIBRARY_DIR})
set(Path_LIBS
# Robot
Part
area-native
FreeCADApp
)
@ -30,6 +31,7 @@ generate_from_xml(PathPy)
generate_from_xml(ToolPy)
generate_from_xml(TooltablePy)
generate_from_xml(FeaturePathCompoundPy)
generate_from_xml(AreaPy)
SET(Python_SRCS
CommandPy.xml
@ -41,6 +43,8 @@ SET(Python_SRCS
TooltablePyImp.cpp
FeaturePathCompoundPy.xml
FeaturePathCompoundPyImp.cpp
AreaPy.xml
AreaPyImp.cpp
)
SET(Mod_SRCS
@ -67,6 +71,12 @@ SET(Path_SRCS
FeaturePathCompound.h
FeaturePathShape.cpp
FeaturePathShape.h
Area.cpp
Area.h
AreaParams.h
ParamsHelper.h
FeatureArea.cpp
FeatureArea.h
${Mod_SRCS}
${Python_SRCS}
)

View File

@ -0,0 +1,157 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <BRep_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include "FeatureArea.h"
#include <App/DocumentObjectPy.h>
#include <Base/Placement.h>
#include <Mod/Part/App/PartFeature.h>
using namespace Path;
PROPERTY_SOURCE(Path::FeatureArea, Part::Feature)
PARAM_ENUM_STRING_DECLARE(static const char *Enums,AREA_PARAMS_ALL)
FeatureArea::FeatureArea()
{
ADD_PROPERTY(Sources,(0));
ADD_PROPERTY(WorkPlane,(TopoDS_Shape()));
PARAM_PROP_ADD("Area",AREA_PARAMS_OPCODE);
PARAM_PROP_ADD("Area",AREA_PARAMS_BASE);
PARAM_PROP_ADD("Offset",AREA_PARAMS_OFFSET);
PARAM_PROP_ADD("Pocket",AREA_PARAMS_POCKET);
PARAM_PROP_ADD("Offset Settings", AREA_PARAMS_OFFSET_CONF);
PARAM_PROP_ADD("libarea Settings",AREA_PARAMS_CAREA);
PARAM_PROP_SET_ENUM(Enums,AREA_PARAMS_ALL);
PocketMode.setValue((long)0);
}
FeatureArea::~FeatureArea()
{
}
App::DocumentObjectExecReturn *FeatureArea::execute(void)
{
std::vector<App::DocumentObject*> links = Sources.getValues();
if (links.empty())
return new App::DocumentObjectExecReturn("No shapes linked");
for (std::vector<App::DocumentObject*>::iterator it = links.begin(); it != links.end(); ++it) {
if (!(*it && (*it)->isDerivedFrom(Part::Feature::getClassTypeId())))
return new App::DocumentObjectExecReturn("Linked object is not a Part object (has no Shape).");
TopoDS_Shape shape = static_cast<Part::Feature*>(*it)->Shape.getShape().getShape();
if (shape.IsNull())
return new App::DocumentObjectExecReturn("Linked shape object is empty");
}
AreaParams params;
#define AREA_PROP_GET(_param) \
params.PARAM_FNAME(_param) = PARAM_FNAME(_param).getValue();
PARAM_FOREACH(AREA_PROP_GET,AREA_PARAMS_CONF)
Area area(&params);
TopoDS_Shape workPlane = WorkPlane.getShape().getShape();
if(!workPlane.IsNull())
area.setPlane(workPlane);
area.clean(true);
for (std::vector<App::DocumentObject*>::iterator it = links.begin(); it != links.end(); ++it) {
area.add(static_cast<Part::Feature*>(*it)->Shape.getShape().getShape(),
PARAM_PROP_ARGS(AREA_PARAMS_OPCODE));
}
std::list<TopoDS_Shape> shapes;
if(fabs(Offset.getValue())>Precision::Confusion())
area.makeOffset(shapes,PARAM_PROP_ARGS(AREA_PARAMS_OFFSET));
if(PocketMode.getValue()) {
Area areaPocket(&params);
if(shapes.empty())
areaPocket.add(area.getShape());
else{
bool front = true;
if(shapes.size()>1) {
double step = Stepover.getValue();
if(fabs(step)<Precision::Confusion())
step = Offset.getValue();
front = step>0;
}
areaPocket.add(front?shapes.front():shapes.back());
}
shapes.push_back(areaPocket.makePocket(PARAM_PROP_ARGS(AREA_PARAMS_POCKET)));
}
if(shapes.empty())
this->Shape.setValue(area.getShape());
else if(shapes.size()==1)
this->Shape.setValue(shapes.front());
else {
BRep_Builder builder;
TopoDS_Compound compound;
builder.MakeCompound(compound);
for(const TopoDS_Shape &s : shapes)
builder.Add(compound,s);
this->Shape.setValue(compound);
}
return Part::Feature::execute();
}
short FeatureArea::mustExecute(void) const
{
if (Sources.isTouched())
return 1;
if (WorkPlane.isTouched())
return 1;
PARAM_PROP_TOUCHED(AREA_PARAMS_ALL)
return Part::Feature::mustExecute();
}
// Python Area feature ---------------------------------------------------------
namespace App {
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(Path::FeatureAreaPython, Path::FeatureArea)
template<> const char* Path::FeatureAreaPython::getViewProviderName(void) const {
return "PathGui::ViewProviderArea";
}
/// @endcond
// explicit template instantiation
template class PathExport FeaturePythonT<Path::FeatureArea>;
}

View File

@ -0,0 +1,65 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#ifndef PATH_FeatureArea_H
#define PATH_FeatureArea_H
#include <App/DocumentObject.h>
#include <App/GeoFeature.h>
#include <App/PropertyFile.h>
#include <App/PropertyGeo.h>
#include <App/FeaturePython.h>
#include "Mod/Part/App/PartFeature.h"
#include "Area.h"
namespace Path
{
class PathExport FeatureArea : public Part::Feature
{
PROPERTY_HEADER(Path::FeatureArea);
public:
/// Constructor
FeatureArea(void);
virtual ~FeatureArea();
/// returns the type name of the ViewProvider
virtual const char* getViewProviderName(void) const {
return "PathGui::ViewProviderArea";
}
virtual App::DocumentObjectExecReturn *execute(void);
virtual short mustExecute(void) const;
App::PropertyLinkList Sources;
Part::PropertyPartShape WorkPlane;
PARAM_PROP_DECLARE(AREA_PARAMS_ALL)
};
typedef App::FeaturePythonT<FeatureArea> FeatureAreaPython;
} //namespace Path
#endif // PATH_FeaturePath_H

View File

@ -0,0 +1,998 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#ifndef PARAMS_HELPER_H
#define PARAMS_HELPER_H
/** \page ParamPage Parameter helper macros
* Collections of macros for managing groups of parameters.
*
* \section Motivation
*
* For an application like FreeCAD, there are often cases where the same set of
* parameters are refered in dozons of different places. The macros here is
* designed to help managing those parameters, so that you can define groups of
* parameters once, and refer them everywhere in groups with simple macro calls for
* all kinds of purposes. Any changing, adding and removing of parameters in the
* group become much easier. And by everywhere, I mean \ref ParamCommon
* "class definition, impelentation", \ref ParamProperty "document object properties",
* \ref ParamPy "python c++ classes", and even \ref ParamDoc "doc string",
* pretty much everything except the python code, which although not implemented
* yet, is in fact also possible to be done using C preprocessor (No one says C
* preprocessor must produce C code :). It is also possible (not implemented
* yet) to use macros to generate python wrapper class instead of using
* FreeCAD's current xml python export.
*
* \section Debugging
*
* Extensive use of macros has one noticable disadvantage, though. If some thing
* goes wrong, the compiler error message is kind of cryptic. If so, first
* double check your macro definition of the parameter is correctly, not missing
* or having extra parathesis or comma. Then, you can use the CMake
* intermeidate file target to get the preprocessor output for checking. For
* example, for a file located at \c src/Mod/Path/App/Area.cpp,
* \code{.sh}
* cd <your_build_dir>/src/Mod/Path/App
* make Area.cpp.i
* \endcode
*
* The preprocessed intermediate output will be at,
* \code{.sh}
* <your_build_dir>/src/Mod/Path/App.CMakeFiles/Path.dir/Area.cpp.i
* \endcode
*
* \section Intrudction of Boost.Preprocessor
*
* The macros here make heavy use of the awsome
* [Boost.Preprocessor](http://www.boost.org/libs/preprocessor/) (short for
* Boost.PP). Here are some brief introduction on Boost.PP conecept in order to
* explain why this marco library is designed the way it is.
*
* In Boost.PP, a sequence is defined as,
* \code{.sh}
* (a)(b)(c)...
* \endcode
*
* A sequence cannot be empty. Thus, \c () is not a sequence. And also those
* <tt>a, b, c</tt> here cannot directly contain <tt>,</tt>. These restriction
* is due to the fact that <tt>( ) ,</tt> are among those very few special
* characters recognized by the preprocssor. \c a can itself be a sequence or
* other Boost.PP types, so by right, our parameter can be defined as something
* like
* \code{.sh}
* ((type)(name)(default)...)
* \endcode
*
* A bit awkward to write. So another Boost.PP type is chosen, tuple, to define
* each individual parameter. A tuple is defined as
* \code{.sh}
* (a,b,c ...)
* \endcode
*
* This is why the parameter definition requires a double parathesis, as shown
* in the following section.
*
* \section Library Overview
*
* In this macro library, a parameter is defined using a tuple inside a sequence,
* \code{.sh}
* ((<type>, <arg>, <name>, <default>, <doc>, <seq>, <info>))
* \endcode
*
* - \c type is the type of the parameter. Currently only five types of
* parameters are defined, <tt>short, long, double, bool, enum, enum2</tt>.
* \enum2 type is the same as \enum with additional information to be able to
* map to a user defined C enum type. To add more types, search this file for
* keyword \a _short, and supply all relavant macros. It's quite trivial
* actually.
*
* - \c arg is the agument name. It is intended to be used as function argument.
* By convention, the name shall be all small cases, but that's not required.
* This \c arg can be repurposed, if the parameter is not going to be used as
* function agument. The #AREA_PARAMS_CAREA parameters repurposed this field
* to CArea internal setting variables to implement save, apply and restore
* function using CAreaConfig class.
*
* - \c name is normally a %CamelCase name which are used as member variable and
* property name. Because of this, make sure the names are unique to avoid
* conflicts.
*
* - \c default is the default value of this parameter. Right now, you must
* supply a default value. Boost.PP has trouble dealing with empty values.
* Remember that a sequence cannot be empty. Neight can tuple. Only array,
* something like <tt>(0,())</tt> for an empty array. It is awkward to write,
* and didn't add much functionality I want, hence the restriction of
* non-empty defaults here.
*
* - \c doc is a string to describe the variable.
*
* - \c seq \anchor ParamSeq. Right now this field is used by \c enum and
* \c enum2 type parameter to define its enumerations. As the name suggests,
* it must be a sequence. It is not a tuple because looping through tuple is
* not as easy as sequence. Other type of parameter do not need to have this
* field
*
* - \c info is used to provide the supplimentery information for \c enum2 type
* of parameter, which can be converted to a user defined enum type by
* #PARAM_ENUM_CONVERT. \c info must be a tuple, with the user defined enum
* type as the first element, and a prefix as the second element. For \c enum2
* type of parameter, this field is mandatory.
*
* The common usage is that you define a macro of a group of parameters. And use
* the macro helper here to do operation on each parameter in the group. See
* AreaParams.h file for an example of parameter definitions.
*
* Area.h, Area.cpp, FeatureArea.h, FeatureArea.cpp for usage of variouse macros.
*
* See struct AreaDoc for an example of doc string generation.
*
* Each field of the parameter can be refered to with various
* \ref ParamAccessor "various accessor macros", and can be easily
* \ref ParamStringizer "stringified".
*
* \anchor ParamField You can also use #PARAM_FIELD(_field,_param) to refer to
* each field, where \a _field is one of <tt>TYPE, ARG, NAME, DEF, DOC, or SEQ</tt>.
* And #PARAM_FIELD_STR to stringify.
*
* Here \a _param is the parameter definition described above in the form of a
* Boost.PP tuple, and is usally supplied by various \ref ParamLooper "looper macros"
*
* You can of course directly use various Boost.PP sequence looper to pass
* aditional arguments to the operation macro. See #PARAM_PY_DICT_SET_VALUE for
* an example of using tuple, and the more complex example #PARAM_ENUM_CONVERT
*
* Note that when generating comma separated list, the first and last comma are
* conveniently ommited, so that the macros can be mixed with others intuitively
*/
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/pop_front.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/logical/not.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/comparison/greater.hpp>
/** \defgroup ParamHelper Parameters helper macros
* Collections of macros for managing groups of parameters */
/**
* \defgroup ParamAccessor Field accessors
* To abstract parameter field details
* \ingroup ParamHelper
* @{
*/
#define PARAM_ITYPE 0
#define PARAM_IARG 1
#define PARAM_INAME 2
#define PARAM_IDEF 3
#define PARAM_IDOC 4
#define PARAM_ISEQ 5
#define PARAM_IINFO 6
#define PARAM_FIELD(_idx,_param) BOOST_PP_TUPLE_ELEM(PARAM_I##_idx,_param)
#define PARAM_FTYPE(_param) PARAM_FIELD(TYPE,_param)
#define PARAM_FARG(_param) PARAM_FIELD(ARG,_param)
#define PARAM_FNAME(_param) PARAM_FIELD(NAME,_param)
#define PARAM_FDEF(_param) PARAM_FIELD(DEF,_param)
#define PARAM_FDOC(_param) PARAM_FIELD(DOC,_param)
#define PARAM_FSEQ(_param) PARAM_FIELD(SEQ,_param)
#define PARAM_FINFO(_param) PARAM_FIELD(INFO,_param)
#define PARAM_FENUM_TYPE(_param) BOOST_PP_TUPLE_ELEM(0,PARAM_FINFO(_param))
#define PARAM_FENUM_PREFIX(_param) BOOST_PP_TUPLE_ELEM(1,PARAM_FINFO(_param))
/** @} */
/**
* \defgroup ParamStringizer Field stringizers
* \ingroup ParamHelper
* @{ */
#define PARAM_FIELD_STR(_idx,_param) \
BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(PARAM_I##_idx,_param))
#define PARAM_FTYPE_STR(_param) PARAM_FIELD_STR(TYPE,_param)
#define PARAM_FARG_STR(_param) PARAM_FIELD_STR(ARG,_param)
#define PARAM_FNAME_STR(_param) PARAM_FIELD_STR(NAME,_param)
#define PARAM_FDEF_STR(_param) PARAM_FIELD_STR(DEF,_param)
/** @} */
/** Helper for #PARAM_FSEQ_STR */
#define PARAM_FSEQ_STR_(_i,_elem) \
BOOST_PP_COMMA_IF(_i) BOOST_PP_STRINGIZE(_elem)
/** \c SEQ stringizer will stringify each element separately
*
* Expands to:
* #seq[0], #seq[1] ...
* \ingroup ParamHelper
*/
#define PARAM_FSEQ_STR(_param) \
PARAM_FOREACH_I(PARAM_FSEQ_STR_,PARAM_FSEQ(_param))
/** \defgroup ParamLooper Looper macros
* Macros for looping through sequence to parameters
* \ingroup ParamHelper
*/
/** Helper for #PARAM_FOREACH */
#define PARAM_FOREACH_(_,_op,_param) _op(_param)
/** Apply macro \a _op to each parameter in sequence \a _seq
*
* Operation macro \a _op shoud be defined as,
* \code
* _op(_param)
* \endcode
* \ingroup ParamLooper
*/
#define PARAM_FOREACH(_op,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_FOREACH_,_op,_seq)
/** Helper for #PARAM_FOREACH_I */
#define PARAM_FOREACH_I_(_,_op,_i,_param) _op(_i,_param)
/** Apply macro \a _op to each parameter in sequence \a _seq with additional index
*
* Operation macro \a _op shoud be defined as,
* \code
* _op(_i,_param)
* \endcode
* \ingroup ParamLooper
* */
#define PARAM_FOREACH_I(_op,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_FOREACH_I_,_op,_seq)
/** Helper for #PARAM_TYPED_FOREACH */
#define PARAM_TYPED_FOREACH_(_1,_op,_param) \
PARAM_TYPED(_op,_param)(_param)
/** Type depended macro construction
*
* Convert macro \a _op to \a _op##\<type\>. Note that it only converts the
* macro name, not contsucts a macro call. To expand to a macro call, simply
* \code
* PARAM_TYPED(_op,_param)(_param)
* \endcode
* \ingroup ParamLooper
*/
#define PARAM_TYPED(_op,_param) \
BOOST_PP_CAT(_op,PARAM_FTYPE(_param))
/** Apply type dependent macro call to a sequence of parameters
*
* \a _op will be converted to \a _op##\<type\> for each parameter
* \ingroup ParamLooper
*/
#define PARAM_TYPED_FOREACH(_op,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_TYPED_FOREACH_,_op,_seq)
/** \defgroup ParamCommon Common helpers
* \ingroup ParamHelper
*/
#define PARAM_TYPE_short short
#define PARAM_TYPE_long long
#define PARAM_TYPE_double double
#define PARAM_TYPE_bool bool
#define PARAM_TYPE_enum short
#define PARAM_TYPE_enum2 short
/** Obtain parameter type
*
* The main purpose is to alias enum type to short
* \ingroup ParamCommon
*/
#define PARAM_TYPE(_param) \
PARAM_TYPED(PARAM_TYPE_,_param)
/** Helper for #PARAM_DECLARE */
#define PARAM_DECLARE_(_1,_field,_param) \
PARAM_TYPE(_param) PARAM_FIELD(_field,_param);
/**
* Delcares parameters using the given field as name
*
* \arg \c _field: specifies the \ref ParamField "field" to use as name
*
* Expands to:
* \code{.unparsed}
* type1 _field1;type2 _field2; ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_DECLARE(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_,_field,_seq)
/** Helper for #PARAM_DECLARE_INIT */
#define PARAM_DECLARE_INIT_(_1,_field,_param) \
PARAM_TYPE(_param) PARAM_FIELD(_field,_param) = PARAM_FDEF(_param);
/**
* Delcares parameters with initialization to default using the given field as
* name
*
* \arg \c _field: \ref ParamField "field" to use as name
*
* Expands to:
* \code{.unparsed}
* type1 _field1=_def1;type2 _field2=_def2; ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_DECLARE_INIT(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_INIT_,_field,_seq)
#define PARAM_ENUM_DECLARE_enum_(_1,_name,_i,_elem) \
BOOST_PP_COMMA_IF(_i) BOOST_PP_CAT(_name,_elem)
#define PARAM_ENUM_DECLARE_enum(_param) \
enum {BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_DECLARE_enum_,PARAM_FNAME(_param),PARAM_FSEQ(_param))};
#define PARAM_ENUM_DECLARE_short(_param)
#define PARAM_ENUM_DECLARE_long(_param)
#define PARAM_ENUM_DECLARE_double(_param)
#define PARAM_ENUM_DECLARE_bool(_param)
#define PARAM_ENUM_DECLARE_enum2 PARAM_ENUM_DECLARE_enum
/** \defgroup ParamEnumHelper Enum convert helpers
* \ingroup ParamCommon
*/
/** Make anonymous \c enum type
*
* Make anonymous \c enum type for \c enum type parameters in \a _seq. All other
* types are ignored. The enum member is prefixed with \a _name. Expand to:
* \code{.unparsed}
* enum {_name1##_seq1[0], _name1##_seq1[1] ...};
* enum {_name2##_seq2[0], _name2##_seq2[1] ...};
* ...
* \endcode
* \ingroup ParamEnumHelper*/
#define PARAM_ENUM_DECLARE(_seq) \
PARAM_TYPED_FOREACH(PARAM_ENUM_DECLARE_,_seq)
/** \addgroup ParamEnumHelper Enum convert helpers
* @{ */
#define PARAM_ENUM_CONVERT_short(...)
#define PARAM_ENUM_CONVERT_long(...)
#define PARAM_ENUM_CONVERT_double(...)
#define PARAM_ENUM_CONVERT_bool(...)
#define PARAM_ENUM_CONVERT_enum(...)
#define PARAM_ENUM_CONVERT_enum2 PARAM_ENUM_CONVERT_SINGLE
#define PARAM_ENUM_CONVERT_enum_(_dst,_name,_prefix,_elem) \
case BOOST_PP_CAT(_name,_elem):\
_dst(_name) = \
BOOST_PP_CAT(_prefix,_elem);\
break;
#define PARAM_ENUM_CONVERT__(_1,_args,_i,_elem) \
PARAM_ENUM_CONVERT_enum_(BOOST_PP_TUPLE_ELEM(0,_args),\
BOOST_PP_TUPLE_ELEM(1,_args),\
BOOST_PP_TUPLE_ELEM(2,_args),\
_elem);
#define PARAM_ENUM_CONVERT_(_1,_args,_param) \
PARAM_TYPED(PARAM_ENUM_CONVERT_,_param)(BOOST_PP_TUPLE_ELEM(0,_args),\
BOOST_PP_TUPLE_ELEM(1,_args),\
BOOST_PP_TUPLE_ELEM(2,_args),\
_param)
/** Convert single enum parameter value into user defined enum type
*
* This macro is used by #PARAM_ENUM_CONVERT to convert each parameter, but
* you can use it directly for a single parameter. Check #PARAM_NUM_CONVERT
* for more detail. Make sure the outer parathesis of \c _param is stripped,
* i.e. not double but single parathesis
*/
#define PARAM_ENUM_CONVERT_SINGLE(_src,_dst,_default,_param) \
PARAM_FENUM_TYPE(_param) _dst(PARAM_FNAME(_param));\
switch(_src(PARAM_FNAME(_param))) {\
BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_CONVERT__,\
(_dst,PARAM_FNAME(_param),PARAM_FENUM_PREFIX(_param)),PARAM_FSEQ(_param))\
default: \
_default(_param);\
}
/** Default handling in #PARAM_ENUM_CONVERT and #PARAM_ENUM_CHECK*/
#define PARAM_ENUM_EXCEPT(_param) \
throw Base::ValueError("invalid value for enum " PARAM_FNAME_STR(_param))
/** @} */
/* Convert ParamHelper defined enum type to user defined ones
*
* This assumes the user defined enum type is given in \ref ParamSeq "seq_type"
* of the parameter definition, and it has the same postfix as the ones
* speficied in \ref ParamSeq "seq" member of the parameter definition. See
* \ref ParamEnumHelper "here" for implementations
*
* \ingroup ParamEnumHelper
*
* \arg \c _src: Optional macro to generate source variable. The signature must
* be <tt>_src(_name)<\tt>, where \c _name will be the parameters \a name field.
* In case you just want \c _name as the source variable name, you can simply
* omit this argument, because newer C++ preprocessor allows empty argument.
* \arg \c _dst: Optional macro to generate destination variable. Same as above.
* \arg \c _default: A macro to call for invalid value. Signature should be
* <tt>_default(_param)<\tt>, where \c _param is the parameter definition. You
* can use #PARAM_ENUM_EXCEPT to throw Base::ValueError exception in FreeCAD
* \arg \c _seq: Parameter sequence
*
* For example, with the following parameter definition
* \code{.unparsed}
* #define MY_PARAM_TEST \
* ((enum,test1,Test1,0,"it's a test",(Foo)(Bar),(MyEnum1,myEnum1)) \
* ((enum,test2,Test2,0,"it's a test",(Foo)(Bar),(MyEnum2,myEnum2)))
*
* #define MY_DST(_v) BOOST_PP_CAT(my,_v)
* \code{.unparsed}
*
* calling (note that the \c _src macro is omitted)
* \code{.unparsed}
* PARAM_ENUM_CONVERT(,MY_DST,My,PARAM_ENUM_EXCEP,MY_PARAM_TEST)
* \code{.unparsed}
*
* expands to
* \code{.unparsed}
* MyEnum1 myTest1;
* switch(Test1) {
* case Test1Foo:
* myTest1 = myEnum1Foo;
* break;
* case Test1Bar:
* myTest1 = myEnum1Bar;
* break;
* default:
* throw Base::ValueError("invalid value for enum Test1");
* }
* MyEnum2 myTest2;
* switch(Test2) {
* case Test1Foo:
* myTest2 = myEnum2Foo;
* break;
* case Test2Bar:
* myTest2 = myEnum2Bar;
* break;
* default:
* throw Base::ValueError("invalid value for enum Test2");
* }
* \endcode
*
* The above code assumes you've already defined \a Test1 and \a Test2 some
* where as the source variable.
*/
#define PARAM_ENUM_CONVERT(_src,_dst,_default,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_ENUM_CONVERT_,(_src,_dst,_default),_seq)
#define PARAM_ENUM_CHECK_short(...)
#define PARAM_ENUM_CHECK_long(...)
#define PARAM_ENUM_CHECK_double(...)
#define PARAM_ENUM_CHECK_bool(...)
#define PARAM_ENUM_CHECK_enum PARAM_ENUM_CHECK_SINGLE
#define PARAM_ENUM_CHECK_enum2 PARAM_ENUM_CHECK_SINGLE
#define PARAM_ENUM_CHECK_enum_(_1,_name,_i,_elem) \
case BOOST_PP_CAT(_name,_elem): break;
#define PARAM_ENUM_CHECK_(_1,_args,_param) \
PARAM_TYPED(PARAM_ENUM_CHECK_,_param)(BOOST_PP_TUPLE_ELEM(0,_args),\
BOOST_PP_TUPLE_ELEM(1,_args),\
_param)
#define PARAM_ENUM_CHECK_SINGLE(_src,_default,_param) \
switch(_src(PARAM_FNAME(_param))) {\
BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_CHECK_enum_,\
PARAM_FNAME(_param),PARAM_FSEQ(_param))\
default: \
_default(_param);\
}
/* Validate enum type parameters
*
* This macro validates the value a variable of enum type parameters. See
* similar macro #PARAM_ENUM_CONVERT for detail usage.
*
* \ingroup ParamEnumHelper
*
* \arg \c _src: Optional macro to generate source variable. The signature must
* be <tt>_src(_name)<\tt>, where \c _name will be the parameters \a name field.
* In case you just want \c _name as the source variable name, you can simply
* omit this argument, because newer C++ preprocessor allows empty argument.
*
* \arg \c _default: A macro to call for invalid value. Signature should be
* <tt>_default(_param)<\tt>, where \c _param is the parameter definition. You
* can use #PARAM_ENUM_EXCEPT to throw Base::ValueError exception in FreeCAD
*
* \arg \c _seq: Parameter sequence
*/
#define PARAM_ENUM_CHECK(_src,_default,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_ENUM_CHECK_,(_src,_default),_seq)
#define PARAM_ENUM_STRING_DECLARE_short(...)
#define PARAM_ENUM_STRING_DECLARE_long(...)
#define PARAM_ENUM_STRING_DECLARE_double(...)
#define PARAM_ENUM_STRING_DECLARE_bool(...)
#define PARAM_ENUM_STRING_DECLARE_enum2 PARAM_ENUM_STRING_DECLARE_enum
/** Helper for #PARAM_ENUM_STRING_DECLARE */
#define PARAM_ENUM_STRING_DECLARE_enum(_prefix,_param) \
BOOST_PP_CAT(_prefix,PARAM_FNAME(_param))[] = {PARAM_FSEQ_STR(_param),NULL};
/** Helper for #PARAM_ENUM_STRING_DECLARE */
#define PARAM_ENUM_STRING_DECLARE_(_1,_prefix,_param) \
PARAM_TYPED(PARAM_ENUM_STRING_DECLARE_,_param)(_prefix,_param)
/** Make \c enum string list
*
* Roughly translated:
* \code{.unparsed}
* _prefix##_name1[] = {#seq1[0], #seq1[1], ...,NULL};
* _prefix##_name2[] = {#seq2[0], #seq2[1], ...,NULL};
* ...
* \endcode
* Example usage:
* PARAM_ENUM_STRING_DECLARE(static const char *Enum, MyParamsSeq)
* \ingroup ParamEnumHelper
*/
#define PARAM_ENUM_STRING_DECLARE(_prefix,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_ENUM_STRING_DECLARE_,_prefix,_seq)
/** Helper for #PARAM_INIT */
#define PARAM_INIT_(_,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_FIELD(_field,_param)(PARAM_FDEF(_param))
/** Constructor initialization
*
* \arg \c _field: specifies the \ref ParamField "field" to use as name
*
* Expand to,
* \code{.unparsed}
* field1(def1), field2(def2)...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_INIT(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_INIT_,_field,_seq)
/** Helper for #PARAM_ARGS_DEF */
#define PARAM_ARGS_DEF_(_,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) PARAM_FIELD(_field,_param)=PARAM_FDEF(_param)
/** Delcare the parameters as function argument list with defaults.
*
* \arg \c _field: specifies the \ref ParamField "field" to use as name
*
* Expand to:
* \code{.unparsed}
* type1 field1=def1, type2 field2=def2 ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_ARGS_DEF(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_DEF_,_field,_seq)
/** Helper for #PARAM_ARGS */
#define PARAM_ARGS_(_,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) PARAM_FIELD(_field,_param)
/** Delcare the parameters as function argument list without defaults.
*
* \arg \c _field: specifies the \ref ParamField "field" to use as name
*
* Expand to:
* \code{.unparsed}
* type1 field1, type2 field2 ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_ARGS(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_,_field,_seq)
/** \defgroup ParamPy Python helper
* Helper macros for Python bindings
* \ingroup ParamHelper
*/
/** \defgroup ParamDoc Python doc helper
* Generate argument doc string for Python
* \ingroup ParamPy
*/
/** Helper for #PARAM_PY_DOC_enum */
#define PARAM_PY_DOC_enum_(_i,_elem) \
BOOST_PP_IF(_i,","," ") #_i "=" #_elem
/** Generate doc for an enum parameter */
#define PARAM_PY_DOC_enum(_field,_param) \
"\n* " PARAM_FIELD_STR(_field,_param) "(" PARAM_FDEF_STR(_param)"):" \
PARAM_FOREACH_I(PARAM_PY_DOC_enum_, PARAM_FSEQ(_param)) ". " \
PARAM_FDOC(_param) "\n"
/* Generate doc for other type of parameter */
#define PARAM_PY_DOC_short(_field,_param) \
"\n* " PARAM_FIELD_STR(_field,_param) "(" PARAM_FDEF_STR(_param)"): " \
PARAM_FDOC(_param) "\n"
#define PARAM_PY_DOC_long PARAM_PY_DOC_short
#define PARAM_PY_DOC_double PARAM_PY_DOC_short
#define PARAM_PY_DOC_bool PARAM_PY_DOC_short
#define PARAM_PY_DOC_enum2 PARAM_PY_DOC_enum
#define PARAM_PY_DOC_(_,_field,_param) \
PARAM_TYPED(PARAM_PY_DOC_,_param)(_field,_param)
/* Generate document of a sequence of parameters
* \ingroup ParamDoc
*/
#define PARAM_PY_DOC(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DOC_,_field,_seq)
/** Helper for #PARAM_PY_ARGS_DOC */
#define PARAM_PY_ARGS_DOC_(_,_field,_i,_param) \
BOOST_PP_IF(_i,", "," ") PARAM_FIELD_STR(_field,_param) "=" PARAM_FDEF_STR(_param)
/** Generate argument list string
* \arg \c _field: specifies the \ref ParamField "field" to use as name
*
* Expand to a single string:
* \code{.unparsed}
* "_field1=_def1,_field2=_def2 ..."
* \endcode
*
* \ingroup ParamDoc
*/
#define PARAM_PY_ARGS_DOC(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_PY_ARGS_DOC_,_field,_seq)
/** Helper for #PARAM_FIELDS */
#define PARAM_FIELDS_(_1,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_FIELD(_field,_param)
/** Expand to a list of the given field in the parameter sequence
*
* For example, PARAM_FIELDS(ARG, _seq) expands to:
* \code{.unparsed}
* arg1,arg2 ...
* \endcode
* \ingroup ParamCommon ParamPy
*/
#define PARAM_FIELDS(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELDS_,_field,_seq)
#define PARAM_PY_CAST_short(_v) (_v)
#define PARAM_PY_CAST_long(_v) (_v)
#define PARAM_PY_CAST_double(_v) (_v)
#define PARAM_PY_CAST_bool(_v) ((_v)?Py_True:Py_False)
#define PARAM_PY_CAST_enum(_v) (_v)
#define PARAM_PY_CAST_enum2(_v) (_v)
#define PARAM_CAST_PY_short(_v) (_v)
#define PARAM_CAST_PY_long(_v) (_v)
#define PARAM_CAST_PY_double(_v) (_v)
#define PARAM_CAST_PY_bool(_v) (PyObject_IsTrue(_v)?true:false)
#define PARAM_CAST_PY_enum(_v) (_v)
#define PARAM_CAST_PY_enum2(_v) (_v)
/** Helper for #PARAM_PY_FIELDS */
#define PARAM_PY_FIELDS_(_1,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_TYPED(PARAM_CAST_PY_,_param)(PARAM_FIELD(_field,_param))
/** Expand to a list of the given field in the sequence
*
* The field will be casted from python C to C type
* \ingroup ParamCommon ParamPy
*/
#define PARAM_PY_FIELDS(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_PY_FIELDS_,_field,_seq)
/** Helper for #PARAM_FIELD_STRINGS */
#define PARAM_FIELD_STRINGS_(_1,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_FIELD_STR(_field,_param)
/** Expand to a list of stringified fields
* \ingroup ParamCommon ParamPy
*/
#define PARAM_FIELD_STRINGS(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELD_STRINGS_,_field,_seq)
#define PARAM_PYARG_short "h"
#define PARAM_PYARG_long "l"
#define PARAM_PYARG_double "d"
#define PARAM_PYARG_bool "O"
#define PARAM_PYARG_enum "h"
#define PARAM_PYARG_enum2 "h"
/** Helper for #PARAM_PY_KWDS */
#define PARAM_PY_KWDS_(_param) \
PARAM_TYPED(PARAM_PYARG_,_param)
/** Generate a format string for kewords based argument
* \ingroup ParamPy
*/
#define PARAM_PY_KWDS(_seq) \
PARAM_FOREACH(PARAM_PY_KWDS_,_seq)
#define PARAM_PY_TYPE_short short
#define PARAM_PY_TYPE_long long
#define PARAM_PY_TYPE_double double
#define PARAM_PY_TYPE_bool PyObject*
#define PARAM_PY_TYPE_enum short
#define PARAM_PY_TYPE_enum2 short
/** Helper for #PARAM_PY_DECLARE */
#define PARAM_PY_DECLARE_(_1,_field,_param) \
PARAM_TYPED(PARAM_PY_TYPE_,_param) PARAM_FIELD(_field,_param);
/** Declare field variables for Python C type without initialization
* \ingroup ParamPy
*/
#define PARAM_PY_DECLARE(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_,_field,_seq)
#define PARAM_PY_INIT_short(_v) _v
#define PARAM_PY_INIT_long(_v) _v
#define PARAM_PY_INIT_double(_v) _v
#define PARAM_PY_INIT_bool(_v) ((_v)?Py_True:Py_False)
#define PARAM_PY_INIT_enum(_v) _v
#define PARAM_PY_INIT_enum2(_v) _v
/** Helper for #PARAM_PY_DECLARE_INIT */
#define PARAM_PY_DECLARE_INIT_(_1,_field,_param) \
PARAM_TYPED(PARAM_PY_TYPE_,_param) PARAM_FIELD(_field,_param) = \
PARAM_TYPED(PARAM_PY_INIT_,_param)(PARAM_FDEF(_param));
/** Declare field variables of Python c type with initialization to default
* \ingroup ParamPy
*/
#define PARAM_PY_DECLARE_INIT(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_INIT_,_field,_seq)
/** Helper for #PARAM_REF */
#define PARAM_REF_(_1,_field,_i,_param) \
BOOST_PP_COMMA_IF(_i) &PARAM_FIELD(_field,_param)
/** Generate a list of field references
*
* Expand to:
* \code{.unparsed}
* &_field1, &_field2 ...
* \endcode
* \ingroup ParamPy
*/
#define PARAM_REF(_field,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_REF_,_field,_seq)
#define PARAM_CAST_PYOBJ_short(_v) PyInt_FromLong(_v)
#define PARAM_CAST_PYOBJ_long(_v) PyInt_FromLong(_v)
#define PARAM_CAST_PYOBJ_double(_v) PyFloat_FromDouble(_v)
#define PARAM_CAST_PYOBJ_bool(_v) ((_v)?Py_True:Py_False)
#define PARAM_CAST_PYOBJ_enum PARAM_CAST_PYOBJ_short
#define PARAM_CAST_PYOBJ_enum2 PARAM_CAST_PYOBJ_short
/** Stringize field to a Python string */
#define PARAM_PY_STRINGIZE(_field,_param) \
PyString_FromString(PARAM_FIELD_STR(_field,_param))
/** Helper for #PARAM_PY_DICT_SET_VALUE */
#define PARAM_PY_DICT_SET_VALUE_(_1,_args,_param) \
PyDict_SetItem(BOOST_PP_TUPLE_ELEM(0,_args), \
PARAM_PY_STRINGIZE(NAME,_param),\
PARAM_TYPED(PARAM_CAST_PYOBJ_,_param)(\
BOOST_PP_TUPLE_ELEM(1,_args)(PARAM_FIELD(NAME,_param))));
/** Populate a Python dict with a structure variable
*
* \arg \c _dict: the Python dictionary object
* \arg \c _src: Optional macro to generate source variable. The signature must
* be <tt>_src(_name)<\tt>, where \c _name will be the parameters \a name field.
* In case you just want \c _name as the source variable name, you can simply
* omit this argument, because newer C++ preprocessor allows empty argument.
*
* Roughly translated to:
* \code{.unparsed}
* PyDict_SetItem(_dict,#name1,_src(name1));
* PyDict_SetItem(_dict,#name2,_src(name2));
* ...
* \endcode
* \ingroup ParamPy
*/
#define PARAM_PY_DICT_SET_VALUE(_dict,_src,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_VALUE_,(_dict,_src),_seq)
#define PARAM_PY_DICT_DOC_enum_(_i,_elem) \
BOOST_PP_IF(_i,","," ") #_i "=" #_elem
/** Generate doc for an enum parameter */
#define PARAM_PY_DICT_DOC_enum(_param) \
"(" PARAM_FDEF_STR(_param) ") - " \
PARAM_FOREACH_I(PARAM_PY_DOC_enum_, PARAM_FSEQ(_param)) ".\n" \
PARAM_FDOC(_param) "\n"
/* Generate doc for other type of parameter */
#define PARAM_PY_DICT_DOC_(_param) \
"(" PARAM_FDEF_STR(_param) ") - " PARAM_FDOC(_param) "\n"
#define PARAM_PY_DICT_DOC_short PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_long PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_double PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_bool PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_enum2 PARAM_PY_DICT_DOC_enum
/** Helper for #PARAM_PY_DICT_SET_DOC */
#define PARAM_PY_DICT_SET_DOC_(_1,_dict,_param) \
PyDict_SetItem(_dict, PARAM_PY_STRINGIZE(NAME,_param),\
PyString_FromString(PARAM_TYPED(PARAM_PY_DICT_DOC_,_param)(_param)));
/** Populate a Python dict with the doc field of the parameter sequence
*
* Roughly translated to:
* \code{.unparsed}
* PyDict_SetItem(_dict,#name1,doc1);
* PyDict_SetItem(_dict,#name2,doc2);
* ...
* \endcode
* \ingroup ParamDoc
*/
#define PARAM_PY_DICT_SET_DOC(_dict,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_DOC_,_dict,_seq)
/** \defgroup ParamProperty Property Macros
* Helper macros for FreeCAD properties
* \ingroup ParamHelper
* @{*/
#define PARAM_PROP_bool(_v) App::PropertyBool _v
#define PARAM_PROP_double(_v) App::PropertyFloat _v
#define PARAM_PROP_short(_v) App::PropertyInteger _v
#define PARAM_PROP_long(_v) App::PropertyInteger _v
#define PARAM_PROP_enum(_v) App::PropertyEnumeration _v
#define PARAM_PROP_enum2(_v) App::PropertyEnumeration _v
/** @} */
/** Helper for #PARAM_PROP_DECLARE */
#define PARAM_PROP_DECLARE_(_param) \
PARAM_TYPED(PARAM_PROP_,_param)(PARAM_FNAME(_param));
/** Declare FreeCAD properties
* \ingroup ParamProperty
*/
#define PARAM_PROP_DECLARE(_seq) \
PARAM_FOREACH(PARAM_PROP_DECLARE_,_seq)
/** Replace FreeCAD #ADD_PROPERTY_TYPE to fix singifying macro */
#define PARAM_ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_,_type_,_Docu_) \
do { \
this->_prop_.setValue _defaultval_;\
this->_prop_.setContainer(this); \
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), BOOST_PP_STRINGIZE(_prop_), &this->_prop_, (_group_),(_type_),(_Docu_)); \
} while (0)
/** Generic property adding */
#define PARAM_PROP_ADD_(_group,_param) \
PARAM_ADD_PROPERTY_TYPE(PARAM_FNAME(_param), (PARAM_FDEF(_param)),\
_group,App::Prop_None,PARAM_FDOC(_param));
#define PARAM_PROP_ADD_short PARAM_PROP_ADD_
#define PARAM_PROP_ADD_long PARAM_PROP_ADD_
#define PARAM_PROP_ADD_double PARAM_PROP_ADD_
#define PARAM_PROP_ADD_bool PARAM_PROP_ADD_
#define PARAM_PROP_ADD_enum2 PARAM_PROP_ADD_enum
/** Add \c enum type parameter as property */
#define PARAM_PROP_ADD_enum(_group,_param) \
PARAM_ADD_PROPERTY_TYPE(PARAM_FNAME(_param), ((long)PARAM_FDEF(_param)),\
_group,App::Prop_None,PARAM_FDOC(_param));
/** Helper for #PARAM_PROP_ADD */
#define PARAM_PROP_ADD_TYPED(_1,_group,_i,_param) \
PARAM_TYPED(PARAM_PROP_ADD_,_param)(_group,_param)
/** Add FreeCAD properties
* \ingroup ParamProperty
*/
#define PARAM_PROP_ADD(_group,_seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_PROP_ADD_TYPED,_group,_seq)
#define PARAM_PROP_SET_ENUM_short(...)
#define PARAM_PROP_SET_ENUM_long(...)
#define PARAM_PROP_SET_ENUM_bool(...)
#define PARAM_PROP_SET_ENUM_double(...)
#define PARAM_PROP_SET_ENUM_enum2 PARAM_PROP_SET_ENUM_enum
/** Setup \c enum type parameter */
#define PARAM_PROP_SET_ENUM_enum(_prefix,_param) \
PARAM_FNAME(_param).setEnums(BOOST_PP_CAT(_prefix,PARAM_FNAME(_param)));
/** Helper for #PARAM_PROP_SET_ENUM */
#define PARAM_PROP_SET_ENUM_TYPED(_1,_prefix,_param) \
PARAM_TYPED(PARAM_PROP_SET_ENUM_,_param)(_prefix,_param)
/* Setup the \c enum string list for \c enum type properties
* \ingroup ParamProperty
*/
#define PARAM_PROP_SET_ENUM(_prefix,_seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PROP_SET_ENUM_TYPED,_prefix,_seq)
/** Helper for #PARAM_PROP_ARGS */
#define PARAM_PROP_ARGS_(_i,_param) \
BOOST_PP_COMMA_IF(_i) PARAM_FNAME(_param).getValue()
/** Expand the property list as function arguments
*
* Expand to:
* \code{.unparsed}
* name1.getValue(), name2.getValue() ...
* \endcode
* \ingroup ParamProperty
*/
#define PARAM_PROP_ARGS(_seq) \
PARAM_FOREACH_I(PARAM_PROP_ARGS_,_seq)
/** Helper for #PARAM_PROP_TOUCHED */
#define PARAM_PROP_TOUCHED_(_param) \
if(PARAM_FNAME(_param).isTouched()) return 1;
/** Returns 1 if any properties is touched
*
* Expand to:
* \code{.unparsed}
* if(name1.isTouched()) return 1;
* if(name2.isTouched()) return 1;
* ...
* \ingroup ParamProperty
*/
#define PARAM_PROP_TOUCHED(_seq) \
PARAM_FOREACH(PARAM_PROP_TOUCHED_,_seq)
#endif // PARAMS_HELPER_H

View File

@ -35,6 +35,7 @@
#include "DlgSettingsPathColor.h"
#include "ViewProviderPathCompound.h"
#include "ViewProviderPathShape.h"
#include "ViewProviderArea.h"
// use a different name to CreateCommand()
void CreatePathCommands(void);
@ -77,6 +78,8 @@ PyMODINIT_FUNC initPathGui()
PathGui::ViewProviderPathCompoundPython ::init();
PathGui::ViewProviderPathShape ::init();
PathGui::ViewProviderPathPython ::init();
PathGui::ViewProviderArea ::init();
PathGui::ViewProviderAreaPython ::init();
// add resources and reloads the translators
loadPathResource();

View File

@ -81,6 +81,8 @@ SET(PathGui_SRCS_ViewProvider
ViewProviderPathCompound.h
ViewProviderPathShape.cpp
ViewProviderPathShape.h
ViewProviderArea.cpp
ViewProviderArea.h
)
SOURCE_GROUP("ViewProvider" FILES ${PathGui_SRCS_ViewProvider})

View File

@ -25,6 +25,8 @@
#ifndef _PreComp_
#endif
#include <TopExp_Explorer.hxx>
#include <Base/Console.h>
#include <App/Application.h>
#include <Gui/Application.h>
@ -39,8 +41,86 @@
#include <Mod/Path/App/FeaturePathCompound.h>
#include <Mod/Path/App/FeaturePathShape.h>
#include <Mod/Part/App/PartFeature.h>
#include <Mod/Path/App/FeatureArea.h>
// Path Area #####################################################################################################
DEF_STD_CMD_A(CmdPathArea)
CmdPathArea::CmdPathArea()
:Command("Path_Area")
{
sAppModule = "Path";
sGroup = QT_TR_NOOP("Path");
sMenuText = QT_TR_NOOP("Area");
sToolTipText = QT_TR_NOOP("Creates a feature area from selected objects");
sWhatsThis = "Path_Area";
sStatusTip = sToolTipText;
sPixmap = "Path-Area";
sAccel = "P,A";
}
void CmdPathArea::activated(int iMsg)
{
Q_UNUSED(iMsg);
std::vector<Gui::SelectionObject> Sel =
getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId());
std::list<std::string> cmds;
std::ostringstream sources;
if (Sel.size() > 0) {
for(const Gui::SelectionObject &selObj : Sel) {
const Part::Feature *pcObj = static_cast<const Part::Feature*>(selObj.getObject());
if(selObj.getSubNames().empty()) {
const TopoDS_Shape &shape = pcObj->Shape.getShape().getShape();
TopExp_Explorer it(shape, TopAbs_SHELL);
if(it.More()) {
Base::Console().Error("Selected shape is not 2D\n");
return;
}
sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ",";
continue;
}
for(const std::string &name : selObj.getSubNames()) {
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('Path::Feature','" << sub_fname << "').Shape = " <<
pcObj->getNameInDocument() << '.' << name.substr(0,4) << '[' << index-1 << ']';
cmds.push_back(cmd.str());
sources << "FreeCAD.activeDocument()." << sub_fname << ",";
}
}
}
std::string FeatName = getUniqueObjectName("FeatureArea");
openCommand("Create Path Area");
for(const std::string &cmd : cmds)
doCommand(Doc,cmd.c_str());
doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureArea','%s')",FeatName.c_str());
doCommand(Doc,"FreeCAD.activeDocument().%s.Sources = [ %s ]",FeatName.c_str(),sources.str().c_str());
commitCommand();
updateActive();
}
bool CmdPathArea::isActive(void)
{
return hasActiveDocument();
}
// Path compound #####################################################################################################
@ -95,7 +175,6 @@ bool CmdPathCompound::isActive(void)
return hasActiveDocument();
}
// Path Shape #####################################################################################################
@ -149,4 +228,5 @@ void CreatePathCommands(void)
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdPathCompound());
rcCmdMgr.addCommand(new CmdPathShape());
rcCmdMgr.addCommand(new CmdPathArea());
}

View File

@ -46,6 +46,7 @@
<file>icons/Path-ToolChange.svg</file>
<file>icons/Path-Toolpath.svg</file>
<file>icons/Path-ToolTable.svg</file>
<file>icons/Path-Area.svg</file>
<file>icons/preferences-path.svg</file>
<file>panels/ContourEdit.ui</file>
<file>panels/DlgJobChooser.ui</file>

View File

@ -0,0 +1,648 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="Path-Area.svg">
<defs
id="defs2818">
<linearGradient
inkscape:collect="always"
id="linearGradient4066">
<stop
style="stop-color:#4e9a06;stop-opacity:1"
offset="0"
id="stop4068" />
<stop
style="stop-color:#8ae234;stop-opacity:1"
offset="1"
id="stop4070" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4022">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop4024" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop4026" />
</linearGradient>
<linearGradient
id="linearGradient4513">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517" />
</linearGradient>
<linearGradient
id="linearGradient3681">
<stop
id="stop3697"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3687"
x1="37.89756"
y1="41.087898"
x2="4.0605712"
y2="40.168594"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3695"
x1="37.894287"
y1="40.484772"
x2="59.811455"
y2="43.558987"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
id="linearGradient3681-3">
<stop
id="stop3697-3"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685-4" />
</linearGradient>
<linearGradient
y2="43.558987"
x2="59.811455"
y1="40.484772"
x1="37.894287"
gradientTransform="translate(-37.00068,-20.487365)"
gradientUnits="userSpaceOnUse"
id="linearGradient3608"
xlink:href="#linearGradient3681-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-2" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-4" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538"
xlink:href="#linearGradient4513-2"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538-6"
xlink:href="#linearGradient4513-1"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-7" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-5" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3069"
xlink:href="#linearGradient4513-1-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-6" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3102"
xlink:href="#linearGradient4513-1-2"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4513-1"
id="radialGradient3132"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.39497909,0,0,1.1841158,64.952609,-7.0541574)"
cx="32.151962"
cy="27.950663"
fx="32.151962"
fy="27.950663"
r="23.634638" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4022"
id="linearGradient4028"
x1="36.538239"
y1="45.928013"
x2="36.804447"
y2="23.332739"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4066"
id="linearGradient4072"
x1="41.610756"
y1="59.853931"
x2="32.101425"
y2="10.99928"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4031"
id="linearGradient4055"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(71.494719,-3.1982556)"
x1="30.000002"
y1="14"
x2="36"
y2="54.227272" />
<linearGradient
id="linearGradient4031">
<stop
id="stop4033"
offset="0"
style="stop-color:#d3d7cf;stop-opacity:1" />
<stop
id="stop4035"
offset="1"
style="stop-color:#888a85;stop-opacity:1" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11"
inkscape:cx="25.031078"
inkscape:cy="28.81298"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1375"
inkscape:window-height="876"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid3234"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title>Path-FaceProfile</dc:title>
<dc:date>2016-01-19</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="color:#000000;fill:url(#linearGradient4028);fill-opacity:1;fill-rule:nonzero;stroke:#0b1521;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 33,21 17,45 41,49 53,33 49,23 z"
id="rect3083"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="color:#000000;fill:none;stroke:#172a04;stroke-width:8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50,13 39,11 c -4,-1 -5.171573,-1.4142136 -8,0 -2,1 -3,2 -5,5 L 7,46 c -3,4 -1,7 3,8 l 28,4 c 5,1 9,-2 12,-6 l 4,-5"
id="rect3083-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
style="color:#000000;fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 33.965909,23.113636 20.349931,43.571429 40.140486,46.839294 50.710729,32.691101 47.57301,24.842851 z"
id="rect3083-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="color:#000000;fill:none;stroke:url(#linearGradient4072);stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50,13 39,11 c -4,-1 -5.171573,-1.414214 -8,0 -2,1 -3,2 -5,5 L 7,46 c -3,4 -1,7 3,8 l 28,4 c 5,1 9,-2 12,-6 l 4,-5"
id="rect3083-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
style="color:#000000;fill:none;stroke:#8ae234;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 50.064283,12 38.517882,9.919647 c -4,-1.0000001 -5.171573,-1.4142141 -8,0 -2,1 -3,2 -5,5 l -19.0000003,30 c -2.9999999,4 -1,7 2.9999998,8 l 28.0000005,4 c 5,1 9,-2 12,-6 L 53,46"
id="rect3083-0-6-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,125 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <Gui/Application.h>
#include <Mod/Path/App/FeatureArea.h>
#include "ViewProviderArea.h"
using namespace PathGui;
PROPERTY_SOURCE(PathGui::ViewProviderArea, PartGui::ViewProviderPlaneParametric)
ViewProviderArea::ViewProviderArea()
{
sPixmap = "Path-Area.svg";
}
ViewProviderArea::~ViewProviderArea()
{
}
std::vector<App::DocumentObject*> ViewProviderArea::claimChildren(void) const
{
return std::vector<App::DocumentObject*>(
static_cast<Path::FeatureArea*>(getObject())->Sources.getValues());
}
bool ViewProviderArea::canDragObjects() const
{
return true;
}
bool ViewProviderArea::canDragObject(App::DocumentObject* obj) const
{
return obj && obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId());
}
void ViewProviderArea::dragObject(App::DocumentObject* obj)
{
Path::FeatureArea* area = static_cast<Path::FeatureArea*>(getObject());
std::vector<App::DocumentObject*> sources = area->Sources.getValues();
for (std::vector<App::DocumentObject*>::iterator it = sources.begin(); it != sources.end(); ++it) {
if (*it == obj) {
sources.erase(it);
area->Sources.setValues(sources);
break;
}
}
}
bool ViewProviderArea::canDropObjects() const
{
return true;
}
bool ViewProviderArea::canDropObject(App::DocumentObject* obj) const
{
return canDragObject(obj);
}
void ViewProviderArea::dropObject(App::DocumentObject* obj)
{
Path::FeatureArea* area = static_cast<Path::FeatureArea*>(getObject());
std::vector<App::DocumentObject*> sources = area->Sources.getValues();
sources.push_back(obj);
area->Sources.setValues(sources);
}
void ViewProviderArea::updateData(const App::Property* prop)
{
PartGui::ViewProviderPart::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 ViewProviderArea::onDelete(const std::vector<std::string> &)
{
// get the input shapes
Path::FeatureArea* area = static_cast<Path::FeatureArea*>(getObject());
std::vector<App::DocumentObject*> pShapes =area->Sources.getValues();
for (std::vector<App::DocumentObject*>::iterator it = pShapes.begin(); it != pShapes.end(); ++it) {
if (*it)
Gui::Application::Instance->showViewProvider(*it);
}
return true;
}
// Python object -----------------------------------------------------------------------
namespace Gui {
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderAreaPython, PathGui::ViewProviderArea)
/// @endcond
// explicit template instantiation
template class PathGuiExport ViewProviderPythonFeatureT<PathGui::ViewProviderArea>;
}

View File

@ -0,0 +1,60 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#ifndef PATH_ViewProviderArea_H
#define PATH_ViewProviderArea_H
#include <Gui/ViewProviderPythonFeature.h>
#include <Mod/Part/Gui/ViewProviderPlaneParametric.h>
namespace PathGui
{
class PathGuiExport ViewProviderArea : public PartGui::ViewProviderPlaneParametric
{
PROPERTY_HEADER(PathGui::ViewProviderArea);
public:
ViewProviderArea();
virtual ~ViewProviderArea();
/// 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*);
};
typedef Gui::ViewProviderPythonFeatureT<ViewProviderArea> ViewProviderAreaPython;
} //namespace PathGui
#endif // PATH_ViewProviderArea_H

View File

@ -86,7 +86,7 @@ class PathWorkbench (Workbench):
threedopcmdlist = ["Path_Surfacing"]
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"]
extracmdlist = ["Path_SelectLoop"]
extracmdlist = ["Path_SelectLoop", "Path_Area"]
#modcmdmore = ["Path_Hop",]
#remotecmdlist = ["Path_Remote"]