FreeCAD / src /Mod /CAM /App /AreaPyImp.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* 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 <Base/GeometryPyCXX.h>
#include <Base/PyWrapParseTupleAndKeywords.h>
#include <Mod/Part/App/OCCError.h>
#include <Mod/Part/App/TopoShapePy.h>
// inclusion of the generated files (generated out of AreaPy.xml)
#include "PathPy.h"
#include "AreaPy.h"
#include "AreaPy.cpp"
static PyObject* areaAbort(PyObject*, PyObject* args, PyObject* kwd)
{
static const std::array<const char*, 2> kwlist {"aborting", nullptr};
PyObject* pObj = Py_True;
if (!Base::Wrapped_ParseTupleAndKeywords(args, kwd, "|O!", kwlist, &PyBool_Type, &pObj)) {
return nullptr;
}
Area::abort(Base::asBoolean(pObj));
Py_Return;
}
static PyObject* areaSetParams(PyObject*, PyObject* args, PyObject* kwd)
{
static const std::array<const char*, 43> kwlist {
PARAM_FIELD_STRINGS(NAME, AREA_PARAMS_STATIC_CONF),
nullptr
};
if (args && PySequence_Size(args) > 0) {
PyErr_SetString(PyExc_ValueError, "Non-keyword argument is not supported");
}
// Declare variables defined in the NAME field of the CONF parameter list
PARAM_PY_DECLARE(PARAM_FNAME, AREA_PARAMS_STATIC_CONF);
AreaStaticParams params = Area::getDefaultParams();
#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_STATIC_CONF)
// Parse arguments to overwrite CONF variables
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
kwd,
"|" PARAM_PY_KWDS(AREA_PARAMS_STATIC_CONF),
kwlist,
PARAM_REF(PARAM_FNAME, AREA_PARAMS_STATIC_CONF)
)) {
return nullptr;
}
#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_STATIC_CONF)
Area::setDefaultParams(params);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject* areaGetParams(PyObject*, PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
const AreaStaticParams& params = Area::getDefaultParams();
PyObject* dict = PyDict_New();
#define AREA_SRC(_param) params.PARAM_FNAME(_param)
PARAM_PY_DICT_SET_VALUE(dict, NAME, AREA_SRC, AREA_PARAMS_STATIC_CONF)
return dict;
}
static PyObject* areaGetParamsDesc(PyObject*, PyObject* args, PyObject* kwd)
{
PyObject* pcObj = Py_False;
static const std::array<const char*, 2> kwlist {"as_string", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args, kwd, "|O!", kwlist, &PyBool_Type, &pcObj)) {
return nullptr;
}
if (Base::asBoolean(pcObj)) {
return PyUnicode_FromString(PARAM_PY_DOC(NAME, AREA_PARAMS_STATIC_CONF));
}
PyObject* dict = PyDict_New();
PARAM_PY_DICT_SET_DOC(dict, NAME, AREA_PARAMS_STATIC_CONF)
return dict;
}
static const PyMethodDef areaOverrides[] = {
{"setParams",
nullptr,
0,
"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",
nullptr,
0,
"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 unioned "
"together regardless of the op code given\n"
"(except for 'Compound'). Subsequent "
"shape's wire will be combined using the "
"op code.\n"
"All shape wires shall be coplanar, and "
"are used to determine a working plane for "
"face\n"
"making and offsetting. You can call "
"setPlane() to supply a reference shape to "
"determine\n"
"the workplane in case the added shapes "
"are all colinear lines.\n",
},
{
"makeOffset",
nullptr,
0,
"makeOffset(index=-1, " PARAM_PY_ARGS_DOC(
ARG,
AREA_PARAMS_OFFSET
) "):\n"
"Make an 2D offset of the shape.\n"
"\n* index (-1): the index of the section. -1 means all sections. "
"No effect on planar shape.\n" PARAM_PY_DOC(ARG, AREA_PARAMS_OFFSET),
},
{
"makePocket",
nullptr,
0,
"makePocket(index=-1, " PARAM_PY_ARGS_DOC(
ARG,
AREA_PARAMS_POCKET
) "):\n"
"Generate pocket toolpath of the shape.\n"
"\n* index (-1): the index of the section. -1 means all sections. "
"No effect on planar shape.\n" PARAM_PY_DOC(ARG, AREA_PARAMS_POCKET),
},
{
"makeSections",
nullptr,
0,
"makeSections(" PARAM_PY_ARGS_DOC(
ARG,
AREA_PARAMS_SECTION_EXTRA
) ", heights=[], plane=None):\n"
"Make a list of area holding the sectioned children shapes "
"on given heights\n" PARAM_PY_DOC(
ARG,
AREA_PARAMS_SECTION_EXTRA
) "\n* heights ([]): a list of "
"section heights, the "
"meaning of the value is "
"determined by 'mode'.\n"
"If not specified, the "
"current SectionCount, and "
"SectionOffset of this Area "
"is used.\n"
"\n* plane (None): optional "
"shape to specify a section "
"plane. If not give, the "
"current workplane\n"
"of this Area is used if "
"section mode is "
"'Workplane'.",
},
{
"getClearedArea",
nullptr,
0,
"getClearedArea(path, diameter, zmax, bbox):\n"
"Gets the area cleared when a tool of the specified diameter follows the gcode represented "
"in the path, ignoring cleared space above zmax and path segments that don't affect space "
"within the x/y space of bbox.\n",
},
{
"getRestArea",
nullptr,
0,
"getRestArea(clearedAreas, diameter):\n"
"Rest machining: gets the area left to be machined, assuming some of this area has already "
"been cleared previous tool paths.\n"
"clearedAreas: the regions already cleared.\n"
"diameter: the tool diameter that finishes clearing this area.\n",
},
{"toTopoShape", nullptr, 0, "toTopoShape():\n"},
{"setDefaultParams",
reinterpret_cast<PyCFunction>(reinterpret_cast<void (*)()>(areaSetParams)),
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
"setDefaultParams(key=value...):\n"
"Static method to set the default parameters of all following Path.Area, plus the following\n"
"additional parameters.\n"},
{"getDefaultParams",
(PyCFunction)areaGetParams,
METH_VARARGS | METH_STATIC,
"getDefaultParams(): Static method to return the current default parameters."},
{
"abort",
reinterpret_cast<PyCFunction>(reinterpret_cast<void (*)()>(areaAbort)),
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
"abort(aborting=True): Static method to abort any ongoing operation\n"
"\nTo ensure no stray abortion is left in the previous operation, it is advised to "
"manually clear\n"
"the aborting flag by calling abort(False) before starting a new operation.",
},
{"getParamsDesc",
reinterpret_cast<PyCFunction>(reinterpret_cast<void (*)()>(areaGetParamsDesc)),
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
"getParamsDesc(as_string=False): Returns a list of supported parameters and their "
"descriptions.\n"
"\n* as_string: if False, then return a dictionary of documents of all supported parameters."},
};
struct AreaPyModifier
{
AreaPyModifier()
{
for (auto& method : Path::AreaPy::Methods) {
if (!method.ml_name) {
continue;
}
for (auto& entry : areaOverrides) {
if (std::strcmp(method.ml_name, entry.ml_name) == 0) {
if (entry.ml_doc) {
method.ml_doc = entry.ml_doc;
}
if (entry.ml_meth) {
method.ml_meth = entry.ml_meth;
}
if (entry.ml_flags) {
method.ml_flags = entry.ml_flags;
}
break;
}
}
}
}
};
static AreaPyModifier mod;
using namespace Path;
// returns a string which represents the object e.g. when printed in python
std::string AreaPy::representation() const
{
std::stringstream str;
str << "<Area object at " << getAreaPtr() << ">";
return str.str();
}
PyObject* AreaPy::PyMake(struct _typeobject*, PyObject* args, PyObject* kwd) // Python wrapper
{
AreaPy* ret = new AreaPy(new Area);
if (!ret->setParams(args, kwd)) {
Py_DecRef(ret);
return nullptr;
}
// If setParams() was successful it increments the ref counter.
// So, it must be decremented again.
Py_DecRef(ret);
return ret;
}
// constructor method
int AreaPy::PyInit(PyObject*, PyObject*)
{
return 0;
}
PyObject* AreaPy::setPlane(PyObject* args)
{
PyObject* pcObj;
if (!PyArg_ParseTuple(args, "O!", &(Part::TopoShapePy::Type), &pcObj)) {
return nullptr;
}
#define GET_TOPOSHAPE(_p) static_cast<Part::TopoShapePy*>(_p)->getTopoShapePtr()->getShape()
getAreaPtr()->setPlane(GET_TOPOSHAPE(pcObj));
Py_INCREF(this);
return this;
}
PyObject* AreaPy::getShape(PyObject* args, PyObject* keywds)
{
PyObject* pcObj = Py_False;
short index = -1;
static const std::array<const char*, 3> kwlist {"index", "rebuild", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args, keywds, "|hO!", kwlist, &index, &PyBool_Type, &pcObj)) {
return nullptr;
}
PY_TRY
{
if (Base::asBoolean(pcObj)) {
getAreaPtr()->clean();
}
return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape(index)));
}
PY_CATCH_OCC
}
PyObject* AreaPy::add(PyObject* args, PyObject* keywds)
{
PARAM_PY_DECLARE_INIT(PARAM_FARG, AREA_PARAMS_OPCODE)
PyObject* pcObj;
// Strangely, PyArg_ParseTupleAndKeywords requires all arguments to be keyword based,
// even non-optional ones? That doesn't make sense in python. Seems only in python 3
// they added '$' to address that issue.
static const std::array<const char*, 3> kwlist {
"shape",
PARAM_FIELD_STRINGS(ARG, AREA_PARAMS_OPCODE),
nullptr
};
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
keywds,
"O|" PARAM_PY_KWDS(AREA_PARAMS_OPCODE),
kwlist,
&pcObj,
PARAM_REF(PARAM_FARG, AREA_PARAMS_OPCODE)
)) {
return nullptr;
}
PY_TRY
{
if (PyObject_TypeCheck(pcObj, &(Part::TopoShapePy::Type))) {
getAreaPtr()->add(GET_TOPOSHAPE(pcObj), op);
Py_INCREF(this);
return this;
}
else if (PyObject_TypeCheck(pcObj, &(PyList_Type))
|| PyObject_TypeCheck(pcObj, &(PyTuple_Type))) {
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 nullptr;
}
}
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
PyObject* item = (*it).ptr();
getAreaPtr()->add(GET_TOPOSHAPE(item), PARAM_PY_FIELDS(PARAM_FARG, AREA_PARAMS_OPCODE));
}
Py_INCREF(this);
return this;
}
}
PY_CATCH_OCC
PyErr_SetString(PyExc_TypeError, "shape must be 'TopoShape' or list of 'TopoShape'");
return nullptr;
}
PyObject* AreaPy::makeOffset(PyObject* args, PyObject* keywds)
{
// Generate a keyword string defined in the ARG field of OFFSET parameter list
static const std::array<const char*, 6> kwlist {
"index",
PARAM_FIELD_STRINGS(ARG, AREA_PARAMS_OFFSET),
nullptr
};
short index = -1;
// Declare variables defined in the ARG field of the OFFSET parameter list with
// initialization to defaults
PARAM_PY_DECLARE_INIT(PARAM_FARG, AREA_PARAMS_OFFSET)
// Parse arguments to overwrite the defaults
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
keywds,
"|h" PARAM_PY_KWDS(AREA_PARAMS_OFFSET),
kwlist,
&index,
PARAM_REF(PARAM_FARG, AREA_PARAMS_OFFSET)
)) {
return nullptr;
}
PY_TRY
{
// Expand the variable as function call arguments
TopoDS_Shape resultShape
= getAreaPtr()->makeOffset(index, PARAM_PY_FIELDS(PARAM_FARG, AREA_PARAMS_OFFSET));
return Py::new_reference_to(Part::shape2pyshape(resultShape));
}
PY_CATCH_OCC
}
PyObject* AreaPy::makePocket(PyObject* args, PyObject* keywds)
{
static const std::array<const char*, 11> kwlist {
"index",
PARAM_FIELD_STRINGS(ARG, AREA_PARAMS_POCKET),
nullptr
};
short index = -1;
PARAM_PY_DECLARE_INIT(PARAM_FARG, AREA_PARAMS_POCKET)
// Override pocket mode default
mode = Area::PocketModeZigZagOffset;
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
keywds,
"|h" PARAM_PY_KWDS(AREA_PARAMS_POCKET),
kwlist,
&index,
PARAM_REF(PARAM_FARG, AREA_PARAMS_POCKET)
)) {
return nullptr;
}
PY_TRY
{
TopoDS_Shape resultShape
= getAreaPtr()->makePocket(index, PARAM_PY_FIELDS(PARAM_FARG, AREA_PARAMS_POCKET));
return Py::new_reference_to(Part::shape2pyshape(resultShape));
}
PY_CATCH_OCC
}
PyObject* AreaPy::makeSections(PyObject* args, PyObject* keywds)
{
static const std::array<const char*, 5> kwlist {
PARAM_FIELD_STRINGS(ARG, AREA_PARAMS_SECTION_EXTRA),
"heights",
"plane",
nullptr
};
PyObject* heights = nullptr;
PyObject* plane = nullptr;
PARAM_PY_DECLARE_INIT(PARAM_FARG, AREA_PARAMS_SECTION_EXTRA)
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
keywds,
"|" PARAM_PY_KWDS(AREA_PARAMS_SECTION_EXTRA) "OO!",
kwlist,
PARAM_REF(PARAM_FARG, AREA_PARAMS_SECTION_EXTRA),
&heights,
&(Part::TopoShapePy::Type),
&plane
)) {
return nullptr;
}
PY_TRY
{
std::vector<double> h;
if (heights) {
if (PyObject_TypeCheck(heights, &(PyFloat_Type))) {
h.push_back(PyFloat_AsDouble(heights));
}
else if (PyObject_TypeCheck(heights, &(PyList_Type))
|| PyObject_TypeCheck(heights, &(PyTuple_Type))) {
Py::Sequence shapeSeq(heights);
h.reserve(shapeSeq.size());
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
PyObject* item = (*it).ptr();
if (!PyObject_TypeCheck(item, &(PyFloat_Type))) {
PyErr_SetString(PyExc_TypeError, "heights must only contain float type");
return nullptr;
}
h.push_back(PyFloat_AsDouble(item));
}
}
else {
PyErr_SetString(PyExc_TypeError, "heights must be of type float or list/tuple of float");
return nullptr;
}
}
std::vector<std::shared_ptr<Area>> sections = getAreaPtr()->makeSections(
PARAM_PY_FIELDS(PARAM_FARG, AREA_PARAMS_SECTION_EXTRA),
h,
plane ? GET_TOPOSHAPE(plane) : TopoDS_Shape()
);
Py::List ret;
for (auto& area : sections) {
ret.append(Py::asObject(new AreaPy(new Area(*area, true))));
}
return Py::new_reference_to(ret);
}
PY_CATCH_OCC
}
PyObject* AreaPy::getClearedArea(PyObject* args) {PY_TRY {PyObject * pyPath, *pyBbox;
double diameter, zmax;
if (!PyArg_ParseTuple(args, "OddO", &pyPath, &diameter, &zmax, &pyBbox)) {
return nullptr;
}
if (!PyObject_TypeCheck(pyPath, &(PathPy::Type))) {
PyErr_SetString(PyExc_TypeError, "path must be of type PathPy");
return nullptr;
}
if (!PyObject_TypeCheck(pyBbox, &(Base::BoundBoxPy::Type))) {
PyErr_SetString(PyExc_TypeError, "bbox must be of type BoundBoxPy");
return nullptr;
}
const PathPy* path = static_cast<PathPy*>(pyPath);
const Py::BoundingBox bbox(pyBbox, false);
std::shared_ptr<Area> clearedArea
= getAreaPtr()->getClearedArea(path->getToolpathPtr(), diameter, zmax, bbox.getValue());
auto pyClearedArea = Py::asObject(new AreaPy(new Area(*clearedArea, true)));
return Py::new_reference_to(pyClearedArea);
}
PY_CATCH_OCC
}
PyObject* AreaPy::getRestArea(PyObject* args) {PY_TRY {PyObject * pyClearedAreas;
std::vector<std::shared_ptr<Area>> clearedAreas;
double diameter;
if (!PyArg_ParseTuple(args, "Od", &pyClearedAreas, &diameter)) {
return nullptr;
}
if (pyClearedAreas && PyObject_TypeCheck(pyClearedAreas, &PyList_Type)) {
Py::Sequence clearedAreasSeq(pyClearedAreas);
clearedAreas.reserve(clearedAreasSeq.size());
for (Py::Sequence::iterator it = clearedAreasSeq.begin(); it != clearedAreasSeq.end(); ++it) {
PyObject* item = (*it).ptr();
if (!PyObject_TypeCheck(item, &(AreaPy::Type))) {
PyErr_SetString(PyExc_TypeError, "cleared areas must only contain AreaPy type");
return nullptr;
}
clearedAreas.push_back(std::make_shared<Area>(*static_cast<AreaPy*>(item)->getAreaPtr(), true));
}
}
else {
PyErr_SetString(PyExc_TypeError, "clearedAreas must be of type list of AreaPy");
return nullptr;
}
std::shared_ptr<Area> restArea = getAreaPtr()->getRestArea(clearedAreas, diameter);
if (!restArea) {
return Py_None;
}
auto pyRestArea = Py::asObject(new AreaPy(new Area(*restArea, true)));
return Py::new_reference_to(pyRestArea);
}
PY_CATCH_OCC
}
PyObject* AreaPy::toTopoShape(PyObject* args) {PY_TRY {if (!PyArg_ParseTuple(args, "")) return nullptr;
return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->toTopoShape()));
}
PY_CATCH_OCC
}
PyObject* AreaPy::setDefaultParams(PyObject*, PyObject*)
{
return nullptr;
}
PyObject* AreaPy::setParams(PyObject* args, PyObject* keywds)
{
static const std::array<const char*, 43> kwlist {
PARAM_FIELD_STRINGS(NAME, AREA_PARAMS_CONF),
nullptr
};
// Declare variables defined in the NAME field of the CONF parameter list
PARAM_PY_DECLARE(PARAM_FNAME, AREA_PARAMS_CONF);
AreaParams params = getAreaPtr()->getParams();
// populate the CONF variables with params
PARAM_FOREACH(AREA_SET, AREA_PARAMS_CONF)
// Parse arguments to overwrite CONF variables
if (!Base::Wrapped_ParseTupleAndKeywords(
args,
keywds,
"|" PARAM_PY_KWDS(AREA_PARAMS_CONF),
kwlist,
PARAM_REF(PARAM_FNAME, AREA_PARAMS_CONF)
)) {
return nullptr;
}
PY_TRY
{
// populate 'params' with the CONF variables
PARAM_FOREACH(AREA_GET, AREA_PARAMS_CONF)
getAreaPtr()->setParams(params);
Py_INCREF(this);
return this;
}
PY_CATCH_OCC
}
PyObject* AreaPy::getParams(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
const AreaParams& params = getAreaPtr()->getParams();
PyObject* dict = PyDict_New();
PARAM_PY_DICT_SET_VALUE(dict, NAME, AREA_SRC, AREA_PARAMS_CONF)
return dict;
}
PyObject* AreaPy::getDefaultParams(PyObject*)
{
return nullptr;
}
PyObject* AreaPy::abort(PyObject*, PyObject*)
{
return nullptr;
}
PyObject* AreaPy::getParamsDesc(PyObject*, PyObject*)
{
return nullptr;
}
Py::List AreaPy::getSections() const
{
Py::List ret;
Area* area = getAreaPtr();
for (size_t i = 0, count = area->getSectionCount(); i < count; ++i) {
ret.append(Part::shape2pyshape(getAreaPtr()->getShape(i)));
}
return ret;
}
Py::List AreaPy::getShapes() const
{
Py::List ret;
Area* area = getAreaPtr();
const std::list<Area::Shape>& shapes = area->getChildren();
for (auto& s : shapes) {
ret.append(Py::TupleN(Part::shape2pyshape(s.shape), Py::Long(s.op)));
}
return ret;
}
Py::Object AreaPy::getWorkplane() const
{
return Part::shape2pyshape(getAreaPtr()->getPlane());
}
void AreaPy::setWorkplane(Py::Object obj)
{
PyObject* p = obj.ptr();
if (!PyObject_TypeCheck(p, &(Part::TopoShapePy::Type))) {
std::string error = std::string("type must be 'TopoShape', not ");
error += p->ob_type->tp_name;
throw Py::TypeError(error);
}
getAreaPtr()->setPlane(GET_TOPOSHAPE(p));
}
// custom attributes get/set
PyObject* AreaPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int AreaPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}