diff --git a/CMakeLists.txt b/CMakeLists.txt index 3216dc016a..c1dc1bbdb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,7 @@ OPTION(BUILD_MESH_PART "Build the FreeCAD mesh part module" ON) OPTION(BUILD_OPENSCAD "Build the FreeCAD openscad module" ON) OPTION(BUILD_PART "Build the FreeCAD part module" ON) OPTION(BUILD_PART_DESIGN "Build the FreeCAD part design module" ON) +OPTION(BUILD_PATH "Build the FreeCAD path module" ON) OPTION(BUILD_PLOT "Build the FreeCAD plot module" ON) OPTION(BUILD_POINTS "Build the FreeCAD points module" ON) OPTION(BUILD_RAYTRACING "Build the FreeCAD ray tracing module" ON) @@ -297,6 +298,12 @@ if(BUILD_ROBOT) set(BUILD_PART ON) endif(BUILD_ROBOT) +#path module dependencies. +if(BUILD_PATH) + set(BUILD_PART ON) + set(BUILD_ROBOT ON) +endif(BUILD_PATH) + #inferred from cmakelists.txt. appears to be working. if(BUILD_SKETCHER) set(BUILD_PART ON) diff --git a/src/Mod/CMakeLists.txt b/src/Mod/CMakeLists.txt index 4f05118433..b0df390399 100644 --- a/src/Mod/CMakeLists.txt +++ b/src/Mod/CMakeLists.txt @@ -125,3 +125,7 @@ endif(BUILD_SPREADSHEET) if(BUILD_JTREADER) add_subdirectory(JtReader) endif(BUILD_JTREADER) + +if(BUILD_PATH) + add_subdirectory(Path) +endif(BUILD_PATH) diff --git a/src/Mod/Path/App/AppPath.cpp b/src/Mod/Path/App/AppPath.cpp new file mode 100644 index 0000000000..b4fc8f752d --- /dev/null +++ b/src/Mod/Path/App/AppPath.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include +#include + +#include "Command.h" +#include "CommandPy.h" +#include "Path.h" +#include "PathPy.h" +#include "Tooltable.h" +#include "ToolPy.h" +#include "TooltablePy.h" +#include "PropertyPath.h" +#include "FeaturePath.h" +#include "PropertyTooltable.h" +#include "FeaturePathCompound.h" +#include "FeaturePathShape.h" + +extern struct PyMethodDef Path_methods[]; + +PyDoc_STRVAR(module_Path_doc, +"This module is the Path module."); + + +/* Python entry */ +extern "C" { +void PathExport initPath() +{ + PyObject* pathModule = Py_InitModule3("Path", Path_methods, module_Path_doc); /* mod name, table ptr */ + 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"); + + // NOTE: To finish the initialization of our own type objects we must + // call PyType_Ready, otherwise we run into a segmentation fault, later on. + // This function is responsible for adding inherited slots from a type's base class. + Path::Command ::init(); + Path::Toolpath ::init(); + Path::Tool ::init(); + Path::Tooltable ::init(); + Path::PropertyPath ::init(); + Path::Feature ::init(); + Path::FeaturePython ::init(); + Path::PropertyTooltable ::init(); + Path::FeatureCompound ::init(); + Path::FeatureCompoundPython ::init(); + Path::FeatureShape ::init(); + Path::FeatureShapePython ::init(); +} + +} // extern "C" diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp new file mode 100644 index 0000000000..833144fd3f --- /dev/null +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "CommandPy.h" +#include "PathPy.h" +#include "Path.h" +#include "FeaturePath.h" +#include "FeaturePathCompound.h" + +using namespace Path; + + +static PyObject * write (PyObject *self, PyObject *args) +{ + char* Name; + PyObject* pObj; + if (!PyArg_ParseTuple(args, "Oet",&pObj,"utf-8",&Name)) + return NULL; + std::string EncodedName = std::string(Name); + PyMem_Free(Name); + Base::FileInfo file(EncodedName.c_str()); + + if (PyObject_TypeCheck(pObj, &(App::DocumentObjectPy::Type))) { + App::DocumentObject* obj = static_cast(pObj)->getDocumentObjectPtr(); + if (obj->getTypeId().isDerivedFrom(Base::Type::fromName("Path::Feature"))) { + const Toolpath& path = static_cast(obj)->Path.getValue(); + std::string gcode = path.toGCode(); + std::ofstream ofile(EncodedName.c_str()); + ofile << gcode; + ofile.close(); + } else + Py_Error(Base::BaseExceptionFreeCADError, "The given file is not a path"); + } + Py_Return; +} + + +static PyObject * read (PyObject *self, PyObject *args) +{ + char* Name; + const char* DocName=0; + if (!PyArg_ParseTuple(args, "et|s","utf-8",&Name,&DocName)) + return NULL; + std::string EncodedName = std::string(Name); + PyMem_Free(Name); + + Base::FileInfo file(EncodedName.c_str()); + if (!file.exists()) + Py_Error(Base::BaseExceptionFreeCADError, "File doesn't exist"); + App::Document *pcDoc; + if (DocName) + pcDoc = App::GetApplication().getDocument(DocName); + else + pcDoc = App::GetApplication().getActiveDocument(); + if (!pcDoc) + pcDoc = App::GetApplication().newDocument(DocName); + + PY_TRY { + // read the gcode file + std::ifstream filestr(file.filePath().c_str()); + std::stringstream buffer; + buffer << filestr.rdbuf(); + std::string gcode = buffer.str(); + Toolpath path; + path.setFromGCode(gcode); + Path::Feature *object = static_cast(pcDoc->addObject("Path::Feature",file.fileNamePure().c_str())); + object->Path.setValue(path); + pcDoc->recompute(); + } PY_CATCH; + Py_Return; +} + + +static PyObject * show (PyObject *self, PyObject *args) +{ + PyObject *pcObj; + if (!PyArg_ParseTuple(args, "O!", &(PathPy::Type), &pcObj)) // convert args: Python->C + return NULL; // NULL triggers exception + + PY_TRY { + App::Document *pcDoc = App::GetApplication().getActiveDocument(); + if (!pcDoc) + pcDoc = App::GetApplication().newDocument(); + PathPy* pPath = static_cast(pcObj); + Path::Feature *pcFeature = (Path::Feature *)pcDoc->addObject("Path::Feature", "Path"); + Path::Toolpath* pa = pPath->getToolpathPtr(); + if (!pa) { + PyErr_SetString(PyExc_ReferenceError, + "object doesn't reference a valid path"); + return 0; + } + // copy the data + pcFeature->Path.setValue(*pa); + } PY_CATCH; + + Py_Return; +} + + +/* registration table */ +struct PyMethodDef Path_methods[] = { + {"write" ,write ,METH_VARARGS, + "write(object,filename): Exports a given path object to a GCode file"}, + {"read" ,read ,METH_VARARGS, + "read(filename,[document]): Imports a GCode file into the given document"}, + {"show" ,show ,METH_VARARGS, + "show(path): Add the path to the active document or create one if no document exists."}, + {NULL, NULL} /* end of table marker */ +}; diff --git a/src/Mod/Path/App/CMakeLists.txt b/src/Mod/Path/App/CMakeLists.txt new file mode 100644 index 0000000000..ee2882bc39 --- /dev/null +++ b/src/Mod/Path/App/CMakeLists.txt @@ -0,0 +1,127 @@ +if(MSVC) + add_definitions(-DHAVE_ACOSH -DHAVE_ASINH -DHAVE_ATANH) +else(MSVC) + add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H) +endif(MSVC) + + +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_BINARY_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + ${Boost_INCLUDE_DIRS} + ${OCC_INCLUDE_DIR} + ${EIGEN3_INCLUDE_DIR} + ${PYTHON_INCLUDE_PATH} + ${ZLIB_INCLUDE_DIR} + ${QT_INCLUDE_DIR} + ${XERCESC_INCLUDE_DIR} +) +link_directories(${OCC_LIBRARY_DIR}) + +if(MSVC) + set(Path_LIBS + Robot + ${QT_QTCORE_LIBRARY} + ${QT_QTCORE_LIBRARY_DEBUG} + FreeCADApp + ) +else(MSVC) + set(Path_LIBS + Robot + Part + ${QT_QTCORE_LIBRARY} + FreeCADApp + ) +endif(MSVC) + +generate_from_xml(CommandPy) +generate_from_xml(PathPy) +generate_from_xml(ToolPy) +generate_from_xml(TooltablePy) + +SET(Python_SRCS + CommandPy.xml + CommandPyImp.cpp + PathPy.xml + PathPyImp.cpp + ToolPy.xml + TooltablePy.xml + TooltablePyImp.cpp +) + +SET(Mod_SRCS + AppPath.cpp + AppPathPy.cpp + PreCompiled.cpp + PreCompiled.h +) + +SET(Path_SRCS + Command.cpp + Command.h + Path.cpp + Path.h + Tooltable.cpp + Tooltable.h + PropertyPath.cpp + PropertyPath.h + FeaturePath.cpp + FeaturePath.h + PropertyTooltable.cpp + PropertyTooltable.h + FeaturePathCompound.cpp + FeaturePathCompound.h + FeaturePathShape.cpp + FeaturePathShape.h + ${Mod_SRCS} + ${Python_SRCS} +) + +SOURCE_GROUP("Python" FILES ${Python_SRCS}) +SOURCE_GROUP("Module" FILES ${Mod_SRCS}) + +if (WIN32) + add_definitions(-DEIGEN2_SUPPORT) + FILE( GLOB KDL_SRCS ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/[^.]*.cpp ) + FILE( GLOB KDL_HPPS ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/[^.]*.hpp + ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/[^.]*.inl) + + FILE( GLOB UTIL_SRCS ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/utilities/[^.]*.cpp + ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/utilities/[^.]*.cxx) + FILE( GLOB UTIL_HPPS ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/utilities/[^.]*.h + ${CMAKE_SOURCE_DIR}/src/Mod/Robot/App/kdl_cp/utilities/[^.]*.hpp) + + SET(Path_SRCS + ${Path_SRCS} + ${KDL_SRCS} + ${KDL_HPPS} + ${UTIL_SRCS} + ${UTIL_HPPS} + ) + + SOURCE_GROUP("KDL" FILES ${KDL_SRCS} ${KDL_HPPS} ${UTIL_SRCS} ${UTIL_HPPS} ) +endif(WIN32) + +SET(Path_Scripts + Init.py +) + +add_library(Path SHARED ${Path_SRCS}) +target_link_libraries(Path ${Path_LIBS}) + +fc_target_copy_resource(Path + ${CMAKE_SOURCE_DIR}/src/Mod/Path + ${CMAKE_BINARY_DIR}/Mod/Path + ${Path_Scripts}) + +fc_target_copy_resource(Path + ${CMAKE_SOURCE_DIR}/src/Mod/Path + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Path + ${Path_Resources}) + +SET_BIN_DIR(Path Path /Mod/Path) +SET_PYTHON_PREFIX_SUFFIX(Path) + +INSTALL(TARGETS Path DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/Mod/Path/App/Command.cpp b/src/Mod/Path/App/Command.cpp new file mode 100644 index 0000000000..53496c6dd3 --- /dev/null +++ b/src/Mod/Path/App/Command.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ + +#endif +#include +#include +#include +#include +#include +#include +#include +#include "Command.h" + +using namespace Base; +using namespace Path; + +TYPESYSTEM_SOURCE(Path::Command , Base::Persistence); + +// Constructors & destructors + +Command::Command(const char* name, + const std::map& parameters) +:Name(name),Parameters(parameters) +{ +} + +Command::Command() +{ +} + +Command::~Command() +{ +} + +// New methods + +Placement Command::getPlacement (void) +{ + std::string x = "X"; + std::string y = "Y"; + std::string z = "Z"; + std::string a = "A"; + std::string b = "B"; + std::string c = "C"; + double xval = 0.0; + double yval = 0.0; + double zval = 0.0; + double aval = 0.0; + double bval = 0.0; + double cval = 0.0; + if (Parameters.count(x)) + xval = Parameters[x]; + if (Parameters.count(y)) + yval = Parameters[y]; + if (Parameters.count(z)) + zval = Parameters[z]; + if (Parameters.count(a)) + aval = Parameters[a]; + if (Parameters.count(b)) + bval = Parameters[b]; + if (Parameters.count(c)) + cval = Parameters[c]; + Vector3d vec(xval,yval,zval); + Rotation rot; + rot.setYawPitchRoll(aval,bval,cval); + Placement plac(vec,rot); + return plac; +} + +Vector3d Command::getCenter (void) +{ + std::string i = "I"; + std::string j = "J"; + std::string k = "K"; + double ival = 0.0; + double jval = 0.0; + double kval = 0.0; + if (Parameters.count(i)) + ival = Parameters[i]; + if (Parameters.count(j)) + jval = Parameters[j]; + if (Parameters.count(k)) + kval = Parameters[k]; + Vector3d vec(ival,jval,kval); + return vec; +} + +const double Command::getValue(const std::string attr) +{ + std::string a(attr); + boost::to_upper(a); + double val = 0.0; + if (Parameters.count(a)) + val = Parameters[a]; + return val; +} + +const bool Command::has(const std::string attr) +{ + std::string a(attr); + boost::to_upper(a); + return (bool)Parameters.count(a); +} + +std::string Command::toGCode (void) const +{ + std::stringstream str; + str.precision(5); + str << Name; + for(std::map::const_iterator i = Parameters.begin(); i != Parameters.end(); ++i) { + std::string k = i->first; + std::string v = boost::lexical_cast(i->second); + str << " " << k << v; + } + return str.str(); +} + +void Command::setFromGCode (std::string str) +{ + Parameters.clear(); + std::string mode = "none"; + std::string key; + std::string value; + for (unsigned int i=0; i < str.size(); i++) { + if ( (isdigit(str[i])) || (str[i] == '-') || (str[i] == '.') ) { + value += str[i]; + } else if (isalpha(str[i])) { + if (mode == "command") { + if (!key.empty() && !value.empty()) { + std::string cmd = key + value; + boost::to_upper(cmd); + Name = cmd; + key = ""; + value = ""; + mode = "argument"; + } else { + throw Base::Exception("Badly formatted GCode command"); + } + mode = "argument"; + } else if (mode == "none") { + mode = "command"; + } else if (mode == "argument") { + if (!key.empty() && !value.empty()) { + double val = std::atof(value.c_str()); + boost::to_upper(key); + Parameters[key] = val; + key = ""; + value = ""; + } else { + throw Base::Exception("Badly formatted GCode argument"); + } + } else if (mode == "comment") { + value += str[i]; + } + key = str[i]; + } else if (str[i] == '(') { + mode = "comment"; + } else if (str[i] == ')') { + key = "("; + value += ")"; + } else { + // add non-ascii characters only if this is a comment + if (mode == "comment") { + value += str[i]; + } + } + } + if (!key.empty() && !value.empty()) { + if ( (mode == "command") || (mode == "comment") ) { + std::string cmd = key + value; + if (mode == "command") + boost::to_upper(cmd); + Name = cmd; + } else { + double val = std::atof(value.c_str()); + boost::to_upper(key); + Parameters[key] = val; + } + } else { + throw Base::Exception("Badly formatted GCode argument"); + } +} + +void Command::setFromPlacement (const Base::Placement &plac) +{ + Name = "G1"; + Parameters.clear(); + std::string x = "X"; + std::string y = "Y"; + std::string z = "Z"; + std::string a = "A"; + std::string b = "B"; + std::string c = "C"; + double xval, yval, zval, aval, bval, cval; + xval = plac.getPosition().x; + yval = plac.getPosition().y; + zval = plac.getPosition().z; + plac.getRotation().getYawPitchRoll(aval,bval,cval); + if (xval != 0.0) + Parameters[x] = xval; + if (yval != 0.0) + Parameters[y] = yval; + if (zval != 0.0) + Parameters[z] = zval; + if (aval != 0.0) + Parameters[a] = aval; + if (bval != 0.0) + Parameters[b] = bval; + if (cval != 0.0) + Parameters[c] = cval; +} + +void Command::setCenter(const Base::Vector3d &pos, bool clockwise) +{ + if (clockwise) { + Name = "G2"; + } else { + Name = "G3"; + } + std::string i = "I"; + std::string j = "J"; + std::string k = "K"; + double ival, jval, kval; + ival = pos.x; + jval = pos.y; + kval = pos.z; + Parameters[i] = ival; + Parameters[j] = jval; + Parameters[k] = kval; +} + +Command Command::transform(const Base::Placement other) +{ + Base::Placement plac = getPlacement(); + plac *= other; + double xval, yval, zval, aval, bval, cval; + xval = plac.getPosition().x; + yval = plac.getPosition().y; + zval = plac.getPosition().z; + plac.getRotation().getYawPitchRoll(aval,bval,cval); + Command c = Command(); + c.Name = Name; + for(std::map::const_iterator i = Parameters.begin(); i != Parameters.end(); ++i) { + std::string k = i->first; + double v = i->second; + if (k == "X") + v = xval; + if (k == "Y") + v = yval; + if (k == "Z") + v = zval; + if (k == "A") + v = aval; + if (k == "B") + v = bval; + if (k == "C") + v = cval; + c.Parameters[k] = v; + } + return c; +} + +// Reimplemented from base class + +unsigned int Command::getMemSize (void) const +{ + return toGCode().size(); +} + +void Command::Save (Writer &writer) const +{ + // this will only get used if saved as XML (probably never) + writer.Stream() << writer.ind() << ""; + writer.Stream()<< std::endl; +} + +void Command::Restore(XMLReader &reader) +{ + reader.readElement("Command"); + std::string gcode = reader.getAttribute("gcode"); + setFromGCode(gcode); +} + diff --git a/src/Mod/Path/App/Command.h b/src/Mod/Path/App/Command.h new file mode 100644 index 0000000000..0b02a14945 --- /dev/null +++ b/src/Mod/Path/App/Command.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_COMMAND_H +#define PATH_COMMAND_H + +#include +#include +#include +#include +#include + +namespace Path +{ + /** The representation of a cnc command in a path */ + class PathExport Command : public Base::Persistence + { + TYPESYSTEM_HEADER(); + + public: + //constructors + Command(); + Command(const char* name, + const std::map& parameters); + ~Command(); + // from base class + virtual unsigned int getMemSize (void) const; + virtual void Save (Base::Writer &/*writer*/) const; + virtual void Restore(Base::XMLReader &/*reader*/); + + // specific methods + Base::Placement getPlacement (void); // returns a placement from the x,y,z,a,b,c parameters + Base::Vector3d getCenter (void); // returns a 3d vector from the i,j,k parameters + void setCenter(const Base::Vector3d&, bool clockwise=true); // sets the center coordinates and the command name + std::string toGCode (void) const; // returns a GCode string representation of the command + void setFromGCode (std::string); // sets the parameters from the contents of the given GCode string + void setFromPlacement (const Base::Placement&); // sets the parameters from the contents of the given placement + const bool has(const std::string); // returns true if the given string exists in the parameters + Command transform(const Base::Placement); // returns a transformed copy of this command + const double getValue(const std::string); // returns the value of a given parameter + + // attributes + std::string Name; + std::map Parameters; + }; + +} //namespace Path + +#endif // PATH_COMMAND_H diff --git a/src/Mod/Path/App/CommandPy.xml b/src/Mod/Path/App/CommandPy.xml new file mode 100644 index 0000000000..0da50518aa --- /dev/null +++ b/src/Mod/Path/App/CommandPy.xml @@ -0,0 +1,55 @@ + + + + + + Command([name],[parameters]): Represents a basic Gcode command +name (optional) is the name of the command, ex. G1 +parameters (optional) is a dictionary containing string:number +pairs, or a placement, or a vector + + + + The name of the command + + + + + + The parameters of the command + + + + + + The coordinates of the endpoint of the command + + + + + + toGCode(): returns a GCode representation of the command + + + + + toGCode(): returns a GCode representation of the command + + + + + transform(Placement): returns a copy of this command transformed by the given placement + + + + diff --git a/src/Mod/Path/App/CommandPyImp.cpp b/src/Mod/Path/App/CommandPyImp.cpp new file mode 100644 index 0000000000..48d94595d8 --- /dev/null +++ b/src/Mod/Path/App/CommandPyImp.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include + +#include +#include +#include +#include +#include "Mod/Path/App/Command.h" + +// files generated out of CommandPy.xml +#include "CommandPy.h" +#include "CommandPy.cpp" + +using namespace Path; + +// returns a string which represents the object e.g. when printed in python +std::string CommandPy::representation(void) const +{ + std::stringstream str; + str.precision(5); + str << "Command "; + str << getCommandPtr()->Name; + str << " ["; + for(std::map::iterator i = getCommandPtr()->Parameters.begin(); i != getCommandPtr()->Parameters.end(); ++i) { + std::string k = i->first; + double v = i->second; + str << " " << k << ":" << v; + } + str << " ]"; + return str.str(); +} + + +PyObject *CommandPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of CommandPy and the Twin object + return new CommandPy(new Command); +} + +// constructor method +int CommandPy::PyInit(PyObject* args, PyObject* kwd) +{ + PyObject *parameters; + char *name = ""; + static char *kwlist[] = {"name", "parameters", NULL}; + if ( PyArg_ParseTupleAndKeywords(args, kwd, "|sO!", kwlist, &name, &PyDict_Type, ¶meters) ) { + std::string sname(name); + boost::to_upper(sname); + if (!sname.empty()) + getCommandPtr()->setFromGCode(name); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(parameters, &pos, &key, &value)) { + if ( !PyObject_TypeCheck(key,&(PyString_Type)) || (!PyObject_TypeCheck(value,&(PyFloat_Type)) && !PyObject_TypeCheck(value,&(PyInt_Type))) ) { + PyErr_SetString(PyExc_TypeError, "The dictionary can only contain string:number pairs"); + return -1; + } + std::string ckey = PyString_AsString(key); + boost::to_upper(ckey); + double cvalue; + if (PyObject_TypeCheck(value,&(PyInt_Type))) { + cvalue = (double)PyInt_AsLong(value); + } else { + cvalue = PyFloat_AsDouble(value); + } + getCommandPtr()->Parameters[ckey]=cvalue; + } + return 0; + } + PyErr_Clear(); // set by PyArg_ParseTuple() + + if ( PyArg_ParseTupleAndKeywords(args, kwd, "|sO!", kwlist, &name, &(Base::PlacementPy::Type), ¶meters) ) { + std::string sname(name); + boost::to_upper(sname); + if (!sname.empty()) + getCommandPtr()->setFromGCode(name); + Base::PlacementPy *p = static_cast(parameters); + getCommandPtr()->setFromPlacement( *p->getPlacementPtr() ); + return 0; + } + return -1; +} + +// Name attribute + +Py::String CommandPy::getName(void) const +{ + return Py::String(getCommandPtr()->Name.c_str()); +} + +void CommandPy::setName(Py::String arg) +{ + std::string cmd = arg.as_std_string(); + boost::to_upper(cmd); + getCommandPtr()->Name = cmd; +} + +// Parameters attribute get/set + +Py::Dict CommandPy::getParameters(void) const +{ + PyObject *dict = PyDict_New(); + for(std::map::iterator i = getCommandPtr()->Parameters.begin(); i != getCommandPtr()->Parameters.end(); ++i) { + PyDict_SetItem(dict,PyString_FromString(i->first.c_str()),PyFloat_FromDouble(i->second)); + } + return Py::Dict(dict); +} + +void CommandPy::setParameters(Py::Dict arg) +{ + PyObject* dict_copy = PyDict_Copy(arg.ptr()); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict_copy, &pos, &key, &value)) { + if ( PyObject_TypeCheck(key,&(PyString_Type)) && (PyObject_TypeCheck(value,&(PyFloat_Type)) || PyObject_TypeCheck(value,&(PyInt_Type)) ) ) { + std::string ckey = PyString_AsString(key); + boost::to_upper(ckey); + double cvalue; + if (PyObject_TypeCheck(value,&(PyInt_Type))) { + cvalue = (double)PyInt_AsLong(value); + } else { + cvalue = PyFloat_AsDouble(value); + } + getCommandPtr()->Parameters[ckey]=cvalue; + } else { + throw Py::Exception("The dictionary can only contain string:number pairs"); + } + } +} + +// GCode methods + +PyObject* CommandPy::toGCode(PyObject *args) +{ + if (PyArg_ParseTuple(args, "")) { + return PyString_FromString(getCommandPtr()->toGCode().c_str()); + } + throw Py::Exception("This method accepts no argument"); +} + +PyObject* CommandPy::setFromGCode(PyObject *args) +{ + char *pstr=0; + if (PyArg_ParseTuple(args, "s", &pstr)) { + std::string gcode(pstr); + getCommandPtr()->setFromGCode(gcode); + return Py_None; + } + throw Py::Exception("Argument must be a string"); +} + +// Placement attribute get/set + +Py::Object CommandPy::getPlacement(void) const +{ + return Py::Object(new Base::PlacementPy(new Base::Placement(getCommandPtr()->getPlacement()))); +} + +void CommandPy::setPlacement(Py::Object arg) +{ + union PyType_Object pyType = {&(Base::PlacementPy::Type)}; + Py::Type PlacementType(pyType.o); + if(arg.isType(PlacementType)) { + getCommandPtr()->setFromPlacement( *static_cast((*arg))->getPlacementPtr() ); + } else + throw Py::Exception("Argument must be a placement"); +} + +PyObject* CommandPy::transform(PyObject *args) +{ + PyObject *placement; + if ( PyArg_ParseTuple(args, "O!", &(Base::PlacementPy::Type), &placement) ) { + Base::PlacementPy *p = static_cast(placement); + Path::Command trCmd = getCommandPtr()->transform( *p->getPlacementPtr() ); + return new CommandPy(new Path::Command(trCmd)); + } else + throw Py::Exception("Argument must be a placement"); +} + +// custom attributes get/set + +PyObject *CommandPy::getCustomAttributes(const char* attr) const +{ + std::string satt(attr); + if (satt.length() == 1) { + if (isalpha(satt[0])) { + boost::to_upper(satt); + if (getCommandPtr()->Parameters.count(satt)) { + return PyFloat_FromDouble(getCommandPtr()->Parameters[satt]); + } + return Py_None; + } + } + return 0; +} + +int CommandPy::setCustomAttributes(const char* attr, PyObject* obj) +{ + std::string satt(attr); + if (satt.length() == 1) { + if (isalpha(satt[0])) { + boost::to_upper(satt); + double cvalue; + if (PyObject_TypeCheck(obj,&(PyInt_Type))) { + cvalue = (double)PyInt_AsLong(obj); + } else if (PyObject_TypeCheck(obj,&(PyFloat_Type))) { + cvalue = PyFloat_AsDouble(obj); + } else { + return 0; + } + getCommandPtr()->Parameters[satt]=cvalue; + return 1; + } + } + return 0; +} + + + + diff --git a/src/Mod/Path/App/FeaturePath.cpp b/src/Mod/Path/App/FeaturePath.cpp new file mode 100644 index 0000000000..a391a4b1e2 --- /dev/null +++ b/src/Mod/Path/App/FeaturePath.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "FeaturePath.h" +#include +#include + +using namespace Path; + +PROPERTY_SOURCE(Path::Feature, App::GeoFeature) + + +Feature::Feature() +{ + ADD_PROPERTY_TYPE(Path,(Path::Toolpath()),"Base",App::Prop_None,"The path data of this feature"); +} + +Feature::~Feature() +{ +} + +short Feature::mustExecute(void) const +{ + return App::GeoFeature::mustExecute(); +} + +PyObject *Feature::getPyObject() +{ + if (PythonObject.is(Py::_None())){ + // ref counter is set to 1 + PythonObject = Py::Object(new App::DocumentObjectPy(this),true); + } + return Py::new_reference_to(PythonObject); +} + +void Feature::onChanged(const App::Property* prop) +{ + App::GeoFeature::onChanged(prop); +} + +// Python Path feature --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Path::FeaturePython, Path::Feature) +template<> const char* Path::FeaturePython::getViewProviderName(void) const { + return "PathGui::ViewProviderPathPython"; +} +/// @endcond + +// explicit template instantiation +template class PathExport FeaturePythonT; +} diff --git a/src/Mod/Path/App/FeaturePath.h b/src/Mod/Path/App/FeaturePath.h new file mode 100644 index 0000000000..0cf587bddb --- /dev/null +++ b/src/Mod/Path/App/FeaturePath.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_FeaturePath_H +#define PATH_FeaturePath_H + +#include +#include +#include +#include +#include + +#include "Path.h" +#include "PropertyPath.h" + +namespace Path +{ + +class PathExport Feature : public App::GeoFeature +{ + PROPERTY_HEADER(Path::Feature); + +public: + /// Constructor + Feature(void); + virtual ~Feature(); + + /// returns the type name of the ViewProvider + virtual const char* getViewProviderName(void) const { + return "PathGui::ViewProviderPath"; + } + virtual App::DocumentObjectExecReturn *execute(void) { + return App::DocumentObject::StdReturn; + } + virtual short mustExecute(void) const; + virtual PyObject *getPyObject(void); + + PropertyPath Path; + + +protected: + /// get called by the container when a property has changed + virtual void onChanged (const App::Property* prop); + +}; + +typedef App::FeaturePythonT FeaturePython; + +} //namespace Path + + +#endif // PATH_FeaturePath_H diff --git a/src/Mod/Path/App/FeaturePathCompound.cpp b/src/Mod/Path/App/FeaturePathCompound.cpp new file mode 100644 index 0000000000..b1d9943a61 --- /dev/null +++ b/src/Mod/Path/App/FeaturePathCompound.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "FeaturePathCompound.h" +#include "Command.h" +#include "Path.h" + +using namespace Path; +using namespace App; + +PROPERTY_SOURCE(Path::FeatureCompound, Path::Feature) + + +FeatureCompound::FeatureCompound() +{ + ADD_PROPERTY_TYPE( Group, (0), "Base",Prop_None,"Ordered list of paths to combine"); + ADD_PROPERTY_TYPE( UsePlacements, (false), "Base",Prop_None,"Specifies if the placements of children must be computed"); +} + +FeatureCompound::~FeatureCompound() +{ +} + +App::DocumentObjectExecReturn *FeatureCompound::execute(void) +{ + const std::vector &Paths = Group.getValues(); + Path::Toolpath result; + + for (std::vector::const_iterator it= Paths.begin();it!=Paths.end();++it) { + if ((*it)->getTypeId().isDerivedFrom(Path::Feature::getClassTypeId())){ + const std::vector &cmds = static_cast(*it)->Path.getValue().getCommands(); + const Base::Placement pl = static_cast(*it)->Placement.getValue(); + for (std::vector::const_iterator it2= cmds.begin();it2!=cmds.end();++it2) { + if (UsePlacements.getValue() == true) { + result.addCommand((*it2)->transform(pl)); + } else { + result.addCommand(**it2); + } + } + }else + return new App::DocumentObjectExecReturn("Not all objects in group are paths!"); + } + + Path.setValue(result); + + return App::DocumentObject::StdReturn; +} + +bool FeatureCompound::hasObject(const DocumentObject* obj) const +{ + const std::vector& grp = Group.getValues(); + for (std::vector::const_iterator it = grp.begin(); it != grp.end(); ++it) { + if (*it == obj) + return true; + } + + return false; +} + +void FeatureCompound::addObject(DocumentObject* obj) +{ + if (!hasObject(obj)) { + std::vector grp = Group.getValues(); + grp.push_back(obj); + Group.setValues(grp); + } +} + +void FeatureCompound::removeObject(DocumentObject* obj) +{ + std::vector grp = Group.getValues(); + for (std::vector::iterator it = grp.begin(); it != grp.end(); ++it) { + if (*it == obj) { + grp.erase(it); + Group.setValues(grp); + break; + } + } +} + +// Python Path Compound feature --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Path::FeatureCompoundPython, Path::FeatureCompound) +template<> const char* Path::FeatureCompoundPython::getViewProviderName(void) const { + return "PathGui::ViewProviderPathCompoundPython"; +} +/// @endcond + +// explicit template instantiation +template class PathExport FeaturePythonT; +} diff --git a/src/Mod/Path/App/FeaturePathCompound.h b/src/Mod/Path/App/FeaturePathCompound.h new file mode 100644 index 0000000000..56763ba4ec --- /dev/null +++ b/src/Mod/Path/App/FeaturePathCompound.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_FeatureCompound_H +#define PATH_FeatureCompound_H + +#include +#include +#include +#include + +#include "Path.h" +#include "FeaturePath.h" +#include "PropertyPath.h" + +namespace Path +{ + +class PathExport FeatureCompound : public Path::Feature +{ + PROPERTY_HEADER(Path::Feature); + +public: + /// Constructor + FeatureCompound(void); + virtual ~FeatureCompound(); + + App::PropertyLinkList Group; + App::PropertyBool UsePlacements; + + /// returns the type name of the ViewProvider + virtual const char* getViewProviderName(void) const { + return "PathGui::ViewProviderPathCompound"; + } + virtual App::DocumentObjectExecReturn *execute(void); + + /// Checks whether the object \a obj is part of this group. + bool hasObject(const DocumentObject* obj) const; + /// Adds an object to this group. + void addObject(DocumentObject* obj); + /// Removes an object from this group. + void removeObject(DocumentObject* obj); + +}; + +typedef App::FeaturePythonT FeatureCompoundPython; + +} //namespace Path + + +#endif // PATH_FeatureCompound_H diff --git a/src/Mod/Path/App/FeaturePathShape.cpp b/src/Mod/Path/App/FeaturePathShape.cpp new file mode 100644 index 0000000000..14c2a48dd7 --- /dev/null +++ b/src/Mod/Path/App/FeaturePathShape.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "FeaturePathShape.h" +#include "Command.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace Path; + +PROPERTY_SOURCE(Path::FeatureShape, Path::Feature) + + +FeatureShape::FeatureShape() +{ + ADD_PROPERTY_TYPE(Shape,(TopoDS_Shape()),"Path",App::Prop_None,"The shape data of this feature"); +} + +FeatureShape::~FeatureShape() +{ +} + +short FeatureShape::mustExecute(void) const +{ + return Path::Feature::mustExecute(); +} + +App::DocumentObjectExecReturn *FeatureShape::execute(void) +{ + TopoDS_Shape shape = Shape.getValue(); + if (!shape.IsNull()) { + if (shape.ShapeType() == TopAbs_WIRE) { + Path::Toolpath result; + bool first = true; + Base::Placement last; + + TopExp_Explorer ExpEdges (shape,TopAbs_EDGE); + while (ExpEdges.More()) { + const TopoDS_Edge& edge = TopoDS::Edge(ExpEdges.Current()); + TopExp_Explorer ExpVerts(edge,TopAbs_VERTEX); + bool vfirst = true; + while (ExpVerts.More()) { + const TopoDS_Vertex& vert = TopoDS::Vertex(ExpVerts.Current()); + gp_Pnt pnt = BRep_Tool::Pnt(vert); + Base::Placement tpl; + tpl.setPosition(Base::Vector3d(pnt.X(),pnt.Y(),pnt.Z())); + if (first) { + // add first point as a G0 move + Path::Command cmd; + std::ostringstream ctxt; + ctxt << "G0 X" << tpl.getPosition().x << " Y" << tpl.getPosition().y << " Z" << tpl.getPosition().z; + cmd.setFromGCode(ctxt.str()); + result.addCommand(cmd); + first = false; + vfirst = false; + } else { + if (vfirst) + vfirst = false; + else { + Path::Command cmd; + cmd.setFromPlacement(tpl); + + // write arc data if needed + BRepAdaptor_Curve adapt(edge); + if (adapt.GetType() == GeomAbs_Circle) { + gp_Circ circ = adapt.Circle(); + gp_Pnt c = circ.Location(); + bool clockwise = false; + gp_Dir n = circ.Axis().Direction(); + if (n.Z() < 0) + clockwise = true; + Base::Vector3d center = Base::Vector3d(c.X(),c.Y(),c.Z()); + // center coords must be relative to last point + center -= last.getPosition(); + cmd.setCenter(center,clockwise); + } + result.addCommand(cmd); + } + } + ExpVerts.Next(); + last = tpl; + } + ExpEdges.Next(); + } + + Path.setValue(result); + } + } + return App::DocumentObject::StdReturn; +} + +// Python Path Shape feature --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Path::FeatureShapePython, Path::FeatureShape) +template<> const char* Path::FeatureShapePython::getViewProviderName(void) const { + return "PathGui::ViewProviderPathShape"; +} +/// @endcond + +// explicit template instantiation +template class PathExport FeaturePythonT; +} + + + diff --git a/src/Mod/Path/App/FeaturePathShape.h b/src/Mod/Path/App/FeaturePathShape.h new file mode 100644 index 0000000000..23648abcb3 --- /dev/null +++ b/src/Mod/Path/App/FeaturePathShape.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_FeaturePathShape_H +#define PATH_FeaturePathShape_H + +#include +#include +#include +#include +#include +#include "Mod/Part/App/PropertyTopoShape.h" + +#include "Path.h" +#include "PropertyPath.h" +#include "FeaturePath.h" + +namespace Path +{ + +class PathExport FeatureShape : public Path::Feature +{ + PROPERTY_HEADER(Path::FeatureShape); + +public: + /// Constructor + FeatureShape(void); + virtual ~FeatureShape(); + + Part::PropertyPartShape Shape; + + //@{ + /// recalculate the feature + virtual App::DocumentObjectExecReturn *execute(void); + virtual short mustExecute(void) const; + //@} + + /// returns the type name of the ViewProvider + virtual const char* getViewProviderName(void) const { + return "PathGui::ViewProviderPathShape"; + } + +protected: + /// get called by the container when a property has changed + //virtual void onChanged (const App::Property* prop); + +}; + +typedef App::FeaturePythonT FeatureShapePython; + +} //namespace Path + + +#endif // PATH_FeaturePathShape_H diff --git a/src/Mod/Path/App/Path.cpp b/src/Mod/Path/App/Path.cpp new file mode 100644 index 0000000000..9fdda9f259 --- /dev/null +++ b/src/Mod/Path/App/Path.cpp @@ -0,0 +1,331 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include +#include + +#include +#include +#include +#include + +#include "Mod/Robot/App/kdl_cp/path_line.hpp" +#include "Mod/Robot/App/kdl_cp/path_circle.hpp" +#include "Mod/Robot/App/kdl_cp/rotational_interpolation_sa.hpp" +#include "Mod/Robot/App/kdl_cp/utilities/error.h" + +#include "Path.h" + +using namespace Path; +using namespace Base; + +TYPESYSTEM_SOURCE(Path::Toolpath , Base::Persistence); + +Toolpath::Toolpath() +{ +} + +Toolpath::Toolpath(const Toolpath& otherPath) +:vpcCommands(otherPath.vpcCommands.size()) +{ + operator=(otherPath); + recalculate(); +} + +Toolpath::~Toolpath() +{ + clear(); +} + +Toolpath &Toolpath::operator=(const Toolpath& otherPath) +{ + clear(); + vpcCommands.resize(otherPath.vpcCommands.size()); + int i = 0; + for (std::vector::const_iterator it=otherPath.vpcCommands.begin();it!=otherPath.vpcCommands.end();++it,i++) + vpcCommands[i] = new Command(**it); + recalculate(); + return *this; +} + +void Toolpath::clear(void) +{ + for(std::vector::iterator it = vpcCommands.begin();it!=vpcCommands.end();++it) + delete ( *it ); + vpcCommands.clear(); + recalculate(); +} + +void Toolpath::addCommand(const Command &Cmd) +{ + Command *tmp = new Command(Cmd); + vpcCommands.push_back(tmp); + recalculate(); +} + +void Toolpath::insertCommand(const Command &Cmd, int pos) +{ + if (pos == -1) { + addCommand(Cmd); + } else if (pos <= vpcCommands.size()) { + Command *tmp = new Command(Cmd); + vpcCommands.insert(vpcCommands.begin()+pos,tmp); + } else { + throw Base::Exception("Index not in range"); + } + recalculate(); +} + +void Toolpath::deleteCommand(int pos) +{ + if (pos == -1) { + //delete(*vpcCommands.rbegin()); // causes crash + vpcCommands.pop_back(); + } else if (pos <= vpcCommands.size()) { + vpcCommands.erase (vpcCommands.begin()+pos); + } else { + throw Base::Exception("Index not in range"); + } + recalculate(); +} + +double Toolpath::getLength() +{ + if(vpcCommands.size()==0) + return 0; + double l = 0; + Vector3d last(0,0,0); + Vector3d next; + for(std::vector::const_iterator it = vpcCommands.begin();it!=vpcCommands.end();++it) { + std::string name = (*it)->Name; + next = (*it)->getPlacement().getPosition(); + if ( (name == "G0") || (name == "G00") || (name == "G1") || (name == "G01") ) { + // straight line + l += (next - last).Length(); + last = next; + } else if ( (name == "G2") || (name == "G02") || (name == "G3") || (name == "G03") ) { + // arc + Vector3d center = (*it)->getCenter(); + double radius = (last - center).Length(); + double angle = (next - center).GetAngle(last - center); + l += angle * radius; + last = next; + } + } + return l; +} + +void Toolpath::setFromGCode(const std::string instr) +{ + clear(); + + // remove comments + //boost::regex e("\\(.*?\\)"); + //std::string str = boost::regex_replace(instr, e, ""); + std::string str(instr); + + // split input string by () or G or M commands + std::string mode = "command"; + std::size_t found = str.find_first_of("(gGmM"); + int last = -1; + while (found!=std::string::npos) + { + if (str[found] == '(') { + // start of comment + if ( (last > -1) && (mode == "command") ) { + // before opening a comment, add the last found command + std::string gcodestr = str.substr(last,found-last); + Command *tmp = new Command(); + tmp->setFromGCode(gcodestr); + vpcCommands.push_back(tmp); + } + mode = "comment"; + last = found; + found=str.find_first_of(")",found+1); + } else if (str[found] == ')') { + // end of comment + std::string gcodestr = str.substr(last,found-last+1); + Command *tmp = new Command(); + tmp->setFromGCode(gcodestr); + vpcCommands.push_back(tmp); + last = -1; + found=str.find_first_of("(gGmM",found+1); + mode = "command"; + } else if (mode == "command") { + // command + if (last > -1) { + std::string gcodestr = str.substr(last,found-last); + Command *tmp = new Command(); + tmp->setFromGCode(gcodestr); + vpcCommands.push_back(tmp); + } + last = found; + found=str.find_first_of("(gGmM",found+1); + } + } + // add the last command found, if any + if (last > -1) { + if (mode == "command") { + std::string gcodestr = str.substr(last,std::string::npos); + Command *tmp = new Command(); + tmp->setFromGCode(gcodestr); + vpcCommands.push_back(tmp); + } + } + recalculate(); +} + +std::string Toolpath::toGCode(void) const +{ + std::string result; + for (std::vector::const_iterator it=vpcCommands.begin();it!=vpcCommands.end();++it) { + result += (*it)->toGCode(); + result += "\n"; + } + return result; +} + +void Toolpath::recalculate(void) // recalculates the path cache +{ + + if(vpcCommands.size()==0) + return; + + // TODO recalculate the KDL stuff. At the moment, this is unused. + + /* + // delete the old and create a new one + if(pcPath) + delete (pcPath); + + pcPath = new KDL::Path_Composite(); + + KDL::Path *tempPath; + KDL::Frame Last; + + try { + // handle the first waypoint differently + bool first=true; + + for(std::vector::const_iterator it = vpcCommands.begin();it!=vpcCommands.end();++it) { + if(first){ + Last = toFrame((*it)->getPlacement()); + first = false; + }else{ + Base::Placement p = (*it)->getPlacement(); + KDL::Frame Next = toFrame(p); + std::string name = (*it)->Name; + Vector3d zaxis(0,0,1); + + if ( (name == "G0") || (name == "G1") || (name == "G01") ) { + // line segment + tempPath = new KDL::Path_Line(Last, Next, new KDL::RotationalInterpolation_SingleAxis(), 1.0, true); + pcPath->Add(tempPath); + Last = Next; + } else if ( (name == "G2") || (name == "G02") ) { + // clockwise arc + Vector3d fcenter = (*it)->getCenter(); + KDL::Vector center(fcenter.x,fcenter.y,fcenter.z); + Vector3d fnorm; + p.getRotation().multVec(zaxis,fnorm); + KDL::Vector norm(fnorm.x,fnorm.y,fnorm.z); + Vector3d fstart = toPlacement(Last).getPosition(); + Vector3d fend = toPlacement(Last).getPosition(); + Rotation frot(fstart-fcenter,fend-fcenter); + double q0,q1,q2,q3; + frot.getValue(q0,q1,q2,q3); + KDL::Rotation rot; + rot.Quaternion(q0,q1,q2,q3); + tempPath = new KDL::Path_Circle(Last, center, norm, rot, 0.0, new KDL::RotationalInterpolation_SingleAxis(), 1.0, true); + pcPath->Add(tempPath); + Last = Next; + } + } + } + } catch (KDL::Error &e) { + throw Base::Exception(e.Description()); + } + */ +} + +// reimplemented from base class + +unsigned int Toolpath::getMemSize (void) const +{ + return toGCode().size(); +} + +void Toolpath::Save (Writer &writer) const +{ + if (writer.isForceXML()) { + writer.Stream() << writer.ind() << "" << std::endl; + writer.incInd(); + for(unsigned int i = 0;iSave(writer); + writer.decInd(); + writer.Stream() << writer.ind() << "" << std::endl; + } else { + writer.Stream() << writer.ind() + << "" << std::endl; + } +} + +void Toolpath::SaveDocFile (Base::Writer &writer) const +{ + if (toGCode().empty()) + return; + writer.Stream() << toGCode(); +} + +void Toolpath::Restore(XMLReader &reader) +{ + reader.readElement("Path"); + std::string file (reader.getAttribute("file") ); + + if (!file.empty()) { + // initate a file read + reader.addFile(file.c_str(),this); + } +} + +void Toolpath::RestoreDocFile(Base::Reader &reader) +{ + std::string gcode; + std::string line; + while (reader >> line) { + gcode += line; + gcode += " "; + } + setFromGCode(gcode); + +} + + + + + diff --git a/src/Mod/Path/App/Path.h b/src/Mod/Path/App/Path.h new file mode 100644 index 0000000000..aa38c26e10 --- /dev/null +++ b/src/Mod/Path/App/Path.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_Path_H +#define PATH_Path_H + +#include "Command.h" +#include "Mod/Robot/App/kdl_cp/path_composite.hpp" +#include "Mod/Robot/App/kdl_cp/frames_io.hpp" +#include +#include + +namespace Path +{ + + /** The representation of a CNC Toolpath */ + + class PathExport Toolpath : public Base::Persistence + { + TYPESYSTEM_HEADER(); + + public: + Toolpath(); + Toolpath(const Toolpath&); + ~Toolpath(); + + Toolpath &operator=(const Toolpath&); + + // from base class + virtual unsigned int getMemSize (void) const; + virtual void Save (Base::Writer &/*writer*/) const; + virtual void Restore(Base::XMLReader &/*reader*/); + void SaveDocFile (Base::Writer &writer) const; + void RestoreDocFile(Base::Reader &reader); + + // interface + void clear(void); // clears the internal data + void addCommand(const Command &Cmd); // adds a command at the end + void insertCommand(const Command &Cmd, int); // inserts a command + void deleteCommand(int); // deletes a command + double getLength(void); // return the Length (mm) of the Path + void recalculate(void); // recalculates the points + void setFromGCode(const std::string); // sets the path from the contents of the given GCode string + std::string toGCode(void) const; // gets a gcode string representation from the Path + + // shortcut functions + unsigned int getSize(void) const{return vpcCommands.size();} + const std::vector &getCommands(void)const{return vpcCommands;} + const Command &getCommand(unsigned int pos)const {return *vpcCommands[pos];} + + protected: + std::vector vpcCommands; + KDL::Path_Composite *pcPath; + + inline KDL::Frame toFrame(const Base::Placement &To){ + return KDL::Frame(KDL::Rotation::Quaternion(To.getRotation()[0], + To.getRotation()[1], + To.getRotation()[2], + To.getRotation()[3]), + KDL::Vector(To.getPosition()[0], + To.getPosition()[1], + To.getPosition()[2])); + } + inline Base::Placement toPlacement(const KDL::Frame &To){ + double x,y,z,w; + To.M.GetQuaternion(x,y,z,w); + return Base::Placement(Base::Vector3d(To.p[0],To.p[1],To.p[2]),Base::Rotation(x,y,z,w)); + } + }; + +} //namespace Path + + +#endif // PATH_Path_H diff --git a/src/Mod/Path/App/PathPy.xml b/src/Mod/Path/App/PathPy.xml new file mode 100644 index 0000000000..49a964a13e --- /dev/null +++ b/src/Mod/Path/App/PathPy.xml @@ -0,0 +1,74 @@ + + + + + + Path([commands]): Represents a basic Gcode path +commands (optional) is a list of Path commands + + + + the total length of this path in mm + + + + + + the number of commands in this path + + + + + + the list of commands of this path + + + + + + adds a command or a list of commands at the end of the path + + + + + insertCommand(Command,[int]): +adds a command at the given position or at the end of the path + + + + + deleteCommand([int]): +deletes the command found at the given position or from the end of the path + + + + + sets the contents of the path from a gcode string + + + + + returns a gcode string representing the path + + + + + returns a copy of this path + + + + bool touched; + + + + diff --git a/src/Mod/Path/App/PathPyImp.cpp b/src/Mod/Path/App/PathPyImp.cpp new file mode 100644 index 0000000000..cdc18f9273 --- /dev/null +++ b/src/Mod/Path/App/PathPyImp.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#include "Mod/Path/App/Path.h" + +// inclusion of the generated files (generated out of PathPy.xml) +#include "PathPy.h" +#include "PathPy.cpp" + +#include "CommandPy.h" + +using namespace Path; + +// returns a string which represents the object e.g. when printed in python +std::string PathPy::representation(void) const +{ + std::stringstream str; + str.precision(5); + str << "Path [ "; + str << "size:" << getToolpathPtr()->getSize() << " "; + str << "length:" << getToolpathPtr()->getLength(); + str << " ]"; + + return str.str(); +} + +PyObject *PathPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of PathPy and the Twin object + return new PathPy(new Toolpath); +} + +// constructor method +int PathPy::PyInit(PyObject* args, PyObject* /*kwd*/) +{ + PyObject *pcObj=0; + char *gcode; + if (PyArg_ParseTuple(args, "|O!", &(PyList_Type), &pcObj)) { + if (pcObj) { + Py::List list(pcObj); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Path::CommandPy::Type))) { + Path::Command &cmd = *static_cast((*it).ptr())->getCommandPtr(); + getToolpathPtr()->addCommand(cmd); + } else { + PyErr_SetString(PyExc_TypeError, "The list must contain only Path Commands"); + return -1; + } + } + } + return 0; + } + PyErr_Clear(); // set by PyArg_ParseTuple() + if (PyArg_ParseTuple(args, "|s", &gcode)) { + getToolpathPtr()->setFromGCode(gcode); + return 0; + } + PyErr_SetString(PyExc_TypeError, "Argument must be a list of commands or a gcode string"); + return -1; +} + + +// Commands get/set + +Py::List PathPy::getCommands(void) const +{ + Py::List list; + for(unsigned int i = 0; i < getToolpathPtr()->getSize(); i++) + list.append(Py::Object(new Path::CommandPy(new Path::Command(getToolpathPtr()->getCommand(i))))); + return list; +} + +void PathPy::setCommands(Py::List list) +{ + getToolpathPtr()->clear(); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Path::CommandPy::Type))) { + Path::Command &cmd = *static_cast((*it).ptr())->getCommandPtr(); + getToolpathPtr()->addCommand(cmd); + } else { + throw Py::Exception("The list can only contain Path Commands"); + } + } +} + +// read-only attributes + +Py::Float PathPy::getLength(void) const +{ + return Py::Float(getToolpathPtr()->getLength()); +} + +Py::Int PathPy::getSize(void) const +{ + return Py::Int((int)getToolpathPtr()->getSize()); +} + +// specific methods + +PyObject* PathPy::copy(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + return new PathPy(new Path::Toolpath(*getToolpathPtr())); + } + throw Py::Exception("This method accepts no argument"); +} + +PyObject* PathPy::addCommands(PyObject * args) +{ + PyObject* o; + if (PyArg_ParseTuple(args, "O!", &(Path::CommandPy::Type), &o)) { + Path::Command &cmd = *static_cast(o)->getCommandPtr(); + getToolpathPtr()->addCommand(cmd); + return new PathPy(new Path::Toolpath(*getToolpathPtr())); + //Py_Return; + } + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O!", &(PyList_Type), &o)) { + Py::List list(o); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Path::CommandPy::Type))) { + Path::Command &cmd = *static_cast((*it).ptr())->getCommandPtr(); + getToolpathPtr()->addCommand(cmd); + } + } + return new PathPy(new Path::Toolpath(*getToolpathPtr())); + } + Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters - command or list of commands expected"); +} + +PyObject* PathPy::insertCommand(PyObject * args) +{ + PyObject* o; + int pos = -1; + if (PyArg_ParseTuple(args, "O!|i", &(Path::CommandPy::Type), &o, &pos)) { + Path::Command &cmd = *static_cast(o)->getCommandPtr(); + getToolpathPtr()->insertCommand(cmd,pos); + return new PathPy(new Path::Toolpath(*getToolpathPtr())); + } + Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters - expected command and optional integer"); +} + +PyObject* PathPy::deleteCommand(PyObject * args) +{ + int pos = -1; + if (PyArg_ParseTuple(args, "|i", &pos)) { + getToolpathPtr()->deleteCommand(pos); + return new PathPy(new Path::Toolpath(*getToolpathPtr())); + } + Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters - expected an integer (optional)"); +} + +// GCode methods + +PyObject* PathPy::toGCode(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + std::string result = getToolpathPtr()->toGCode(); + return PyString_FromString(result.c_str()); + } + throw Py::Exception("This method accepts no argument"); +} + +PyObject* PathPy::setFromGCode(PyObject * args) +{ + char *pstr=0; + if (PyArg_ParseTuple(args, "s", &pstr)) { + std::string gcode(pstr); + getToolpathPtr()->setFromGCode(gcode); + return Py_None; + } + throw Py::Exception("Argument must be a string"); +} + +// custom attributes get/set + +PyObject *PathPy::getCustomAttributes(const char* /*attr*/) const +{ + return 0; +} + +int PathPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} + + diff --git a/src/Mod/Path/App/PreCompiled.cpp b/src/Mod/Path/App/PreCompiled.cpp new file mode 100644 index 0000000000..a405fd9683 --- /dev/null +++ b/src/Mod/Path/App/PreCompiled.cpp @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" diff --git a/src/Mod/Path/App/PreCompiled.h b/src/Mod/Path/App/PreCompiled.h new file mode 100644 index 0000000000..07a2af006f --- /dev/null +++ b/src/Mod/Path/App/PreCompiled.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_PRECOMPILED_H +#define PATH_PRECOMPILED_H + +#include + +// Exporting of App classes +#ifdef FC_OS_WIN32 +# define PathExport __declspec(dllexport) +# define RobotExport __declspec(dllexport) +# define PartExport __declspec(dllexport) +# define BaseExport __declspec(dllimport) +#else // for Linux +# define PathExport +# define RobotExport +# define PartExport +# define BaseExport +#endif + +#ifdef _PreComp_ + +// standard +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#endif // _PreComp_ +#endif + diff --git a/src/Mod/Path/App/PropertyPath.cpp b/src/Mod/Path/App/PropertyPath.cpp new file mode 100644 index 0000000000..e96ce5b305 --- /dev/null +++ b/src/Mod/Path/App/PropertyPath.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +#endif + + +#include +#include +#include +#include +#include +#include +#include + +#include "PropertyPath.h" +#include "PathPy.h" + +using namespace Path; + +TYPESYSTEM_SOURCE(Path::PropertyPath, App::Property); + +PropertyPath::PropertyPath() +{ +} + +PropertyPath::~PropertyPath() +{ +} + +void PropertyPath::setValue(const Toolpath& pa) +{ + aboutToSetValue(); + _Path = pa; + hasSetValue(); +} + + +const Toolpath &PropertyPath::getValue(void)const +{ + return _Path; +} + +PyObject *PropertyPath::getPyObject(void) +{ + return new PathPy(new Toolpath(_Path)); +} + +void PropertyPath::setPyObject(PyObject *value) +{ + if (PyObject_TypeCheck(value, &(PathPy::Type))) { + PathPy *pcObject = static_cast(value); + setValue(*pcObject->getToolpathPtr()); + } + else { + std::string error = std::string("type must be 'Path', not "); + error += value->ob_type->tp_name; + throw Base::TypeError(error); + } +} + +App::Property *PropertyPath::Copy(void) const +{ + PropertyPath *prop = new PropertyPath(); + prop->_Path = this->_Path; + + return prop; +} + +void PropertyPath::Paste(const App::Property &from) +{ + aboutToSetValue(); + _Path = dynamic_cast(from)._Path; + hasSetValue(); +} + +unsigned int PropertyPath::getMemSize (void) const +{ + return _Path.getMemSize(); +} + +void PropertyPath::Save (Base::Writer &writer) const +{ + _Path.Save(writer); +} + +void PropertyPath::Restore(Base::XMLReader &reader) +{ + reader.readElement("Path"); + std::string file (reader.getAttribute("file") ); + + if (!file.empty()) { + // initate a file read + reader.addFile(file.c_str(),this); + } +} + +void PropertyPath::SaveDocFile (Base::Writer &writer) const +{ + // does nothing +} + +void PropertyPath::RestoreDocFile(Base::Reader &reader) +{ + aboutToSetValue(); + _Path.RestoreDocFile(reader); + hasSetValue(); +} + + + diff --git a/src/Mod/Path/App/PropertyPath.h b/src/Mod/Path/App/PropertyPath.h new file mode 100644 index 0000000000..d27f4c1bc7 --- /dev/null +++ b/src/Mod/Path/App/PropertyPath.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PROPERTYPATH_H +#define PROPERTYPATH_H + +#include "Path.h" +#include + +namespace Path +{ + + +/** The path property class. */ +class PathExport PropertyPath : public App::Property +{ + TYPESYSTEM_HEADER(); + +public: + PropertyPath(); + ~PropertyPath(); + + /** @name Getter/setter */ + //@{ + /// set the part shape + void setValue(const Toolpath&); + /// get the part shape + const Toolpath &getValue(void) const; + //@} + + /** @name Python interface */ + //@{ + PyObject* getPyObject(void); + void setPyObject(PyObject *value); + //@} + + /** @name Save/restore */ + //@{ + void Save (Base::Writer &writer) const; + void Restore(Base::XMLReader &reader); + void SaveDocFile (Base::Writer &writer) const; + void RestoreDocFile(Base::Reader &reader); + + App::Property *Copy(void) const; + void Paste(const App::Property &from); + unsigned int getMemSize (void) const; + //@} + +private: + Toolpath _Path; +}; + + +} //namespace Path + + +#endif // PROPERTYPATH_H diff --git a/src/Mod/Path/App/PropertyTooltable.cpp b/src/Mod/Path/App/PropertyTooltable.cpp new file mode 100644 index 0000000000..d740873054 --- /dev/null +++ b/src/Mod/Path/App/PropertyTooltable.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +#endif + + +#include +#include +#include +#include +#include +#include +#include + +#include "PropertyTooltable.h" +#include "TooltablePy.h" + +using namespace Path; + +TYPESYSTEM_SOURCE(Path::PropertyTooltable, App::Property); + +PropertyTooltable::PropertyTooltable() +{ +} + +PropertyTooltable::~PropertyTooltable() +{ +} + +void PropertyTooltable::setValue(const Tooltable& tt) +{ + aboutToSetValue(); + _Tooltable = tt; + hasSetValue(); +} + + +const Tooltable &PropertyTooltable::getValue(void)const +{ + return _Tooltable; +} + +PyObject *PropertyTooltable::getPyObject(void) +{ + return new TooltablePy(new Tooltable(_Tooltable)); +} + +void PropertyTooltable::setPyObject(PyObject *value) +{ + if (PyObject_TypeCheck(value, &(TooltablePy::Type))) { + TooltablePy *pcObject = static_cast(value); + setValue(*pcObject->getTooltablePtr()); + } + else { + std::string error = std::string("type must be 'Tooltable', not "); + error += value->ob_type->tp_name; + throw Base::TypeError(error); + } +} + +App::Property *PropertyTooltable::Copy(void) const +{ + PropertyTooltable *prop = new PropertyTooltable(); + prop->_Tooltable = this->_Tooltable; + + return prop; +} + +void PropertyTooltable::Paste(const App::Property &from) +{ + aboutToSetValue(); + _Tooltable = dynamic_cast(from)._Tooltable; + hasSetValue(); +} + +unsigned int PropertyTooltable::getMemSize (void) const +{ + return _Tooltable.getMemSize(); +} + +void PropertyTooltable::Save (Base::Writer &writer) const +{ + _Tooltable.Save(writer); +} + +void PropertyTooltable::Restore(Base::XMLReader &reader) +{ + Path::Tooltable temp; + temp.Restore(reader); + setValue(temp); +} + + diff --git a/src/Mod/Path/App/PropertyTooltable.h b/src/Mod/Path/App/PropertyTooltable.h new file mode 100644 index 0000000000..d4b62fd2d1 --- /dev/null +++ b/src/Mod/Path/App/PropertyTooltable.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PROPERTYTOOLTABLE_H +#define PROPERTYTOOLTABLE_H + +#include "Tooltable.h" +#include + +namespace Path +{ + + +/** The tooltable property class. */ +class PathExport PropertyTooltable : public App::Property +{ + TYPESYSTEM_HEADER(); + +public: + PropertyTooltable(); + ~PropertyTooltable(); + + /** @name Getter/setter */ + //@{ + /// set the part shape + void setValue(const Tooltable&); + /// get the part shape + const Tooltable &getValue(void) const; + //@} + + /** @name Python interface */ + //@{ + PyObject* getPyObject(void); + void setPyObject(PyObject *value); + //@} + + /** @name Save/restore */ + //@{ + void Save (Base::Writer &writer) const; + void Restore(Base::XMLReader &reader); + + App::Property *Copy(void) const; + void Paste(const App::Property &from); + unsigned int getMemSize (void) const; + //@} + +private: + Tooltable _Tooltable; +}; + + +} //namespace Path + + +#endif // PROPERTYTOOLTABLE_H diff --git a/src/Mod/Path/App/ToolPy.xml b/src/Mod/Path/App/ToolPy.xml new file mode 100644 index 0000000000..ba4b8cb15f --- /dev/null +++ b/src/Mod/Path/App/ToolPy.xml @@ -0,0 +1,93 @@ + + + + + + The Tool objects holds the properties of a CNC tool. +optional attributes: + name: a user-defined name for this tool + tooltype: Drill, CenterDrill, CounterSink, CounterBore, Reamer, Tap, EndMill, SlotCutter, BallEndMill, ChamferMill, CornerRound, Engraver or Undefined + material: HighSpeedSteel, HighCarbonToolSteel, Carbide, CastAlloy, Ceramics, Diamond, Sialon or Undefined + diameter : the diameter of this tool + lengthOffset + flatRadius + cornerRadius + cuttingEdgeAngle + cuttingEdgeHeight + + + + the name of this tool in mm + + + + + + the type of this tool: Drill, CenterDrill, CounterSink, CounterBore, Reamer, Tap, +EndMill, SlotCutter, BallEndMill, ChamferMill, CornerRound, Engraver or Undefined + + + + + + the material of this tool: Steel, Carbide, HighSpeedSteel, +HighCarbonToolSteel CastAlloy, Ceramics, Diamond, Sialon or Undefined + + + + + + the diameter of this tool in mm + + + + + + the length offset of this tool in mm + + + + + + the flat radius of this tool in mm + + + + + + the corner radius of this tool in mm + + + + + + the cutting edge angle of this tool + + + + + + the cutting edge height of this tool in mm + + + + + + returns a copy of this tool + + + + bool touched; + + + diff --git a/src/Mod/Path/App/Tooltable.cpp b/src/Mod/Path/App/Tooltable.cpp new file mode 100644 index 0000000000..8fa89d9133 --- /dev/null +++ b/src/Mod/Path/App/Tooltable.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ + +#endif +#include +#include +#include +#include "Tooltable.h" + +using namespace Base; +using namespace Path; + + +// TOOL + + +TYPESYSTEM_SOURCE(Path::Tool , Base::Persistence); + +// Constructors & destructors + +Tool::Tool(const char* name, + ToolType type, + ToolMaterial material, + double diameter, + double lengthoffset, + double flatradius, + double cornerradius, + double cuttingedgeangle, + double cuttingedgeheight) +:Name(name),Type(type),Diameter(diameter),LengthOffset(lengthoffset), +FlatRadius(flatradius),CornerRadius(cornerradius),CuttingEdgeAngle(cuttingedgeangle), +CuttingEdgeHeight(cuttingedgeheight) +{ +} + +Tool::Tool() +{ +} + +Tool::~Tool() +{ +} + +// Reimplemented from base class + +unsigned int Tool::getMemSize (void) const +{ + return 0; +} + +void Tool::Save (Writer &writer) const +{ + writer.Stream() << writer.ind() << " "; + else if(Material == Tool::HIGHSPEEDSTEEL) + writer.Stream() << " mat=\"HighSpeedSteel\" /> "; + else if(Material == Tool::HIGHCARBONTOOLSTEEL) + writer.Stream() << " mat=\"HighCarbonToolSteel\" /> "; + else if(Material == Tool::CASTALLOY) + writer.Stream() << " mat=\"CastAlloy\" /> "; + else if(Material == Tool::CERAMICS) + writer.Stream() << " mat=\"Ceramics\" /> "; + else if(Material == Tool::DIAMOND) + writer.Stream() << " mat=\"Diamond\" /> "; + else if(Material == Tool::SIALON) + writer.Stream() << " mat=\"Sialon\" /> "; + else + writer.Stream() << " mat=\"Undefined\" /> "; + writer.Stream()<< std::endl; +} + +void Tool::Restore(XMLReader &reader) +{ + reader.readElement("Tool"); + Name = reader.getAttribute("name"); + Diameter = (double) reader.getAttributeAsFloat("diameter"); + LengthOffset = (double) reader.getAttributeAsFloat("length"); + FlatRadius = (double) reader.getAttributeAsFloat("flat"); + CornerRadius = (double) reader.getAttributeAsFloat("corner"); + CuttingEdgeAngle = (double) reader.getAttributeAsFloat("angle"); + CuttingEdgeHeight = (double) reader.getAttributeAsFloat("height"); + + std::string type = reader.getAttribute("type"); + if(type=="EndMill") + Type = Tool::ENDMILL; + else if(type=="Drill") + Type = Tool::DRILL; + else if(type=="CenterDrill") + Type = Tool::CENTERDRILL; + else if(type=="CounterSink") + Type = Tool::COUNTERSINK; + else if(type=="CounterBore") + Type = Tool::COUNTERBORE; + else if(type=="Reamer") + Type = Tool::REAMER; + else if(type=="Tap") + Type = Tool::TAP; + else if(type=="SlotCutter") + Type = Tool::SLOTCUTTER; + else if(type=="BallEndMill") + Type = Tool::BALLENDMILL; + else if(type=="ChamferMill") + Type = Tool::CHAMFERMILL; + else if(type=="CornerRound") + Type = Tool::CORNERROUND; + else if(type=="Engraver") + Type = Tool::ENGRAVER; + else + Type = Tool::UNDEFINED; + + std::string mat = reader.getAttribute("mat"); + if(mat=="Carbide") + Material = Tool::CARBIDE; + else if(mat=="HighSpeedSteel") + Material = Tool::HIGHSPEEDSTEEL; + else if(mat=="HighCarbonToolSteel") + Material = Tool::HIGHCARBONTOOLSTEEL; + else if(mat=="CastAlloy") + Material = Tool::CASTALLOY; + else if(mat=="Ceramics") + Material = Tool::CERAMICS; + else if(mat=="Diamond") + Material = Tool::DIAMOND; + else if(mat=="Sialon") + Material = Tool::SIALON; + else + Material = Tool::MATUNDEFINED; +} + + + +// TOOLTABLE + + + +TYPESYSTEM_SOURCE(Path::Tooltable , Base::Persistence); + +Tooltable::Tooltable() +{ +} + +Tooltable::~Tooltable() +{ +} + +void Tooltable::addTool(const Tool &tool) +{ + Tool *tmp = new Tool(tool); + if (!Tools.empty()) { + int max = 0; + for(std::map::const_iterator i = Tools.begin(); i != Tools.end(); ++i) { + int k = i->first; + if (k > max) + max = k; + } + Tools[max+1]= tmp; + } else + Tools[1] = tmp; +} + +void Tooltable::setTool(const Tool &tool, int pos) +{ + if (pos == -1) { + addTool(tool); + } else { + Tool *tmp = new Tool(tool); + Tools[pos] = tmp; + } +} + +void Tooltable::deleteTool(int pos) +{ + if (Tools.find(pos) != Tools.end()) { + Tools.erase(pos); + } else { + throw Base::Exception("Index not found"); + } +} + +unsigned int Tooltable::getMemSize (void) const +{ + return 0; +} + +void Tooltable::Save (Writer &writer) const +{ + writer.Stream() << writer.ind() << "" << std::endl; + writer.incInd(); + for(std::map::const_iterator i = Tools.begin(); i != Tools.end(); ++i) { + int k = i->first; + Tool *v = i->second; + writer.Stream() << writer.ind() << "" << std::endl; + writer.incInd(); + v->Save(writer); + writer.decInd(); + writer.Stream() << writer.ind() << "" << std::endl; + } + writer.decInd(); + writer.Stream() << writer.ind() << "" << std::endl ; + +} + +void Tooltable::Restore (XMLReader &reader) +{ + Tools.clear(); + reader.readElement("Tooltable"); + int count = reader.getAttributeAsInteger("count"); + for (int i = 0; i < count; i++) { + reader.readElement("Toolslot"); + int id = reader.getAttributeAsInteger("number"); + Tool *tmp = new Tool(); + tmp->Restore(reader); + Tools[id] = tmp; + } +} + + + + diff --git a/src/Mod/Path/App/Tooltable.h b/src/Mod/Path/App/Tooltable.h new file mode 100644 index 0000000000..8a35d6a0ac --- /dev/null +++ b/src/Mod/Path/App/Tooltable.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_TOOLTABLE_H +#define PATH_TOOLTABLE_H + +#include +#include +#include +#include + +namespace Path +{ + + /** The representation of a single tool */ + class PathExport Tool : public Base::Persistence + { + TYPESYSTEM_HEADER(); + + public: + enum ToolType { + UNDEFINED, + DRILL, + CENTERDRILL, + COUNTERSINK, + COUNTERBORE, + REAMER, + TAP, + ENDMILL, + SLOTCUTTER, + BALLENDMILL, + CHAMFERMILL, + CORNERROUND, + ENGRAVER }; + + enum ToolMaterial { + MATUNDEFINED, + HIGHSPEEDSTEEL, + HIGHCARBONTOOLSTEEL, + CASTALLOY, + CARBIDE, + CERAMICS, + DIAMOND, + SIALON }; + + //constructors + Tool(); + Tool(const char* name, + ToolType type=Tool::UNDEFINED, + ToolMaterial material=Tool::MATUNDEFINED, + double diameter=10.0, + double lengthoffset=100, + double flatradius=0, + double cornerradius=0, + double cuttingedgeangle=0, + double cuttingedgeheight=0); + ~Tool(); + + // from base class + virtual unsigned int getMemSize (void) const; + virtual void Save (Base::Writer &/*writer*/) const; + virtual void Restore(Base::XMLReader &/*reader*/); + + // attributes + std::string Name; + ToolType Type; + ToolMaterial Material; + double Diameter; + double LengthOffset; + double FlatRadius; + double CornerRadius; + double CuttingEdgeAngle; + double CuttingEdgeHeight; + + }; + + /** The representation of a table of tools */ + class PathExport Tooltable : public Base::Persistence + { + TYPESYSTEM_HEADER(); + + public: + //constructors + Tooltable(); + ~Tooltable(); + + // from base class + virtual unsigned int getMemSize (void) const; + virtual void Save (Base::Writer &/*writer*/) const; + virtual void Restore(Base::XMLReader &/*reader*/); + + // new functions + void addTool(const Tool &tool); // adds a tool at the end + void setTool(const Tool &tool, int); // inserts a tool + void deleteTool(int); // deletes a tool + + // auto + unsigned int getSize(void) const {return Tools.size();} + const Tool &getTool(int pos) {return *Tools[pos];} + const std::map &getTools(void) const {return Tools;} + bool hasTool(int pos) const {return (Tools.count(pos) != 0);} + + // attributes + std::map Tools; + }; + +} //namespace Path + +#endif // PATH_TOOLTABLE_H diff --git a/src/Mod/Path/App/TooltablePy.xml b/src/Mod/Path/App/TooltablePy.xml new file mode 100644 index 0000000000..c270c36e2a --- /dev/null +++ b/src/Mod/Path/App/TooltablePy.xml @@ -0,0 +1,56 @@ + + + + + + The Tooltable object holds a table of CNC tools + + + + the dictionary of tools of this table + + + + + + returns a copy of this tooltable + + + + + adds a tool or a list of tools at the end of the table + + + + + getTool(int): +returns the tool found at the given position, or None + + + + + setTool(int,tool): +adds a tool at the given position + + + + + deleteTool(int): +deletes the tool found at the given position + + + + bool touched; + + + diff --git a/src/Mod/Path/App/TooltablePyImp.cpp b/src/Mod/Path/App/TooltablePyImp.cpp new file mode 100644 index 0000000000..05cfc010f7 --- /dev/null +++ b/src/Mod/Path/App/TooltablePyImp.cpp @@ -0,0 +1,502 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#include "Mod/Path/App/Tooltable.h" + +// inclusion of the generated files (generated out of ToolPy.xml and TooltablePy.xml) +#include "ToolPy.h" +#include "ToolPy.cpp" +#include "TooltablePy.h" +#include "TooltablePy.cpp" + +using namespace Path; + + + +// ToolPy + + + +// returns a string which represents the object e.g. when printed in python +std::string ToolPy::representation(void) const +{ + std::stringstream str; + str.precision(5); + str << "Tool "; + str << getToolPtr()->Name; + return str.str(); +} + +PyObject *ToolPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of ToolPy and the Twin object + return new ToolPy(new Tool); +} + +// constructor method +int ToolPy::PyInit(PyObject* args, PyObject* kwd) +{ + PyObject *pos; + char *name="Default tool"; + char *type = "Undefined"; + char *mat = "Undefined"; + PyObject *dia = 0; + PyObject *len = 0; + PyObject *fla = 0; + PyObject *cor = 0; + PyObject *ang = 0; + PyObject *hei = 0; + + static char *kwlist[] = {"name", "tooltype", "material", "diameter", "lengthOffset", "flatRadius", "cornerRadius", "cuttingEdgeAngle", "cuttingEdgeHeight" ,NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwd, "|sssOOOOOO", kwlist, + &name, &type, &mat, &dia, &len, &fla, &cor, &ang, &hei )) + return -1; + + getToolPtr()->Name = name; + std::string typeStr(type); + if(typeStr=="Drill") + getToolPtr()->Type = Tool::DRILL; + else if(typeStr=="CenterDrill") + getToolPtr()->Type = Tool::CENTERDRILL; + if(typeStr=="CounterSink") + getToolPtr()->Type = Tool::COUNTERSINK; + if(typeStr=="CounterBore") + getToolPtr()->Type = Tool::COUNTERBORE; + if(typeStr=="Reamer") + getToolPtr()->Type = Tool::REAMER; + if(typeStr=="Tap") + getToolPtr()->Type = Tool::TAP; + else if(typeStr=="EndMill") + getToolPtr()->Type = Tool::ENDMILL; + else if(typeStr=="SlotCutter") + getToolPtr()->Type = Tool::SLOTCUTTER; + else if(typeStr=="BallEndMill") + getToolPtr()->Type = Tool::BALLENDMILL; + else if(typeStr=="ChamferMill") + getToolPtr()->Type = Tool::CHAMFERMILL; + else if(typeStr=="CornerRound") + getToolPtr()->Type = Tool::CORNERROUND; + else if(typeStr=="Engraver") + getToolPtr()->Type = Tool::ENGRAVER; + else + getToolPtr()->Type = Tool::UNDEFINED; + + std::string matStr(mat); + if(matStr=="HighSpeedSteel") + getToolPtr()->Material = Tool::HIGHSPEEDSTEEL; + else if(matStr=="Carbide") + getToolPtr()->Material = Tool::CARBIDE; + else if(matStr=="HighCarbonToolSteel") + getToolPtr()->Material = Tool::HIGHCARBONTOOLSTEEL; + else if(matStr=="CastAlloy") + getToolPtr()->Material = Tool::CASTALLOY; + else if(matStr=="Ceramics") + getToolPtr()->Material = Tool::CERAMICS; + else if(matStr=="Diamond") + getToolPtr()->Material = Tool::DIAMOND; + else if(matStr=="Sialon") + getToolPtr()->Material = Tool::SIALON; + else + getToolPtr()->Material = Tool::MATUNDEFINED; + + getToolPtr()->Diameter = PyFloat_AsDouble(dia); + getToolPtr()->LengthOffset = PyFloat_AsDouble(len); + getToolPtr()->FlatRadius = PyFloat_AsDouble(fla); + getToolPtr()->CornerRadius = PyFloat_AsDouble(cor); + getToolPtr()->CuttingEdgeAngle = PyFloat_AsDouble(ang); + getToolPtr()->CuttingEdgeHeight = PyFloat_AsDouble(hei); + + return 0; +} + +// attributes get/setters + +Py::String ToolPy::getName(void) const +{ + return Py::String(getToolPtr()->Name.c_str()); +} + +void ToolPy::setName(Py::String arg) +{ + std::string name = arg.as_std_string(); + getToolPtr()->Name = name; +} + +Py::String ToolPy::getToolType(void) const +{ + if(getToolPtr()->Type == Tool::DRILL) + return Py::String("Drill"); + else if(getToolPtr()->Type == Tool::CENTERDRILL) + return Py::String("CenterDrill"); + else if(getToolPtr()->Type == Tool::COUNTERSINK) + return Py::String("CounterSink"); + else if(getToolPtr()->Type == Tool::COUNTERBORE) + return Py::String("CounterBore"); + else if(getToolPtr()->Type == Tool::REAMER) + return Py::String("Reamer"); + else if(getToolPtr()->Type == Tool::TAP) + return Py::String("Tap"); + else if(getToolPtr()->Type == Tool::ENDMILL) + return Py::String("EndMill"); + else if(getToolPtr()->Type == Tool::SLOTCUTTER) + return Py::String("SlotCutter"); + else if(getToolPtr()->Type == Tool::BALLENDMILL) + return Py::String("BallEndMill"); + else if(getToolPtr()->Type == Tool::CHAMFERMILL) + return Py::String("ChamferMill"); + else if(getToolPtr()->Type == Tool::CORNERROUND) + return Py::String("CornerRound"); + else if(getToolPtr()->Type == Tool::ENGRAVER) + return Py::String("Engraver"); + else + return Py::String("Undefined"); +} + +void ToolPy::setToolType(Py::String arg) +{ + std::string typeStr(arg.as_std_string()); + if(typeStr=="Drill") + getToolPtr()->Type = Tool::DRILL; + else if(typeStr=="CenterDrill") + getToolPtr()->Type = Tool::CENTERDRILL; + else if(typeStr=="CounterSink") + getToolPtr()->Type = Tool::COUNTERSINK; + else if(typeStr=="CounterBore") + getToolPtr()->Type = Tool::COUNTERBORE; + else if(typeStr=="Reamer") + getToolPtr()->Type = Tool::REAMER; + else if(typeStr=="Tap") + getToolPtr()->Type = Tool::TAP; + else if(typeStr=="EndMill") + getToolPtr()->Type = Tool::ENDMILL; + else if(typeStr=="SlotCutter") + getToolPtr()->Type = Tool::SLOTCUTTER; + else if(typeStr=="BallEndMill") + getToolPtr()->Type = Tool::BALLENDMILL; + else if(typeStr=="ChamferMill") + getToolPtr()->Type = Tool::CHAMFERMILL; + else if(typeStr=="CornerRound") + getToolPtr()->Type = Tool::CORNERROUND; + + else if(typeStr=="Engraver") + getToolPtr()->Type = Tool::ENGRAVER; + else + getToolPtr()->Type = Tool::UNDEFINED; +} + +Py::String ToolPy::getMaterial(void) const +{ + if(getToolPtr()->Material == Tool::HIGHSPEEDSTEEL) + return Py::String("HighSpeedSteel"); + else if(getToolPtr()->Material == Tool::CARBIDE) + return Py::String("Carbide"); + else if(getToolPtr()->Material == Tool::HIGHCARBONTOOLSTEEL) + return Py::String("HighCarbonToolSteel"); + else if(getToolPtr()->Material == Tool::CASTALLOY) + return Py::String("CastAlloy"); + else if(getToolPtr()->Material == Tool::CERAMICS) + return Py::String("Ceramics"); + else if(getToolPtr()->Material == Tool::DIAMOND) + return Py::String("Diamond"); + else if(getToolPtr()->Material == Tool::SIALON) + return Py::String("Sialon"); + else + return Py::String("Undefined"); +} + +void ToolPy::setMaterial(Py::String arg) +{ + std::string matStr(arg.as_std_string()); + if(matStr=="HighSpeedSteel") + getToolPtr()->Material = Tool::HIGHSPEEDSTEEL; + else if(matStr=="Carbide") + getToolPtr()->Material = Tool::CARBIDE; + else if(matStr=="HighCarbonToolSteel") + getToolPtr()->Material = Tool::HIGHCARBONTOOLSTEEL; + else if(matStr=="CastAlloy") + getToolPtr()->Material = Tool::CASTALLOY; + else if(matStr=="Ceramics") + getToolPtr()->Material = Tool::CERAMICS; + else if(matStr=="Diamond") + getToolPtr()->Material = Tool::DIAMOND; + else if(matStr=="Sialon") + getToolPtr()->Material = Tool::SIALON; + else + getToolPtr()->Material = Tool::MATUNDEFINED; +} + +Py::Float ToolPy::getDiameter(void) const +{ + return Py::Float(getToolPtr()->Diameter); +} + +void ToolPy::setDiameter(Py::Float arg) +{ + getToolPtr()->Diameter = arg.operator double(); +} + +Py::Float ToolPy::getLengthOffset(void) const +{ + return Py::Float(getToolPtr()->LengthOffset); +} + +void ToolPy::setLengthOffset(Py::Float arg) +{ + getToolPtr()->LengthOffset = arg.operator double(); +} + +Py::Float ToolPy::getFlatRadius(void) const +{ + return Py::Float(getToolPtr()->FlatRadius); +} + +void ToolPy::setFlatRadius(Py::Float arg) +{ + getToolPtr()->FlatRadius = arg.operator double(); +} + +Py::Float ToolPy::getCornerRadius(void) const +{ + return Py::Float(getToolPtr()->CornerRadius); +} + +void ToolPy::setCornerRadius(Py::Float arg) +{ + getToolPtr()->CornerRadius = arg.operator double(); +} + +Py::Float ToolPy::getCuttingEdgeAngle(void) const +{ + return Py::Float(getToolPtr()->CuttingEdgeAngle); +} + +void ToolPy::setCuttingEdgeAngle(Py::Float arg) +{ + getToolPtr()->CuttingEdgeAngle = arg.operator double(); +} + +Py::Float ToolPy::getCuttingEdgeHeight(void) const +{ + return Py::Float(getToolPtr()->CuttingEdgeHeight); +} + +void ToolPy::setCuttingEdgeHeight(Py::Float arg) +{ + getToolPtr()->CuttingEdgeHeight = arg.operator double(); +} + +// custom attributes get/set + +PyObject *ToolPy::getCustomAttributes(const char* /*attr*/) const +{ + return 0; +} + +int ToolPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} + +PyObject* ToolPy::copy(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + return new ToolPy(new Path::Tool(*getToolPtr())); + } + throw Py::Exception("This method accepts no argument"); +} + + + + +// TooltablePy + + + + +// returns a string which represents the object e.g. when printed in python +std::string TooltablePy::representation(void) const +{ + std::stringstream str; + str.precision(5); + str << "Tooltable containing "; + str << getTooltablePtr()->getSize() << " tools"; + return str.str(); +} + +PyObject *TooltablePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + return new TooltablePy(new Tooltable); +} + +// constructor method +int TooltablePy::PyInit(PyObject* args, PyObject* /*kwd*/) +{ + PyObject *pcObj; + if (PyArg_ParseTuple(args, "|O!", &(PyDict_Type), &pcObj)) { + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(pcObj, &pos, &key, &value)) { + if ( !PyObject_TypeCheck(key,&(PyInt_Type)) || !PyObject_TypeCheck(value,&(Path::ToolPy::Type)) ) { + PyErr_SetString(PyExc_TypeError, "The dictionary can only contain int:tool pairs"); + return -1; + } + int ckey = (int)PyInt_AsLong(key); + Path::Tool &tool = *static_cast(value)->getToolPtr(); + getTooltablePtr()->setTool(tool,ckey); + } + return 0; + } + PyErr_Clear(); // set by PyArg_ParseTuple() + + if (PyArg_ParseTuple(args, "|O!", &(PyList_Type), &pcObj)) { + Py::List list(pcObj); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Path::ToolPy::Type))) { + Path::Tool &tool = *static_cast((*it).ptr())->getToolPtr(); + getTooltablePtr()->addTool(tool); + } + } + return 0; + } + + PyErr_SetString(PyExc_TypeError, "Argument must be a list or a dictionary"); + return -1; +} + +// Commands get/set + +Py::Dict TooltablePy::getTools(void) const +{ + PyObject *dict = PyDict_New(); + for(std::map::iterator i = getTooltablePtr()->Tools.begin(); i != getTooltablePtr()->Tools.end(); ++i) { + PyObject *tool = new Path::ToolPy(i->second); + PyDict_SetItem(dict,PyInt_FromLong(i->first),tool); + } + return Py::Dict(dict); +} + +void TooltablePy::setTools(Py::Dict arg) +{ + getTooltablePtr()->Tools.clear(); + PyObject* dict_copy = PyDict_Copy(arg.ptr()); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict_copy, &pos, &key, &value)) { + if ( PyObject_TypeCheck(key,&(PyInt_Type)) && (PyObject_TypeCheck(value,&(Path::ToolPy::Type))) ) { + int ckey = (int)PyInt_AsLong(key); + Path::Tool &tool = *static_cast(value)->getToolPtr(); + getTooltablePtr()->setTool(tool,ckey); + } else { + throw Py::Exception("The dictionary can only contain int:tool pairs"); + } + } +} + +// specific methods + +PyObject* TooltablePy::copy(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + return new TooltablePy(new Path::Tooltable(*getTooltablePtr())); + } + throw Py::Exception("This method accepts no argument"); +} + +PyObject* TooltablePy::addTools(PyObject * args) +{ + PyObject* o; + if (PyArg_ParseTuple(args, "O!", &(Path::ToolPy::Type), &o)) { + Path::Tool &tool = *static_cast(o)->getToolPtr(); + getTooltablePtr()->addTool(tool); + //return new TooltablePy(new Path::Tooltable(*getTooltablePtr())); + return Py_None; + } + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O!", &(PyList_Type), &o)) { + Py::List list(o); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Path::ToolPy::Type))) { + Path::Tool &tool = *static_cast((*it).ptr())->getToolPtr(); + getTooltablePtr()->addTool(tool); + } + } + //return new TooltablePy(new Path::Tooltable(*getTooltablePtr())); + return Py_None; + } + Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters - tool or list of tools expected"); +} + +PyObject* TooltablePy::setTool(PyObject * args) +{ + PyObject* o; + int pos = -1; + if (PyArg_ParseTuple(args, "iO!", &pos, &(Path::ToolPy::Type), &o)) { + Path::Tool &tool = *static_cast(o)->getToolPtr(); + getTooltablePtr()->setTool(tool,pos); + //return new TooltablePy(new Path::Tooltable(*getTooltablePtr())); + return Py_None; + } + Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters - expected tool and optional integer"); +} + +PyObject* TooltablePy::getTool(PyObject * args) +{ + int pos = -1; + if (PyArg_ParseTuple(args, "i", &pos)) { + if (getTooltablePtr()->hasTool(pos)) + { + Path::Tool tool = getTooltablePtr()->getTool(pos); + return new ToolPy(new Path::Tool(tool)); + } else + return Py_None; + } + Py_Error(Base::BaseExceptionFreeCADError, "Argument must be integer"); +} + +PyObject* TooltablePy::deleteTool(PyObject * args) +{ + int pos = -1; + if (PyArg_ParseTuple(args, "|i", &pos)) { + getTooltablePtr()->deleteTool(pos); + //return new TooltablePy(new Path::Tooltable(*getTooltablePtr())); + return Py_None; + } + Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters - expected an integer (optional)"); +} + +// custom attributes get/set + +PyObject *TooltablePy::getCustomAttributes(const char* /*attr*/) const +{ + return 0; +} + +int TooltablePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} + + diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt new file mode 100644 index 0000000000..b06b5c077d --- /dev/null +++ b/src/Mod/Path/CMakeLists.txt @@ -0,0 +1,63 @@ +add_subdirectory(App) + +if(BUILD_GUI) + add_subdirectory(Gui) +endif(BUILD_GUI) + +INSTALL( + FILES + Init.py + InitGui.py + DESTINATION + Mod/Path +) + +SET(PathScripts_SRCS + PathScripts/__init__.py + PathScripts/PostUtils.py + PathScripts/example_pre.py + PathScripts/opensbp_pre.py + PathScripts/example_post.py + PathScripts/linuxcnc_post.py + PathScripts/centroid_post.py + PathScripts/comparams_post.py + PathScripts/dumper_post.py + PathScripts/TooltableEditor.py + PathScripts/PathProfile.py + PathScripts/PathPocket.py + PathScripts/PathDrilling.py + PathScripts/PathDressup.py + PathScripts/PathHop.py + PathScripts/PathUtils.py + PathScripts/PathSelection.py + PathScripts/PathFixture.py + PathScripts/PathCopy.py + PathScripts/PathCompoundExtended.py + PathScripts/PathProject.py + PathScripts/PathToolTableEdit.py + PathScripts/PathStock.py + PathScripts/PathPlane.py + PathScripts/PathPost.py + PathScripts/PathLoadTool.py + PathScripts/PathToolLenOffset.py + PathScripts/PathComment.py + PathScripts/PathStop.py + PathScripts/PathMachine.py + PathScripts/PathFromShape.py + PathScripts/DlgSettingsPath.ui + PathScripts/PathKurveUtils.py + PathScripts/PathKurve.py +) + +ADD_CUSTOM_TARGET(PathScripts ALL + SOURCES ${PathScripts_SRCS} +) + +fc_copy_sources(PathScripts "${CMAKE_BINARY_DIR}/Mod/Path" ${PathScripts_SRCS}) + +INSTALL( + FILES + ${PathScripts_SRCS} + DESTINATION + Mod/Path/PathScripts + ) diff --git a/src/Mod/Path/DemoParts/hole_puzzle.fcstd b/src/Mod/Path/DemoParts/hole_puzzle.fcstd new file mode 100644 index 0000000000..d6fdf74c45 Binary files /dev/null and b/src/Mod/Path/DemoParts/hole_puzzle.fcstd differ diff --git a/src/Mod/Path/DemoParts/motor_mount_inch.fcstd b/src/Mod/Path/DemoParts/motor_mount_inch.fcstd new file mode 100644 index 0000000000..de1d668249 Binary files /dev/null and b/src/Mod/Path/DemoParts/motor_mount_inch.fcstd differ diff --git a/src/Mod/Path/DemoParts/strange_part_with_holes.fcstd b/src/Mod/Path/DemoParts/strange_part_with_holes.fcstd new file mode 100644 index 0000000000..5345505ebd Binary files /dev/null and b/src/Mod/Path/DemoParts/strange_part_with_holes.fcstd differ diff --git a/src/Mod/Path/Gui/AppPathGui.cpp b/src/Mod/Path/Gui/AppPathGui.cpp new file mode 100644 index 0000000000..3d10c27f16 --- /dev/null +++ b/src/Mod/Path/Gui/AppPathGui.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include +#include +#include +#include +#include +#include "ViewProviderPath.h" +#include "DlgSettingsPathColor.h" +#include "ViewProviderPathCompound.h" +#include "ViewProviderPathShape.h" + +// use a different name to CreateCommand() +void CreatePathCommands(void); + +void loadPathResource() +{ + // add resources and reloads the translators + Q_INIT_RESOURCE(Path); + Gui::Translator::instance()->refresh(); +} + +/* registration table */ +extern struct PyMethodDef PathGui_methods[]; + + +/* Python entry */ +extern "C" { +void PathGuiExport initPathGui() +{ + if (!Gui::Application::Instance) { + PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application."); + return; + } + try { + Base::Interpreter().runString("import Path"); + } + catch(const Base::Exception& e) { + PyErr_SetString(PyExc_ImportError, e.what()); + return; + } + (void) Py_InitModule("PathGui", PathGui_methods); /* mod name, table ptr */ + Base::Console().Log("Loading GUI of Path module... done\n"); + + // instantiating the commands + CreatePathCommands(); + + // addition objects + PathGui::ViewProviderPath ::init(); + PathGui::ViewProviderPathCompound ::init(); + PathGui::ViewProviderPathCompoundPython ::init(); + PathGui::ViewProviderPathShape ::init(); + PathGui::ViewProviderPathPython ::init(); + + // add resources and reloads the translators + loadPathResource(); + + // register preferences pages + new Gui::PrefPageProducer ("Path"); +} + +} // extern "C" { diff --git a/src/Mod/Path/Gui/AppPathGuiPy.cpp b/src/Mod/Path/Gui/AppPathGuiPy.cpp new file mode 100644 index 0000000000..6205525115 --- /dev/null +++ b/src/Mod/Path/Gui/AppPathGuiPy.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "ViewProviderPath.h" +#include "DlgProcessorChooser.h" +#include "ui_DlgProcessorChooser.h" + +using namespace PathGui; + +static PyObject * open(PyObject *self, PyObject *args) +{ + char* Name; + if (!PyArg_ParseTuple(args, "et","utf-8",&Name)) + return NULL; + std::string EncodedName = std::string(Name); + PyMem_Free(Name); + Base::FileInfo fi(EncodedName); + if (!fi.exists()) + Py_Error(Base::BaseExceptionFreeCADError, "File not found"); + Gui::WaitCursor wc; + wc.restoreCursor(); + + PY_TRY { + std::string path = App::GetApplication().getHomePath(); + path += "Mod/Path/PathScripts/"; + QDir dir1(QString::fromUtf8(path.c_str()), QString::fromAscii("*_pre.py")); + std::string cMacroPath = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro") + ->GetASCII("MacroPath",App::Application::getUserAppDataDir().c_str()); + QDir dir2(QString::fromUtf8(cMacroPath.c_str()), QString::fromAscii("*_pre.py")); + QFileInfoList list = dir1.entryInfoList(); + list << dir2.entryInfoList(); + std::vector scripts; + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + scripts.push_back(fileInfo.baseName().toStdString()); + } + std::string selected; + PathGui::DlgProcessorChooser Dlg(scripts); + if (Dlg.exec() != QDialog::Accepted) { + Py_Return; + } + selected = Dlg.getSelected(); + + std::ostringstream pre; + std::ostringstream cmd; + if (selected.empty()) { + App::Document *pcDoc = App::GetApplication().newDocument("Unnamed"); + Gui::Command::runCommand(Gui::Command::Gui,"import Path"); + cmd << "Path.read(\"" << EncodedName << "\",\"" << pcDoc->getName() << "\")"; + Gui::Command::runCommand(Gui::Command::Gui,cmd.str().c_str()); + } else { + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + if (fileInfo.baseName().toStdString() == selected) { + if (fileInfo.absoluteFilePath().contains(QString::fromAscii("PathScripts"))) { + pre << "from PathScripts import " << selected; + } else { + pre << "import " << selected; + } + Gui::Command::runCommand(Gui::Command::Gui,pre.str().c_str()); + cmd << selected << ".open(\"" << EncodedName << "\")"; + Gui::Command::runCommand(Gui::Command::Gui,cmd.str().c_str()); + } + } + } + } PY_CATCH; + Py_Return; +} + +static PyObject * importer(PyObject *self, PyObject *args) +{ + char* Name; + char* DocName=0; + if (!PyArg_ParseTuple(args, "et|s","utf-8",&Name,&DocName)) + return NULL; + std::string EncodedName = std::string(Name); + PyMem_Free(Name); + Base::FileInfo fi(EncodedName); + if (!fi.exists()) + Py_Error(Base::BaseExceptionFreeCADError, "File not found"); + Gui::WaitCursor wc; + wc.restoreCursor(); + + PY_TRY { + std::string path = App::GetApplication().getHomePath(); + path += "Mod/Path/PathScripts/"; + QDir dir1(QString::fromUtf8(path.c_str()), QString::fromAscii("*_pre.py")); + std::string cMacroPath = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro") + ->GetASCII("MacroPath",App::Application::getUserAppDataDir().c_str()); + QDir dir2(QString::fromUtf8(cMacroPath.c_str()), QString::fromAscii("*_pre.py")); + QFileInfoList list = dir1.entryInfoList(); + list << dir2.entryInfoList(); + std::vector scripts; + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + scripts.push_back(fileInfo.baseName().toStdString()); + } + std::string selected; + PathGui::DlgProcessorChooser Dlg(scripts); + if (Dlg.exec() != QDialog::Accepted) { + Py_Return; + } + selected = Dlg.getSelected(); + + App::Document *pcDoc = 0; + if (DocName) + pcDoc = App::GetApplication().getDocument(DocName); + else + pcDoc = App::GetApplication().getActiveDocument(); + + if (!pcDoc) { + pcDoc = App::GetApplication().newDocument(DocName); + } + + std::ostringstream pre; + std::ostringstream cmd; + if (selected.empty()) { + Gui::Command::runCommand(Gui::Command::Gui,"import Path"); + cmd << "Path.read(\"" << EncodedName << "\",\"" << pcDoc->getName() << "\")"; + Gui::Command::runCommand(Gui::Command::Gui,cmd.str().c_str()); + } else { + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + if (fileInfo.baseName().toStdString() == selected) { + if (fileInfo.absoluteFilePath().contains(QString::fromAscii("PathScripts"))) { + pre << "from PathScripts import " << selected; + } else { + pre << "import " << selected; + } + Gui::Command::runCommand(Gui::Command::Gui,pre.str().c_str()); + cmd << selected << ".insert(\"" << EncodedName << "\",\"" << pcDoc->getName() << "\")"; + Gui::Command::runCommand(Gui::Command::Gui,cmd.str().c_str()); + } + } + } + } PY_CATCH; + Py_Return; +} + +static PyObject * exporter(PyObject *self, PyObject *args) +{ + PyObject* object; + char* Name; + if (!PyArg_ParseTuple(args, "Oet",&object,"utf-8",&Name)) + return NULL; + std::string EncodedName = std::string(Name); + PyMem_Free(Name); + Gui::WaitCursor wc; + wc.restoreCursor(); + + PY_TRY { + Py::Sequence objlist(object); + if (objlist.size() == 0) + Py_Error(Base::BaseExceptionFreeCADError, "No object to export"); + std::string path = App::GetApplication().getHomePath(); + path += "Mod/Path/PathScripts/"; + QDir dir1(QString::fromUtf8(path.c_str()), QString::fromAscii("*_post.py")); + std::string cMacroPath = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro") + ->GetASCII("MacroPath",App::Application::getUserAppDataDir().c_str()); + QDir dir2(QString::fromUtf8(cMacroPath.c_str()), QString::fromAscii("*_post.py")); + QFileInfoList list = dir1.entryInfoList(); + list << dir2.entryInfoList(); + std::vector scripts; + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + scripts.push_back(fileInfo.baseName().toStdString()); + } + std::string selected; + PathGui::DlgProcessorChooser Dlg(scripts); + if (Dlg.exec() != QDialog::Accepted) { + Py_Return; + } + selected = Dlg.getSelected(); + + std::ostringstream pre; + std::ostringstream cmd; + if (selected.empty()) { + if (objlist.size() > 1) { + Py_Error(Base::BaseExceptionFreeCADError, "Cannot export more than one object without using a post script"); + } + PyObject* item = objlist[0].ptr(); + if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) { + App::DocumentObject* obj = static_cast(item)->getDocumentObjectPtr(); + App::Document* doc = obj->getDocument(); + Gui::Command::runCommand(Gui::Command::Gui,"import Path"); + cmd << "Path.write(FreeCAD.getDocument(\"" << doc->getName() << "\").getObject(\"" << obj->getNameInDocument() << "\"),\"" << EncodedName << "\")"; + Gui::Command::runCommand(Gui::Command::Gui,cmd.str().c_str()); + } else { + Py_Return; + } + } else { + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + if (fileInfo.baseName().toStdString() == selected) { + if (fileInfo.absoluteFilePath().contains(QString::fromAscii("PathScripts"))) { + pre << "from PathScripts import " << selected; + } else { + pre << "import " << selected; + } + Gui::Command::runCommand(Gui::Command::Gui,pre.str().c_str()); + cmd << selected << ".export(__objs__,\"" << EncodedName << "\")"; + Gui::Command::runCommand(Gui::Command::Gui,cmd.str().c_str()); + } + } + } + } PY_CATCH; + Py_Return; +} + +/* registration table */ +struct PyMethodDef PathGui_methods[] = { + {"open" ,open ,METH_VARARGS, + "open(filename): Opens a GCode file as a new document"}, + {"insert" ,importer ,METH_VARARGS, + "insert(filename,docname): Imports a given GCode file into the given document"}, + {"export" ,exporter ,METH_VARARGS, + "export(objectslist,filename): Exports a given list of Path objects to a GCode file"}, + {NULL, NULL} /* end of table marker */ +}; diff --git a/src/Mod/Path/Gui/CMakeLists.txt b/src/Mod/Path/Gui/CMakeLists.txt new file mode 100644 index 0000000000..f6557f62c0 --- /dev/null +++ b/src/Mod/Path/Gui/CMakeLists.txt @@ -0,0 +1,98 @@ +if(MSVC) + add_definitions(-DFCGuiPath -DHAVE_ACOSH -DHAVE_ASINH -DHAVE_ATANH) +else(MSVC) + add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H) +endif(MSVC) + +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + ${Boost_INCLUDE_DIRS} + ${COIN3D_INCLUDE_DIR} + ${ZLIB_INCLUDE_DIR} + ${OCC_INCLUDE_DIR} + ${QT_INCLUDE_DIR} + ${EIGEN3_INCLUDE_DIR} + ${PYTHON_INCLUDE_PATH} + ${XERCESC_INCLUDE_DIR} +) + +link_directories(${OCC_LIBRARY_DIR}) + +set(PathGui_LIBS + Path + FreeCADGui +) + +qt4_add_resources(PathResource_SRCS Resources/Path.qrc) + +SOURCE_GROUP("Resources" FILES ${PathResource_SRCS}) + +set(PathGui_MOC_HDRS + DlgSettingsPathColor.h + TaskDlgPathCompound.h + DlgProcessorChooser.h +) +fc_wrap_cpp(PathGui_MOC_SRCS ${PathGui_MOC_HDRS}) + +SOURCE_GROUP("Moc" FILES ${PathGui_MOC_SRCS}) + +set(PathGui_UIC_SRCS + DlgSettingsPathColor.ui + TaskDlgPathCompound.ui + DlgProcessorChooser.ui +) +qt4_wrap_ui(PathGui_UIC_HDRS ${PathGui_UIC_SRCS}) + +SET(PathGui_SRCS_Module + Command.cpp + AppPathGui.cpp + AppPathGuiPy.cpp + Resources/Path.qrc + PreCompiled.cpp + PreCompiled.h + DlgSettingsPathColor.ui + DlgSettingsPathColor.cpp + DlgSettingsPathColor.h + TaskDlgPathCompound.ui + TaskDlgPathCompound.cpp + TaskDlgPathCompound.h + DlgProcessorChooser.ui + DlgProcessorChooser.cpp + DlgProcessorChooser.h +) + +SOURCE_GROUP("Module" FILES ${PathGui_SRCS_Module}) + +SET(PathGui_SRCS_ViewProvider + ViewProviderPath.cpp + ViewProviderPath.h + ViewProviderPathCompound.cpp + ViewProviderPathCompound.h + ViewProviderPathShape.cpp + ViewProviderPathShape.h +) + +SOURCE_GROUP("ViewProvider" FILES ${PathGui_SRCS_ViewProvider}) + +SET(PathGui_SRCS + ${PathResource_SRCS} + ${PathGui_UIC_HDRS} + ${PathGui_SRCS_Module} + ${PathGui_SRCS_ViewProvider} +) + +add_library(PathGui SHARED ${PathGui_SRCS}) +target_link_libraries(PathGui ${PathGui_LIBS}) + + +fc_target_copy_resource(PathGui + ${CMAKE_SOURCE_DIR}/src/Mod/Path + ${CMAKE_BINARY_DIR}/Mod/Path + InitGui.py) + +SET_BIN_DIR(PathGui PathGui /Mod/Path) +SET_PYTHON_PREFIX_SUFFIX(PathGui) + +INSTALL(TARGETS PathGui DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/Mod/Path/Gui/Command.cpp b/src/Mod/Path/Gui/Command.cpp new file mode 100644 index 0000000000..5951d056fa --- /dev/null +++ b/src/Mod/Path/Gui/Command.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +// Path compound ##################################################################################################### + + +DEF_STD_CMD_A(CmdPathCompound); + +CmdPathCompound::CmdPathCompound() + :Command("Path_Compound") +{ + sAppModule = "Path"; + sGroup = QT_TR_NOOP("Path"); + sMenuText = QT_TR_NOOP("Compound"); + sToolTipText = QT_TR_NOOP("Creates a compound from selected paths"); + sWhatsThis = "Path_Compound"; + sStatusTip = sToolTipText; + sPixmap = "Path-Compound"; + sAccel = "P,C"; + +} + +void CmdPathCompound::activated(int iMsg) +{ + std::vector Sel = getSelection().getSelection(); + if (Sel.size() > 0) { + std::ostringstream cmd; + cmd << "["; + Path::Feature *pcPathObject; + for (std::vector::const_iterator it=Sel.begin();it!=Sel.end();it++) { + if ((*it).pObject->getTypeId().isDerivedFrom(Path::Feature::getClassTypeId())) { + pcPathObject = dynamic_cast((*it).pObject); + cmd << "FreeCAD.activeDocument()." << pcPathObject->getNameInDocument() << ","; + } else { + Base::Console().Error("Only Path objects must be selected before running this command\n"); + return; + } + } + cmd << "]"; + std::string FeatName = getUniqueObjectName("PathCompound"); + openCommand("Create Path Compound"); + doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureCompound','%s')",FeatName.c_str()); + doCommand(Doc,"FreeCAD.activeDocument().%s.Group = %s",FeatName.c_str(),cmd.str().c_str()); + commitCommand(); + updateActive(); + } else { + Base::Console().Error("At least one Path object must be selected\n"); + return; + } +} + +bool CmdPathCompound::isActive(void) +{ + return hasActiveDocument(); +} + + + // Path Shape ##################################################################################################### + + +DEF_STD_CMD_A(CmdPathShape); + +CmdPathShape::CmdPathShape() + :Command("Path_Shape") +{ + sAppModule = "Path"; + sGroup = QT_TR_NOOP("Path"); + sMenuText = QT_TR_NOOP("From Shape"); + sToolTipText = QT_TR_NOOP("Creates a path from a selected shape"); + sWhatsThis = "Path_Shape"; + sStatusTip = sToolTipText; + sPixmap = "Path-Shape"; + sAccel = "P,S"; +} + +void CmdPathShape::activated(int iMsg) +{ + std::vector Sel = getSelection().getSelection(); + if (Sel.size() == 1) { + if (Sel[0].pObject->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + Part::Feature *pcPartObject = dynamic_cast(Sel[0].pObject); + std::string FeatName = getUniqueObjectName("PathShape"); + openCommand("Create Path Compound"); + doCommand(Doc,"FreeCAD.activeDocument().addObject('Path::FeatureShape','%s')",FeatName.c_str()); + doCommand(Doc,"FreeCAD.activeDocument().%s.Shape = FreeCAD.activeDocument().%s.Shape.copy()",FeatName.c_str(),pcPartObject->getNameInDocument()); + commitCommand(); + updateActive(); + } else { + Base::Console().Error("Exactly one shape object must be selected\n"); + return; + } + } else { + Base::Console().Error("Exactly one shape object must be selected\n"); + return; + } +} + +bool CmdPathShape::isActive(void) +{ + return hasActiveDocument(); +} + + + +void CreatePathCommands(void) +{ + Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager(); + rcCmdMgr.addCommand(new CmdPathCompound()); + rcCmdMgr.addCommand(new CmdPathShape()); +} diff --git a/src/Mod/Path/Gui/DlgProcessorChooser.cpp b/src/Mod/Path/Gui/DlgProcessorChooser.cpp new file mode 100644 index 0000000000..34f04c0814 --- /dev/null +++ b/src/Mod/Path/Gui/DlgProcessorChooser.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +#endif + +#include +#include +#include + +#include +#include + +#include "DlgProcessorChooser.h" +#include "ui_DlgProcessorChooser.h" + +using namespace PathGui; + +DlgProcessorChooser::DlgProcessorChooser(std::vector &scriptnames) + : QDialog(Gui::getMainWindow()), ui(new Ui_DlgProcessorChooser) +{ + ui->setupUi(this); + ui->comboBox->addItem(tr("None")); + for (std::vector::const_iterator it = scriptnames.begin(); it != scriptnames.end(); it++) + ui->comboBox->addItem(QString::fromUtf8((*it).c_str())); + QMetaObject::connectSlotsByName(this); +} + +DlgProcessorChooser::~DlgProcessorChooser() +{ +} + +std::string DlgProcessorChooser::getSelected() +{ + return entry; +} + +void DlgProcessorChooser::accept() +{ + if (ui->comboBox->currentText() == tr("None")) + entry = ""; + else + entry = ui->comboBox->currentText().toStdString(); + QDialog::accept(); +} +#include "moc_DlgProcessorChooser.cpp" diff --git a/src/Mod/Path/Gui/DlgProcessorChooser.h b/src/Mod/Path/Gui/DlgProcessorChooser.h new file mode 100644 index 0000000000..ae7a6178f6 --- /dev/null +++ b/src/Mod/Path/Gui/DlgProcessorChooser.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef PATH_DlgProcessorChooser_H +#define PATH_DlgProcessorChooser_H + +#include +#include +#include + +class Ui_DlgProcessorChooser; + +namespace PathGui { + +class DlgProcessorChooser : public QDialog +{ + Q_OBJECT + +public: + DlgProcessorChooser(std::vector &scriptnames); + ~DlgProcessorChooser(); + + std::string getSelected(); + + void accept(); + +protected Q_SLOTS: + +private: + Ui_DlgProcessorChooser* ui; + std::string entry; +}; + +} + +#endif // PATH_DlgProcessorChooser_H diff --git a/src/Mod/Path/Gui/DlgProcessorChooser.ui b/src/Mod/Path/Gui/DlgProcessorChooser.ui new file mode 100644 index 0000000000..a8c8636a62 --- /dev/null +++ b/src/Mod/Path/Gui/DlgProcessorChooser.ui @@ -0,0 +1,67 @@ + + + DlgProcessorChooser + + + + 0 + 0 + 239 + 82 + + + + Choose a processor + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DlgProcessorChooser + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DlgProcessorChooser + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Mod/Path/Gui/DlgSettingsPathColor.cpp b/src/Mod/Path/Gui/DlgSettingsPathColor.cpp new file mode 100644 index 0000000000..ee4b1b4fd6 --- /dev/null +++ b/src/Mod/Path/Gui/DlgSettingsPathColor.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "DlgSettingsPathColor.h" +#include + +using namespace PathGui; + +/* TRANSLATOR PartGui::DlgSettingsPathColor */ + +/** + * Constructs a DlgSettingsObjectColor which is a child of 'parent', with the + * name 'name' and widget flags set to 'f' + */ +DlgSettingsPathColor::DlgSettingsPathColor(QWidget* parent) + : PreferencePage(parent) +{ + this->setupUi(this); +} + +/** + * Destroys the object and frees any allocated resources + */ +DlgSettingsPathColor::~DlgSettingsPathColor() +{ + // no need to delete child widgets, Qt does it all for us +} + +void DlgSettingsPathColor::saveSettings() +{ + // Part + DefaultNormalPathColor->onSave(); + DefaultRapidPathColor->onSave(); + DefaultPathLineWidth->onSave(); + DefaultPathMarkerColor->onSave(); + DefaultExtentsColor->onSave(); +} + +void DlgSettingsPathColor::loadSettings() +{ + // Part + DefaultNormalPathColor->onRestore(); + DefaultRapidPathColor->onRestore(); + DefaultPathLineWidth->onRestore(); + DefaultPathMarkerColor->onRestore(); + DefaultExtentsColor->onRestore(); +} + +/** + * Sets the strings of the subwidgets using the current language. + */ +void DlgSettingsPathColor::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + retranslateUi(this); + } + else { + QWidget::changeEvent(e); + } +} + +#include "moc_DlgSettingsPathColor.cpp" + diff --git a/src/Mod/Path/Gui/DlgSettingsPathColor.h b/src/Mod/Path/Gui/DlgSettingsPathColor.h new file mode 100644 index 0000000000..a3332d2674 --- /dev/null +++ b/src/Mod/Path/Gui/DlgSettingsPathColor.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATHGUI_DIALOG_DLGSETTINGSPATHCOLOR_H +#define PATHGUI_DIALOG_DLGSETTINGSPATHCOLOR_H + +#include "ui_DlgSettingsPathColor.h" +#include + +namespace PathGui { + +class DlgSettingsPathColor : public Gui::Dialog::PreferencePage, public Ui_DlgSettingsPathColor +{ + Q_OBJECT + +public: + DlgSettingsPathColor(QWidget* parent = 0); + ~DlgSettingsPathColor(); + + void saveSettings(); + void loadSettings(); + +protected: + void changeEvent(QEvent *e); +}; + +} // namespace PathGui + +#endif // PATHGUI_DIALOG_DLGSETTINGSPATHCOLOR_H diff --git a/src/Mod/Path/Gui/DlgSettingsPathColor.ui b/src/Mod/Path/Gui/DlgSettingsPathColor.ui new file mode 100644 index 0000000000..226a6d57da --- /dev/null +++ b/src/Mod/Path/Gui/DlgSettingsPathColor.ui @@ -0,0 +1,232 @@ + + + PathGui::DlgSettingsPathColor + + + + 0 + 0 + 359 + 282 + + + + Path colors + + + + + + Default Path colors + + + + + + + + + 182 + 0 + + + + Default normal path color + + + + + + + The default color for new shapes + + + + 0 + 170 + 0 + + + + DefaultNormalPathColor + + + Mod/Path + + + + + + + + 182 + 0 + + + + Default pathline width + + + + + + + The default line thickness for new shapes + + + px + + + 9 + + + 1 + + + DefaultPathLineWidth + + + Mod/Path + + + + + + + + 182 + 0 + + + + Default path marker color + + + + + + + The default line color for new shapes + + + + 85 + 255 + 0 + + + + DefaultPathMarkerColor + + + Mod/Path + + + + + + + + 182 + 0 + + + + Rapid path color + + + + + + + The default line color for new shapes + + + + 170 + 0 + 0 + + + + DefaultRapidPathColor + + + Mod/Path + + + + + + + Machine extents color + + + + + + + DefaultExtentsColor + + + Mod/Path + + + + + + + + + Qt::Horizontal + + + + 28 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 217 + + + + + + + + + Gui::ColorButton + QPushButton +
Gui/Widgets.h
+
+ + Gui::PrefSpinBox + QSpinBox +
Gui/PrefWidgets.h
+
+ + Gui::PrefColorButton + Gui::ColorButton +
Gui/PrefWidgets.h
+
+
+ + DefaultNormalPathColor + DefaultPathLineWidth + + + +
diff --git a/src/Mod/Path/Gui/PreCompiled.cpp b/src/Mod/Path/Gui/PreCompiled.cpp new file mode 100644 index 0000000000..29d24420ed --- /dev/null +++ b/src/Mod/Path/Gui/PreCompiled.cpp @@ -0,0 +1,23 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" diff --git a/src/Mod/Path/Gui/PreCompiled.h b/src/Mod/Path/Gui/PreCompiled.h new file mode 100644 index 0000000000..5b5232312b --- /dev/null +++ b/src/Mod/Path/Gui/PreCompiled.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef PATHGUI_PRECOMPILED_H +#define PATHGUI_PRECOMPILED_H + +#include + +// Importing of App classes +#ifdef FC_OS_WIN32 +# define PartExport __declspec(dllimport) +# define PathExport __declspec(dllimport) +# define PathGuiExport __declspec(dllexport) +#else // for Linux +# define PartExport +# define PathExport +# define PathGuiExport +#endif + +#include + +#ifdef _PreComp_ + +// Python +#include + +// standard +#include +#include +#include + +// STL +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FC_OS_WIN32 +# include +#endif + + +// Qt Toolkit +#ifndef __Qt4All__ +# include +#endif + +#endif //_PreComp_ + +#endif // PATHGUI_PRECOMPILED_H diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc new file mode 100644 index 0000000000..6d70808611 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -0,0 +1,27 @@ + + + icons/preferences-path.svg + icons/Path-Toolpath.svg + icons/Path-Compound.svg + icons/Path-Shape.svg + icons/Path-Profile.svg + icons/Path-Pocket.svg + icons/Path-Drilling.svg + icons/Path-Project.svg + icons/Path-Dressup.svg + icons/Path-Hop.svg + icons/Path-Datums.svg + icons/Path-Copy.svg + icons/Path-ToolTable.svg + icons/Path-LengthOffset.svg + icons/Path-Axis.svg + icons/Path-Stock.svg + icons/Path-Plane.svg + icons/Path-Post.svg + icons/Path-LoadTool.svg + icons/Path-Comment.svg + icons/Path-Stop.svg + icons/Path-Machine.svg + icons/Path-Kurve.svg + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Axis.svg b/src/Mod/Path/Gui/Resources/icons/Path-Axis.svg new file mode 100644 index 0000000000..901493c773 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Axis.svg @@ -0,0 +1,62079 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Comment.svg b/src/Mod/Path/Gui/Resources/icons/Path-Comment.svg new file mode 100644 index 0000000000..e557ca42d5 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Comment.svg @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + ( + ) + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Compound.svg b/src/Mod/Path/Gui/Resources/icons/Path-Compound.svg new file mode 100644 index 0000000000..f23c1c3778 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Compound.svg @@ -0,0 +1,784 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Copy.svg b/src/Mod/Path/Gui/Resources/icons/Path-Copy.svg new file mode 100644 index 0000000000..f11e4b3e14 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Copy.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Datums.svg b/src/Mod/Path/Gui/Resources/icons/Path-Datums.svg new file mode 100644 index 0000000000..7b1e3a7a85 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Datums.svg @@ -0,0 +1,162 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Dressup.svg b/src/Mod/Path/Gui/Resources/icons/Path-Dressup.svg new file mode 100644 index 0000000000..c5000327c3 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Dressup.svg @@ -0,0 +1,548 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Drilling.svg b/src/Mod/Path/Gui/Resources/icons/Path-Drilling.svg new file mode 100644 index 0000000000..0445ae8cba --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Drilling.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Hop.svg b/src/Mod/Path/Gui/Resources/icons/Path-Hop.svg new file mode 100644 index 0000000000..9891d47e55 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Hop.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Kurve.svg b/src/Mod/Path/Gui/Resources/icons/Path-Kurve.svg new file mode 100644 index 0000000000..4cff2a2345 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Kurve.svg @@ -0,0 +1,75 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-LengthOffset.svg b/src/Mod/Path/Gui/Resources/icons/Path-LengthOffset.svg new file mode 100644 index 0000000000..a928b1e1d2 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-LengthOffset.svg @@ -0,0 +1,541 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-LoadTool.svg b/src/Mod/Path/Gui/Resources/icons/Path-LoadTool.svg new file mode 100644 index 0000000000..347a4aaf1c --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-LoadTool.svg @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + /\ + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Machine.svg b/src/Mod/Path/Gui/Resources/icons/Path-Machine.svg new file mode 100644 index 0000000000..822066473c --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Machine.svg @@ -0,0 +1,591 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Plane.svg b/src/Mod/Path/Gui/Resources/icons/Path-Plane.svg new file mode 100644 index 0000000000..a9740230e8 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Plane.svg @@ -0,0 +1,13961 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Pocket.svg b/src/Mod/Path/Gui/Resources/icons/Path-Pocket.svg new file mode 100644 index 0000000000..b984632e5d --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Pocket.svg @@ -0,0 +1,548 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Post.svg b/src/Mod/Path/Gui/Resources/icons/Path-Post.svg new file mode 100644 index 0000000000..81ba4b2f0a --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Post.svg @@ -0,0 +1,421 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + - + > + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Profile.svg b/src/Mod/Path/Gui/Resources/icons/Path-Profile.svg new file mode 100644 index 0000000000..cd32c85621 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Profile.svg @@ -0,0 +1,543 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Project.svg b/src/Mod/Path/Gui/Resources/icons/Path-Project.svg new file mode 100644 index 0000000000..57bc6dff7e --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Project.svg @@ -0,0 +1,1184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Shape.svg b/src/Mod/Path/Gui/Resources/icons/Path-Shape.svg new file mode 100644 index 0000000000..756d89a08b --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Shape.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg b/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg new file mode 100644 index 0000000000..0a34071600 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Stop.svg b/src/Mod/Path/Gui/Resources/icons/Path-Stop.svg new file mode 100644 index 0000000000..a1e6147987 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Stop.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + ! + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-ToolTable.svg b/src/Mod/Path/Gui/Resources/icons/Path-ToolTable.svg new file mode 100644 index 0000000000..5292f84ccf --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-ToolTable.svg @@ -0,0 +1,610 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Toolpath.svg b/src/Mod/Path/Gui/Resources/icons/Path-Toolpath.svg new file mode 100644 index 0000000000..517de11d93 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Toolpath.svg @@ -0,0 +1,538 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/preferences-path.svg b/src/Mod/Path/Gui/Resources/icons/preferences-path.svg new file mode 100644 index 0000000000..0a6fa189b2 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/preferences-path.svg @@ -0,0 +1,527 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/translations/Path.ts b/src/Mod/Path/Gui/Resources/translations/Path.ts new file mode 100644 index 0000000000..657e68170e --- /dev/null +++ b/src/Mod/Path/Gui/Resources/translations/Path.ts @@ -0,0 +1,333 @@ + + + + PathPocket + + + Pocket + + + + + Creates a Path Pocket object from a loop of edges or a face + + + + + Please select an edges loop from one object, or a single face + + + + + + Please select only edges or a single face + + + + + + The selected edges don't form a loop + + + + + + Create Pocket + + + + + PathProfile + + + The base geometry of this object + + + + + The tool number to use + + + + + Profile + + + + + Creates a Path Profile object from selected faces + + + + + Please select one face or wire + + + + + + Please select only one face or wire + + + + + + Please select only a face or a wire + + + + + + Create Profile + + + + + TooltableEditor + + + Tooltable editor + + + + + Tools list + + + + + Import... + + + + + Export... + + + + + Slot + + + + + Tool + + + + + Add new + + + + + Delete + + + + + Move up + + + + + Move down + + + + + Tool properties + + + + + Name + + + + + Type + + + + + Undefined + + + + + Drill + + + + + Center Drill + + + + + Counter Sink + + + + + Counter Bore + + + + + Reamer + + + + + Tap + + + + + End Mill + + + + + Slot Cutter + + + + + Ball End Mill + + + + + Chamfer Mill + + + + + Corner Round + + + + + Engraver + + + + + Material + + + + + High Speed Steel + + + + + High Carbon Tool Steel + + + + + Cast Alloy + + + + + Carbide + + + + + Ceramics + + + + + Diamond + + + + + Sialon + + + + + Properties + + + + + Diameter + + + + + mm + + + + + Length offset + + + + + Flat radius + + + + + Corner radius + + + + + Cutting edge angle + + + + + ° + + + + + Cutting edge height + + + + + Open tooltable + + + + + Tooltable XML (*.xml);;HeeksCAD tooltable (*.tooltable) + + + + + Save tooltable + + + + + Tooltable XML (*.xml) + + + + + Object not found + + + + + Object doesn't have a tooltable property + + + + diff --git a/src/Mod/Path/Gui/TaskDlgPathCompound.cpp b/src/Mod/Path/Gui/TaskDlgPathCompound.cpp new file mode 100644 index 0000000000..7477d0ca23 --- /dev/null +++ b/src/Mod/Path/Gui/TaskDlgPathCompound.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "TaskDlgPathCompound.h" +#include "ui_TaskDlgPathCompound.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +using namespace PathGui; +using namespace Gui; + +/* TRANSLATOR PathGui::TaskWidgetPathCompound */ + + +//************************************************************************** +// TaskWidget +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskWidgetPathCompound::TaskWidgetPathCompound(ViewProviderPathCompound *CompoundView,QWidget *parent) + : TaskBox(Gui::BitmapFactory().pixmap("Path-Compound"),tr("Compound paths"),true, parent),CompoundView(CompoundView) +{ + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskDlgPathCompound(); + ui->setupUi(proxy); + QMetaObject::connectSlotsByName(this); + + this->groupLayout()->addWidget(proxy); + + Path::FeatureCompound* pcCompound = static_cast(CompoundView->getObject()); + const std::vector &Paths = pcCompound->Group.getValues(); + for (std::vector::const_iterator it= Paths.begin();it!=Paths.end();++it) { + QString name = QString::fromAscii((*it)->getNameInDocument()); + name += QString::fromAscii(" ("); + name += QString::fromUtf8((*it)->Label.getValue()); + name += QString::fromAscii(")"); + ui->PathsList->addItem(name); + } +} + +TaskWidgetPathCompound::~TaskWidgetPathCompound() +{ + delete ui; +} + +std::vector TaskWidgetPathCompound::getList(void) const { + std::vector names; + for(int i = 0; i < ui->PathsList->count(); i++) + { + QListWidgetItem* item = ui->PathsList->item(i); + QString name = item->text(); + QStringList result; + result = name.split(QRegExp(QString::fromAscii("\\s+"))); + std::cout << result[0].toStdString() << std::endl; + names.push_back(result[0].toStdString()); + } + return names; +} + +void TaskWidgetPathCompound::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(proxy); + } +} + +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgPathCompound::TaskDlgPathCompound(PathGui::ViewProviderPathCompound *obj) + : TaskDialog(),CompoundView(obj) +{ + assert(CompoundView); + parameter = new TaskWidgetPathCompound(CompoundView); + Content.push_back(parameter); +} + +TaskDlgPathCompound::~TaskDlgPathCompound() +{ +} + +//==== calls from the TaskView =============================================================== + + +void TaskDlgPathCompound::open() +{ +} + +void TaskDlgPathCompound::clicked(int button) +{ +} + +bool TaskDlgPathCompound::accept() +{ + std::vector paths; + Path::FeatureCompound* pcCompound = static_cast(CompoundView->getObject()); + App::Document* pcDoc = static_cast(pcCompound->getDocument()); + std::vector names = parameter->getList(); + for(int i = 0; i < names.size(); i++) + { + App::DocumentObject* pcPath = static_cast(pcDoc->getObject(names[i].c_str())); + paths.push_back(pcPath); + } + pcCompound->Group.setValues(paths); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + return true; +} + +bool TaskDlgPathCompound::reject() +{ + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + return true; +} + +void TaskDlgPathCompound::helpRequested() +{ +} + + +#include "moc_TaskDlgPathCompound.cpp" diff --git a/src/Mod/Path/Gui/TaskDlgPathCompound.h b/src/Mod/Path/Gui/TaskDlgPathCompound.h new file mode 100644 index 0000000000..cfb8294958 --- /dev/null +++ b/src/Mod/Path/Gui/TaskDlgPathCompound.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATHGUI_TaskDlgPathCompound_H +#define PATHGUI_TaskDlgPathCompound_H + +#include +#include +#include +#include + +#include "ViewProviderPathCompound.h" + +class Ui_TaskDlgPathCompound; + +namespace PathGui { + +/// Widget +class TaskWidgetPathCompound : public Gui::TaskView::TaskBox +{ + Q_OBJECT + +public: + TaskWidgetPathCompound(ViewProviderPathCompound *CompoundView, QWidget *parent=0); + ~TaskWidgetPathCompound(); + + std::vector getList(void) const; + +protected: + void changeEvent(QEvent *e); + +private: + QWidget* proxy; + Ui_TaskDlgPathCompound* ui; + ViewProviderPathCompound *CompoundView; +}; + +/// Task Dialog +class PathGuiExport TaskDlgPathCompound : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskDlgPathCompound(PathGui::ViewProviderPathCompound *); + ~TaskDlgPathCompound(); + +public: + /// is called the TaskView when the dialog is opened + virtual void open(); + /// is called by the framework if an button is clicked which has no accept or rject role + virtual void clicked(int); + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + /// is called by the framework if the dialog is rejected (Cancel) + virtual bool reject(); + /// is called by the framework if the user press the help button + virtual void helpRequested(); + + /// returns for Close and Help button + virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const + { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } + +protected: + PathGui::ViewProviderPathCompound *CompoundView; + TaskWidgetPathCompound *parameter; +}; + + + +} //namespace RobotGui + +#endif // PATHGUI_TASKDLGPATHCOMPOUND_H diff --git a/src/Mod/Path/Gui/TaskDlgPathCompound.ui b/src/Mod/Path/Gui/TaskDlgPathCompound.ui new file mode 100644 index 0000000000..2c801e7528 --- /dev/null +++ b/src/Mod/Path/Gui/TaskDlgPathCompound.ui @@ -0,0 +1,47 @@ + + + TaskDlgPathCompound + + + + 0 + 0 + 285 + 385 + + + + Paths list + + + + + + Reorder children by dragging and dropping them to their correct location + + + true + + + + + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + + + + + + diff --git a/src/Mod/Path/Gui/ViewProviderPath.cpp b/src/Mod/Path/Gui/ViewProviderPath.cpp new file mode 100644 index 0000000000..955ec11c2d --- /dev/null +++ b/src/Mod/Path/Gui/ViewProviderPath.cpp @@ -0,0 +1,426 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "ViewProviderPath.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef M_PI + #define M_PI 3.14159265358979323846 + #define M_PI 3.14159265358979323846 /* pi */ +#endif + +#ifndef M_PI_2 + #define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif + +using namespace Gui; +using namespace PathGui; +using namespace Path; + +PROPERTY_SOURCE(PathGui::ViewProviderPath, Gui::ViewProviderGeometryObject) + +ViewProviderPath::ViewProviderPath() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Path"); + unsigned long lcol = hGrp->GetUnsigned("DefaultNormalPathColor",11141375UL); // dark green (0,170,0) + float lr,lg,lb; + lr = ((lcol >> 24) & 0xff) / 255.0; lg = ((lcol >> 16) & 0xff) / 255.0; lb = ((lcol >> 8) & 0xff) / 255.0; + unsigned long mcol = hGrp->GetUnsigned("DefaultPathMarkerColor",1442775295UL); // lime green (85,255,0) + float mr,mg,mb; + mr = ((mcol >> 24) & 0xff) / 255.0; mg = ((mcol >> 16) & 0xff) / 255.0; mb = ((mcol >> 8) & 0xff) / 255.0; + int lwidth = hGrp->GetInt("DefaultPathLineWidth",1); + ADD_PROPERTY_TYPE(NormalColor,(lr,lg,lb),"Path",App::Prop_None,"The color of the feed rate moves"); + ADD_PROPERTY_TYPE(MarkerColor,(mr,mg,mb),"Path",App::Prop_None,"The color of the markers"); + ADD_PROPERTY_TYPE(LineWidth,(lwidth),"Path",App::Prop_None,"The line width of this path"); + ADD_PROPERTY_TYPE(ShowFirstRapid,(true),"Path",App::Prop_None,"Turns the display of the first rapid move on/off"); + + pcPathRoot = new Gui::SoFCSelection(); + + pcPathRoot->style = Gui::SoFCSelection::EMISSIVE; + pcPathRoot->highlightMode = Gui::SoFCSelection::AUTO; + pcPathRoot->selectionMode = Gui::SoFCSelection::SEL_ON; + pcPathRoot->ref(); + + pcTransform = new SoTransform(); + pcTransform->ref(); + + pcLineCoords = new SoCoordinate3(); + pcLineCoords->ref(); + + pcMarkerCoords = new SoCoordinate3(); + pcMarkerCoords->ref(); + + pcDrawStyle = new SoDrawStyle(); + pcDrawStyle->ref(); + pcDrawStyle->style = SoDrawStyle::LINES; + pcDrawStyle->lineWidth = LineWidth.getValue(); + + pcLines = new SoIndexedLineSet; + pcLines->ref(); + + pcLineColor = new SoMaterial; + pcLineColor->ref(); + + pcMatBind = new SoMaterialBinding; + pcMatBind->ref(); + pcMatBind->value = SoMaterialBinding::OVERALL; + + pcMarkerColor = new SoBaseColor; + pcMarkerColor->ref(); + + NormalColor.touch(); + MarkerColor.touch(); +} + +ViewProviderPath::~ViewProviderPath() +{ + pcPathRoot->unref(); + pcTransform->unref(); + pcLineCoords->unref(); + pcMarkerCoords->unref(); + pcDrawStyle->unref(); + pcLines->unref(); + pcLineColor->unref(); + pcMatBind->unref(); + pcMarkerColor->unref(); +} + +void ViewProviderPath::attach(App::DocumentObject *pcObj) +{ + ViewProviderDocumentObject::attach(pcObj); + + // Draw trajectory lines + SoSeparator* linesep = new SoSeparator; + linesep->addChild(pcLineColor); + linesep->addChild(pcMatBind); + linesep->addChild(pcDrawStyle); + linesep->addChild(pcLineCoords); + linesep->addChild(pcLines); + + // Draw markers + SoSeparator* markersep = new SoSeparator; + SoMarkerSet* marker = new SoMarkerSet; + marker->markerIndex=SoMarkerSet::PLUS_7_7; + markersep->addChild(pcMarkerColor); + markersep->addChild(pcMarkerCoords); + markersep->addChild(marker); + + pcPathRoot->addChild(pcTransform); + pcPathRoot->addChild(linesep); + pcPathRoot->addChild(markersep); + + addDisplayMaskMode(pcPathRoot, "Waypoints"); + pcPathRoot->objectName = pcObj->getNameInDocument(); + pcPathRoot->documentName = pcObj->getDocument()->getName(); + pcPathRoot->subElementName = "Path"; +} + +void ViewProviderPath::setDisplayMode(const char* ModeName) +{ + if ( strcmp("Waypoints",ModeName)==0 ) + setDisplayMaskMode("Waypoints"); + ViewProviderGeometryObject::setDisplayMode( ModeName ); +} + +std::vector ViewProviderPath::getDisplayModes(void) const +{ + std::vector StrList; + StrList.push_back("Waypoints"); + return StrList; +} + +void ViewProviderPath::onChanged(const App::Property* prop) +{ + if (prop == &LineWidth) { + pcDrawStyle->lineWidth = LineWidth.getValue(); + } else if (prop == &NormalColor) { + if (colorindex.size() > 0) { + const App::Color& c = NormalColor.getValue(); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Path"); + unsigned long rcol = hGrp->GetUnsigned("DefaultRapidPathColor",2852126975UL); // dark red (170,0,0) + float rr,rg,rb; + rr = ((rcol >> 24) & 0xff) / 255.0; rg = ((rcol >> 16) & 0xff) / 255.0; rb = ((rcol >> 8) & 0xff) / 255.0; + + pcMatBind->value = SoMaterialBinding::PER_PART; + // resizing and writing the color vector: + pcLineColor->diffuseColor.setNum(colorindex.size()); + SbColor* colors = pcLineColor->diffuseColor.startEditing(); + for(unsigned int i=0;idiffuseColor.finishEditing(); + } + } else if (prop == &MarkerColor) { + const App::Color& c = MarkerColor.getValue(); + pcMarkerColor->rgb.setValue(c.r,c.g,c.b); + } else if (prop == &ShowFirstRapid) { + Path::Feature* pcPathObj = static_cast(pcObject); + this->updateData(&pcPathObj->Path); + } else { + ViewProviderGeometryObject::onChanged(prop); + } +} + +void ViewProviderPath::updateData(const App::Property* prop) +{ + Path::Feature* pcPathObj = static_cast(pcObject); + + if ( prop == &pcPathObj->Path) { + + const Toolpath &tp = pcPathObj->Path.getValue(); + if(tp.getSize()==0) + return; + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Part"); + float deviation = hGrp->GetFloat("MeshDeviation",0.2); + std::vector points; + std::vector markers; + Base::Vector3d last(0,0,0); + colorindex.clear(); + bool absolute = true; + bool first = true; + + for (int i = 0; i < tp.getSize(); i++) { + Path::Command cmd = tp.getCommand(i); + std::string name = cmd.Name; + Base::Vector3d next = cmd.getPlacement().getPosition(); + if (!absolute) + next = last + next; + if (!cmd.has("X")) + next.x = last.x; + if (!cmd.has("Y")) + next.y = last.y; + if (!cmd.has("Z")) + next.z = last.z; + + if ( (name == "G0") || (name == "G00") || (name == "G1") || (name == "G01") ) { + // straight line + if ( (!first) || (ShowFirstRapid.getValue() == true) || (name == "G1") || (name == "G01") ) { + if (first) { + points.push_back(last); + markers.push_back(last); // startpoint of path + } + points.push_back(next); + //markers.push_back(next); // endpoint + last = next; + if ( (name == "G0") || (name == "G00") ) + colorindex.push_back(0); // rapid color + else + colorindex.push_back(1); // std color + } else { + // don't show first G0 move if ShowFirstRapid is False + last = next; + points.push_back(last); + markers.push_back(last); // startpoint of path + } + first = false; + + } else if ( (name == "G2") || (name == "G02") || (name == "G3") || (name == "G03") ) { + // arc + Base::Vector3d norm; + if ( (name == "G2") || (name == "G02") ) + norm.Set(0,0,-1); + else + norm.Set(0,0,1); + Base::Vector3d center = (last + cmd.getCenter()); + double radius = (last - center).Length(); + double angle = (next - center).GetAngle(last - center); + // BUGGY: not needed anyway? + //Base::Vector3d anorm = (last - center) % (next - center); + //if (anorm.z < 0) + // angle = M_PI - angle; + int segments = 3/(deviation/angle); //we use a rather simple rule here, provisorily + for (int j = 1; j < segments; j++) { + //std::cout << "vector " << j << std::endl; + Base::Vector3d inter; + Base::Rotation rot(norm,(angle/segments)*j); + //std::cout << "angle " << (angle/segments)*j << std::endl; + rot.multVec((last - center),inter); + //std::cout << "result " << inter.x << " , " << inter.y << " , " << inter.z << std::endl; + points.push_back( center + inter); + colorindex.push_back(1); + } + //std::cout << "next " << next.x << " , " << next.y << " , " << next.z << std::endl; + points.push_back(next); + //markers.push_back(next); // endpoint + //markers.push_back(center); // add a marker at center too + last = next; + colorindex.push_back(1); + + } else if (name == "G90") { + // absolute mode + absolute = true; + + } else if (name == "G91") { + // relative mode + absolute = false; + + } else if ((name=="G81")||(name=="G82")||(name=="G83")||(name=="G84")||(name=="G85")||(name=="G86")||(name=="G89")){ + // drill,tap,bore + double r; + if (cmd.has("R")) + r = cmd.getValue("R"); + Base::Vector3d p1(next.x,next.y,last.z); +// Base::Vector3d p1(next.x,next.y,r); + points.push_back(p1); + //markers.push_back(p1); + colorindex.push_back(0); + Base::Vector3d p2(next.x,next.y,r); + points.push_back(p2); + //markers.push_back(p2); + colorindex.push_back(0); + points.push_back(next); + //markers.push_back(next); + colorindex.push_back(1); + double q; + if (cmd.has("Q")) { + q = cmd.getValue("Q"); + double tempz = r; + while (tempz > next.z) { + Base::Vector3d temp(next.x,next.y,tempz); + markers.push_back(temp); + tempz -= q; + } + } + Base::Vector3d p3(next.x,next.y,last.z); + points.push_back(p3); + //markers.push_back(p2); + colorindex.push_back(0); + } + } + + if (!points.empty()) { + pcLineCoords->point.deleteValues(0); + pcLineCoords->point.setNum(points.size()); + std::vector ei; + for(unsigned int i=0;ipoint.set1Value(i,points[i].x,points[i].y,points[i].z); + ei.push_back(i); + } + int* segs = &ei[0]; + pcLines->coordIndex.setNum(points.size()); + pcLines->coordIndex.setValues(0,points.size(),(const int32_t*)segs); + + pcMarkerCoords->point.deleteValues(0); + + // putting one marker at each node makes the display awfully slow + // following 2 lines leave just one at the origin + //pcMarkerCoords->point.setNum(1); + //pcMarkerCoords->point.set1Value(0,markers[0].x,markers[0].y,markers[0].z); + pcMarkerCoords->point.setNum(markers.size()); + for(unsigned int i=0;ipoint.set1Value(i,markers[i].x,markers[i].y,markers[i].z); + + // update the coloring after we changed the color vector + NormalColor.touch(); + recomputeBoundingBox(); + } + + } else if ( prop == &pcPathObj->Placement) { + + Base::Placement pl = *(&pcPathObj->Placement.getValue()); + Base::Vector3d pos = pl.getPosition(); + double q1, q2, q3, q4; + pl.getRotation().getValue(q1,q2,q3,q4); + pcTransform->translation.setValue(pos.x,pos.y,pos.z); + pcTransform->rotation.setValue(q1,q2,q3,q4); + recomputeBoundingBox(); + } +} + +void ViewProviderPath::recomputeBoundingBox() +{ + // update the boundbox + double MinX,MinY,MinZ,MaxX,MaxY,MaxZ; + MinX = 999999999.0; + MinY = 999999999.0; + MinZ = 999999999.0; + MaxX = -999999999.0; + MaxY = -999999999.0; + MaxZ = -999999999.0; + Path::Feature* pcPathObj = static_cast(pcObject); + Base::Placement pl = *(&pcPathObj->Placement.getValue()); + Base::Vector3d pt; + for (unsigned int i=0;ipoint.getNum();i++) { + pt.x = pcLineCoords->point[i].getValue()[0]; + pt.y = pcLineCoords->point[i].getValue()[1]; + pt.z = pcLineCoords->point[i].getValue()[2]; + pl.multVec(pt,pt); + if (pt.x < MinX) MinX = pt.x; + if (pt.y < MinY) MinY = pt.y; + if (pt.z < MinZ) MinZ = pt.z; + if (pt.x > MaxX) MaxX = pt.x; + if (pt.y > MaxY) MaxY = pt.y; + if (pt.z > MaxZ) MaxZ = pt.z; + } + pcBoundingBox->minBounds.setValue(MinX, MinY, MinZ); + pcBoundingBox->maxBounds.setValue(MaxX, MaxY, MaxZ); +} + +QIcon ViewProviderPath::getIcon() const +{ + return Gui::BitmapFactory().pixmap("Path-Toolpath"); +} + +// Python object ----------------------------------------------------------------------- + +namespace Gui { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderPathPython, PathGui::ViewProviderPath) +/// @endcond + +// explicit template instantiation +template class PathGuiExport ViewProviderPythonFeatureT; +} + diff --git a/src/Mod/Path/Gui/ViewProviderPath.h b/src/Mod/Path/Gui/ViewProviderPath.h new file mode 100644 index 0000000000..8e14e2d00b --- /dev/null +++ b/src/Mod/Path/Gui/ViewProviderPath.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_ViewProviderPath_H +#define PATH_ViewProviderPath_H + +#include +#include +#include + +class SoCoordinate3; +class SoDrawStyle; +class SoIndexedLineSet; +class SoMaterial; +class SoBaseColor; +class SoMaterialBinding; +class SoTransform; + +namespace PathGui +{ + +class PathGuiExport ViewProviderPath : public Gui::ViewProviderGeometryObject +{ + PROPERTY_HEADER(PathGui::ViewProviderPath); + +public: + /// constructor. + ViewProviderPath(); + + /// destructor. + ~ViewProviderPath(); + + // Display properties + App::PropertyInteger LineWidth; + App::PropertyColor NormalColor; + App::PropertyColor MarkerColor; + App::PropertyBool ShowFirstRapid; + + void attach(App::DocumentObject *pcObject); + void setDisplayMode(const char* ModeName); + std::vector getDisplayModes() const; + void updateData(const App::Property*); + void recomputeBoundingBox(); + virtual QIcon getIcon() const; + +protected: + + virtual void onChanged(const App::Property* prop); + + Gui::SoFCSelection * pcPathRoot; + SoTransform * pcTransform; + SoCoordinate3 * pcLineCoords; + SoCoordinate3 * pcMarkerCoords; + SoDrawStyle * pcDrawStyle; + SoIndexedLineSet * pcLines; + SoMaterial * pcLineColor; + SoBaseColor * pcMarkerColor; + SoMaterialBinding * pcMatBind; + std::vector colorindex; + + }; + + typedef Gui::ViewProviderPythonFeatureT ViewProviderPathPython; + +} //namespace PathGui + + +#endif // PATH_VIEWPROVIDERPATH_H diff --git a/src/Mod/Path/Gui/ViewProviderPathCompound.cpp b/src/Mod/Path/Gui/ViewProviderPathCompound.cpp new file mode 100644 index 0000000000..58bccecd62 --- /dev/null +++ b/src/Mod/Path/Gui/ViewProviderPathCompound.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include +#include + +#include + +#include "TaskDlgPathCompound.h" + + +using namespace Gui; +using namespace PathGui; + +PROPERTY_SOURCE(PathGui::ViewProviderPathCompound, PathGui::ViewProviderPath) + + +bool ViewProviderPathCompound::setEdit(int ModNum) +{ + Gui::TaskView::TaskDialog* dlg = new TaskDlgPathCompound(this); + Gui::Control().showDialog(dlg); + return true; +} + +void ViewProviderPathCompound::unsetEdit(int ModNum) +{ + // when pressing ESC make sure to close the dialog + Gui::Control().closeDialog(); +} + +std::vector ViewProviderPathCompound::claimChildren(void)const +{ + return std::vector(static_cast(getObject())->Group.getValues()); +} + +bool ViewProviderPathCompound::canDragObjects() const +{ + return true; +} + +void ViewProviderPathCompound::dragObject(App::DocumentObject* obj) +{ + static_cast(getObject())->removeObject(obj); +} + +bool ViewProviderPathCompound::canDropObjects() const +{ + return true; +} + +void ViewProviderPathCompound::dropObject(App::DocumentObject* obj) +{ + static_cast(getObject())->addObject(obj); +} + +QIcon ViewProviderPathCompound::getIcon() const +{ + return Gui::BitmapFactory().pixmap("Path-Compound"); +} + +// Python object ----------------------------------------------------------------------- + +namespace Gui { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(PathGui::ViewProviderPathCompoundPython, PathGui::ViewProviderPathCompound) +/// @endcond + +// explicit template instantiation +template class PathGuiExport ViewProviderPythonFeatureT; +} diff --git a/src/Mod/Path/Gui/ViewProviderPathCompound.h b/src/Mod/Path/Gui/ViewProviderPathCompound.h new file mode 100644 index 0000000000..0b22380ef9 --- /dev/null +++ b/src/Mod/Path/Gui/ViewProviderPathCompound.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_ViewProviderPathCompound_H +#define PATH_ViewProviderPathCompound_H + +#include "ViewProviderPath.h" + +namespace PathGui +{ + +class PathGuiExport ViewProviderPathCompound: public ViewProviderPath +{ + PROPERTY_HEADER(PathGui::ViewProviderPathCompound); + +public: + + std::vector claimChildren(void)const; + virtual bool canDragObjects() const; + virtual void dragObject(App::DocumentObject*); + virtual bool canDropObjects() const; + virtual void dropObject(App::DocumentObject*); + QIcon getIcon(void) const; + +protected: + virtual bool setEdit(int ModNum); + virtual void unsetEdit(int ModNum); + +}; + +typedef Gui::ViewProviderPythonFeatureT ViewProviderPathCompoundPython; + +} //namespace PathGui + + +#endif // PATH_ViewProviderPathCompound_H diff --git a/src/Mod/Path/Gui/ViewProviderPathShape.cpp b/src/Mod/Path/Gui/ViewProviderPathShape.cpp new file mode 100644 index 0000000000..52f4c71a08 --- /dev/null +++ b/src/Mod/Path/Gui/ViewProviderPathShape.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "ViewProviderPathShape.h" +#include + +using namespace Gui; +using namespace PathGui; + +PROPERTY_SOURCE(PathGui::ViewProviderPathShape, PathGui::ViewProviderPath) + +QIcon ViewProviderPathShape::getIcon() const +{ + return Gui::BitmapFactory().pixmap("Path-Shape"); +} diff --git a/src/Mod/Path/Gui/ViewProviderPathShape.h b/src/Mod/Path/Gui/ViewProviderPathShape.h new file mode 100644 index 0000000000..8e525dcf46 --- /dev/null +++ b/src/Mod/Path/Gui/ViewProviderPathShape.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PATH_ViewProviderPathShape_H +#define PATH_ViewProviderPathShape_H + +#include "ViewProviderPath.h" + +namespace PathGui +{ + +class PathGuiExport ViewProviderPathShape: public ViewProviderPath +{ + PROPERTY_HEADER(PathGui::ViewProviderPathShape); + +public: + + QIcon getIcon(void) const; +}; + +} //namespace PathGui + + +#endif // PATH_ViewProviderPathShape_H diff --git a/src/Mod/Path/Init.py b/src/Mod/Path/Init.py new file mode 100644 index 0000000000..28c1bc22b5 --- /dev/null +++ b/src/Mod/Path/Init.py @@ -0,0 +1,31 @@ +#*************************************************************************** +#* (c) Yorik van Havre (yorik@uncreated.net) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + +# Get the Parameter Group of this module +ParGrp = App.ParamGet("System parameter:Modules").GetGroup("Path") + +# Set the needed information +ParGrp.SetString("HelpIndex", "Path/Help/index.html") +ParGrp.SetString("WorkBenchName", "Path") +ParGrp.SetString("WorkBenchModule", "PathWorkbench.py") + diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py new file mode 100644 index 0000000000..d97a54aaf5 --- /dev/null +++ b/src/Mod/Path/InitGui.py @@ -0,0 +1,128 @@ +#*************************************************************************** +#* (c) Yorik van Havre (yorik@uncreated.net) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +class PathWorkbench ( Workbench ): + "Path workbench" + Icon = """ + /* XPM */ + static char * Path_xpm[] = { + "16 16 9 1", + " c None", + ". c #262623", + "+ c #452F16", + "@ c #525451", + "# c #7E5629", + "$ c #838582", + "% c #BE823B", + "& c #989A97", + "* c #CFD1CE", + " .@@@@@@@@@@. ", + " $**********$ ", + " @$$$&&&&$$$@ ", + " .$&&&&$. ", + " @******. ", + " @******. ", + " ...@@... ", + " .&&@. ", + " .@. . ", + " .&&. ", + " .$*$. ", + " .$. . ", + "+###+ .@&.+###+", + "+%%%+ .$$. +%%%+", + "+%%%%#.. .#%%%%+", + ".++++++..++++++."}; + """ + MenuText = "Path" + ToolTip = "Path workbench" + + def Initialize(self): + # load the builtin modules + import Path + import PathGui + # load python modules + from PathScripts import PathProfile + from PathScripts import PathPocket + from PathScripts import PathDrilling + from PathScripts import PathDressup + from PathScripts import PathHop + from PathScripts import PathCopy + from PathScripts import PathFixture + from PathScripts import PathCompoundExtended + from PathScripts import PathProject + from PathScripts import PathToolTableEdit + from PathScripts import PathStock + from PathScripts import PathPlane + from PathScripts import PathPost + from PathScripts import PathToolLenOffset + from PathScripts import PathLoadTool + from PathScripts import PathComment + from PathScripts import PathStop + from PathScripts import PathMachine + from PathScripts import PathFromShape + from PathScripts import PathKurve + + # build commands list + commands =["Path_Stock","Path_Plane","Path_Fixture","Path_ToolTableEdit","Path_Profile","Path_Kurve","Path_Pocket","Path_Drilling",\ + "Path_Dressup","Path_Hop","Path_Shape","Path_Copy","Path_CompoundExtended","Path_Project"] + + projcmdlist = ["Path_Project", "Path_ToolTableEdit","Path_Post"] + prepcmdlist = ["Path_Plane","Path_Fixture","Path_LoadTool","Path_ToolLenOffset","Path_Comment","Path_Stop"] + opcmdlist = ["Path_Profile","Path_Kurve","Path_Pocket","Path_Drilling","Path_FromShape"] + modcmdlist = ["Path_Copy","Path_CompoundExtended","Path_Dressup","Path_Hop"] + + + # Add commands to menu and toolbar + def QT_TRANSLATE_NOOP(scope, text): return text +# self.appendToolbar(QT_TRANSLATE_NOOP("PathWorkbench","Path"),commands) + self.appendToolbar(QT_TRANSLATE_NOOP("PathWorkbench","Commands for setting up Project"),projcmdlist) + self.appendToolbar(QT_TRANSLATE_NOOP("PathWorkbench","Prepatory Commands"),prepcmdlist) + self.appendToolbar(QT_TRANSLATE_NOOP("PathWorkbench","Operations"),opcmdlist) + self.appendToolbar(QT_TRANSLATE_NOOP("PathWorkbench","Commands for grouping,copying, and organizing operations"),modcmdlist) + +# self.appendMenu(QT_TRANSLATE_NOOP("PathWorkbench","Path"),commands) + self.appendMenu([QT_TRANSLATE_NOOP("PathWorkbench","Path"),QT_TRANSLATE_NOOP("Path","Project Setup")],projcmdlist) + self.appendMenu([QT_TRANSLATE_NOOP("PathWorkbench","Path"),QT_TRANSLATE_NOOP("Path","Prepatory Commands")],prepcmdlist) + self.appendMenu([QT_TRANSLATE_NOOP("PathWorkbench","Path"),QT_TRANSLATE_NOOP("Path","New Operation")],opcmdlist) + self.appendMenu([QT_TRANSLATE_NOOP("PathWorkbench","Path"),QT_TRANSLATE_NOOP("Path","Path Modification")],modcmdlist) + + # Add preferences pages + import os + FreeCADGui.addPreferencePage(FreeCAD.getHomePath()+os.sep+"Mod"+os.sep+"Path"+os.sep+"PathScripts"+os.sep+"DlgSettingsPath.ui","Path") + + Log ('Loading Path workbench... done\n') + + def GetClassName(self): + return "Gui::PythonWorkbench" + + def Activated(self): + Msg("Path workbench activated\n") + + def Deactivated(self): + Msg("Path workbench deactivated\n") + +Gui.addWorkbench(PathWorkbench()) + +FreeCAD.addImportType("GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap)","PathGui") +FreeCAD.addExportType("GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap)","PathGui") diff --git a/src/Mod/Path/PathScripts/DlgSettingsPath.ui b/src/Mod/Path/PathScripts/DlgSettingsPath.ui new file mode 100644 index 0000000000..6eb41293e4 --- /dev/null +++ b/src/Mod/Path/PathScripts/DlgSettingsPath.ui @@ -0,0 +1,142 @@ + + + Gui::Dialog::DlgSettingsPath + + + + 0 + 0 + 503 + 526 + + + + General Path settings + + + + 6 + + + 9 + + + + + General Path settings + + + + + + + + If this is checked, object names will be prefixed with the IFC ID number + + + Test setting 1 + + + pathTestSetting1 + + + Mod/Path + + + + + + + + + + + Test setting 2 + + + + + + + A comma-separated list of Ifc entities to exclude from import + + + + + + + + + pathTestSetting2 + + + Mod/Path + + + + + + + + + + + + Test group + + + + + + + + Some IFC viewers don't like objects exported as extrusions. Use this to force all objects to be exported as BREP geometry. + + + Test setting 3 + + + pathTestSetting3 + + + Mod/Path + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + qPixmapFromMimeSource + + + Gui::PrefCheckBox + QCheckBox +
Gui/PrefWidgets.h
+
+ + Gui::PrefLineEdit + QLineEdit +
Gui/PrefWidgets.h
+
+
+ + +
diff --git a/src/Mod/Path/PathScripts/Macros/create_tool.py b/src/Mod/Path/PathScripts/Macros/create_tool.py new file mode 100644 index 0000000000..2f0398dc67 --- /dev/null +++ b/src/Mod/Path/PathScripts/Macros/create_tool.py @@ -0,0 +1,39 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2014 Daniel Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +''' +This macro is used in conjunction with the toolpathparams script to create an object that represents a tool for use in a CNC program. Create a group and then select it- then run the macro. +You will have to edit the parameters inside the Data tab of the tool object. +''' +import PathScripts +import toolpathparams as tp + + +tl = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Tools") + +tp.ToolParams(tl) +tp.ViewProviderToolParams(tl.ViewObject) + +sel = FreeCADGui.Selection.getSelection() +g = sel[0] +g.addObject(tl) +App.activeDocument().recompute() diff --git a/src/Mod/Path/PathScripts/Macros/holefinder.py b/src/Mod/Path/PathScripts/Macros/holefinder.py new file mode 100644 index 0000000000..173fdb478f --- /dev/null +++ b/src/Mod/Path/PathScripts/Macros/holefinder.py @@ -0,0 +1,65 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2014 Daniel Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +import FreeCAD +from FreeCAD import Base +import Part +import Draft +from PySide import QtGui,QtCore +from math import pi +''' +This macro makes a list of holes for drilling from a solid +1. Select a solid object that has holes in it and run the macro +2. It only collects info on holes that are parallel to the Z axis- I don't have a 4 or 5 axis mill at the moment +3. It pulls the center of the hole bounding box and the XLength for it's diameter +4. It will place a list of the holes on the clipboard +5. Uncomment the line that starts with '#Draft.makeLine' and manipulate it, if you want to see lines down the center of each hole. +6. Manipulate the line that starts with 'holelist.append' to make the list fit your own needs- I've put the ZMax at the ZMax of the solid's bounding box +because I want to make sure that my drill tip doesn't collide with anything on the top of the part. YMMV. +''' +def findholes(obj): + facelist = [] + holelist =[] + vz = Base.Vector(0,0,1) + for f in obj.Faces: + if ( round(f.ParameterRange[0], 8)==0.0 ) and ( round(f.ParameterRange[1],8) == round(pi*2, 8) ) : #eliminate flat faces + facelist.append(f) + + for h in facelist: + for w in h.Wires: + for c in w.Edges: + if ( isinstance(c.Curve,Part.Line)): + v0=Base.Vector(c.Vertexes[0].X, c.Vertexes[0].Y, c.Vertexes[0].Z); v1=Base.Vector(c.Vertexes[1].X,c.Vertexes[1].Y, c.Vertexes[1].Z) + if (v1.sub(v0).x == 0) and (v1.sub(v0).y == 0): + lsp = Base.Vector(h.BoundBox.Center.x,h.BoundBox.Center.y,h.BoundBox.ZMax) + lep = Base.Vector(h.BoundBox.Center.x,h.BoundBox.Center.y,h.BoundBox.ZMin) + if obj.isInside(lsp, 0,False) or obj.isInside(lep, 0,False): + pass + else: + Draft.makeLine((h.BoundBox.Center.x,h.BoundBox.Center.y,obj.BoundBox.ZMax ),(h.BoundBox.Center.x,h.BoundBox.Center.y,h.BoundBox.ZMin )) + x =h.BoundBox.Center.x;y=h.BoundBox.Center.y;zmax=obj.BoundBox.ZMax;zmin=h.BoundBox.ZMin;diameter=h.BoundBox.XLength + holelist.append((diameter, x,y,zmax,zmin)) + clipboard = QtGui.QApplication.clipboard() + clipboard.setText(str(holelist)) + +sel=Gui.Selection.getSelection() +obj = sel[0].Shape +findholes(obj) diff --git a/src/Mod/Path/PathScripts/Macros/outer_profile.py b/src/Mod/Path/PathScripts/Macros/outer_profile.py new file mode 100644 index 0000000000..5ebf2b9403 --- /dev/null +++ b/src/Mod/Path/PathScripts/Macros/outer_profile.py @@ -0,0 +1,45 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2014 Daniel Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' +This macro finds the outside profile of a 3D shape. Right now it just creates wires that represent the shape,but it could be used for finding the outside profile of an object for a toolpath +''' +import FreeCADGui +import DraftGeomUtils +from FreeCAD import Vector +from PathScripts import find_outer_profile as fop + +sel = FreeCADGui.Selection.getSelection()[0] +obj = sel +el = fop.edgelist(obj) +hl = fop.horizontal(el) +connected = DraftGeomUtils.findWires(hl) +goodwires = fop.openFilter(connected) + +outerwires ,innerwires, same = fop.findOutsideWire(goodwires) +#get distance from outerwires Z to bottom of part +zdiff = obj.Shape.BoundBox.ZMin- outerwires.BoundBox.ZMax +outerwires.Placement.move(Vector(0,0,zdiff)) +Part.show(outerwires) + +zupperouter = outerwires +zupperouter.Placement.move(Vector(0,0,obj.Shape.BoundBox.ZMax)) +Part.show(zupperouter) diff --git a/src/Mod/Path/PathScripts/PathComment.py b/src/Mod/Path/PathScripts/PathComment.py new file mode 100644 index 0000000000..db22d83e68 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathComment.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used for CNC machine comments for Path module. Create a comment and place it in the Document tree.''' + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class Comment: + def __init__(self,obj): + obj.addProperty("App::PropertyString","Comment","Path",translate("Comment","Comment or note for CNC program")) + obj.Proxy = self + mode = 2 + obj.setEditorMode('Placement',mode) + + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def onChanged(self,obj,prop): + pass + + def execute(self,obj): + output ="" + output += '('+ str(obj.Comment)+')\n' + path = Path.Path(output) + obj.Path = path + +class _ViewProviderComment: + def __init__(self,vobj): #mandatory +# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Comment.svg" + + def onChanged(self,vobj,prop): #optional + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def getIcon(self): + return ":/icons/Path-Comment.svg" + + +class CommandPathComment: + def GetResources(self): + return {'Pixmap' : 'Path-Comment', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathComment","Comment"), + 'Accel': "P, C", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathComment","Add a Comment to your CNC program")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathComment","Create a Comment in your CNC program")) + FreeCADGui.addModule("PathScripts.PathComment") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathUtils +obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Comment") +PathScripts.PathComment.Comment(obj) +PathScripts.PathComment._ViewProviderComment(obj.ViewObject) + +PathUtils.addToProject(obj) +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Comment',CommandPathComment()) + + +FreeCAD.Console.PrintLog("Loading PathComment... done\n") diff --git a/src/Mod/Path/PathScripts/PathCompoundExtended.py b/src/Mod/Path/PathScripts/PathCompoundExtended.py new file mode 100644 index 0000000000..229b8db5e0 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathCompoundExtended.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui + +"""Path Compound Extended object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectCompoundExtended: + + + def __init__(self,obj): + obj.addProperty("App::PropertyString","Description", "Path",translate("PathCompoundExtended","An optional description of this compounded operation")) +# obj.addProperty("App::PropertySpeed", "FeedRate", "Path",translate("PathCompoundExtended","The feed rate of the paths in these compounded operations")) +# obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Path",translate("PathCompoundExtended","The spindle speed, in revolutions per minute, of the tool used in these compounded operations")) + obj.addProperty("App::PropertyLength","SafeHeight", "Path",translate("PathCompoundExtended","The safe height for this operation")) + obj.addProperty("App::PropertyLength","RetractHeight","Path",translate("PathCompoundExtended","The retract height, above top surface of part, between compounded operations inside clamping area")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + cmds = [] + for child in obj.Group: + if child.isDerivedFrom("Path::Feature"): + cmds.extend(child.Path.Commands) + if cmds: + path = Path.Path(cmds) + obj.Path = path + + +class ViewProviderCompoundExtended: + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def getIcon(self): + return ":/icons/Path-Compound.svg" + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + +class CommandCompoundExtended: + + + def GetResources(self): + return {'Pixmap' : 'Path-Compound', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathCompoundExtended","Compound"), + 'Accel': "P, C", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathCompoundExtended","Creates a Path Compound object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + + FreeCAD.ActiveDocument.openTransaction(translate("PathCompoundExtended","Create Compound")) + FreeCADGui.addModule("PathScripts.PathCompoundExtended") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathUtils +incl = [] +prjexists = False +sel = FreeCADGui.Selection.getSelection() +for s in sel: + if s.isDerivedFrom("Path::Feature"): + incl.append(s) + +obj = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython","Compound") +PathScripts.PathCompoundExtended.ObjectCompoundExtended(obj) +PathScripts.PathCompoundExtended.ViewProviderCompoundExtended(obj.ViewObject) +project = PathUtils.addToProject(obj) + +if incl: + children = [] + p = project.Group + + g = obj.Group + for child in incl: + p.remove(child) + children.append(FreeCAD.ActiveDocument.getObject(child.Name)) + project.Group = p + g.append(children) + obj.Group = children +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_CompoundExtended',CommandCompoundExtended()) + +FreeCAD.Console.PrintLog("Loading PathCompoundExtended... done\n") diff --git a/src/Mod/Path/PathScripts/PathCopy.py b/src/Mod/Path/PathScripts/PathCopy.py new file mode 100644 index 0000000000..07c0887f89 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathCopy.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui + +"""Path Copy object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectPathCopy: + + + def __init__(self,obj): + obj.addProperty("App::PropertyLink","Base","Path",translate("PathCopy","The path to be copied")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + if obj.Base: + if obj.Base.Path: + obj.Path = obj.Base.Path.copy() + + +class ViewProviderPathCopy: + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def getIcon(self): + return ":/icons/Path-Copy.svg" + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + +class CommandPathCopy: + + + def GetResources(self): + return {'Pixmap' : 'Path-Copy', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathCopy","Copy"), + 'Accel': "P, Y", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathCopy","Creates a linked copy of another path")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + + FreeCAD.ActiveDocument.openTransaction(translate("PathCopy","Create Copy")) + FreeCADGui.addModule("PathScripts.PathCopy") + + consolecode = ''' +import Path +import PathScripts +from PathScripts import PathCopy +selGood = True +# check that the selection contains exactly what we want +selection = FreeCADGui.Selection.getSelection() +proj = selection[0].InList[0] #get the group that the selectied object is inside + +if len(selection) != 1: + FreeCAD.Console.PrintError(translate("PathCopy","Please select one path object\\n")) + selGood = False + +if not selection[0].isDerivedFrom("Path::Feature"): + FreeCAD.Console.PrintError(translate("PathCopy","The selected object is not a path\\n")) + selGood = False + +if selGood: + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", str(selection[0].Name)+'_Copy') + PathScripts.PathCopy.ObjectPathCopy(obj) + PathScripts.PathCopy.ViewProviderPathCopy(obj.ViewObject) + obj.Base = FreeCAD.ActiveDocument.getObject(selection[0].Name) + +g = proj.Group +g.append(obj) +proj.Group = g + +FreeCAD.ActiveDocument.recompute() + +''' + + FreeCADGui.doCommand(consolecode) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Copy',CommandPathCopy()) + +FreeCAD.Console.PrintLog("Loading PathCopy... done\n") diff --git a/src/Mod/Path/PathScripts/PathDressup.py b/src/Mod/Path/PathScripts/PathDressup.py new file mode 100644 index 0000000000..d38a6c17ae --- /dev/null +++ b/src/Mod/Path/PathScripts/PathDressup.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui + +"""Path Dressup object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectDressup: + + + def __init__(self,obj): + obj.addProperty("App::PropertyLink","Base","Path",translate("PathDressup","The base path to modify")) + obj.addProperty("App::PropertyInteger","Position","Path",translate("PathDressup","The position of this dressup in the base path")) + obj.addProperty("Path::PropertyPath","Modification","Path",translate("PathDressup","The modification to be added")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + + if obj.Base: + if obj.Base.isDerivedFrom("Path::Feature"): + before = [] + after = [] + oldtool = None + if obj.Base.Path: + if obj.Base.Path.Commands: + # split the base path + before = obj.Base.Path.Commands[:obj.Position] + after = obj.Base.Path.Commands[obj.Position:] + # join everything + commands = before + obj.Modification.Commands + after + path = Path.Path(commands) + obj.Path = path + + +class ViewProviderDressup: + + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def claimChildren(self): + return [self.Object.Base] + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + +class CommandPathDressup: + + + def GetResources(self): + return {'Pixmap' : 'Path-Dressup', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathDressup","Dress-up"), + 'Accel': "P, S", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathDressup","Creates a Path Dess-up object from a selected path")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelection() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("PathDressup","Please select one path object\n")) + return + if not selection[0].isDerivedFrom("Path::Feature"): + FreeCAD.Console.PrintError(translate("PathDressup","The selected object is not a path\n")) + return + if selection[0].isDerivedFrom("Path::FeatureCompoundPython"): + FreeCAD.Console.PrintError(translate("PathDressup", "Please select a Path object")) + return + + # everything ok! + FreeCAD.ActiveDocument.openTransaction(translate("PathDressup","Create Dress-up")) + FreeCADGui.addModule("PathScripts.PathDressup") + FreeCADGui.addModule("PathScripts.PathUtils") + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Dressup")') + FreeCADGui.doCommand('PathScripts.PathDressup.ObjectDressup(obj)') + FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' + selection[0].Name) + FreeCADGui.doCommand('PathScripts.PathDressup.ViewProviderDressup(obj.ViewObject)') + FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Dressup',CommandPathDressup()) + +FreeCAD.Console.PrintLog("Loading PathDressup... done\n") diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py new file mode 100644 index 0000000000..10bc9c0c14 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -0,0 +1,199 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui +from PathScripts import PathUtils,PathSelection,PathProject + +"""Path Drilling object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectDrilling: + + + def __init__(self,obj): + #obj.addProperty("App::PropertyVector","StartPoint","Path",translate("PathProfile","The start position of the drilling")) + + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyVectorList","locations","Path","The drilling locations") + + obj.addProperty("App::PropertyLength", "PeckDepth", "Drilling", translate("PeckDepth","Incremental Drill depth before retracting to clear chips")) + #obj.PeckDepth = (0,0,1000,1) + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Drilling", translate("Clearance Height","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Drilling", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "RetractHeight", "Drilling", translate("Retract Height","The height where feed starts and height during retract tool when path is finished")) + obj.addProperty("App::PropertyLength", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) + + #obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate for horizontal moves")) #not needed for drilling + + obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) + obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) + + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) + obj.ToolNumber = (0,0,1000,1) + obj.setEditorMode('ToolNumber',1) #make this read only + + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + output = "G90 G98\n" + # rapid to first hole location, with spindle still retracted: + p0 = obj.locations[0] + output += "G0 X"+str(p0.x) + " Y" + str(p0.y)+ "\n" + # move tool to clearance plane + output += "G0 Z" + str(obj.ClearanceHeight.Value) + "\n" + if obj.PeckDepth.Value > 0: + cmd = "G83" + qword = " Q"+ str(obj.PeckDepth.Value) + else: + cmd = "G81" + qword = "" + + for p in obj.locations: + output += cmd + " X" + str(p.x) + " Y" + str(p.y) + " Z" + str(obj.FinalDepth.Value) + qword + " R" + str(obj.RetractHeight.Value) + " F" + str(obj.VertFeed.Value) + "\n" + + output += "G80\n" + + print output + path = Path.Path(output) + obj.Path = path + + # tie the toolnumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.ToolNumber= tl + + +class _ViewProviderDrill: + def __init__(self,obj): #mandatory +# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + obj.Proxy = self + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Drilling.svg" + +# def attach(self): #optional +# # this is executed on object creation and object load from file +# pass + + def onChanged(self,obj,prop): #optional + # this is executed when a property of the VIEW PROVIDER changes + pass + + def updateData(self,obj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + + + +class CommandPathDrilling: + + + def GetResources(self): + return {'Pixmap' : 'Path-Drilling', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathDrilling","Drilling"), + 'Accel': "P, D", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathDrilling","Creates a Path Drilling object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + import Path + import Part + + from PathScripts import PathUtils,PathDrilling,PathProject + prjexists = False + selection = FreeCADGui.Selection.getSelectionEx() + + if not selection: + return + # if everything is ok, execute and register the transaction in the undo/redo stack + FreeCAD.ActiveDocument.openTransaction(translate("PathDrilling","Create Drilling")) + FreeCADGui.addModule("PathScripts.PathDrilling") + + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Drilling") + PathDrilling.ObjectDrilling(obj) + + myList = obj.locations + for sub in selection: + for point in sub.SubObjects: + if isinstance(point,Part.Vertex): + myList.append(FreeCAD.Vector(point.X, point.Y, point.Z)) + if isinstance(point,Part.Edge): + if isinstance(point.Curve,Part.Circle): + center = point.Curve.Center + myList.append(FreeCAD.Vector(center.x,center.y,center.z)) + + obj.locations = myList + + PathDrilling._ViewProviderDrill(obj.ViewObject) +# obj.ViewObject.Proxy = 0 + obj.Active = True + + project = PathUtils.addToProject(obj) + + tl = PathUtils.changeTool(obj,project) + if tl: + obj.ToolNumber = tl + + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Drilling',CommandPathDrilling()) + +FreeCAD.Console.PrintLog("Loading PathDrilling... done\n") diff --git a/src/Mod/Path/PathScripts/PathFixture.py b/src/Mod/Path/PathScripts/PathFixture.py new file mode 100644 index 0000000000..a65cd3fb70 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathFixture.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used to create CNC machine fixture offsets such as G54,G55, etc...''' + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class Fixture: + def __init__(self,obj): + obj.addProperty("App::PropertyEnumeration", "Fixture", "Fixture Parameters", translate("Fixture Offset", "Fixture Offset Number")) + obj.Fixture=['G53','G54','G55','G56','G57','G58','G59','G59.1', 'G59.2', 'G59.3', 'G59.4', 'G59.5','G59.6','G59.7', 'G59.8', 'G59.9'] + obj.addProperty("App::PropertyBool","Active","Sequence Parameters",translate("Active","Make False, to prevent operation from generating code")) + + obj.Proxy = self + + def execute(self,obj): + fixlist = ['G53','G54','G55','G56','G57','G58','G59','G59.1', 'G59.2', 'G59.3', 'G59.4', 'G59.5','G59.6','G59.7', 'G59.8', 'G59.9'] + fixture=fixlist.index(obj.Fixture) + obj.Path = Path.Path(str(obj.Fixture)) + obj.Label = "Fixture"+str(fixture) + if obj.Active: + obj.Path = Path.Path(str(obj.Fixture)) + obj.ViewObject.Visibility = True + else: + obj.Path = Path.Path("(inactive operation)") + obj.ViewObject.Visibility = False + + +class _ViewProviderFixture: + + def __init__(self,vobj): #mandatory +# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Datums.svg" + +# def attach(self): #optional +# # this is executed on object creation and object load from file +# pass + + def onChanged(self,vobj,prop): #optional + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def updateData(self,vobj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + + +class CommandPathFixture: + def GetResources(self): + return {'Pixmap' : 'Path-Datums', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFixture","Fixture"), + 'Accel': "P, F", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFixture","Creates a Fixture Offset object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathFixture","Create a Fixture Offset")) + FreeCADGui.addModule("PathScripts.PathFixture") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathUtils +prjexists = False +obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Fixture") +PathScripts.PathFixture.Fixture(obj) +obj.Active = True +PathScripts.PathFixture._ViewProviderFixture(obj.ViewObject) + +PathUtils.addToProject(obj) + +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Fixture',CommandPathFixture()) + + +FreeCAD.Console.PrintLog("Loading PathFixture... done\n") diff --git a/src/Mod/Path/PathScripts/PathFromShape.py b/src/Mod/Path/PathScripts/PathFromShape.py new file mode 100644 index 0000000000..21518197de --- /dev/null +++ b/src/Mod/Path/PathScripts/PathFromShape.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used to make GCode from FreeCAD shapes - Wires and Edges/Curves ''' + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class FromShape: + def __init__(self,obj): + obj.addProperty("App::PropertyLink","Base","Shape",translate("Shape Object","The base Shape of this toolpath")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + pass + + +class _ViewProviderFromShape: + + def __init__(self,vobj): #mandatory + vobj.Proxy = self + + def attach(self, vobj): + self.Object = vobj.Object + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Shape.svg" + + +class CommandFromShape: + def GetResources(self): + return {'Pixmap' : 'Path-Shape', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFromShape","Gcode from a Shape"), + 'Accel': "P, S", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFromShape","Creates GCode from a FreeCAD wire/curve")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathFromShape","Create GCode from a wire/curve")) + FreeCADGui.addModule("PathScripts.PathFromShape") + + consolecode = ''' +import Path +import PathScripts +from PathScripts import PathFromShape +from PathScripts import PathUtils + +obj = FreeCAD.activeDocument().addObject('Path::FeatureShape','PathShape') +obj.Shape= FreeCAD.activeDocument().Rectangle.Shape.copy() + +PathUtils.addToProject(obj) + +PathScripts.PathFromShape._ViewProviderFromShape(obj.ViewObject) +App.ActiveDocument.recompute() +''' + FreeCADGui.doCommand(consolecode) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_FromShape',CommandFromShape()) + +FreeCAD.Console.PrintLog("Loading PathFromShape... done\n") diff --git a/src/Mod/Path/PathScripts/PathHop.py b/src/Mod/Path/PathScripts/PathHop.py new file mode 100644 index 0000000000..7d90b2af97 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathHop.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui + +"""Path Hop object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectHop: + + + def __init__(self,obj): + obj.addProperty("App::PropertyLink","NextObject","Path",translate("PathHop","The object to be reached by this hop")) + obj.addProperty("App::PropertyDistance","HopHeight","Path",translate("PathHop","The Z height of the hop")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + nextpoint = FreeCAD.Vector() + if obj.NextObject: + if obj.NextObject.isDerivedFrom("Path::Feature"): + # look for the first position of the next path + for c in obj.NextObject.Path.Commands: + if c.Name in ["G0","G00","G1","G01","G2","G02","G3","G03"]: + nextpoint = c.Placement.Base + break + + # absolute coords, millimeters, cancel offsets + output = "G90\nG21\nG40\n" + + # go up to the given height + output += "G0 Z" + str(obj.HopHeight.Value) + "\n" + + # go horizontally to the position of nextpoint + output += "G0 X" + str(nextpoint.x) + " Y" + str(nextpoint.y) + "\n" + + #print output + path = Path.Path(output) + obj.Path = path + + + +class ViewProviderPathHop: + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def getIcon(self): + return ":/icons/Path-Hop.svg" + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + + +class CommandPathHop: + + + def GetResources(self): + return {'Pixmap' : 'Path-Hop', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathHop","Hop"), + 'Accel': "P, H", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathHop","Creates a Path Hop object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelection() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("PathHop","Please select one path object\n")) + return + if not selection[0].isDerivedFrom("Path::Feature"): + FreeCAD.Console.PrintError(translate("PathHop","The selected object is not a path\n")) + return + + FreeCAD.ActiveDocument.openTransaction(translate("PathHop","Create Hop")) + FreeCADGui.addModule("PathScripts.PathHop") + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Hop")') + FreeCADGui.doCommand('PathScripts.PathHop.ObjectHop(obj)') + FreeCADGui.doCommand('PathScripts.PathHop.ViewProviderPathHop(obj.ViewObject)') + FreeCADGui.doCommand('obj.NextObject = FreeCAD.ActiveDocument.' + selection[0].Name) + FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Hop',CommandPathHop()) + +FreeCAD.Console.PrintLog("Loading PathHop... done\n") diff --git a/src/Mod/Path/PathScripts/PathKurve.py b/src/Mod/Path/PathScripts/PathKurve.py new file mode 100644 index 0000000000..0ffb04a34b --- /dev/null +++ b/src/Mod/Path/PathScripts/PathKurve.py @@ -0,0 +1,290 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +'''PathKurve - Path Profile operation using libarea (created by Dan Heeks) for making simple CNC paths. +libarea, originally from HeeksCNC project must be present for this to work.''' + + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject,PathUtils,PathKurveUtils,PathSelection +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class PathProfile: + def __init__(self,obj): + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyLinkSub","StartPoint", "Path", translate("Start Point","Linked Start Point of Profile")) + obj.addProperty("App::PropertyLinkSub","EndPoint", "Path", translate("End Point","Linked End Point of Profile")) + obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString","Comment","Path",translate("Comment","An optional comment for this profile")) + + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) + obj.ToolNumber = (0,0,1000,1) + obj.setEditorMode('ToolNumber',1) #make this read only + #Depth Properties + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Clearance Height","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyLength", "StepDown", "Depth", translate("StepDown","Incremental Step Down of Tool")) +# obj.addProperty("App::PropertyBool","UseStartDepth","Depth",translate("Use Start Depth","make True, if manually specifying a Start Start Depth")) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Start Depth","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("Retract Height","The height desired to retract tool when path is finished")) + + #Feed Properties + obj.addProperty("App::PropertyLength", "VertFeed", "Feed",translate("Vert Feed","Feed rate (in units per minute) for vertical moves in Z")) + obj.addProperty("App::PropertyLength", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate (in units per minute) for horizontal moves")) + + #Profile Properties + obj.addProperty("App::PropertyEnumeration", "Side", "Profile", translate("Side","Side of edge that tool should cut")) + obj.Side = ['left','right','on'] #side of profile that cutter is on in relation to direction of profile + obj.addProperty("App::PropertyEnumeration", "Direction", "Profile",translate("Direction", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) + obj.Direction = ['CW','CCW'] #this is the direction that the profile runs + obj.addProperty("App::PropertyBool","UseComp","Profile",translate("Use Cutter Comp","make True, if using Cutter Radius Compensation")) + obj.addProperty("App::PropertyIntegerList","Edgelist","Profile",translate("Edge List", "List of edges selected")) + obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile",translate("OffsetExtra","Extra value to stay away from final profile- good for roughing toolpath")) +# obj.addProperty("App::PropertyLength", "SegLen", "Profile",translate("Seg Len","Tesselation value for tool paths made from beziers, bsplines, and ellipses")) + +# #Start Point Properties + obj.addProperty("App::PropertyString","StartPtName","Profile",translate("Start Point","The name of the start point of this path")) + obj.addProperty("App::PropertyBool","UseStartPt","Profile",translate("Use Start Point","Make True, if specifying a Start Point")) +# obj.addProperty("App::PropertyLength", "ExtendAtStart", "Profile", translate("extend at start", "extra length of tool path before start of part edge")) +# obj.addProperty("App::PropertyLength", "LeadInLineLen", "Profile", translate("lead in length","length of straight segment of toolpath that comes in at angle to first part edge")) + +# #End Point Properties + obj.addProperty("App::PropertyString","EndPtName","Profile",translate("End Point","The name of the end point of this path")) + obj.addProperty("App::PropertyBool","UseEndPt","Profile",translate("Use End Point","Make True, if specifying an End Point")) +# obj.addProperty("App::PropertyLength", "ExtendAtEnd", "Profile", translate("extend at end","extra length of tool path after end of part edge")) +# obj.addProperty("App::PropertyLength", "LeadOutLineLen", "Profile", translate("lead_out_line_len","length of straight segment of toolpath that comes in at angle to last edge selected")) + +# obj.addProperty("App::PropertyDistance", "RollRadius", "Profile", translate("Roll Radius","Radius at start and end")) + + obj.Proxy = self + + + + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + + if obj.Base: + + # tie the toolnumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.ToolNumber= tl + + tool = PathUtils.getTool(obj,obj.ToolNumber) + if tool: + self.radius = tool.Diameter/2 + else: + # temporary value,in case we don't have any tools defined already + self.radius = 0.25 +# self.radius = 0.25 + self.clearance = obj.ClearanceHeight.Value + self.step_down=obj.StepDown.Value + self.start_depth=obj.StartDepth.Value + self.final_depth=obj.FinalDepth.Value + self.rapid_safety_space=obj.RetractHeight.Value + self.side=obj.Side + self.offset_extra=obj.OffsetExtra.Value + self.use_CRC=obj.UseComp + self.vf=obj.VertFeed.Value + self.hf=obj.HorizFeed.Value + + + edgelist = [] + + if obj.StartPtName and obj.UseStartPt: + self.startpt = FreeCAD.ActiveDocument.getObject(obj.StartPtName).Shape + else: + self.startpt = None + + if obj.EndPtName and obj.UseEndPt: + self.endpt = FreeCAD.ActiveDocument.getObject(obj.EndPtName).Shape + else: + self.endpt = None + + for e in obj.Edgelist: + edgelist.append(FreeCAD.ActiveDocument.getObject(obj.Base[0].Name).Shape.Edges[e-1]) + + output=PathKurveUtils.makePath(edgelist,self.side,self.radius,self.vf,self.hf,self.offset_extra, \ + self.rapid_safety_space,self.clearance,self.start_depth,self.step_down, \ + self.final_depth,self.use_CRC,obj.Direction,self.startpt,self.endpt) + + if obj.Active: + path = Path.Path(output) + obj.Path = path + obj.ViewObject.Visibility = True + + else: + path = Path.Path("(inactive operation)") + obj.Path = path + obj.ViewObject.Visibility = False + + + +class _ViewProviderKurve: + + def __init__(self,vobj): #mandatory + vobj.Proxy = self + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Kurve.svg" + + +class CommandPathKurve: + def GetResources(self): + return {'Pixmap' : 'Path-Kurve', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathProfile","Profile"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathProfile","Creates a Path Profile object from selected edges, using libarea for offset algorithm")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathKurve","Create a Profile operation using libarea")) + FreeCADGui.addModule("PathScripts.PathKurve") + snippet = ''' +import Path +from PathScripts import PathSelection,PathProject,PathUtils +import area + +def profileop(): + selection = PathSelection.multiSelect() + + if not selection: + FreeCAD.Console.PrintError('please select some edges\\n') + + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Profile") + PathScripts.PathKurve.PathProfile(obj) + + obj.Active = True + PathScripts.PathKurve._ViewProviderKurve(obj.ViewObject) + + obj.Base = (FreeCAD.ActiveDocument.getObject(selection['objname'])) + + elist = [] + for e in selection['edgenames']: + elist.append(eval(e.lstrip('Edge'))) + + obj.Edgelist = elist + if selection['pointnames']: + FreeCAD.Console.PrintMessage('There are points selected.\\n') + if len(selection['pointnames'])>1: + obj.StartPtName = selection['pointnames'][0] + obj.StartPoint= FreeCAD.ActiveDocument.getObject(obj.StartPtName) + + obj.EndPtName = selection['pointnames'][-1] + obj.EndPoint=FreeCAD.ActiveDocument.getObject(obj.EndPtName) + + else: + obj.StartPtName = selection['pointnames'][0] + obj.StartPoint= FreeCAD.ActiveDocument.getObject(obj.StartPtName) + + obj.ClearanceHeight = 2.0 + obj.StepDown = 1.0 + obj.StartDepth=0.0 + obj.FinalDepth=-1.0 + obj.RetractHeight = 5.0 + obj.Side = 'left' + obj.OffsetExtra = 0.0 + if selection['clockwise']: + obj.Direction = 'CW' + else: + obj.Direction = 'CCW' + obj.UseComp = False + + project = PathUtils.addToProject(obj) + + tl = PathUtils.changeTool(obj,project) + + if tl: + obj.ToolNumber = tl + + +from PathScripts import PathProject,PathUtils,PathKurve, PathKurveUtils,PathSelection +try: + import area + +except: + FreeCAD.Console.PrintError('libarea needs to be installed for this command to work\\n') +profileop() +''' + + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Kurve',CommandPathKurve()) + +FreeCAD.Console.PrintLog("Loading PathKurve... done\n") + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/PathScripts/PathKurveUtils.py b/src/Mod/Path/PathScripts/PathKurveUtils.py new file mode 100644 index 0000000000..9acc77ebda --- /dev/null +++ b/src/Mod/Path/PathScripts/PathKurveUtils.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +'''PathKurveUtils - functions needed for using libarea (created by Dan Heeks) for making simple CNC profile paths ''' +import FreeCAD +from FreeCAD import Vector +import FreeCADGui as Gui +import Part +import DraftGeomUtils,DraftVecUtils +from DraftGeomUtils import geomType +import math +import area +import Path +from PathScripts import PathUtils +import PathSelection + +def makeAreaVertex(seg): + if seg.ShapeType =='Edge': + if isinstance(seg.Curve,Part.Circle): + segtype = int(seg.Curve.Axis.z) #1=ccw arc,-1=cw arc + vertex = area.Vertex(segtype, area.Point(seg.valueAt(seg.LastParameter)[0],seg.valueAt(seg.LastParameter)[1]), area.Point(seg.Curve.Center.x, seg.Curve.Center.y)) + elif isinstance(seg.Curve,Part.Line): + point1 = seg.valueAt(seg.FirstParameter)[0],seg.valueAt(seg.FirstParameter)[1] + point2 = seg.valueAt(seg.LastParameter)[0],seg.valueAt(seg.LastParameter)[1] + segtype = 0 #0=line + vertex = area.Point(seg.valueAt(seg.LastParameter)[0],seg.valueAt(seg.LastParameter)[1]) + else: + pass + return vertex + +def makeAreaCurve(edges,direction,startpt=None,endpt=None): + curveobj = area.Curve() + + cleanededges = PathUtils.cleanedges(edges, 0.01) + + #sort the edges + vlist,edgestart,common = PathSelection.Sort2Edges([cleanededges[0],cleanededges[1]]) + + if cleanededges[0].valueAt(cleanededges[0].FirstParameter)<>edgestart: + firstedge=PathUtils.reverseEdge(cleanededges[0]) + else: + firstedge=cleanededges[0] + + edgelist=[] + edgelist.append(firstedge) + + #get start and end points of each edge aligned + for e in cleanededges[1:]: + if DraftVecUtils.equals(common,e.valueAt(e.FirstParameter)): + edgelist.append(e) + common= e.valueAt(e.LastParameter) + else: + newedge = PathUtils.reverseEdge(e) + common= newedge.valueAt(newedge.LastParameter) + edgelist.append(newedge) + + curveobj.append(area.Point(edgestart.x,edgestart.y)) + + +# seglist =[] +# if direction=='CW': +# edgelist.reverse() +# for e in edgelist: +# seglist.append(PathUtils.reverseEdge(e)) #swap end points on every segment +# else: +# for e in edgelist: +# seglist.append(e) + + for s in edgelist: + curveobj.append(makeAreaVertex(s)) + + if startpt: + # future nearest point code yet to be worked out -fixme +# v1 = Vector(startpt.X,startpt.Y,startpt.Z) +# perppoint1 = DraftGeomUtils.findPerpendicular(v1,firstedge) +# perppoint1 = DraftGeomUtils.findDistance(v1,firstedge) +# if perppoint1: +# curveobj.ChangeStart(area.Point(perppoint1[0].x,perppoint1[0].y)) +# else: +# curveobj.ChangeStart(area.Point(startpt.X,startpt.Y)) + curveobj.ChangeStart(area.Point(startpt.X,startpt.Y)) + if endpt: + # future nearest point code yet to be worked out -fixme +# v2 = Vector(endpt.X,endpt.Y,endpt.Z) +# perppoint2 = DraftGeomUtils.findPerpendicular(v2,lastedge) +# if perppoint2: +# curveobj.ChangeEnd(area.Point(perppoint2[0].x,perppoint2[0].y)) +# else: +# curveobj.ChangeEnd(area.Point(endpt.X,endpt.Y)) + curveobj.ChangeEnd(area.Point(endpt.X,endpt.Y)) + + if direction == 'CW': + curveobj.Reverse() + + return curveobj + + +# profile command, +# side_of_line should be 'left' or 'right' or 'on' +def profile(curve,side_of_line,radius=1.0,vertfeed=0.0,horizfeed=0.0,offset_extra=0.0, \ + rapid_safety_space=None,clearance=None,start_depth=None,stepdown=None, \ + final_depth=None,use_CRC=False, \ + roll_on=None,roll_off=None,roll_start=False,roll_end=True,roll_radius=None, \ + roll_start_pt=None,roll_end_pt=None): + + output = "" + offset_curve = area.Curve(curve) + if offset_curve.getNumVertices() <= 1: + raise Exception,"Sketch has no elements!" + if side_of_line == "on": + use_CRC =False + + elif (side_of_line == "left") or (side_of_line == "right"): + # get tool radius plus little bit of extra offset, if needed to clean up profile a little more + offset = radius + offset_extra + if side_of_line == 'left': + offset_curve.Offset(offset) + + else: + offset_curve.Offset(-offset) + + if offset_curve == False: + raise Exception, "couldn't offset kurve " + str(offset_curve) + else: + raise Exception,"Side must be 'left','right', or 'on'" + +#=============================================================================== +# #roll_on roll_off section +# roll_on_curve = area.Curve() +# if offset_curve.getNumVertices() <= 1: return +# first_span = offset_curve.GetFirstSpan() +# if roll_on == None: +# rollstart = first_span.p +# elif roll_on == 'auto': +# if roll_radius < 0.0000000001: +# rollstart = first_span.p +# v = first_span.GetVector(0.0) +# if direction == 'right': +# off_v = area.Point(v.y, -v.x) +# else: +# off_v = area.Point(-v.y, v.x) +# rollstart = first_span.p + off_v * roll_radius +# else: +# rollstart = roll_on +# +# rvertex = area.Vertex(first_span.p) +# +# if first_span.p == rollstart: +# rvertex.type = 0 +# else: +# v = first_span.GetVector(0.0) # get start direction +# rvertex.c, rvertex.type = area.TangentialArc(first_span.p, rollstart, -v) +# rvertex.type = -rvertex.type # because TangentialArc was used in reverse +# # add a start roll on point +# roll_on_curve.append(rollstart) +# +# # add the roll on arc +# roll_on_curve.append(rvertex) +# #end of roll_on roll_off section +#=============================================================================== + + # do multiple depths + layer_count = int((start_depth - final_depth) / stepdown) + if layer_count * stepdown + 0.00001 < start_depth - final_depth: + layer_count += 1 + current_start_depth = start_depth + prev_depth = start_depth + for i in range(1, layer_count+1): + if i == layer_count: + depth = final_depth + else: + depth = start_depth - i * stepdown + mat_depth = prev_depth + start_z = mat_depth + #first move + output += "G0 X"+str(PathUtils.fmt(offset_curve.GetFirstSpan().p.x))+\ + " Y"+str(PathUtils.fmt(offset_curve.GetFirstSpan().p.y))+\ + " Z"+str(PathUtils.fmt(mat_depth + rapid_safety_space))+"\n" + # feed down to depth + mat_depth = depth + if start_z > mat_depth: + mat_depth = start_z + # feed down in Z + output += "G1 X"+str(PathUtils.fmt(offset_curve.GetFirstSpan().p.x))+\ + " Y"+str(PathUtils.fmt(offset_curve.GetFirstSpan().p.y))+" Z"+str(PathUtils.fmt(depth))+\ + " F"+str(PathUtils.fmt(vertfeed))+"\n" + if use_CRC: + if side_of_line == 'left': + output +="G41"+"\n" + else: + output +="G42"+"\n" + # cut the main kurve + current_perim = 0.0 + lastx=offset_curve.GetFirstSpan().p.x + lasty=offset_curve.GetFirstSpan().p.y + for span in offset_curve.GetSpans(): + current_perim += span.Length() + if span.v.type == 0:#line + #feed(span.v.p.x, span.v.p.y, ez) + output +="G1 X"+str(PathUtils.fmt(span.v.p.x))+" Y"+str(PathUtils.fmt(span.v.p.y))+\ + " Z"+str(PathUtils.fmt(depth))+" F"+str(PathUtils.fmt(horizfeed))+"\n" + lastx = span.v.p.x + lasty = span.v.p.y + elif (span.v.type == 1) or (span.v.type == -1): + if span.v.type == 1:# anti-clockwise arc + command = 'G3' + elif span.v.type == -1:#clockwise arc + command = 'G2' + arc_I= span.v.c.x-lastx + arc_J= span.v.c.y-lasty + output +=command +"X"+str(PathUtils.fmt(span.v.p.x))+" Y"+ str(PathUtils.fmt(span.v.p.y))#+" Z"+ str(PathUtils.fmt(depth)) + output +=" I"+str(PathUtils.fmt(arc_I))+ " J"+str(PathUtils.fmt(arc_J))+" F"+str(PathUtils.fmt(horizfeed))+'\n'#" K"+str(PathUtils.fmt(depth)) +"\n" + lastx = span.v.p.x + lasty = span.v.p.y + else: + raise Exception, "valid geometry identifier needed" + if use_CRC: + #end_CRC() + output +="G40"+"\n" + # rapid up to the clearance height + output +="G0 Z"+str(PathUtils.fmt(clearance))+"\n" + + del offset_curve + + return output + +def makePath(edges,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC,direction,startpt=None,endpt=None): + + curve = makeAreaCurve(edges,direction,startpt, endpt) + if direction == 'CW': + curve.Reverse() + path = profile(curve,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC) + del curve + return path + + + + diff --git a/src/Mod/Path/PathScripts/PathLoadTool.py b/src/Mod/Path/PathScripts/PathLoadTool.py new file mode 100644 index 0000000000..ec1a424780 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathLoadTool.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used for CNC machine to load cutting Tool ie M6T3''' + +import FreeCAD,FreeCADGui,Path,PathGui +import PathScripts, PathUtils +from PathScripts import PathProject +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class LoadTool: + def __init__(self,obj): + obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber","Tool", translate( "Tool Number", "The active tool")) + obj.ToolNumber = (0,0,10000,1) + obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Tool", translate("Spindle Speed","The speed of the cutting spindle in RPM")) + obj.addProperty("App::PropertyEnumeration", "SpindleDir", "Tool", translate("Spindle Dir","Direction of spindle rotation")) + obj.SpindleDir = ['Forward','Reverse'] + obj.Proxy = self + mode = 2 + obj.setEditorMode('Placement',mode) + + + + def execute(self,obj): + commands = "" + commands = 'M6T'+str(obj.ToolNumber)+'\n' + + if obj.SpindleDir =='Forward': + commands +='M3S'+str(obj.SpindleSpeed)+'\n' + + else: + commands +='M4S'+str(obj.SpindleSpeed)+'\n' + + obj.Path = Path.Path(commands) + obj.Label = "Tool"+str(obj.ToolNumber) + + + def onChanged(self,obj,prop): + mode = 2 + obj.setEditorMode('Placement',mode) + if prop == "ToolNumber": + proj = PathUtils.findProj() + for g in proj.Group: + if not(isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool)): + g.touch() + + +class _ViewProviderLoadTool: + def __init__(self,vobj): #mandatory + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-LoadTool.svg" + + def onChanged(self,vobj,prop): #optional + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def updateData(self,vobj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + +class CommandPathLoadTool: + def GetResources(self): + return {'Pixmap' : 'Path-LoadTool', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathLoadTool","Tool Number to Load"), + 'Accel': "P, T", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathLoadTool","Tool Number to Load")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("Current Tool","Tool Number to Load")) + FreeCADGui.addModule("PathScripts.PathLoadTool") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathProject, PathUtils +prjexists = False +obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Tool") +PathScripts.PathLoadTool.LoadTool(obj) + +PathScripts.PathLoadTool._ViewProviderLoadTool(obj.ViewObject) + +PathUtils.addToProject(obj) +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_LoadTool', CommandPathLoadTool()) + +FreeCAD.Console.PrintLog("Loading PathLoadTool... done\n") + + + + + diff --git a/src/Mod/Path/PathScripts/PathMachine.py b/src/Mod/Path/PathScripts/PathMachine.py new file mode 100644 index 0000000000..225601205f --- /dev/null +++ b/src/Mod/Path/PathScripts/PathMachine.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' A CNC machine object to define how code is posted ''' + +import FreeCAD,FreeCADGui,Path,PathGui +import PathScripts +from PathScripts import PathProject, PathUtils +from PySide import QtCore,QtGui +import os, sys + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class Machine: + def __init__(self,obj): + + obj.addProperty("App::PropertyString", "MachineName","Base",translate("Machine Name","Name of the Machine that will use the CNC program")) + + obj.addProperty("App::PropertyFile", "PostProcessor", "CodeOutput", translate("Post Processor","Select the Post Processor file for this machine")) + obj.addProperty("App::PropertyEnumeration", "MachineUnits","CodeOutput", translate( "Machine Units", "Units that the machine works in, ie Metric or Inch")) + obj.MachineUnits=['Metric', 'Inch'] + + obj.addProperty("Path::PropertyTooltable","Tooltable", "Base",translate("Tool Table","The tooltable used for this CNC program")) + + obj.addProperty("App::PropertyDistance", "X_Max", "Limits", translate("X Maximum Limit","The Maximum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Y_Max", "Limits", translate("Y Maximum Limit","The Maximum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Z_Max", "Limits", translate("Y Maximum Limit","The Maximum distance in X the machine can travel")) + + obj.addProperty("App::PropertyDistance", "X_Min", "Limits", translate("X Minimum Limit","The Minimum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Y_Min", "Limits", translate("Y Minimum Limit","The Minimum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Z_Min", "Limits", translate("Y Minimum Limit","The Minimum distance in X the machine can travel")) + + obj.addProperty("App::PropertyDistance", "X", "HomePosition", translate("X Home Position","Home position of machine, in X (mainly for visualization)")) + obj.addProperty("App::PropertyDistance", "Y", "HomePosition", translate("Y Home Position","Home position of machine, in Y (mainly for visualization)")) + obj.addProperty("App::PropertyDistance", "Z", "HomePosition", translate("Z Home Position","Home position of machine, in Z (mainly for visualization)")) + + obj.Proxy = self + mode = 2 + obj.setEditorMode('Placement',mode) + + def execute(self,obj): + obj.Label = "Machine_"+str(obj.MachineName) + gcode = 'G0 X'+str(obj.X.Value)+' Y'+str(obj.Y.Value)+' Z'+str(obj.Z.Value) #need to filter this path out in post- only for visualization + obj.Path = Path.Path(gcode) + + def onChanged(self,obj,prop): + mode = 2 + obj.setEditorMode('Placement',mode) + + if prop == "PostProcessor": + sys.path.append(os.path.split(obj.PostProcessor)[0]) + lessextn = os.path.splitext(obj.PostProcessor)[0] + postname = os.path.split(lessextn)[1] + + exec "import %s as current_post" % postname + if hasattr (current_post, "UNITS"): + if current_post.UNITS == "G21": + obj.MachineUnits = "Metric" + else: + obj.MachineUnits = "Inch" + if hasattr (current_post, "MACHINE_NAME"): obj.MachineName = current_post.MACHINE_NAME + + if hasattr (current_post, "CORNER_MAX"): + obj.X_Max = current_post.CORNER_MAX['x'] + obj.Y_Max = current_post.CORNER_MAX['y'] + obj.Z_Max = current_post.CORNER_MAX['z'] + + if hasattr (current_post, "CORNER_MIN"): + obj.X_Min = current_post.CORNER_MIN['x'] + obj.Y_Min = current_post.CORNER_MIN['y'] + obj.Z_Min = current_post.CORNER_MIN['z'] + + if prop == "Tooltable": + proj = PathUtils.findProj() + for g in proj.Group: + if not(isinstance(g.Proxy, PathScripts.PathMachine.Machine)): + g.touch() + + + +class _ViewProviderMachine: + def __init__(self,vobj): + vobj.Proxy = self + vobj.addProperty("App::PropertyBool","ShowLimits","Path",translate("ShowMinMaxTravel","Switch the machine max and minimum travel bounding box on/off")) + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',0) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Machine.svg" + + def attach(self,vobj): + from pivy import coin + self.extentsBox = coin.SoSeparator() + vobj.RootNode.addChild(self.extentsBox) + + def onChanged(self,vobj,prop): + + if prop == "ShowLimits": + self.extentsBox.removeAllChildren() + if vobj.ShowLimits and hasattr(vobj,"Object"): + from pivy import coin + parent = coin.SoType.fromName("SoSkipBoundingGroup").createInstance() + self.extentsBox.addChild(parent) + # set pattern + pattern = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part").GetInt("GridLinePattern",0x0f0f) + defStyle = coin.SoDrawStyle() + defStyle.lineWidth = 1 + defStyle.linePattern = pattern + parent.addChild(defStyle) + # set color + c = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path").GetUnsigned("DefaultExtentsColor",3418866943) + r = float((c>>24)&0xFF)/255.0 + g = float((c>>16)&0xFF)/255.0 + b = float((c>>8)&0xFF)/255.0 + color = coin.SoBaseColor() + parent.addChild(color) + # set boundbox + extents = coin.SoType.fromName("SoFCBoundingBox").createInstance() + extents.coordsOn.setValue(False) + extents.dimensionsOn.setValue(False) + + XMax, YMax, ZMax =vobj.Object.X_Max.Value , vobj.Object.Y_Max.Value , vobj.Object.Z_Max.Value + XMin, YMin, ZMin =vobj.Object.X_Min.Value , vobj.Object.Y_Min.Value , vobj.Object.Z_Min.Value + UnitParams = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") + + extents.minBounds.setValue(XMax, YMax, ZMax) + extents.maxBounds.setValue(XMin, YMin, ZMin) + + parent.addChild(extents) + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',0) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + + + + + def updateData(self,vobj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + +class CommandPathMachine: + def GetResources(self): + return {'Pixmap' : 'Path-Machine', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathMachine","Machine Object"), + 'Accel': "P, M", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathMachine","Create a Machine object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathMachine","Create a Machine object")) + CommandPathMachine.Create() + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + @staticmethod + def Create(): + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Machine") + Machine(obj) + _ViewProviderMachine(obj.ViewObject) + + PathUtils.addToProject(obj) + + UnitParams = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") + if UnitParams.GetInt('UserSchema') == 0: + obj.MachineUnits = 'Metric' + #metric mode + else: + obj.MachineUnits = 'Inch' + + obj.ViewObject.ShowFirstRapid = False + return obj + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Machine',CommandPathMachine()) + + +FreeCAD.Console.PrintLog("Loading PathMachine... done\n") + + diff --git a/src/Mod/Path/PathScripts/PathPlane.py b/src/Mod/Path/PathScripts/PathPlane.py new file mode 100644 index 0000000000..94a5009e39 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathPlane.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used for CNC machine plane selection G17,G18,G19 ''' + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class Plane: + def __init__(self,obj): + obj.addProperty("App::PropertyEnumeration", "SelectionPlane","Plane", translate( "Selection Plane", "Orientation plane of CNC path")) + obj.SelectionPlane=['XY', 'XZ', 'YZ'] + obj.addProperty("App::PropertyBool","Active","Sequence Parameters",translate("Active","Make False, to prevent operation from generating code")) + obj.Proxy = self + + def execute(self,obj): + clonelist = ['XY', 'XZ', 'YZ'] + cindx = clonelist.index(str(obj.SelectionPlane)) + pathlist = ['G17', 'G18', 'G19'] +# obj.Path = Path.Path(pathlist[cindx]) + labelindx = clonelist.index(obj.SelectionPlane)+1 + obj.Label = "Plane"+str(labelindx) + if obj.Active: + obj.Path = Path.Path(pathlist[cindx]) + obj.ViewObject.Visibility = True + else: + obj.Path = Path.Path("(inactive operation)") + obj.ViewObject.Visibility = False + + +class _ViewProviderPlane: + def __init__(self,vobj): #mandatory + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Plane.svg" + + def onChanged(self,vobj,prop): #optional + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def updateData(self,vobj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + +class CommandPathPlane: + def GetResources(self): + return {'Pixmap' : 'Path-Plane', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPlane","Selection Plane"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPlane","Create a Selection Plane object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathPlane","Create a Selection Plane object")) + FreeCADGui.addModule("PathScripts.PathPlane") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathUtils +prjexists = False +obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Plane") +PathScripts.PathPlane.Plane(obj) +obj.Active = True +PathScripts.PathPlane._ViewProviderPlane(obj.ViewObject) +PathUtils.addToProject(obj) + +''' + + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Plane',CommandPathPlane()) + + +FreeCAD.Console.PrintLog("Loading PathPlane... done\n") + + diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py new file mode 100644 index 0000000000..7b65223869 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -0,0 +1,325 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui +from PathScripts import PathUtils + +"""Path Pocket object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +def frange(start, stop, step, finish): + x = [] + curdepth = start + if step == 0: + return x + # do the base cuts until finishing round + while curdepth >= stop + step + finish: + curdepth = curdepth - step + if curdepth <= stop + finish: + curdepth = stop + finish + x.append(curdepth) + + # we might have to do a last pass or else finish round might be too far away + if curdepth - stop > finish: + x.append(stop + finish) + + # do the the finishing round + if curdepth >= stop: + curdepth = stop + x.append(curdepth) + + # Why this? +# if start >= stop: +# start = stop +# x.append (start) + + return x + +class ObjectPocket: + + + def __init__(self,obj): + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("PathProject","The base geometry of this object")) + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool", + translate("PathProfile","The tool number in use")) + obj.ToolNumber = (0, 0, 1000, 0) + + + obj.addProperty("App::PropertyFloat", "ClearanceHeight", "Pocket", translate("PathProject","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Pocket", translate("PathProject","Incremental Step Down of Tool")) + obj.StepDown = (0.0, 0.0, 100.0, 1.0) + + obj.addProperty("App::PropertyFloat", "StartDepth", "Pocket", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyBool","UseStartDepth","Pocket",translate("PathProject","make True, if manually specifying a Start Start Depth")) + obj.addProperty("App::PropertyFloat", "FinalDepth", "Pocket", translate("PathProject","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyFloat", "RetractHeight", "Pocket", translate("PathProject","The height desired to retract tool when path is finished")) + + obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket",translate("PathProject", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) + obj.CutMode = ['Climb','Conventional'] + obj.addProperty("App::PropertyFloat", "MaterialAllowance", "Pocket", translate("PathProject","Amount of material to leave")) + obj.addProperty("App::PropertyFloat", "FinishDepth", "Pocket", translate("PathProject","Maximum material removed on final pass.")) + + obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket",translate("PathProject", "Start pocketing at center or boundary")) + obj.StartAt = ['Center', 'Edge'] + + obj.addProperty("App::PropertyFloatConstraint", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) + obj.VertFeed = (0.0, 0.0, 100000.0, 1.0) + + obj.addProperty("App::PropertyFloatConstraint", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate for horizontal moves")) + obj.HorizFeed = (0.0, 0.0, 100000.0, 1.0) + + + obj.addProperty("App::PropertyBool","Active","Path",translate("PathProject","Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) + + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + + def getStock(self,obj): + "retrieves a stock object from hosting project if any" + for o in obj.InList: + if hasattr(o,"Group"): + for g in o.Group: + if hasattr(g,"Height_Allowance"): + return o + # not found? search one level up + for o in obj.InList: + return self.getStock(o) + return None + + + + def execute(self,obj): + if obj.Base: + tool = PathUtils.getLastTool(obj) + + if tool: + radius = tool.Diameter/2 + if radius < 0:# safe guard + radius -= radius + else: + # temporary value, to be taken from the properties later on + radius = 1 + + import Part, DraftGeomUtils + if "Face" in obj.Base[1][0]: + shape = getattr(obj.Base[0].Shape,obj.Base[1][0]) + else: + edges = [getattr(obj.Base[0].Shape,sub) for sub in obj.Base[1]] + shape = Part.Wire(edges) + print len(edges) + + # absolute coords, millimeters, cancel offsets + output = "G90\nG21\nG40\n" + # save tool + if obj.ToolNumber > 0 and tool.ToolNumber != obj.ToolNumber: + output += "M06 T" + str(tool.ToolNumber) + "\n" + + # build offsets + offsets = [] + nextradius = radius + result = DraftGeomUtils.pocket2d(shape,nextradius) + while result: + offsets.extend(result) + nextradius += radius + result = DraftGeomUtils.pocket2d(shape,nextradius) + + # first move will be rapid, subsequent will be at feed rate + first = True + startPoint = None + fastZPos = max(obj.StartDepth + 2, obj.RetractHeight) + + # revert the list so we start with the outer wires + if obj.StartAt != 'Edge': + offsets.reverse() + +# print "startDepth: " + str(obj.StartDepth) +# print "finalDepth: " + str(obj.FinalDepth) +# print "stepDown: " + str(obj.StepDown) +# print "finishDepth" + str(obj.FinishDepth) +# print "offsets:", len(offsets) + + def prnt(vlu): return str(round(vlu, 4)) + + for vpos in frange(obj.StartDepth, obj.FinalDepth, obj.StepDown, obj.FinishDepth): +# print "vpos: " + str(vpos) + # loop over successive wires + for currentWire in offsets: +# print "new line (offset)" + last = None + for edge in currentWire.Edges: +# print "new edge" + if not last: + # we set the base GO to our fast move to our starting pos + if first: + startPoint = edge.Vertexes[0].Point + output += "G0 X" + prnt(startPoint.x) + " Y" + prnt(startPoint.y) +\ + " Z" + prnt(fastZPos) + "\n" + first = False + #then move slow down to our starting point for our profile + last = edge.Vertexes[0].Point + output += "G1 X" + prnt(last.x) + " Y" + prnt(last.y) + " Z" + prnt(vpos) + "\n" + if isinstance(edge.Curve,Part.Circle): + point = edge.Vertexes[-1].Point + if point == last: # edges can come flipped + point = edge.Vertexes[0].Point +# print "flipped" + center = edge.Curve.Center + relcenter = center.sub(last) + v1 = last.sub(center) + v2 = point.sub(center) + if v1.cross(v2).z < 0: + output += "G2" + else: + output += "G3" + output += " X" + prnt(point.x) + " Y" + prnt(point.y) + " Z" + prnt(vpos) + output += " I" + prnt(relcenter.x) + " J" +prnt(relcenter.y) + " K" + prnt(relcenter.z) + output += "\n" + last = point + else: + point = edge.Vertexes[-1].Point + if point == last: # edges can come flipped + point = edge.Vertexes[0].Point + output += "G1 X" + prnt(point.x) + " Y" + prnt(point.y) + " Z" + prnt(vpos) + "\n" + last = point + + #move back up + output += "G1 Z" + prnt(fastZPos) + "\n" +# print output +# path = Path.Path(output) +# obj.Path = path + if obj.Active: + path = Path.Path(output) + obj.Path = path + obj.ViewObject.Visibility = True + else: + path = Path.Path("(inactive operation)") + obj.Path = path + obj.ViewObject.Visibility = False + + +class ViewProviderPocket: + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def getIcon(self): + return ":/icons/Path-Pocket.svg" + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + +class CommandPathPocket: + + + def GetResources(self): + return {'Pixmap' : 'Path-Pocket', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket","Pocket"), + 'Accel': "P, O", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket","Creates a Path Pocket object from a loop of edges or a face")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) + return + if len(selection[0].SubObjects) == 0: + FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) + return + for s in selection[0].SubObjects: + if s.ShapeType != "Edge": + if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): + FreeCAD.Console.PrintError(translate("PathPocket","Please select only edges or a single face\n")) + return + if selection[0].SubObjects[0].ShapeType == "Edge": + try: + import Part + w = Part.Wire(selection[0].SubObjects) + except: + FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) + return + + # if everything is ok, execute and register the transaction in the undo/redo stack + FreeCAD.ActiveDocument.openTransaction(translate("PathPocket","Create Pocket")) + FreeCADGui.addModule("PathScripts.PathPocket") + FreeCADGui.doCommand('prjexists = False') + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Pocket")') + FreeCADGui.doCommand('PathScripts.PathPocket.ObjectPocket(obj)') + FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)') + subs = "[" + for s in selection[0].SubElementNames: + subs += '"' + s + '",' + subs += "]" + FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') + FreeCADGui.doCommand('obj.Active = True') + snippet = ''' +from PathScripts import PathUtils +PathUtils.addToProject(obj) + +ZMax = obj.Base[0].Shape.BoundBox.ZMax +ZMin = obj.Base[0].Shape.BoundBox.ZMin +obj.StepDown = 1.0 +obj.StartDepth = ZMax +obj.FinalDepth = ZMin +obj.ClearanceHeight = ZMax + 5.0 + +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Pocket',CommandPathPocket()) + +FreeCAD.Console.PrintLog("Loading PathPocket... done\n") diff --git a/src/Mod/Path/PathScripts/PathPost.py b/src/Mod/Path/PathScripts/PathPost.py new file mode 100644 index 0000000000..af6b336c54 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathPost.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Post Process command that will make use of the Output File and Post Processor entries in PathProject ''' +import FreeCAD, FreeCADGui +import Path, PathScripts,PathGui +from PathScripts import PostUtils +from PathScripts import PathProject +import os,sys +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class CommandPathPost: + def GetResources(self): + return {'Pixmap' : 'Path-Post', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPost","Post Process"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPost","Post Process the selected Project")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathPost","Post Process the Selected path(s)")) + FreeCADGui.addModule("PathScripts.PathPost") + #select the PathProject that you want to post output from + obj = FreeCADGui.Selection.getSelection() + + #default to the dumper post and default .tap file + postname = "dumper_post" + filename = "tmp.tap" + + #check if the user has a project and has set the default post and output filename + if hasattr(obj[0],"Group") and hasattr(obj[0],"Path"): + #Check for a machine and use the post processor if it's set + proj = obj[0] + postobj = None + for p in obj[0].Group: + if p.Name == "Machine": + postobj = p + + #need to check for existance of these: obj.PostProcessor, obj.OutputFile + if postobj and postobj.PostProcessor: + sys.path.append(os.path.split(postobj.PostProcessor)[0]) + lessextn = os.path.splitext(postobj.PostProcessor)[0] + postname = os.path.split(lessextn)[1] + + if proj.OutputFile: + filename = proj.OutputFile + + exec "import %s as current_post" % postname + current_post.export(obj,filename) + + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Post',CommandPathPost()) + +FreeCAD.Console.PrintLog("Loading PathPost... done\n") diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py new file mode 100644 index 0000000000..aaf7a00514 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from FreeCAD import Vector +from PySide import QtCore,QtGui +from PathScripts import PathUtils,PathSelection,PathProject + +"""Path Profile object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectProfile: + + def __init__(self,obj): + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyLinkSub","Face1","Path",translate("Face1","First Selected Face to help determine where final depth of tool path is")) + obj.addProperty("App::PropertyLinkSub","Face2","Path",translate("Face2","Second Selected Face to help determine where the upper level of tool path is")) + obj.addProperty("App::PropertyBool","PathClosed","Path",translate("Path Closed","If the toolpath is a closed polyline this is True")) + obj.addProperty("App::PropertyLinkSub","Edge1","Path",translate("Edge 1","First Selected Edge to help determine which geometry to make a toolpath around")) + obj.addProperty("App::PropertyLinkSub","Edge2","Path",translate("Edge 2","Second Selected Edge to help determine which geometry to make a toolpath around")) + obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyBool","UsePlacements","Path",translate("Use Placements","make True, if using the profile operation placement properties to transform toolpath in post processor")) + + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) + obj.ToolNumber = (0,0,1000,1) + obj.setEditorMode('ToolNumber',1) #make this read only + + #Depth Properties + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Clearance Height","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyLength", "StepDown", "Depth", translate("StepDown","Incremental Step Down of Tool")) + obj.addProperty("App::PropertyBool","UseStartDepth","Depth",translate("Use Start Depth","make True, if manually specifying a Start Start Depth")) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Start Depth","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("Retract Height","The height desired to retract tool when path is finished")) + obj.addProperty("App::PropertyString","Comment","Path",translate("Comment","An optional comment for this profile")) + + #Feed Properties + obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate for horizontal moves")) + + #Start Point Properties + obj.addProperty("App::PropertyVector","StartPoint","Start Point",translate("Start Point","The start point of this path")) + obj.addProperty("App::PropertyBool","UseStartPoint","Start Point",translate("Use Start Point","make True, if specifying a Start Point")) + obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", translate("extend at start", "extra length of tool path before start of part edge")) + obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", translate("lead in length","length of straight segment of toolpath that comes in at angle to first part edge")) + + #End Point Properties + obj.addProperty("App::PropertyBool","UseEndPoint","End Point",translate("Use End Point","make True, if specifying an End Point")) + obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", translate("extend at end","extra length of tool path after end of part edge")) + obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", translate("lead_out_line_len","length of straight segment of toolpath that comes in at angle to last part edge")) + obj.addProperty("App::PropertyVector","EndPoint","End Point",translate("End Point","The end point of this path")) + + #Profile Properties + obj.addProperty("App::PropertyEnumeration", "Side", "Profile", translate("Side","Side of edge that tool should cut")) + obj.Side = ['Left','Right','On'] #side of profile that cutter is on in relation to direction of profile + obj.addProperty("App::PropertyEnumeration", "Direction", "Profile",translate("Direction", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) + obj.Direction = ['CW','CCW'] #this is the direction that the profile runs + + obj.addProperty("App::PropertyDistance", "RollRadius", "Profile", translate("Roll Radius","Radius at start and end")) + obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile",translate("OffsetExtra","Extra value to stay away from final profile- good for roughing toolpath")) + obj.addProperty("App::PropertyLength", "SegLen", "Profile",translate("Seg Len","Tesselation value for tool paths made from beziers, bsplines, and ellipses")) + + + + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + if obj.Base: + + # tie the toolnumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.ToolNumber= tl + + + tool = PathUtils.getTool(obj,obj.ToolNumber) + if tool: + radius = tool.Diameter/2 + else: + # temporary value, to be taken from the properties later on + radius = 0.001 + if obj.Base[0].Shape.ShapeType == "Wire": #a pure wire was picked + wire = obj.Base[0].Shape + else: #we are dealing with a face and it's edges or just a face + if obj.Edge1: + e1 = FreeCAD.ActiveDocument.getObject(obj.Base[0].Name).Shape.Edges[eval(obj.Edge1[1][0].lstrip('Edge'))-1] + if e1.BoundBox.ZMax <> e1.BoundBox.ZMin: + FreeCAD.Console.PrintError('vertical edges not valid yet\n') + return + if obj.Base[0].Shape.ShapeType =='Wire': + wire = obj.Base[0].Shape + if obj.Base[0].Shape.ShapeType =='Solid' or obj.Base[0].Shape.ShapeType =='Compound': + shape = obj.Base[0].Shape + for fw in shape.Wires: + if (fw.BoundBox.ZMax == e1.BoundBox.ZMax) and (fw.BoundBox.ZMin == e1.BoundBox.ZMin): + for e in fw.Edges: + if e.isSame(e1): + #FreeCAD.Console.PrintMessage('found the same objects\n') + wire = fw + elif obj.Face1: # we are only dealing with a face or faces + f1 = FreeCAD.ActiveDocument.getObject(obj.Base[0].Name).Shape.Faces[eval(obj.Face1[1][0].lstrip('Face'))-1] + # make the side Left and direction CW for normal cnc milling + obj.Direction = 'CW' + obj.Side = "Left" + # we only consider the outer wire if this is a single Face + wire = f1.OuterWire + + if obj.Direction == 'CCW': + clockwise=False + else: + clockwise=True + output ="" + output += '('+ str(obj.Comment)+')\n' + + FirstEdge= None + if obj.Edge1: + ename = obj.Edge1[1][0] + edgeNumber = int(ename[4:])-1 + FirstEdge = obj.Base[0].Shape.Edges[edgeNumber] + ZMax = obj.Base[0].Shape.BoundBox.ZMax + + ZCurrent = obj.ClearanceHeight.Value + + if obj.UseStartDepth: + output += PathUtils.MakePath(wire,obj.Side,radius,clockwise,obj.ClearanceHeight.Value,obj.StepDown.Value,obj.StartDepth.Value, obj.FinalDepth.Value,FirstEdge,obj.PathClosed,obj.SegLen.Value,obj.VertFeed.Value,obj.HorizFeed.Value) + else: + output += PathUtils.MakePath(wire,obj.Side,radius,clockwise,obj.ClearanceHeight.Value,obj.StepDown.Value,ZMax, obj.FinalDepth.Value,FirstEdge,obj.PathClosed,obj.SegLen.Value,obj.VertFeed.Value,obj.HorizFeed.Value) + + + if obj.Active: + path = Path.Path(output) + obj.Path = path + obj.ViewObject.Visibility = True + + else: + path = Path.Path("(inactive operation)") + obj.Path = path + obj.ViewObject.Visibility = False + + + + +class ViewProviderProfile: + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def getIcon(self): + return ":/icons/Path-Profile.svg" + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + +class CommandPathProfile: + def GetResources(self): + return {'Pixmap' : 'Path-Profile', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathProfile","Profile"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathProfile","Creates a Path Profile object from selected faces")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + import Path + from PathScripts import PathUtils,PathProfile,PathProject + prjexists = False + selection = PathSelection.multiSelect() + + if not selection: + return + + # if everything is ok, execute and register the transaction in the undo/redo stack + FreeCAD.ActiveDocument.openTransaction(translate("PathProfile","Create Profile")) + FreeCADGui.addModule("PathScripts.PathProfile") + + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Profile") + PathProfile.ObjectProfile(obj) + PathProfile.ViewProviderProfile(obj.ViewObject) + + obj.Base = (FreeCAD.ActiveDocument.getObject(selection['objname'])) + + if selection['facenames']: + #FreeCAD.Console.PrintMessage('There are edges selected\n') + obj.Face1 = (FreeCAD.ActiveDocument.getObject(selection['objname']),selection['facenames'][0]) + if len(selection['facenames'])>1: + obj.Face2 = (FreeCAD.ActiveDocument.getObject(selection['objname']),selection['facenames'][-1]) + + if selection['edgenames']: + #FreeCAD.Console.PrintMessage('There are edges selected\n') + + obj.Edge1 =(FreeCAD.ActiveDocument.getObject(selection['objname']),(selection['edgenames'][0])) + if len(selection['edgenames'])>1: + obj.Edge2 =(FreeCAD.ActiveDocument.getObject(selection['objname']),(selection['edgenames'][-1])) + + if selection['pointlist']: + FreeCADGui.doCommand('from FreeCAD import Vector') + stptX, stptY, stptZ = selection['pointlist'][0].X, selection['pointlist'][0].Y, selection['pointlist'][0].Z + obj.StartPoint = Vector((stptX),(stptY),(stptZ)) + if len(selection['pointlist'])>1: # we have more than one point so we have an end point + endptX, endptY, endptZ = selection['pointlist'][-1].X, selection['pointlist'][-1].Y, selection['pointlist'][-1].Z + obj.EndPoint = Vector(endptX,endptY,endptZ) + if selection['pathwire'].isClosed(): + obj.PathClosed = True + if selection['clockwise']: + obj.Side = "Left" + obj.Direction = "CW" + elif selection['clockwise'] == False: + obj.Side = "Right" + obj.Direction = "CCW" + else: + obj.Side = "On" + obj.Direction = "CCW" + obj.PathClosed = False + + ZMax = obj.Base[0].Shape.BoundBox.ZMax + ZMin = obj.Base[0].Shape.BoundBox.ZMin + obj.StepDown.Value = 1.0 + obj.StartDepth.Value = ZMax- obj.StepDown.Value + obj.FinalDepth.Value = ZMin-1.0 + obj.ClearanceHeight.Value = ZMax + 5.0 + obj.SegLen.Value = 0.5 + obj.Active = True + obj.ViewObject.ShowFirstRapid = False + + project = PathUtils.addToProject(obj) + + tl = PathUtils.changeTool(obj,project) + if tl: + obj.ToolNumber = tl + + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Profile',CommandPathProfile()) + +FreeCAD.Console.PrintLog("Loading PathProfile... done\n") diff --git a/src/Mod/Path/PathScripts/PathProject.py b/src/Mod/Path/PathScripts/PathProject.py new file mode 100644 index 0000000000..71b5680e60 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathProject.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui + +"""Path Project object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectPathProject: + + + def __init__(self,obj): +# obj.addProperty("App::PropertyFile", "PostProcessor", "CodeOutput", translate("PostProcessor","Select the Post Processor file for this project")) + obj.addProperty("App::PropertyFile", "OutputFile", "CodeOutput", translate("OutputFile","The NC output file for this project")) +# obj.addProperty("App::PropertyBool","Editor","CodeOutput",translate("Show Editor","Show G-Code in simple editor after posting code")) +# obj.addProperty("Path::PropertyTooltable","Tooltable", "Path",translate("PathProject","The tooltable of this feature")) + obj.addProperty("App::PropertyString", "Description","Path",translate("PathProject","An optional description for this project")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def onChanged(self,obj,prop): + pass + + def execute(self,obj): + cmds = [] + for child in obj.Group: + if child.isDerivedFrom("Path::Feature"): + cmds.extend(child.Path.Commands) + if cmds: + path = Path.Path(cmds) + obj.Path = path + +class ViewProviderProject: + + def __init__(self,vobj): + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) +# vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): + return ":/icons/Path-Project.svg" + + def onChanged(self,vobj,prop): + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) +# vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + +class CommandProject: + + + def GetResources(self): + return {'Pixmap' : 'Path-Project', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathProject","Project"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathProject","Creates a Path Project object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + incl = [] + sel = FreeCADGui.Selection.getSelection() + for obj in sel: + if obj.isDerivedFrom("Path::Feature"): + incl.append(obj) + FreeCAD.ActiveDocument.openTransaction(translate("PathProject","Create Project")) + CommandProject.Create(incl) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + @staticmethod + def Create(pathChildren = []): + """Code to create a project""" + #FreeCADGui.addModule("PathScripts.PathProject") + obj = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython","Project") + ObjectPathProject(obj) + if pathChildren: + for child in pathChildren: + pathChildren.append(FreeCAD.ActiveDocument.getObject(obj.Name)) + obj.Group = pathChildren + ViewProviderProject(obj.ViewObject) + + #create a machine obj + import PathScripts + PathScripts.PathMachine.CommandPathMachine.Create() + + return obj + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Project',CommandProject()) + +FreeCAD.Console.PrintLog("Loading PathProject... done\n") diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py new file mode 100644 index 0000000000..4427ababe8 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +'''Path selection function select a face or faces, two edges, etc to get a dictionary with what was selected in order ''' + +import FreeCAD,FreeCADGui +import Part +from FreeCAD import Vector + + +def Sort2Edges(edgelist): + '''Sort2Edges(edgelist) simple function to reorder the start and end pts of two edges based on their selection order. Returns the list, the start point, and their common point, => edgelist, vertex, vertex''' + if len(edgelist)>=2: + vlist = [] + e0 = edgelist[0] + e1=edgelist[1] + a0 = e0.Vertexes[0] + a1 = e0.Vertexes[1] + b0 = e1.Vertexes[0] + b1 = e1.Vertexes[1] + # comparison routine to order two edges: + if a1.isSame(b0): + vlist.append((a0.Point.x,a0.Point.y)) + vlist.append((a1.Point.x,a1.Point.y)) + vlist.append((b1.Point.x,b1.Point.y)) + + elif a0.isSame(b0): + vlist.append((a1.Point.x,a1.Point.y)) + vlist.append((a0.Point.x,a0.Point.y)) + vlist.append((b1.Point.x,b1.Point.y)) + + elif a0.isSame(b1): + vlist.append((a1.Point.x,a1.Point.y)) + vlist.append((a0.Point.x,a0.Point.y)) + vlist.append((b0.Point.x,b0.Point.y)) + + elif a1.isSame(b1): + vlist.append((a0.Point.x,a0.Point.y)) + vlist.append((a1.Point.x,a1.Point.y)) + vlist.append((b0.Point.x,b0.Point.y)) + + edgestart = Vector(vlist[0][0],vlist[0][1],e0.Vertexes[1].Z) + edgecommon = Vector(vlist[1][0],vlist[1][1],e0.Vertexes[1].Z) + + return vlist,edgestart,edgecommon + +def segments(poly): + ''' A sequence of (x,y) numeric coordinates pairs ''' + return zip(poly, poly[1:] + [poly[0]]) + +def check_clockwise(poly): + ''' + check_clockwise(poly) a function for returning a boolean if the selected wire is clockwise or counter clockwise + based on point order. poly = [(x1,y1),(x2,y2),(x3,y3)] + ''' + clockwise = False + if (sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0: + clockwise = not clockwise + return clockwise + + +def multiSelect(): + ''' + multiSelect() A function for selecting elements of an object for CNC path operations. + Select just a face, an edge,or two edges to indicate direction, a vertex on the object, a point not on the object, + or some combination. Returns a dictionary. + ''' + sel = FreeCADGui.Selection.getSelectionEx() + numobjs = len([selobj.Object for selobj in sel]) + if numobjs == 0: + FreeCAD.Console.PrintError('Please select some objects and try again.\n') + return + goodselect = False + for s in sel: + for i in s.SubObjects: + if i.ShapeType == 'Face': + goodselect = True + if i.ShapeType == 'Edge': + goodselect = True + if i.ShapeType == 'Vertex': + goodselect = True + if not goodselect: + FreeCAD.Console.PrintError('Please select a face and/or edges along with points (optional) and try again.\n') + return + + selItems = {} + selItems['objname']=None #the parent object name - a 3D solid + selItems['pointlist']=None #start and end points + selItems['pointnames']=None #names of points for document object + selItems['facenames']=None # the selected face name + selItems['facelist']=None #list of faces selected + selItems['edgelist']=None #some edges that could be selected along with points and faces + selItems['edgenames']=None + selItems['pathwire']=None #the whole wire around edges of the face + selItems['clockwise']=None + selItems['circles']=None + facenames = [] + edgelist =[] + edgenames=[] + ptlist=[] + ptnames=[] + circlelist=[] + face = False + edges = False + points = False + wireobj = False + circles = False + facelist= [] + for s in sel: + if s.Object.Shape.ShapeType in ['Solid','Compound','Wire','Vertex']: + if not (s.Object.Shape.ShapeType =='Vertex'): + objname = s.ObjectName + selItems['objname'] =objname + if s.Object.Shape.ShapeType == 'Wire': + wireobj = True + if s.Object.Shape.ShapeType == 'Vertex': + ptnames.append(s.ObjectName) +# ptlist.append(s.Object) + points = True + for sub in s.SubObjects: + if sub.ShapeType =='Face': + facelist.append(sub) + face = True + if sub.ShapeType =='Edge': + edge = sub + edgelist.append(edge) + edges = True + if isinstance(sub.Curve,Part.Circle): + circlelist.append(edge) + circles = True + if sub.ShapeType =='Vertex': + ptlist.append(sub) + points = True + + for sub in s.SubElementNames: + if 'Face' in sub: + facename = sub + facenames.append(facename) + if 'Edge' in sub: + edgenames.append(sub) + # now indicate which wire is going to be processed, based on which edges are selected + if facelist: + selItems['facelist']=facelist + + if edges: + if face: + selItems['edgelist'] =edgelist + for fw in facelist[0].Wires: + for e in fw.Edges: + if e.isSame(edge): + pathwire = fw + selItems['pathwire'] =pathwire + elif wireobj: + selItems['pathwire'] =s.Object.Shape + selItems['edgelist'] =edgelist + else: + for w in s.Object.Shape.Wires: + for e in w.Edges: + if e.BoundBox.ZMax == e.BoundBox.ZMin: #if they are on same plane in Z as sel edge + if e.isSame(edge): + pathwire = w + selItems['pathwire'] =pathwire + selItems['edgelist'] =edgelist + + if not edges: + if face: + selItems['pathwire'] =facelist[0].OuterWire + + if edges and (len(edgelist)>=2): + vlist,edgestart,edgecommon=Sort2Edges(edgelist) + edgepts ={} + edgepts['vlist'] = vlist + edgepts['edgestart']=edgestart # start point of edges selected + edgepts['edgecommon']=edgecommon # point where two edges join- will be last point in in first gcode line + selItems['edgepts']=edgepts + + if check_clockwise(vlist): + selItems['clockwise']=True + elif check_clockwise(vlist) == False: + selItems['clockwise']=False + + if points: + selItems['pointlist'] = ptlist + selItems['pointnames'] = ptnames + if edges: + selItems['edgenames']=edgenames + if face: + selItems['facenames'] = facenames + if circles: + selItems['circles'] = circlelist + + return selItems + diff --git a/src/Mod/Path/PathScripts/PathStock.py b/src/Mod/Path/PathScripts/PathStock.py new file mode 100644 index 0000000000..f4cac96fcc --- /dev/null +++ b/src/Mod/Path/PathScripts/PathStock.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +'''used to create material stock around a machined part- for visualization ''' + +import Draft,Part +import FreeCAD, FreeCADGui +from FreeCAD import Vector +from PySide import QtCore, QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class Stock: + def __init__(self, obj): + "Make stock" + obj.addProperty("App::PropertyFloat","Length_Allowance","Stock",translate("Length Allowance","extra allownace from part width")).Length_Allowance = 1.0 + obj.addProperty("App::PropertyFloat","Width_Allowance","Stock",translate("Width Allowance","extra allownace from part width")).Width_Allowance = 1.0 + obj.addProperty("App::PropertyFloat","Height_Allowance","Stock",translate("Height Allowance","extra allownace from part width")).Height_Allowance = 1.0 + obj.addProperty("App::PropertyLink","Base","Base", + "The base object this represents") + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self, obj): + self.Xmin = obj.Base.Shape.BoundBox.XMin + self.Xmax = obj.Base.Shape.BoundBox.XMax + + self.Ymin = obj.Base.Shape.BoundBox.YMin + self.Ymax = obj.Base.Shape.BoundBox.YMax + + self.Zmin = obj.Base.Shape.BoundBox.ZMin + self.Zmax = obj.Base.Shape.BoundBox.ZMax + + self.length = self.Xmax -self.Xmin+obj.Length_Allowance*2.0 + self.width = self.Ymax - self.Ymin+obj.Width_Allowance*2.0 + self.height = self.Zmax - self.Zmin+obj.Height_Allowance*2.0 + self.pnt = Vector(self.Xmin-obj.Length_Allowance , self.Ymin-obj.Width_Allowance, self.Zmin-obj.Height_Allowance) + + obj.Shape = Part.makeBox(self.length,self.width,self.height,self.pnt) + +class _ViewProviderStock: + + def __init__(self,obj): #mandatory +# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + obj.Proxy = self + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Stock.svg" + + def attach(self, vobj): #optional + self.Object = vobj.Object + + + +class CommandPathStock: + def GetResources(self): + return {'Pixmap' : 'Path-Stock', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathStock","Stock"), + 'Accel': "P, S", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathStock","Creates a 3D object to represent raw stock to mill the part out of")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathStock","Creates a 3D object to represent raw stock to mill the part out of")) + FreeCADGui.addModule("PathScripts.PathStock") + snippet = ''' +import FreeCADGui +if len(FreeCADGui.Selection.getSelection())>0: + sel=FreeCADGui.Selection.getSelection() + o = sel[0] + if "Shape" in o.PropertiesList: + obj =FreeCAD.ActiveDocument.addObject('Part::FeaturePython',sel[0].Name+('_Stock')) + PathScripts.PathStock.Stock(obj) + PathScripts.PathStock._ViewProviderStock(obj.ViewObject) + PathScripts.PathUtils.addToProject(obj) + baseobj = sel[0] + obj.Base = baseobj + FreeCADGui.ActiveDocument.getObject(sel[0].Name+("_Stock")).ShapeColor = (0.3333,0.6667,1.0000) + FreeCADGui.ActiveDocument.getObject(sel[0].Name+("_Stock")).Transparency = 75 + FreeCAD.ActiveDocument.recompute() + else: + FreeCAD.Console.PrintMessage("Select a Solid object and try again.\\n") +else: + FreeCAD.Console.PrintMessage("Select the object you want to show stock for and try again.\\n") + ''' + FreeCADGui.doCommand(snippet) + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Stock',CommandPathStock()) + +FreeCAD.Console.PrintLog("Loading PathStock... done\n") + diff --git a/src/Mod/Path/PathScripts/PathStop.py b/src/Mod/Path/PathScripts/PathStop.py new file mode 100644 index 0000000000..e7186762fe --- /dev/null +++ b/src/Mod/Path/PathScripts/PathStop.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used for CNC machine Stops for Path module. Create an Optional or Mandatory Stop.''' + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class Stop: + def __init__(self,obj): + obj.addProperty("App::PropertyEnumeration", "Stop", "Path", translate("Program Stop", "Add Optional or Mandatory Stop to the program")) + obj.Stop=['Optional', 'Mandatory'] + obj.Proxy = self + mode = 2 + obj.setEditorMode('Placement',mode) + + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def onChanged(self,obj,prop): + pass +# FreeCAD.ActiveDocument.recompute() + + def execute(self,obj): + if obj.Stop == 'Optional': + word = 'M1' + else: + word = 'M0' + + output ="" + output = word+'\n' + path = Path.Path(output) + obj.Path = path + +class _ViewProviderStop: + + def __init__(self,vobj): #mandatory +# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-Stop.svg" + + def onChanged(self,vobj,prop): #optional + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + +class CommandPathStop: + def GetResources(self): + return {'Pixmap' : 'Path-Stop', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathStop","Stop"), + 'Accel': "P, C", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathStop","Add Optional or Mandatory Stop to the program")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathStop","Add Optional or Mandatory Stop to the program")) + FreeCADGui.addModule("PathScripts.PathStop") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathUtils +prjexists = False +obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Stop") +PathScripts.PathStop.Stop(obj) + +PathScripts.PathStop._ViewProviderStop(obj.ViewObject) +PathUtils.addToProject(obj) +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Stop',CommandPathStop()) + + +FreeCAD.Console.PrintLog("Loading PathStop... done\n") diff --git a/src/Mod/Path/PathScripts/PathToolLenOffset.py b/src/Mod/Path/PathScripts/PathToolLenOffset.py new file mode 100644 index 0000000000..0675188d7b --- /dev/null +++ b/src/Mod/Path/PathScripts/PathToolLenOffset.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Used for CNC machine Tool Length Offsets ie G43H2''' + +import FreeCAD,FreeCADGui,Path,PathGui +from PathScripts import PathProject,PathUtils +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class ToolLenOffset: + def __init__(self,obj): + obj.addProperty("App::PropertyIntegerConstraint", "HeightNumber","HeightOffset", translate( "Height Offset Number", "The Height offset number of the active tool")) + obj.HeightNumber = (0,0,10000,1) + obj.addProperty("App::PropertyLength", "Height", "HeightOffset", translate("Height","The first height value in Z, to rapid to, before making a feed move in Z")) + obj.addProperty("App::PropertyBool","Active","HeightOffset",translate("Active","Make False, to prevent operation from generating code")) + obj.Proxy = self + mode = 2 + obj.setEditorMode('Placement',mode) + + def execute(self,obj): + + command = 'G43H'+str(obj.HeightNumber)+'G0Z'+str(obj.Height.Value) + obj.Path = Path.Path(command) + obj.Label = "Height"+str(obj.HeightNumber) + if obj.Active: + obj.Path = Path.Path(command) + obj.ViewObject.Visibility = True + else: + obj.Path = Path.Path("(inactive operation)") + obj.ViewObject.Visibility = False + + # tie the HeightNumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.HeightNumber= tl + + def onChanged(self,obj,prop): + if prop == "HeightNumber": + obj.Label = "Height"+str(obj.HeightNumber) + + +class _ViewProviderTLO: + def __init__(self,vobj): #mandatory + vobj.Proxy = self + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + + def __getstate__(self): #mandatory + return None + + def __setstate__(self,state): #mandatory + return None + + def getIcon(self): #optional + return ":/icons/Path-LengthOffset.svg" + + def onChanged(self,vobj,prop): #optional + mode = 2 + vobj.setEditorMode('LineWidth',mode) + vobj.setEditorMode('MarkerColor',mode) + vobj.setEditorMode('NormalColor',mode) + vobj.setEditorMode('ShowFirstRapid',mode) + vobj.setEditorMode('DisplayMode',mode) + vobj.setEditorMode('BoundingBox',mode) + vobj.setEditorMode('Selectable',mode) + vobj.setEditorMode('ShapeColor',mode) + vobj.setEditorMode('Transparency',mode) + vobj.setEditorMode('Visibility',mode) + + def updateData(self,vobj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + + +class CommandPathToolLenOffset: + def GetResources(self): + return {'Pixmap' : 'Path-LengthOffset', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolLenOffset","Tool Length Offset"), + 'Accel': "P, T", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolLenOffset","Create a Tool Length Offset object")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathPlane","Create a Selection Plane object")) + FreeCADGui.addModule("PathScripts.PathToolLenOffset") + snippet = ''' +import Path +import PathScripts +from PathScripts import PathProject,PathUtils +obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","HeightOffset") +PathScripts.PathToolLenOffset.ToolLenOffset(obj) +obj.Active = True +PathScripts.PathToolLenOffset._ViewProviderTLO(obj.ViewObject) +project = PathUtils.addToProject(obj) + +tl = PathUtils.changeTool(obj,project) +if tl: + obj.HeightNumber = tl +obj.ViewObject.ShowFirstRapid = False +FreeCAD.ActiveDocument.recompute() +''' + FreeCADGui.doCommand(snippet) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_ToolLenOffset', CommandPathToolLenOffset()) + +FreeCAD.Console.PrintLog("Loading PathToolLenOffset... done\n") + + + + diff --git a/src/Mod/Path/PathScripts/PathToolTableEdit.py b/src/Mod/Path/PathScripts/PathToolTableEdit.py new file mode 100644 index 0000000000..a4bf401a11 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathToolTableEdit.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui +from PySide import QtCore,QtGui + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + +class CommandPathToolTableEdit: + def GetResources(self): + return {'Pixmap' : 'Path-ToolTable', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolTableEdit","EditToolTable"), + 'Accel': "P, T", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolTableEdit","Edits a Tool Table in a selected Project")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(translate("PathToolTableEdit","Edits a Tool Table in a selected Project")) + FreeCADGui.doCommand("from PathScripts import TooltableEditor") + FreeCADGui.doCommand("from PathScripts import PathUtils") + FreeCADGui.doCommand('machine = PathUtils.findMachine()') + FreeCADGui.doCommand('TooltableEditor.edit(machine.Name)') + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_ToolTableEdit',CommandPathToolTableEdit()) + + +FreeCAD.Console.PrintLog("Loading PathToolTableEdit... done\n") + diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py new file mode 100644 index 0000000000..247a736df1 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -0,0 +1,384 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +'''PathUtils -common functions used in PathScripts for filterig, sorting, and generating gcode toolpath data ''' +import FreeCAD +import Part +from FreeCAD import Vector +import FreeCADGui +import math +import DraftGeomUtils +from DraftGeomUtils import geomType +import DraftVecUtils +import PathScripts +from PathScripts import PathProject + +def cleanedges(splines,precision): + '''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths. + Returns Lines as is. Filters Circle and Arcs for over 180 degrees. Discretizes Ellipses. Ignores other geometry. ''' + edges = [] + for spline in splines: + if geomType(spline)=="BSplineCurve": + arcs = spline.Curve.toBiArcs(precision) + for i in arcs: + edges.append(Part.Edge(i)) + + elif geomType(spline)=="BezierCurve": + newspline=spline.Curve.toBSpline() + arcs = newspline.toBiArcs(precision) + for i in arcs: + edges.append(Part.Edge(i)) + + elif geomType(spline)=="Ellipse": + edges = curvetowire(spline, 1.0) #fixme hardcoded value + + elif geomType(spline)=="Circle": + #arcs=filterArcs(spline) + edges.append(spline) + + elif geomType(spline)=="Line": + edges.append(spline) + + else: + pass + + return edges + +def curvetowire(obj,steps): + '''adapted from DraftGeomUtils, because the discretize function changed a bit ''' + points = obj.copy().discretize(Distance = eval('steps')) + p0 = points[0] + edgelist = [] + for p in points[1:]: + edge = Part.makeLine((p0.x,p0.y,p0.z),(p.x,p.y,p.z)) + edgelist.append(edge) + p0 = p + return edgelist + +def fmt(val): return format(val, '.4f') #fixme set at 4 decimal places for testing + +def isSameEdge(e1,e2): + """isSameEdge(e1,e2): return True if the 2 edges are both lines or arcs/circles and have the same + points - inspired by Yorik's function isSameLine""" + if not (isinstance(e1.Curve,Part.Line) or isinstance(e1.Curve,Part.Circle)): + return False + if not (isinstance(e2.Curve,Part.Line) or isinstance(e2.Curve,Part.Circle)): + return False + if type(e1.Curve) <> type(e2.Curve): + return False + if isinstance(e1.Curve,Part.Line): + if (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point)): + return True + elif (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)): + return True + if isinstance(e1.Curve,Part.Circle): + center = False; radius= False; endpts=False + if e1.Curve.Center == e2.Curve.Center: + center = True + if e1.Curve.Radius == e2.Curve.Radius: + radius = True + if (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point)): + endpts = True + elif (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)): + endpts = True + if (center and radius and endpts): + return True + return False + +def segments(poly): + ''' A sequence of (x,y) numeric coordinates pairs ''' + return zip(poly, poly[1:] + [poly[0]]) + +def check_clockwise(poly): + ''' + check_clockwise(poly) a function for returning a boolean if the selected wire is clockwise or counter clockwise + based on point order. poly = [(x1,y1),(x2,y2),(x3,y3)] + ''' + clockwise = False + if (sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0: + clockwise = not clockwise + return clockwise + +def filterArcs(arcEdge): + '''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list ''' + s = arcEdge + if isinstance(s.Curve,Part.Circle): + splitlist =[] + angle = abs(s.LastParameter-s.FirstParameter) + overhalfcircle = False + goodarc = False + if (angle > math.pi): + overhalfcircle = True + else: + goodarc = True + if not goodarc: + arcstpt = s.valueAt(s.FirstParameter) + arcmid = s.valueAt((s.LastParameter-s.FirstParameter)*0.5+s.FirstParameter) + arcquad1 = s.valueAt((s.LastParameter-s.FirstParameter)*0.25+s.FirstParameter)#future midpt for arc1 + arcquad2 = s.valueAt((s.LastParameter-s.FirstParameter)*0.75+s.FirstParameter) #future midpt for arc2 + arcendpt = s.valueAt(s.LastParameter) + # reconstruct with 2 arcs + arcseg1 = Part.ArcOfCircle(arcstpt,arcquad1,arcmid) + arcseg2 = Part.ArcOfCircle(arcmid,arcquad2,arcendpt) + + eseg1 = arcseg1.toShape() + eseg2 = arcseg2.toShape() + splitlist.append(eseg1) + splitlist.append(eseg2) + else: + splitlist.append(s) + elif isinstance(s.Curve,Part.Line): + pass + return splitlist + +def reverseEdge(e): + if geomType(e) == "Circle": + arcstpt = e.valueAt(e.FirstParameter) + arcmid = e.valueAt((e.LastParameter-e.FirstParameter)*0.5+e.FirstParameter) + arcendpt = e.valueAt(e.LastParameter) + arcofCirc = Part.ArcOfCircle(arcendpt,arcmid,arcstpt) + newedge = arcofCirc.toShape() + elif geomType(e) == "Line": + stpt = e.valueAt(e.FirstParameter) + endpt = e.valueAt(e.LastParameter) + newedge = Part.makeLine(endpt,stpt) + + return newedge + +def convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None,vf=1.0,hf=2.0): + '''convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.''' + last = None + output = "" + # create the path from the offset shape + for edge in toolpath: + if not last: + #set the first point + last = edge.Vertexes[0].Point + #FreeCAD.Console.PrintMessage("last pt= " + str(last)+ "\n") + output += "G1 X"+str(fmt(last.x))+" Y"+str(fmt(last.y))+" Z"+str(fmt(Z))+" F"+str(vf)+"\n" + if isinstance(edge.Curve,Part.Circle): + #FreeCAD.Console.PrintMessage("arc\n") + arcstartpt = edge.valueAt(edge.FirstParameter) + midpt = edge.valueAt((edge.FirstParameter+edge.LastParameter)*0.5) + arcendpt = edge.valueAt(edge.LastParameter) + arcchkpt=edge.valueAt(edge.LastParameter*.99) + + if DraftVecUtils.equals(last,arcstartpt): + startpt = arcstartpt + endpt = arcendpt + else: + startpt = arcendpt + endpt = arcstartpt + center = edge.Curve.Center + relcenter = center.sub(last) + #FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n") + #FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n") + #FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n") + arc_cw = check_clockwise([(startpt.x,startpt.y),(midpt.x,midpt.y),(endpt.x,endpt.y)]) + #FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n") + if arc_cw: + output += "G2" + else: + output += "G3" + output += " X"+str(fmt(endpt.x))+" Y"+str(fmt(endpt.y))+" Z"+str(fmt(Z))+" F"+str(hf) + output += " I" + str(fmt(relcenter.x)) + " J" + str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z)) + output += "\n" + last = endpt + #FreeCAD.Console.PrintMessage("last pt arc= " + str(last)+ "\n") + else: + point = edge.Vertexes[-1].Point + if DraftVecUtils.equals(point , last): # edges can come flipped + point = edge.Vertexes[0].Point + output += "G1 X"+str(fmt(point.x))+" Y"+str(fmt(point.y))+" Z"+str(fmt(Z))+" F"+str(hf)+"\n" + last = point + #FreeCAD.Console.PrintMessage("line\n") + #FreeCAD.Console.PrintMessage("last pt line= " + str(last)+ "\n") + return output + +def SortPath(wire,Side,radius,clockwise,firstedge=None,SegLen =0.5): + '''SortPath(wire,Side,radius,clockwise,firstedge=None,SegLen =0.5) Sorts the wire and reverses it, if needed. Splits arcs over 180 degrees in two. Returns the reordered offset of the wire. ''' + if firstedge: + edgelist = wire.Edges[:] + if wire.isClosed(): + elindex = None + n=0 + for e in edgelist: + if isSameEdge(e,firstedge): +# FreeCAD.Console.PrintMessage('found first edge\n') + elindex = n + n=n+1 + l1 = edgelist[:elindex] + l2 = edgelist[elindex:] + newedgelist = l2+l1 + + if clockwise: + newedgelist.reverse() + last = newedgelist.pop(-1) + newedgelist.insert(0, last) + + preoffset= [] + for e in newedgelist: + if clockwise: + r = reverseEdge(e) + preoffset.append(r) + else: + preoffset.append(e) + + sortedpreoff = DraftGeomUtils.sortEdgesOld(preoffset) + wire = Part.Wire(sortedpreoff) + else: + sortedpreoff = DraftGeomUtils.sortEdgesOld(edgelist) + wire = Part.Wire(sortedpreoff) + + edgelist = [] + for e in wire.Edges: + if geomType(e) == "Circle": + arclist = filterArcs(e) + for a in arclist: + edgelist.append(a) + elif geomType(e) == "Line": + edgelist.append(e) + elif geomType(e) == "BSplineCurve" or \ + geomType(e) == "BezierCurve" or \ + geomType(e) == "Ellipse": + edgelist.append(Part.Wire(curvetowire(e,(SegLen)))) + + newwire = Part.Wire(edgelist) + if Side == 'Left': + # we use the OCC offset feature + offset = newwire.makeOffset(radius)#tool is outside line + elif Side == 'Right': + offset = newwire.makeOffset(-radius)#tool is inside line + else: + if wire.isClosed(): + offset = newwire.makeOffset(0.0) + else: + offset = newwire + + return offset + +def MakePath(wire,Side,radius,clockwise,ZClearance,StepDown,ZStart,ZFinalDepth,firstedge=None,PathClosed=True,SegLen =0.5,VertFeed=1.0,HorizFeed=2.0): + ''' makes the path - just a simple profile for now ''' + offset = SortPath(wire,Side,radius,clockwise,firstedge,SegLen=0.5) + toolpath = offset.Edges[:] + paths = "" + first = toolpath[0].Vertexes[0].Point + paths += "G0 X"+str(fmt(first.x))+"Y"+str(fmt(first.y))+"\n" + ZCurrent = ZStart- StepDown + if PathClosed: + while ZCurrent > ZFinalDepth: + paths += convert(toolpath,Side,radius,clockwise,ZCurrent,firstedge,VertFeed,HorizFeed) + ZCurrent = ZCurrent-abs(StepDown) + paths += convert(toolpath,Side,radius,clockwise,ZFinalDepth,firstedge,VertFeed,HorizFeed) + paths += "G0 Z" + str(ZClearance) + else: + while ZCurrent > ZFinalDepth: + paths += convert(toolpath,Side,radius,clockwise,ZCurrent,firstedge,VertFeed,HorizFeed) + paths += "G0 Z" + str(ZClearance) + paths += "G0 X"+str(fmt(first.x))+"Y"+str(fmt(first.y))+"\n" + ZCurrent = ZCurrent-abs(StepDown) + paths += convert(toolpath,Side,radius,clockwise,ZFinalDepth,firstedge,VertFeed,HorizFeed) + paths += "G0 Z" + str(ZClearance) + return paths + +# the next two functions are for automatically populating tool numbers/height offset numbers based on previously active toolnumbers + +def changeTool(obj,proj): + tlnum = 0 + for p in proj.Group: + if not hasattr(p,"Group"): + if isinstance(p.Proxy,PathScripts.PathLoadTool.LoadTool) and p.ToolNumber > 0: + tlnum = p.ToolNumber + if p == obj: + return tlnum + elif hasattr(p,"Group"): + for g in p.Group: + if isinstance(g.Proxy,PathScripts.PathLoadTool.LoadTool): + tlnum = g.ToolNumber + if g == obj: + return tlnum + + +def getLastTool(obj): + toolNum = obj.ToolNumber + if obj.ToolNumber == 0: + # find tool from previous toolchange + proj = findProj() + toolNum = changeTool(obj, proj) + return getTool(obj, toolNum) + + +def getTool(obj,number=0): + "retrieves a tool from a hosting object with a tooltable, if any" + for o in obj.InList: + if o.TypeId == "Path::FeatureCompoundPython": + for m in o.Group: + if hasattr(m,"Tooltable"): + return m.Tooltable.getTool(number) + # not found? search one level up + for o in obj.InList: + return getTool(o,number) + return None + + +def findProj(): + for o in FreeCAD.ActiveDocument.Objects: + if "Proxy" in o.PropertiesList: + if isinstance(o.Proxy, PathProject.ObjectPathProject): + return o + +def findMachine(): + '''find machine object for the tooltable editor ''' + for o in FreeCAD.ActiveDocument.Objects: + if "Proxy" in o.PropertiesList: + if isinstance(o.Proxy, PathScripts.PathMachine.Machine): + return o + +def addToProject(obj): + """Adds a path obj to this document, if no PathParoject exists it's created on the fly""" + project = findProj() + + if project == None: + project = PathProject.CommandProject.Create() + + g = project.Group + g.append(obj) + project.Group = g + + return project + + +def getLastZ(obj): + ''' find the last z value in the project ''' + lastZ = "" + for g in obj.Group: + for c in g.Path.Commands: + for n in c.Parameters: + if n == 'Z': + lastZ= c.Parameters['Z'] + return lastZ + diff --git a/src/Mod/Path/PathScripts/PostUtils.py b/src/Mod/Path/PathScripts/PostUtils.py new file mode 100644 index 0000000000..423e426b50 --- /dev/null +++ b/src/Mod/Path/PathScripts/PostUtils.py @@ -0,0 +1,162 @@ +#*************************************************************************** +#* (c) Yorik van Havre (yorik@uncreated.net) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +These are a common functions and classes for creating custom post processors. +''' + +from PySide import QtCore, QtGui +import FreeCADGui +import FreeCAD + +class OldHighlighter(QtGui.QSyntaxHighlighter): + def highlightBlock(self, text): + myClassFormat = QtGui.QTextCharFormat() + myClassFormat.setFontWeight(QtGui.QFont.Bold) + myClassFormat.setForeground(QtCore.Qt.green) + # the regex pattern to be colored + pattern = "(G.*?|M.*?)\\s" + expression = QtCore.QRegExp(pattern) + index = text.index(expression) + while index >= 0: + length = expression.matchedLength() + setFormat(index, length, myClassFormat) + index = text.index(expression, index + length) + + + +class GCodeHighlighter(QtGui.QSyntaxHighlighter): + def __init__(self, parent=None): + super(GCodeHighlighter, self).__init__(parent) + + + keywordFormat = QtGui.QTextCharFormat() + keywordFormat.setForeground(QtCore.Qt.cyan) + keywordFormat.setFontWeight(QtGui.QFont.Bold) + keywordPatterns = ["\\bG[0-9]+\\b", + "\\bM[0-9]+\\b"] + + self.highlightingRules = [(QtCore.QRegExp(pattern), keywordFormat) for pattern in keywordPatterns] + + speedFormat = QtGui.QTextCharFormat() + speedFormat.setFontWeight(QtGui.QFont.Bold) + speedFormat.setForeground(QtCore.Qt.green) + self.highlightingRules.append((QtCore.QRegExp("\\bF[0-9\\.]+\\b"),speedFormat)) + + def highlightBlock(self, text): + for pattern, format in self.highlightingRules: + expression = QtCore.QRegExp(pattern) + index = expression.indexIn(text) + while index >= 0: + length = expression.matchedLength() + self.setFormat(index, length, format) + index = expression.indexIn(text, index + length) + + + +class GCodeEditorDialog(QtGui.QDialog): + def __init__(self, parent = FreeCADGui.getMainWindow()): + QtGui.QDialog.__init__(self,parent) + + layout = QtGui.QVBoxLayout(self) + + # nice text editor widget for editing the gcode + self.editor = QtGui.QTextEdit() + font = QtGui.QFont() + font.setFamily("Courier") + font.setFixedPitch(True) + font.setPointSize(10) + self.editor.setFont(font) + self.editor.setText("G01 X55 Y4.5 F300.0") + self.highlighter = GCodeHighlighter(self.editor.document()) + layout.addWidget(self.editor) + + # OK and Cancel buttons + self.buttons = QtGui.QDialogButtonBox( + QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, + QtCore.Qt.Horizontal, self) + layout.addWidget(self.buttons) + + # restore placement and size + self.paramKey = "User parameter:BaseApp/Values/Mod/Path/GCodeEditor/" + params = FreeCAD.ParamGet(self.paramKey) + posX = params.GetInt("posX") + posY = params.GetInt("posY") + if posX > 0 and posY > 0: + self.move(posX, posY) + width = params.GetInt("width") + height = params.GetInt("height") + if width > 0 and height > 0: + self.resize(width, height) + + self.buttons.accepted.connect(self.accept) + self.buttons.rejected.connect(self.reject) + + def done(self, *args, **kwargs): + params = FreeCAD.ParamGet(self.paramKey) + params.SetInt("posX", self.x()) + params.SetInt("posY", self.y()) + params.SetInt("width", self.size().width()) + params.SetInt("height", self.size().height()) + return QtGui.QDialog.done(self, *args, **kwargs) + + +def stringsplit(commandline): + returndict = {'command':None, 'X':None, 'Y':None, 'Z':None, 'A':None, 'B':None, 'F':None, 'T':None, 'S':None, 'I':None, 'J':None,'K':None, 'txt': None} + wordlist = [a.strip() for a in commandline.split(" ")] + if wordlist[0][0] == '(': + returndict['command'] = 'message' + returndict['txt'] = wordlist[0] + else: + returndict['command'] = wordlist[0] + for word in wordlist[1:]: + returndict[word[0]] = word[1:] + + return returndict + +def fmt(num,dec,units): + ''' used to format axis moves, feedrate, etc for decimal places and units''' + if units == 'G21': #metric + fnum = '%.*f' % (dec, num) + else: #inch + fnum = '%.*f' % (dec, num/25.4) #since FreeCAD uses metric units internally + return fnum + +def editor(gcode): + '''pops up a handy little editor to look at the code output ''' + dia = GCodeEditorDialog() + dia.editor.setText(gcode) + result = dia.exec_() + +def fcoms(string,commentsym): + ''' filter and rebuild comments with user preferred comment symbol''' + if len(commentsym)==1: + s1 = string.replace('(', commentsym) + comment = s1.replace(')', '') + else: + return string + return comment + + + diff --git a/src/Mod/Path/PathScripts/TooltableEditor.py b/src/Mod/Path/PathScripts/TooltableEditor.py new file mode 100644 index 0000000000..64b5aaa905 --- /dev/null +++ b/src/Mod/Path/PathScripts/TooltableEditor.py @@ -0,0 +1,663 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + + +import FreeCAD,Path, xml.sax, os +from PySide import QtCore, QtGui +import DraftGui + +# convenience functions + + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + + +# Tooltable XML readers + + +class FreeCADTooltableHandler( xml.sax.ContentHandler ): + # http://www.tutorialspoint.com/python/python_xml_processing.htm + def __init__(self): + self.tooltable = None + self.tool = None + self.number = None + + # Call when an element is found + def startElement(self, tag, attributes): + if tag == "Tooltable": + self.tooltable = Path.Tooltable() + elif tag == "Toolslot": + self.number = int(attributes["number"]) + elif tag == "Tool": + self.tool = Path.Tool() + self.tool.Name = str(attributes["name"]) + self.tool.ToolType = str(attributes["type"]) + self.tool.Material = str(attributes["mat"]) + # for some reason without the following line I get an error + print attributes["diameter"] + self.tool.Diameter = float(attributes["diameter"]) + self.tool.LengthOffset = float(attributes["length"]) + self.tool.FlatRadius = float(attributes["flat"]) + self.tool.CornerRadius = float(attributes["corner"]) + self.tool.CuttingEdgeAngle = float(attributes["angle"]) + self.tool.CuttingEdgeHeight = float(attributes["height"]) + + # Call when an elements ends + def endElement(self, tag): + if tag == "Toolslot": + if self.tooltable and self.tool and self.number: + self.tooltable.setTool(self.number,self.tool) + self.number = None + self.tool = None + + +class HeeksTooltableHandler( xml.sax.ContentHandler ): + def __init__(self): + self.tooltable = Path.Tooltable() + self.tool = None + self.number = None + + # Call when an element is found + def startElement(self, tag, attributes): + if tag == "Tool": + self.tool = Path.Tool() + self.number = int(attributes["tool_number"]) + self.tool.Name = str(attributes["title"]) + elif tag == "params": + t = str(attributes["type"]) + if t == "drill": + self.tool.ToolType = "Drill" + elif t == "center_drill_bit": + self.tool.ToolType = "CenterDrill" + elif t == "end_mill": + self.tool.ToolType = "EndMill" + elif t == "slot_cutter": + self.tool.ToolType = "SlotCutter" + elif t == "ball_end_mill": + self.tool.ToolType = "BallEndMill" + elif t == "chamfer": + self.tool.ToolType = "Chamfer" + elif t == "engraving_bit": + self.tool.ToolType = "Engraver" + m = str(attributes["material"]) + if m == "0": + self.tool.Material = "HighSpeedSteel" + elif m == "1": + self.tool.Material = "Carbide" + # for some reason without the following line I get an error + print attributes["diameter"] + self.tool.Diameter = float(attributes["diameter"]) + self.tool.LengthOffset = float(attributes["tool_length_offset"]) + self.tool.FlatRadius = float(attributes["flat_radius"]) + self.tool.CornerRadius = float(attributes["corner_radius"]) + self.tool.CuttingEdgeAngle = float(attributes["cutting_edge_angle"]) + self.tool.CuttingEdgeHeight = float(attributes["cutting_edge_height"]) + + # Call when an elements ends + def endElement(self, tag): + if tag == "Tool": + if self.tooltable and self.tool and self.number: + self.tooltable.setTool(self.number,self.tool) + self.number = None + self.tool = None + + +# Tooltable Editor + + +class Editor(QtGui.QDialog): + + def __init__(self,obj): + + QtGui.QDialog.__init__(self) + self.setObjectName(_fromUtf8("TooltableEditor")) + self.resize(468, 476) + self.verticalLayout = QtGui.QVBoxLayout(self) + self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) + self.DECIMALS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2) + self.FORMAT = DraftGui.makeFormatSpec(self.DECIMALS,'Length') + # left groupbox + self.groupBox = QtGui.QGroupBox(self) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth()) + self.groupBox.setSizePolicy(sizePolicy) + self.groupBox.setObjectName(_fromUtf8("groupBox")) + self.verticalLayout_2 = QtGui.QVBoxLayout(self.groupBox) + self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) + self.horizontalLayout_9 = QtGui.QHBoxLayout() + self.horizontalLayout_9.setObjectName(_fromUtf8("horizontalLayout_9")) + + # import button + self.ButtonImport = QtGui.QPushButton(self.groupBox) + icon = QtGui.QIcon.fromTheme(_fromUtf8("document-import")) + self.ButtonImport.setIcon(icon) + self.ButtonImport.setObjectName(_fromUtf8("ButtonImport")) + self.horizontalLayout_9.addWidget(self.ButtonImport) + + # export button + self.ButtonExport = QtGui.QPushButton(self.groupBox) + icon = QtGui.QIcon.fromTheme(_fromUtf8("document-export")) + self.ButtonExport.setIcon(icon) + self.ButtonExport.setObjectName(_fromUtf8("ButtonExport")) + self.horizontalLayout_9.addWidget(self.ButtonExport) + + # tools list + self.verticalLayout_2.addLayout(self.horizontalLayout_9) + self.ToolsList = QtGui.QTreeWidget(self.groupBox) + self.ToolsList.setObjectName(_fromUtf8("ToolsList")) + self.ToolsList.header().setDefaultSectionSize(40) + self.verticalLayout_2.addWidget(self.ToolsList) + + # add button + self.horizontalLayout_8 = QtGui.QHBoxLayout() + self.horizontalLayout_8.setObjectName(_fromUtf8("horizontalLayout_8")) + self.ButtonAdd = QtGui.QPushButton(self.groupBox) + icon = QtGui.QIcon.fromTheme(_fromUtf8("edit-add")) + self.ButtonAdd.setIcon(icon) + self.ButtonAdd.setObjectName(_fromUtf8("ButtonAdd")) + self.horizontalLayout_8.addWidget(self.ButtonAdd) + + # delete button + self.ButtonDelete = QtGui.QPushButton(self.groupBox) + icon = QtGui.QIcon.fromTheme(_fromUtf8("edit-delete")) + self.ButtonDelete.setIcon(icon) + self.ButtonDelete.setObjectName(_fromUtf8("ButtonDelete")) + self.horizontalLayout_8.addWidget(self.ButtonDelete) + + # up button + self.ButtonUp = QtGui.QPushButton(self.groupBox) + icon = QtGui.QIcon.fromTheme(_fromUtf8("go-up")) + self.ButtonUp.setIcon(icon) + self.ButtonDelete.setObjectName(_fromUtf8("ButtonUp")) + self.horizontalLayout_8.addWidget(self.ButtonUp) + + # down button + self.ButtonDown = QtGui.QPushButton(self.groupBox) + icon = QtGui.QIcon.fromTheme(_fromUtf8("go-down")) + self.ButtonDown.setIcon(icon) + self.ButtonDown.setObjectName(_fromUtf8("ButtonDown")) + self.horizontalLayout_8.addWidget(self.ButtonDown) + + # right groupbox + self.verticalLayout_2.addLayout(self.horizontalLayout_8) + self.horizontalLayout.addWidget(self.groupBox) + self.groupBox_2 = QtGui.QGroupBox(self) + self.groupBox_2.setObjectName(_fromUtf8("groupBox_2")) + self.verticalLayout_3 = QtGui.QVBoxLayout(self.groupBox_2) + self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) + + # name + self.label = QtGui.QLabel(self.groupBox_2) + self.label.setObjectName(_fromUtf8("label")) + self.verticalLayout_3.addWidget(self.label) + self.NameField = QtGui.QLineEdit(self.groupBox_2) + self.NameField.setObjectName(_fromUtf8("NameField")) + self.verticalLayout_3.addWidget(self.NameField) + + # type + self.label_2 = QtGui.QLabel(self.groupBox_2) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.verticalLayout_3.addWidget(self.label_2) + self.TypeField = QtGui.QComboBox(self.groupBox_2) + self.TypeField.setObjectName(_fromUtf8("TypeField")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.TypeField.addItem(_fromUtf8("")) + self.verticalLayout_3.addWidget(self.TypeField) + + # material + self.label_3 = QtGui.QLabel(self.groupBox_2) + self.label_3.setObjectName(_fromUtf8("label_3")) + self.verticalLayout_3.addWidget(self.label_3) + self.MaterialField = QtGui.QComboBox(self.groupBox_2) + self.MaterialField.setObjectName(_fromUtf8("MaterialField")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.MaterialField.addItem(_fromUtf8("")) + self.verticalLayout_3.addWidget(self.MaterialField) + self.label_4 = QtGui.QLabel(self.groupBox_2) + self.label_4.setObjectName(_fromUtf8("label_4")) + self.verticalLayout_3.addWidget(self.label_4) + + # diameter + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.label_5 = QtGui.QLabel(self.groupBox_2) + self.label_5.setObjectName(_fromUtf8("label_5")) + self.horizontalLayout_2.addWidget(self.label_5) + self.DiameterField = QtGui.QDoubleSpinBox(self.groupBox_2) + self.DiameterField.setMaximum(9999) + self.DiameterField.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.DiameterField.setObjectName(_fromUtf8("DiameterField")) + self.horizontalLayout_2.addWidget(self.DiameterField) + self.verticalLayout_3.addLayout(self.horizontalLayout_2) + + # length offset + self.horizontalLayout_3 = QtGui.QHBoxLayout() + self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3")) + self.label_6 = QtGui.QLabel(self.groupBox_2) + self.label_6.setObjectName(_fromUtf8("label_6")) + self.horizontalLayout_3.addWidget(self.label_6) + self.LengthOffsetField = QtGui.QDoubleSpinBox(self.groupBox_2) + self.LengthOffsetField.setMaximum(9999) + self.LengthOffsetField.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.LengthOffsetField.setObjectName(_fromUtf8("LengthOffsetField")) + self.horizontalLayout_3.addWidget(self.LengthOffsetField) + self.verticalLayout_3.addLayout(self.horizontalLayout_3) + + # flat radius + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) + self.label_7 = QtGui.QLabel(self.groupBox_2) + self.label_7.setObjectName(_fromUtf8("label_7")) + self.horizontalLayout_4.addWidget(self.label_7) + self.FlatRadiusField = QtGui.QDoubleSpinBox(self.groupBox_2) + self.FlatRadiusField.setMaximum(9999) + self.FlatRadiusField.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.FlatRadiusField.setObjectName(_fromUtf8("FlatRadiusField")) + self.horizontalLayout_4.addWidget(self.FlatRadiusField) + self.verticalLayout_3.addLayout(self.horizontalLayout_4) + + # corner radius + self.horizontalLayout_5 = QtGui.QHBoxLayout() + self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5")) + self.label_8 = QtGui.QLabel(self.groupBox_2) + self.label_8.setObjectName(_fromUtf8("label_8")) + self.horizontalLayout_5.addWidget(self.label_8) + self.CornerRadiusField = QtGui.QDoubleSpinBox(self.groupBox_2) + self.CornerRadiusField.setMaximum(9999) + self.CornerRadiusField.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.CornerRadiusField.setObjectName(_fromUtf8("CornerRadiusField")) + self.horizontalLayout_5.addWidget(self.CornerRadiusField) + self.verticalLayout_3.addLayout(self.horizontalLayout_5) + + # cutting edge angle + self.horizontalLayout_6 = QtGui.QHBoxLayout() + self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6")) + self.label_9 = QtGui.QLabel(self.groupBox_2) + self.label_9.setObjectName(_fromUtf8("label_9")) + self.horizontalLayout_6.addWidget(self.label_9) + self.CuttingEdgeAngleField = QtGui.QDoubleSpinBox(self.groupBox_2) + self.CuttingEdgeAngleField.setMaximum(360) + self.CuttingEdgeAngleField.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.CuttingEdgeAngleField.setObjectName(_fromUtf8("CuttingEdgeAngleField")) + self.horizontalLayout_6.addWidget(self.CuttingEdgeAngleField) + self.verticalLayout_3.addLayout(self.horizontalLayout_6) + + # cutting edge height + self.horizontalLayout_7 = QtGui.QHBoxLayout() + self.horizontalLayout_7.setObjectName(_fromUtf8("horizontalLayout_7")) + self.label_10 = QtGui.QLabel(self.groupBox_2) + self.label_10.setObjectName(_fromUtf8("label_10")) + self.horizontalLayout_7.addWidget(self.label_10) + self.CuttingEdgeHeightField = QtGui.QDoubleSpinBox(self.groupBox_2) + self.CuttingEdgeHeightField.setMaximum(9999) + self.CuttingEdgeHeightField.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.CuttingEdgeHeightField.setObjectName(_fromUtf8("CuttingEdgeHeightField")) + self.horizontalLayout_7.addWidget(self.CuttingEdgeHeightField) + self.verticalLayout_3.addLayout(self.horizontalLayout_7) + self.horizontalLayout.addWidget(self.groupBox_2) + self.verticalLayout.addLayout(self.horizontalLayout) + + # ok / cancel box + self.buttonBox = QtGui.QDialogButtonBox(self) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(_fromUtf8("buttonBox")) + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi() + + # connect buttons + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), self.reject) + QtCore.QObject.connect(self.ButtonImport, QtCore.SIGNAL(_fromUtf8("clicked()")), self.read) + QtCore.QObject.connect(self.ButtonExport, QtCore.SIGNAL(_fromUtf8("clicked()")), self.write) + QtCore.QObject.connect(self.ButtonAdd, QtCore.SIGNAL(_fromUtf8("clicked()")), self.addnew) + QtCore.QObject.connect(self.ButtonDelete, QtCore.SIGNAL(_fromUtf8("clicked()")), self.delete) + QtCore.QObject.connect(self.ButtonUp, QtCore.SIGNAL(_fromUtf8("clicked()")), self.moveup) + QtCore.QObject.connect(self.ButtonDown, QtCore.SIGNAL(_fromUtf8("clicked()")), self.movedown) + QtCore.QObject.connect(self.ToolsList, QtCore.SIGNAL(_fromUtf8("currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)")), self.selectTool) + QtCore.QObject.connect(self.NameField, QtCore.SIGNAL(_fromUtf8("textEdited(QString)")), self.changeName) + QtCore.QObject.connect(self.TypeField, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), self.changeType) + QtCore.QObject.connect(self.MaterialField, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), self.changeMaterial) + QtCore.QObject.connect(self.DiameterField, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.changeDiameter) + QtCore.QObject.connect(self.LengthOffsetField, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.changeLengthOffset) + QtCore.QObject.connect(self.FlatRadiusField, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.changeFlatRadius) + QtCore.QObject.connect(self.CornerRadiusField, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.changeCornerRadius) + QtCore.QObject.connect(self.CuttingEdgeAngleField, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.changeCuttingEdgeAngle) + QtCore.QObject.connect(self.CuttingEdgeHeightField, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.changeCuttingEdgeHeight) + QtCore.QMetaObject.connectSlotsByName(self) + self.tooltable = obj.Tooltable.copy() + self.tool = None + self.number = None + self.reset() + + def retranslateUi(self): + self.setWindowTitle(_translate("TooltableEditor", "Tooltable editor", None)) + self.groupBox.setTitle(_translate("TooltableEditor", "Tools list", None)) + self.ButtonImport.setText(_translate("TooltableEditor", "Import...", None)) + self.ButtonExport.setText(_translate("TooltableEditor", "Export...", None)) + self.ToolsList.headerItem().setText(0, _translate("TooltableEditor", "Slot", None)) + self.ToolsList.headerItem().setText(1, _translate("TooltableEditor", "Tool", None)) + self.ButtonAdd.setText(_translate("TooltableEditor", "Add new", None)) + self.ButtonDelete.setText(_translate("TooltableEditor", "Delete", None)) + self.ButtonUp.setText(_translate("TooltableEditor", "Move up", None)) + self.ButtonDown.setText(_translate("TooltableEditor", "Move down", None)) + self.groupBox_2.setTitle(_translate("TooltableEditor", "Tool properties", None)) + self.label.setText(_translate("TooltableEditor", "Name", None)) + self.label_2.setText(_translate("TooltableEditor", "Type", None)) + self.TypeField.setItemText(0, _translate("TooltableEditor", "Undefined", None)) + self.TypeField.setItemText(1, _translate("TooltableEditor", "Drill", None)) + self.TypeField.setItemText(2, _translate("TooltableEditor", "Center Drill", None)) + self.TypeField.setItemText(3, _translate("TooltableEditor", "Counter Sink", None)) + self.TypeField.setItemText(4, _translate("TooltableEditor", "Counter Bore", None)) + self.TypeField.setItemText(5, _translate("TooltableEditor", "Reamer", None)) + self.TypeField.setItemText(6, _translate("TooltableEditor", "Tap", None)) + self.TypeField.setItemText(7, _translate("TooltableEditor", "End Mill", None)) + self.TypeField.setItemText(8, _translate("TooltableEditor", "Slot Cutter", None)) + self.TypeField.setItemText(9, _translate("TooltableEditor", "Ball End Mill", None)) + self.TypeField.setItemText(10, _translate("TooltableEditor", "Chamfer Mill", None)) + self.TypeField.setItemText(11, _translate("TooltableEditor", "Corner Round", None)) + self.TypeField.setItemText(12, _translate("TooltableEditor", "Engraver", None)) + self.label_3.setText(_translate("TooltableEditor", "Material", None)) + self.MaterialField.setItemText(0, _translate("TooltableEditor", "Undefined", None)) + self.MaterialField.setItemText(1, _translate("TooltableEditor", "High Speed Steel", None)) + self.MaterialField.setItemText(2, _translate("TooltableEditor", "High Carbon Tool Steel", None)) + self.MaterialField.setItemText(3, _translate("TooltableEditor", "Cast Alloy", None)) + self.MaterialField.setItemText(4, _translate("TooltableEditor", "Carbide", None)) + self.MaterialField.setItemText(5, _translate("TooltableEditor", "Ceramics", None)) + self.MaterialField.setItemText(6, _translate("TooltableEditor", "Diamond", None)) + self.MaterialField.setItemText(7, _translate("TooltableEditor", "Sialon", None)) + self.label_4.setText(_translate("TooltableEditor", "Properties", None)) + self.label_5.setText(_translate("TooltableEditor", "Diameter", None)) +# self.DiameterField.setSuffix(_translate("TooltableEditor", "mm", None)) + self.label_6.setText(_translate("TooltableEditor", "Length offset", None)) + self.LengthOffsetField.setSuffix(_translate("TooltableEditor", "mm", None)) + self.label_7.setText(_translate("TooltableEditor", "Flat radius", None)) + self.FlatRadiusField.setSuffix(_translate("TooltableEditor", "mm", None)) + self.label_8.setText(_translate("TooltableEditor", "Corner radius", None)) + self.CornerRadiusField.setSuffix(_translate("TooltableEditor", "mm", None)) + self.label_9.setText(_translate("TooltableEditor", "Cutting edge angle", None)) + self.CuttingEdgeAngleField.setSuffix(_translate("TooltableEditor", "°", None)) + self.label_10.setText(_translate("TooltableEditor", "Cutting edge height", None)) + self.CuttingEdgeHeightField.setSuffix(_translate("TooltableEditor", "mm", None)) + + def reset(self): + "resets the editor with the contents of the current internal tooltable" + self.tool = None + self.number = None + self.ToolsList.clear() + for number,tool in self.tooltable.Tools.iteritems(): + item = QtGui.QTreeWidgetItem(self.ToolsList) + item.setText(0,str(number)) + item.setText(1,tool.Name) + self.NameField.setText("") + self.TypeField.setCurrentIndex(-1) + self.MaterialField.setCurrentIndex(-1) + self.DiameterField.setValue(0) + self.LengthOffsetField.setValue(0) + self.FlatRadiusField.setValue(0) + self.CornerRadiusField.setValue(0) + self.CuttingEdgeAngleField.setValue(0) + self.CuttingEdgeHeightField.setValue(0) + + def selectTool(self,current,previous): + "fills the data of the currently selected tool" + if current: + number = int(current.text(0)) + tool = self.tooltable.getTool(number) + if tool: + self.number = number + self.tool = tool + self.NameField.setText(tool.Name) + self.TypeField.setCurrentIndex(self.getType(tool.ToolType)) + self.MaterialField.setCurrentIndex(self.getMaterial(tool.Material)) + self.DiameterField.setValue(tool.Diameter) + self.LengthOffsetField.setValue(tool.LengthOffset) + self.FlatRadiusField.setValue(tool.FlatRadius) + self.CornerRadiusField.setValue(tool.CornerRadius) + self.CuttingEdgeAngleField.setValue(tool.CuttingEdgeAngle) + self.CuttingEdgeHeightField.setValue(tool.CuttingEdgeHeight) + + def getType(self,tooltype): + "gets a combobox index number for a given type or viceversa" + toolslist = ["Drill","CenterDrill","CounterSink","CounterBore", + "Reamer","Tap","EndMill","SlotCutter","BallEndMill", + "ChamferMill","CornerRound","Engraver"] + if isinstance(tooltype,str): + if tooltype in toolslist: + return toolslist.index(tooltype)+1 + else: + return 0 + else: + if tooltype == 0: + return "Undefined" + else: + return toolslist[tooltype-1] + + def getMaterial(self,material): + "gets a combobox index number for a given material or viceversa" + matslist = ["HighSpeedSteel","HighCarbonToolSteel","CastAlloy", + "Carbide","Ceramics","Diamond","Sialon"] + if isinstance(material,str): + if material in matslist: + return matslist.index(material)+1 + else: + return 0 + else: + if material == 0: + return "Undefined" + else: + return matslist[material-1] + + def changeName(self,text): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.Name = str(text) + self.changeTool() + if self.number: + l = self.ToolsList.findItems(str(self.number),QtCore.Qt.MatchExactly,0) + if len(l) == 1: + l[0].setText(1,text) + + def changeType(self,num): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.ToolType = self.getType(num) + self.changeTool() + + def changeMaterial(self,num): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.Material = self.getMaterial(num) + self.changeTool() + + def changeDiameter(self,value): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.Diameter = value + self.changeTool() + + def changeLengthOffset(self,value): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.LengthOffset = value + self.changeTool() + + def changeFlatRadius(self,value): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.FlatRadius = value + self.changeTool() + + def changeCornerRadius(self,value): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.CornerRadius = value + self.changeTool() + + def changeCuttingEdgeAngle(self,value): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.CuttingEdgeAngle = value + self.changeTool() + + def changeCuttingEdgeHeight(self,value): + "called when the corresponding field has changed (needed for nasty pyside bug)" + if self.tool: + self.tool.CuttingEdgeHeight = value + self.changeTool() + + def changeTool(self): + "changes a given tool" + if self.number and self.tool: + self.tooltable.setTool(self.number,self.tool) + + def delete(self): + "deletes the current tool" + if self.number: + self.tooltable.deleteTool(self.number) + self.reset() + + def addnew(self): + "adds a new tool at the end of the table" + tool = Path.Tool() + print self.NameField + if self.NameField.text(): + tool.Name = str(self.NameField.text()) + tool.ToolType = self.getType(self.TypeField.currentIndex()) + tool.Material = self.getMaterial(self.MaterialField.currentIndex()) + tool.Diameter = self.DiameterField.value() + tool.LengthOffset = self.LengthOffsetField.value() + tool.FlatRadius = self.FlatRadiusField.value() + tool.CornerRadius = self.CornerRadiusField.value() + tool.CuttingEdgeAngle = self.CuttingEdgeAngleField.value() + tool.CuttingEdgeHeight = self.CuttingEdgeHeightField.value() + self.tooltable.addTools(tool) + self.reset() + + def read(self): + "imports a tooltable from a file" + filename = QtGui.QFileDialog.getOpenFileName(self, _translate("TooltableEditor","Open tooltable",None),None, _translate("TooltableEditor","Tooltable XML (*.xml);;HeeksCAD tooltable (*.tooltable)",None)) + if filename: + parser = xml.sax.make_parser() + parser.setFeature(xml.sax.handler.feature_namespaces, 0) + if os.path.splitext(filename[0])[1].lower() == ".tooltable": + Handler = HeeksTooltableHandler() + else: + Handler = FreeCADTooltableHandler() + parser.setContentHandler( Handler ) + parser.parse(str(filename[0])) + if Handler.tooltable: + self.tooltable = Handler.tooltable + self.reset() + + def write(self): + "exports the tooltable to a file" + if self.tooltable: + filename = QtGui.QFileDialog.getSaveFileName(self, _translate("TooltableEditor","Save tooltable",None),None, _translate("TooltableEditor","Tooltable XML (*.xml)",None)) + if filename: + fil = open(str(filename[0]),"wb") + fil.write('\n') + fil.write(self.tooltable.Content) + fil.close() + print "Written ",filename[0] + + def moveup(self): + "moves a tool to a lower number, if possible" + if self.number: + if self.number < 2: + return + target = self.number - 1 + t1 = self.tooltable.getTool(self.number).copy() + self.tooltable.deleteTool(self.number) + if target in self.tooltable.Tools.keys(): + t2 = self.tooltable.getTool(target).copy() + self.tooltable.deleteTool(target) + self.tooltable.setTool(self.number,t2) + self.tooltable.setTool(target,t1) + self.reset() + + def movedown(self): + "moves a tool to a higher number, if possible" + if self.number: + target = self.number + 1 + t1 = self.tooltable.getTool(self.number).copy() + self.tooltable.deleteTool(self.number) + if target in self.tooltable.Tools.keys(): + t2 = self.tooltable.getTool(target).copy() + self.tooltable.deleteTool(target) + self.tooltable.setTool(self.number,t2) + self.tooltable.setTool(target,t1) + self.reset() + +def edit(objectname): + """edit(objectname): this is the main function of this module. + opens an editor dialog to edit the Tooltable of the given object""" + obj = FreeCAD.ActiveDocument.getObject(objectname) + if not obj: + raise Exception(_translate("TooltableEditor","Object not found",None)) + if not hasattr(obj,"Tooltable"): + raise Exception(_translate("TooltableEditor","Object doesn't have a tooltable property",None)) + dialog = Editor(obj) + r = dialog.exec_() + if r: + tooltable = dialog.tooltable + FreeCAD.ActiveDocument.openTransaction("Edit Tooltable") + obj.Tooltable = tooltable + FreeCAD.ActiveDocument.commitTransaction() + + obj.ViewObject.finishEditing() + diff --git a/src/Mod/Path/PathScripts/__init__.py b/src/Mod/Path/PathScripts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Mod/Path/PathScripts/centroid_post.py b/src/Mod/Path/PathScripts/centroid_post.py new file mode 100644 index 0000000000..d8acd9b86d --- /dev/null +++ b/src/Mod/Path/PathScripts/centroid_post.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' example post for Centroid CNC mill''' +import FreeCAD +import datetime +now = datetime.datetime.now() +originfile = FreeCAD.ActiveDocument.FileName +import Path, PathScripts +from PathScripts import PostUtils + + +#*************************************************************************** +# user editable stuff here + +UNITS = "G20" #old style inch units for this shop +MACHINE_NAME = "BigMill" +CORNER_MIN = {'x':-609.6, 'y':-152.4, 'z':0 } #use metric for internal units +CORNER_MAX = {'x':609.6, 'y':152.4, 'z':304.8 } #use metric for internal units + +SHOW_EDITOR = True +MODAL = True +COMMENT= ';' #centroid control comment symbol + +HEADER = "" +HEADER += ";Exported by FreeCAD\n" +HEADER += ";Post Processor: " + __name__ +"\n" +HEADER += ";CAM file: "+originfile+"\n" +HEADER += ";Output Time:"+str(now)+"\n" + +TOOLRETURN = '''M5 M25 +G49 H0\n''' #spindle off,height offset canceled,spindle retracted (M25 is a centroid command to retract spindle) + +ZAXISRETURN = '''G91 G28 X0 Z0 +G90\n''' + +SAFETYBLOCK = 'G90 G80 G40 G49\n' + +AXIS_DECIMALS = 4 +FEED_DECIMALS = 1 +SPINDLE_DECIMALS = 0 + +FOOTER = 'M99'+'\n' + +# don't edit with the stuff below the next line unless you know what you're doing :) +#*************************************************************************** + + +if open.__module__ == '__builtin__': + pythonopen = open + +def export(selection,filename): + params = ['X','Y','Z','A','B','I','J','F','H','S','T','Q','R','L'] #Using XY plane most of the time so skipping K + for obj in selection: + if not hasattr(obj,"Path"): + print "the object " + obj.Name + " is not a path. Please select only path and Compounds." + return + myMachine = None + for pathobj in selection: + if hasattr(pathobj,"Group"): #We have a compound or selection. + for p in pathobj.Group: + if p.Name == "Machine": + myMachine = p + if myMachine is None: + print "No machine found in this selection" + else: + if myMachine.MachineUnits == "Metric": + UNITS = "G21" + else: + UNITS = "G20" + + gcode ='' + gcode+= HEADER + gcode+= SAFETYBLOCK + gcode+= UNITS+'\n' + + lastcommand = None + gcode+= COMMENT+ selection[0].Description +'\n' + + gobjects = [] + for g in selection[0].Group: + if g.Name <>'Machine': #filtering out gcode home position from Machine object + gobjects.append(g) + + for obj in gobjects: + for c in obj.Path.Commands: + outstring = [] + command = c.Name + + if command[0]=='(': + command = PostUtils.fcoms(command, COMMENT) + + outstring.append(command) + if MODAL == True: + if command == lastcommand: + outstring.pop(0) + if c.Parameters >= 1: + for param in params: + if param in c.Parameters: + if param == 'F': + outstring.append(param + PostUtils.fmt(c.Parameters['F'], FEED_DECIMALS,UNITS)) + elif param == 'H': + outstring.append(param + str(int(c.Parameters['H']))) + elif param == 'S': + outstring.append(param + PostUtils.fmt(c.Parameters['S'], SPINDLE_DECIMALS,'G21')) #rpm is unitless-therefore I had to 'fake it out' by using metric units which don't get converted from entered value + elif param == 'T': + outstring.append(param + str(int(c.Parameters['T']))) + else: + outstring.append(param + PostUtils.fmt(c.Parameters[param],AXIS_DECIMALS,UNITS)) + outstr = str(outstring) + outstr =outstr.replace('[','') + outstr =outstr.replace(']','') + outstr =outstr.replace("'",'') + outstr =outstr.replace(",",'') + gcode+= outstr + '\n' + lastcommand = c.Name + gcode+= TOOLRETURN + gcode+= SAFETYBLOCK + gcode+= FOOTER + if SHOW_EDITOR: + PostUtils.editor(gcode) + gfile = pythonopen(filename,"wb") + gfile.write(gcode) + gfile.close() + diff --git a/src/Mod/Path/PathScripts/comparams_post.py b/src/Mod/Path/PathScripts/comparams_post.py new file mode 100644 index 0000000000..bd0fb4fcc6 --- /dev/null +++ b/src/Mod/Path/PathScripts/comparams_post.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2015 Dan Falck * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** +''' Example Post, using Path.Commands instead of Path.toGCode strings for Path gcode output. ''' +import FreeCAD +import Path, PathScripts +from PathScripts import PostUtils + +def fmt(num): + fnum = "" + fnum += '%.3f' % (num) + return fnum + +def ffmt(num): + fnum = "" + fnum += '%.1f' % (num) + return fnum + +class saveVals(object): + ''' save command info for modal output''' + def __init__(self, command): + self.com = command.Name + self.params = command.Parameters + + def retVals(self): + return self.com, self.params + +def lineout(command, oldvals, modal): + line = "" + if modal and (oldvals.com == command.Name): + line +="" + else: + line += str(command.Name) + if command.Name == 'M6': + line+= 'T'+str(int(command.Parameters['T'])) + if command.Name == 'M3': + line+= 'S'+str(ffmt(command.Parameters['S'])) + if command.Name == 'M4': + line+= 'S'+str(ffmt(command.Parameters['S'])) + if 'X' in command.Parameters: + line += "X"+str(fmt(command.Parameters['X'])) + if 'Y' in command.Parameters: + line += "Y"+str(fmt(command.Parameters['Y'])) + if 'Z' in command.Parameters: + line += "Z"+str(fmt(command.Parameters['Z'])) + if 'I' in command.Parameters: + line += "I"+str(fmt(command.Parameters['I'])) + if 'J' in command.Parameters: + line += "J"+str(fmt(command.Parameters['J'])) + if 'F' in command.Parameters: + line += "F"+str(ffmt(command.Parameters['F'])) + return line + +def export(obj,filename): + modal=True + commands = obj[0] + gcode = '' + safetyblock1 = 'G90G40G49\n' + gcode+=safetyblock1 + + units = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") + if units.GetInt('UserSchema') == 0: + firstcommand = Path.Command('G21') #metric mode + else: + firstcommand = Path.Command('G20') #inch mode + oldvals = saveVals(firstcommand) #save first command for modal use + fp = obj[0] + gcode+= firstcommand.Name + + if hasattr(fp,"Path"): + for c in fp.Path.Commands: + gcode+= lineout(c, oldvals, modal)+'\n' + oldvals = saveVals(c) + gcode+='M2\n' + gfile = open(filename,"wb") + gfile.write(gcode) + gfile.close() + else: + FreeCAD.Console.PrintError('Select a path object and try again\n') + if obj[0].Editor: + FreeCAD.Console.PrintMessage('Editor Activated\n') + dia = PostUtils.GCodeEditorDialog() + dia.editor.setText(gcode) + dia.exec_() + + diff --git a/src/Mod/Path/PathScripts/dumper_post.py b/src/Mod/Path/PathScripts/dumper_post.py new file mode 100644 index 0000000000..358daa9371 --- /dev/null +++ b/src/Mod/Path/PathScripts/dumper_post.py @@ -0,0 +1,93 @@ +#*************************************************************************** +#* (c) sliptonic (shopinthewoods@gmail.com) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +Dumper is an extremely simple postprocessor file for the Path workbench. It is used +to dump the command list from one or more Path objects for simple inspection. This post +doesn't do any manipulation of the path and doesn't write anything to disk. It just +shows the dialog so you can see it. Useful for debugging, but not much else. +''' + +import datetime +now = datetime.datetime.now() +from PathScripts import PostUtils +SHOW_EDITOR = True + +# to distinguish python built-in open function from the one declared below +if open.__module__ == '__builtin__': + pythonopen = open + + +def export(objectslist,filename): + output = '''(This ouput produced with the dump post processor) +(Dump is useful for inspecting the raw commands in your paths) +(but is not useful for driving machines.) +(Consider setting a default postprocessor in your project or ) +(exporting your paths using a specific post that matches your machine) + +''' + + "called when freecad exports a list of objects" + for obj in objectslist: + + if not hasattr(obj,"Path"): + print "the object " + obj.Name + " is not a path. Please select only path and Compounds." + return + print "postprocessing..." + output += parse(obj) + + if SHOW_EDITOR: + dia = PostUtils.GCodeEditorDialog() + dia.editor.setText(output) + result = dia.exec_() + if result: + final = dia.editor.toPlainText() + else: + final = output + else: + final = output + + print "done postprocessing." + +def parse(pathobj): + out = "" + + if hasattr(pathobj,"Group"): #We have a compound or project. + out += "(compound: " + pathobj.Label + ")\n" + for p in pathobj.Group: + out += parse(p) + return out + else: #parsing simple path + + if not hasattr(pathobj,"Path"): #groups might contain non-path things like stock. + return out + + out += "(Path: " + pathobj.Label + ")\n" + + for c in pathobj.Path.Commands: + out += str(c) + "\n" + return out + +print __name__ + " gcode postprocessor loaded." + diff --git a/src/Mod/Path/PathScripts/example_post.py b/src/Mod/Path/PathScripts/example_post.py new file mode 100644 index 0000000000..8dc85075f9 --- /dev/null +++ b/src/Mod/Path/PathScripts/example_post.py @@ -0,0 +1,102 @@ +#*************************************************************************** +#* (c) Yorik van Havre (yorik@uncreated.net) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +This is an example postprocessor file for the Path workbench. It is used +to save a list of FreeCAD Path objects to a file. + +Read the Path Workbench documentation to know how to convert Path objects +to GCode. +''' + +import datetime +now = datetime.datetime.now() + + +# to distinguish python built-in open function from the one declared below +if open.__module__ == '__builtin__': + pythonopen = open + + +def export(objectslist,filename): + "called when freecad exports a list of objects" + if len(objectslist) > 1: + print "This script is unable to write more than one Path object" + return + obj = objectslist[0] + if not hasattr(obj,"Path"): + print "the given object is not a path" + gcode = obj.Path.toGCode() + gcode = parse(gcode) + gfile = pythonopen(filename,"wb") + gfile.write(gcode) + gfile.close() + + +def parse(inputstring): + "parse(inputstring): returns a parsed output string" + print "postprocessing..." + + output = "" + + # write some stuff first + output += "N10 ;time:"+str(now)+"\n" + output += "N20 G17 G20 G80 G40 G90\n" + output += "N30 (Exported by FreeCAD)\n" + + linenr = 100 + lastcommand = None + # treat the input line by line + lines = inputstring.split("\n") + for line in lines: + # split the G/M command from the arguments + if " " in line: + command,args = line.split(" ",1) + else: + # no space found, which means there are no arguments + command = line + args = "" + # add a line number + output += "N" + str(linenr) + " " + # only print the command if it is not the same as the last one + if command != lastcommand: + output += command + " " + output += args + "\n" + # increment the line number + linenr += 10 + # store the latest command + lastcommand = command + + # write some more stuff at the end + output += "N" + str(linenr) + " M05\n" + output += "N" + str(linenr + 10) + " M25\n" + output += "N" + str(linenr + 20) + " G00 X-1.0 Y1.0\n" + output += "N" + str(linenr + 30) + " G17 G80 G40 G90\n" + output += "N" + str(linenr + 40) + " M99\n" + + print "done postprocessing." + return output + +print __name__ + " gcode postprocessor loaded." + diff --git a/src/Mod/Path/PathScripts/example_pre.py b/src/Mod/Path/PathScripts/example_pre.py new file mode 100644 index 0000000000..fbe43ec99a --- /dev/null +++ b/src/Mod/Path/PathScripts/example_pre.py @@ -0,0 +1,101 @@ +#*************************************************************************** +#* (c) Yorik van Havre (yorik@uncreated.net) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +This is an example preprocessor file for the Path workbench. Its aim is to +open a gcode file, parse its contents, and create the appropriate objects +in FreeCAD. + +Read the Path Workbench documentation to know how to create Path objects +from GCode. +''' + +import os, Path +import FreeCAD + +# to distinguish python built-in open function from the one declared below +if open.__module__ == '__builtin__': + pythonopen = open + + +def open(filename): + "called when freecad opens a file." + docname = os.path.splitext(os.path.basename(filename))[0] + doc = FreeCAD.newDocument(docname) + insert(filename,doc.Name) + + +def insert(filename,docname): + "called when freecad imports a file" + gfile = pythonopen(filename) + gcode = gfile.read() + gfile.close() + gcode = parse(gcode) + doc = FreeCAD.getDocument(docname) + obj = doc.addObject("Path::Feature","Path") + path = Path.Path(gcode) + obj.Path = path + + +def parse(inputstring): + "parse(inputstring): returns a parsed output string" + print "preprocessing..." + + # split the input by line + lines = inputstring.split("\n") + output = "" + lastcommand = None + + for l in lines: + # remove any leftover trailing and preceding spaces + l = l.strip() + if not l: + # discard empty lines + continue + if l[0].upper() in ["N"]: + # remove line numbers + l = l.split(" ",1)[1] + if l[0] in ["(","%","#"]: + # discard comment and other non strictly gcode lines + continue + if l[0].upper() in ["G","M"]: + # found a G or M command: we store it + output += l + "\n" + last = l[0].upper() + for c in l[1:]: + if not c.isdigit(): + break + else: + last += c + lastcommand = last + elif lastcommand: + # no G or M command: we repeat the last one + output += lastcommand + " " + l + "\n" + + print "done preprocessing." + return output + + +print __name__ + " gcode preprocessor loaded." + diff --git a/src/Mod/Path/PathScripts/linuxcnc_post.py b/src/Mod/Path/PathScripts/linuxcnc_post.py new file mode 100644 index 0000000000..5545edb1ba --- /dev/null +++ b/src/Mod/Path/PathScripts/linuxcnc_post.py @@ -0,0 +1,234 @@ +#*************************************************************************** +#* (c) sliptonic (shopinthewoods@gmail.com) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +This is a postprocessor file for the Path workbench. It is used to +take a pseudo-gcode fragment outputted by a Path object, and output +real GCode suitable for a linuxcnc 3 axis mill. This postprocessor, once placed +in the appropriate PathScripts folder, can be used directly from inside FreeCAD, +via the GUI importer or via python scripts with: + +import linuxcnc_post +linuxcnc_post.export(object,"/path/to/file.ncc") +''' + +import datetime +now = datetime.datetime.now() +from PathScripts import PostUtils + +#These globals set common customization preferences +OUTPUT_COMMENTS = True +OUTPUT_HEADER = True +OUTPUT_LINE_NUMBERS = False +SHOW_EDITOR = True +MODAL = False #if true commands are suppressed if the same as previous line. +COMMAND_SPACE = " " +LINENR = 100 #line number starting value + +#These globals will be reflected in the Machine configuration of the project +UNITS = "G21" #G21 for metric, G20 for us standard +MACHINE_NAME = "Millstone" +CORNER_MIN = {'x':0, 'y':0, 'z':0 } +CORNER_MAX = {'x':500, 'y':300, 'z':300 } + +#Preamble text will appear at the beginning of the GCODE output file. +PREAMBLE = '''G17 G90 +''' + +#Postamble text will appear following the last operation. +POSTAMBLE = '''M05 +G00 X-1.0 Y1.0 +G17 G90 +M2 +''' + + +#Pre operation text will be inserted before every operation +PRE_OPERATION = '''''' + +#Post operation text will be inserted after every operation +POST_OPERATION = '''''' + +#Tool Change commands will be inserted before a tool change +TOOL_CHANGE = '''''' + + +# to distinguish python built-in open function from the one declared below +if open.__module__ == '__builtin__': + pythonopen = open + + +def export(objectslist,filename): + global UNITS + for obj in objectslist: + if not hasattr(obj,"Path"): + print "the object " + obj.Name + " is not a path. Please select only path and Compounds." + return + + print "postprocessing..." + gcode = "" + + #Find the machine. + #The user my have overriden post processor defaults in the GUI. Make sure we're using the current values in the Machine Def. + myMachine = None + for pathobj in objectslist: + if hasattr(pathobj,"Group"): #We have a compound or project. + for p in pathobj.Group: + if p.Name == "Machine": + myMachine = p + if myMachine is None: + print "No machine found in this project" + else: + if myMachine.MachineUnits == "Metric": + UNITS = "G21" + else: + UNITS = "G20" + + + # write header + if OUTPUT_HEADER: + gcode += linenumber() + "(Exported by FreeCAD)\n" + gcode += linenumber() + "(Post Processor: " + __name__ +")\n" + gcode += linenumber() + "(Output Time:"+str(now)+")\n" + + #Write the preamble + if OUTPUT_COMMENTS: gcode += linenumber() + "(begin preamble)\n" + for line in PREAMBLE.splitlines(True): + gcode += linenumber() + line + gcode += linenumber() + UNITS + "\n" + + for obj in objectslist: + + #do the pre_op + if OUTPUT_COMMENTS: gcode += linenumber() + "(begin operation: " + obj.Label + ")\n" + for line in PRE_OPERATION.splitlines(True): + gcode += linenumber() + line + + gcode += parse(obj) + + #do the post_op + if OUTPUT_COMMENTS: gcode += linenumber() + "(finish operation: " + obj.Label + ")\n" + for line in POST_OPERATION.splitlines(True): + gcode += linenumber() + line + + #do the post_amble + + if OUTPUT_COMMENTS: gcode += "(begin postamble)\n" + for line in POSTAMBLE.splitlines(True): + gcode += linenumber() + line + + if SHOW_EDITOR: + dia = PostUtils.GCodeEditorDialog() + dia.editor.setText(gcode) + result = dia.exec_() + if result: + final = dia.editor.toPlainText() + else: + final = gcode + else: + final = gcode + + print "done postprocessing." + + gfile = pythonopen(filename,"wb") + gfile.write(gcode) + gfile.close() + + +def linenumber(): + global LINENR + if OUTPUT_LINE_NUMBERS == True: + LINENR += 10 + return "N" + str(LINENR) + " " + return "" + +def parse(pathobj): + out = "" + lastcommand = None + + #params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control the order of parameters + params = ['X','Y','Z','A','B','I','J','F','S','T','Q','R','L'] #linuxcnc doesn't want K properties on XY plane Arcs need work. + + if hasattr(pathobj,"Group"): #We have a compound or project. + if OUTPUT_COMMENTS: out += linenumber() + "(compound: " + pathobj.Label + ")\n" + for p in pathobj.Group: + out += parse(p) + return out + else: #parsing simple path + + if not hasattr(pathobj,"Path"): #groups might contain non-path things like stock. + return out + + if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" + + for c in pathobj.Path.Commands: + outstring = [] + command = c.Name + outstring.append(command) + # if modal: only print the command if it is not the same as the last one + if MODAL == True: + if command == lastcommand: + outstring.pop(0) + + + # Now add the remaining parameters in order + for param in params: + if param in c.Parameters: + if param == 'F': + outstring.append(param + format(c.Parameters['F'], '.2f')) + elif param == 'T': + outstring.append(param + str(c.Parameters['T'])) + else: + outstring.append(param + format(c.Parameters[param], '.4f')) + + # store the latest command + lastcommand = command + + # Check for Tool Change: + if command == 'M6': + if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n" + for line in TOOL_CHANGE.splitlines(True): + out += linenumber() + line + + if command == "message": + if OUTPUT_COMMENTS == False: + out = [] + else: + outstring.pop(0) #remove the command + + #prepend a line number and append a newline + if len(outstring) >= 1: + if OUTPUT_LINE_NUMBERS: + outstring.insert(0,(linenumber())) + + #append the line to the final output + for w in outstring: + out += w + COMMAND_SPACE + out = out.strip() + "\n" + + return out + + +print __name__ + " gcode postprocessor loaded." + diff --git a/src/Mod/Path/PathScripts/opensbp_post.py b/src/Mod/Path/PathScripts/opensbp_post.py new file mode 100644 index 0000000000..82ad7ff6a6 --- /dev/null +++ b/src/Mod/Path/PathScripts/opensbp_post.py @@ -0,0 +1,254 @@ +#*************************************************************************** +#* (c) sliptonic (shopinthewoods@gmail.com) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +This is an postprocessor file for the Path workbench. It will output path data in a format suitable for OpenSBP controllers like shopbot. This postprocessor, once placed in the appropriate PathScripts folder, can be used directly from inside FreeCAD, +via the GUI importer or via python scripts with: + +import Path +Path.write(object,"/path/to/file.ncc","post_opensbp") +''' + +import datetime +now = datetime.datetime.now() +from PathScripts import PostUtils + +OUTPUT_COMMENTS = False +OUTPUT_HEADER = True +SHOW_EDITOR = True +COMMAND_SPACE = "," + +#Preamble text will appear at the beginning of the GCODE output file. +PREAMBLE = ''' +''' +#Postamble text will appear following the last operation. +POSTAMBLE = ''' +''' + +#Pre operation text will be inserted before every operation +PRE_OPERATION = ''' +''' + +#Post operation text will be inserted after every operation +POST_OPERATION = ''' +''' + +#Tool Change commands will be inserted before a tool change +TOOL_CHANGE = ''''A tool change is about to happen +''' + + +def move(commandline): + print "processing a move" + txt = "" + if commandline['F'] != None : #Feed Rate has changed + print "command contains an F" + txt += feedrate(commandline) + + if commandline['command'] == 'G0': + txt += "J3" + else: + txt += "M3" + + for p in ['X','Y','Z']: + if commandline[p] == None: + txt += "," + format(CurrentState[p], '.4f') + else: + txt += "," + format(eval(commandline[p]), '.4f') + CurrentState[p] = eval(commandline[p]) + txt += "\n" + return txt + + + +def feedrate(commandline): + #figure out what kind of feed rate we're talking about jog or move + NOCHANGE = False + txt = "" + setspeed = eval(commandline['F']) + if commandline['command'] == 'G1': #move + movetype = "MS" + else: #jog + movetype = "JS" + print "movetype: " + movetype + + if commandline['X'] == None: + newX = CurrentState['X'] + else: + newX = eval(commandline['X']) + + if commandline['Y'] == None: + newY = CurrentState['Y'] + else: + newY = eval(commandline['Y']) + + if commandline['Z'] == None: + newZ = CurrentState['Z'] + else: + newZ = eval(commandline['Z']) + + if newX == CurrentState['X'] and newY == CurrentState['Y']: + # ZMove only + AXISMOVE = "Z" + + if CurrentState[movetype+'Z'] == setspeed: + NOCHANGE = True + else: + AXISMOVE = "XY" + if CurrentState[movetype+'XY'] == setspeed: + NOCHANGE = True + if AXISMOVE == "XY" and newZ != CurrentState['Z']: + AXISMOVE = "XYZ" + if CurrentState[movetype+'XY'] == setspeed and CurrentState[movetype+'Z'] == setspeed: + NOCHANGE = True + print "axismove: " + AXISMOVE + #figure out if it has actually changed. + if NOCHANGE == True: + txt = "" + else: #something changed + if AXISMOVE == "XY": + txt += movetype + "," + format(setspeed, '.4f') + CurrentState[movetype+'XY'] = setspeed + elif AXISMOVE == "Z": + txt += movetype + ",," + format(setspeed, '.4f') + CurrentState[movetype+'Z'] = setspeed + else: #XYZMOVE + txt += movetype + "," + format(setspeed, '.4f') + "," + format(setspeed, '.4f') + print txt + CurrentState[movetype+'XY'] = setspeed + CurrentState[movetype+'Z'] = setspeed + + txt += "\n" + + return txt + + +def arc(commandline): + if commandline['command'] == 'G2': #CW + dirstring = "1" + else: #G3 means CCW + dirstring = "-1" + txt = "CG,," + txt += format(eval(commandline['X']), '.4f') + "," + txt += format(eval(commandline['Y']), '.4f') + "," + txt += format(eval(commandline['I']), '.4f') + "," + txt += format(eval(commandline['J']), '.4f') + "," + txt += "T" + "," + txt += dirstring + txt += "\n" + return txt + +def tool_change(commandline): + print "tool change" + txt = "" + if OUTPUT_COMMENTS: txt += "'a tool change happens now\n" + for line in TOOL_CHANGE.splitlines(True): + txt += line + txt += "&ToolName = " + commandline['T'] + txt += "\n" + txt += "&Tool=" + commandline['T'] + txt += "\n" + + return txt + +def comment(commandline): + print "a comment" + +def spindle(commandline): + txt ="" + if commandline['command'] == "M3": #CW + pass + else: + pass + txt += "TR," + commandline['S'] + return txt + +#Supported Commands +scommands = {"G0": move, + "G1": move, + "G2": arc, + "G3": arc, + "M6": tool_change, + "M3": spindle, + "message": comment + } + +CurrentState = {'X':0, 'Y':0, 'Z':0, 'F':0, 'S':0, 'JSXY':0, 'JSZ':0, 'MSXY':0, 'MSZ':0} + +def parse(inputstring): + "parse(inputstring): returns a parsed output string" + print "postprocessing..." + + output = "" + params = ['X','Y','Z','A','B','I','J','K','F','S','T'] #This list control the order of parameters + + # write some stuff first + if OUTPUT_HEADER: + print "outputting header" + output += "'Exported by FreeCAD\n" + output += "'Post Processor: " + __name__ +"\n" + output += "'Output Time:"+str(now)+"\n" + + #Write the preamble + if OUTPUT_COMMENTS: output += "'begin preamble\n" + for line in PREAMBLE.splitlines(True): + output += line + + # treat the input line by line + lines = inputstring.splitlines(True) + + for line in lines: + commandline = PostUtils.stringsplit(line) + command = commandline['command'] + try: + print commandline + print "command: " + command + print command in scommands + output += scommands[command](commandline) + except: + print "I don't know what the hell the command: " + command + " means. Maybe I should support it." + + print "finished" + # write some more stuff at the end + if OUTPUT_COMMENTS: output += "'begin postamble\n" + for line in POSTAMBLE.splitlines(True): + output += line + + if SHOW_EDITOR: + dia = PostUtils.GCodeEditorDialog() + dia.editor.setText(output) + result = dia.exec_() + if result: + final = dia.editor.toPlainText() + else: + final = output + else: + final = output + + print "done postprocessing." + return final + + +print __name__ + " gcode postprocessor loaded." + diff --git a/src/Mod/Path/PathScripts/opensbp_pre.py b/src/Mod/Path/PathScripts/opensbp_pre.py new file mode 100644 index 0000000000..e9bca98161 --- /dev/null +++ b/src/Mod/Path/PathScripts/opensbp_pre.py @@ -0,0 +1,202 @@ +#*************************************************************************** +#* (c) sliptonic (shopinthewoodsgmail.com) 2014 * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* FreeCAD 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 Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with FreeCAD; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#***************************************************************************/ + + +''' +This is a preprocessor file for the Path workbench. Its aim is to +parse the contents of a given OpenSBP file, and transform it to make it +suitable for use in a Path object. This preprocessor, once placed in the +appropriate PathScripts folder, can be used directly from inside FreeCAD, +via the GUI importer or via python scripts with: + +import opensbp_pre +opensbp_pre.insert("/path/to/myfile.ngc","DocumentName") + + +DONE +Correctly imports single axis and multi axis moves. +Stores Jog and Feed speeds +Appends Multiaxis Feed speed to G1 moves +Jog rates don't append to G0 moves +Make single axis feed rates work +Imports CG (non-diameter) arcs. +Handles CW and CCW spindle speeds +if operations are preceded by a comment ('New Path ...) They are split into multiple paths + +TODO +Many other OpenSBP commands not handled + +''' + +AXIS = 'X','Y','Z','A','B' #OpenSBP always puts multiaxis move parameters in this order +SPEEDS = 'XY','Z','A','B' + +import FreeCAD +import os, Path + +# to distinguish python built-in open function from the one declared below +if open.__module__ == '__builtin__': + pythonopen = open + + +def open(filename): + "called when freecad opens a file." + docname = os.path.splitext(os.path.basename(filename))[0] + doc = FreeCAD.newDocument(docname) + insert(filename,doc.Name) + + +def insert(filename,docname): + "called when freecad imports a file" + "This insert expects parse to return a list of strings" + "each string will become a separate path" + gfile = pythonopen(filename) + gcode = gfile.read() + gfile.close() + gcode = parse(gcode) + doc = FreeCAD.getDocument(docname) + for subpath in gcode: + obj = doc.addObject("Path::Feature","Path") + path = Path.Path(subpath) + obj.Path = path + + +def parse(inputstring): + "parse(inputstring): returns a list of parsed output string" + print "preprocessing..." + + # split the input by line + lines = inputstring.split("\n") + return_output = [] + output = "" + last = {'X':None,'Y':None,'Z':None,'A':None,'B':None} + lastrapidspeed = {'XY':"50", 'Z':"50", 'A':"50", 'B':"50" } #set default rapid speeds + lastfeedspeed = {'XY':"50", 'Z':"50", 'A':"50", 'B':"50" } #set default feed speed + movecommand = ['G1', 'G0', 'G02', 'G03'] + + for l in lines: + # remove any leftover trailing and preceding spaces + l = l.strip() + if not l: + # discard empty lines + continue + if l[0] in ["'","&"]: + # discard comment and other non strictly gcode lines + if l[0:9] == "'New Path": + # starting new path + if any (x in output for x in movecommand): #make sure the path has at least one move command. + return_output.append(output) + output = "" + continue + + words = [a.strip() for a in l.split(",")] + words[0] = words[0].upper() + if words[0] in ["J2","J3","J4","J5","M2","M3","M4","M5"]: #multi-axis jogs and moves + if words[0][0] == 'J': #jog move + s = "G0 " + else: #feed move + s = "G1 " + speed = lastfeedspeed["XY"] + for i in range (1, len(words)): + if words [i] == '': + if last[AXIS[i-1]] == None: + continue + else: + s += AXIS[i-1] + last[AXIS[i-1]] + else: + s += AXIS[i-1] + words[i] + last[AXIS[i-1]] = words[i] + output += s +" F" + speed + '\n' + + if words[0] in ["JA","JB","JX","JY","JZ","MA","MB","MX","MY","MZ"]: #single axis jogs and moves + if words[0][0] == 'J': #jog move + s = "G0 " + if words[0][1] in ['X','Y']: + speed = lastrapidspeed["XY"] + else: + speed = lastrapidspeed[words[0][1]] + + else: #feed move + s = "G1 " + if words[0][1] in ['X','Y']: + speed = lastfeedspeed["XY"] + else: + speed = lastfeedspeed[words[0][1]] + + + last[words[0][1]] = words[1] + output += s + words[0][1] + str(words[1]) + " F" + speed + "\n" + + if words[0] in ["JS"]: #set jog speed + for i in range (1, len(words)): + if words [i] == '': + continue + else: + lastrapidspeed[SPEEDS[i-1]] = words[i] + + if words[0] in ["MD"]: #move distance with distance and angle. + #unsupported at this time + continue + if words[0] in ["MH"]: #move home + #unsupported at this time + continue + if words[0] in ["MS"]: #set move speed + for i in range (1, len(words)): + if words [i] == '': + continue + else: + lastfeedspeed[SPEEDS[i-1]] = words[i] + if words[0] in ["MO"]: #motors off + #unsupported at this time + continue + + if words[0] in ["TR"]: #Setting spindle speed + if int(words[1]) < 0: + s = "M4 S" + else: + s = "M3 S" + s += str(abs(int(words[1]))) + output += s + '\n' + + if words[0] in ["CG"]: #Gcode circle/arc + if words[1] != "": # diameter mode + print "diameter mode not supported" + continue + + else: + if words[7] == "1": #CW + s = "G2" + else: #CCW + s = "G3" + s += " X" + words[2] + " Y" + words[3] + " I" + words[4] + " J" + words[5] + " F" + str(lastfeedspeed["XY"]) + output += s + '\n' + + #Make sure all appended paths have at least one move command. + if any (x in output for x in movecommand): + return_output.append(output) + print "done preprocessing." + + return return_output + +print __name__ + " gcode preprocessor loaded." +