Merge branch 'master' of https://github.com/FreeCAD/FreeCAD into deburr+dressup

This commit is contained in:
Patrick Felixberger
2020-03-31 17:18:06 +02:00
166 changed files with 11556 additions and 4468 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
SET(Examples_Files
Schenkel.stp
DrawingExample.FCStd
draft_test_objects.FCStd
EngineBlock.FCStd
PartDesignExample.FCStd
RobotExample.FCStd
Binary file not shown.
+1 -1
View File
@@ -38,7 +38,7 @@ using namespace Base;
QString UnitsSchema::toLocale(const Base::Quantity& quant, double factor, const QString& unitString) const
{
//return QString::fromUtf8("%L1 %2").arg(quant.getValue() / factor).arg(unitString);
QLocale Lc = QLocale::system();
QLocale Lc;
const QuantityFormat& format = quant.getFormat();
if (format.option != QuantityFormat::None) {
uint opt = static_cast<uint>(format.option);
-1
View File
@@ -27,7 +27,6 @@
#endif
#include <QString>
#include <QLocale>
#include "Exception.h"
#include "UnitsApi.h"
#include "UnitsSchemaCentimeters.h"
-1
View File
@@ -30,7 +30,6 @@
#endif
#include <QString>
#include <QLocale>
#include "Console.h"
#include "Exception.h"
#include "UnitsApi.h"
-1
View File
@@ -27,7 +27,6 @@
#endif
#include <QString>
#include <QLocale>
#include "Exception.h"
#include "UnitsApi.h"
#include "UnitsSchemaInternal.h"
-1
View File
@@ -27,7 +27,6 @@
#endif
#include <QString>
#include <QLocale>
#include "Exception.h"
#include "UnitsApi.h"
#include "UnitsSchemaMKS.h"
-1
View File
@@ -27,7 +27,6 @@
#endif
#include <QString>
#include <QLocale>
#include "Exception.h"
#include "UnitsApi.h"
#include "UnitsSchemaMmMin.h"
+8 -3
View File
@@ -93,6 +93,7 @@
#include "DocumentRecovery.h"
#include "TransactionObject.h"
#include "FileDialog.h"
#include "ExpressionBindingPy.h"
#include "TextDocumentEditorView.h"
#include "SplitView3DInventor.h"
@@ -304,7 +305,7 @@ Application::Application(bool GUIenabled)
// install the last active language
ParameterGrp::handle hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
hPGrp = hPGrp->GetGroup("Preferences")->GetGroup("General");
QString lang = QLocale::languageToString(QLocale::system().language());
QString lang = QLocale::languageToString(QLocale().language());
Translator::instance()->activateLanguage(hPGrp->GetASCII("Language", (const char*)lang.toLatin1()).c_str());
GetWidgetFactorySupplier();
@@ -318,7 +319,7 @@ Application::Application(bool GUIenabled)
// Check for the symbols for group separator and decimal point. They must be different otherwise
// Qt doesn't work properly.
#if defined(Q_OS_WIN32)
if (QLocale::system().groupSeparator() == QLocale::system().decimalPoint()) {
if (QLocale().groupSeparator() == QLocale().decimalPoint()) {
QMessageBox::critical(0, QLatin1String("Invalid system settings"),
QLatin1String("Your system uses the same symbol for decimal point and group separator.\n\n"
"This causes serious problems and makes the application fail to work properly.\n"
@@ -330,7 +331,7 @@ Application::Application(bool GUIenabled)
// http://forum.freecadweb.org/viewtopic.php?f=10&t=6910
// A workaround is to disable the group separator for double-to-string conversion, i.e.
// setting the flag 'OmitGroupSeparator'.
QLocale loc = QLocale::system();
QLocale loc;
loc.setNumberOptions(QLocale::OmitGroupSeparator);
QLocale::setDefault(loc);
#endif
@@ -384,6 +385,10 @@ Application::Application(bool GUIenabled)
Py_INCREF(pySide->module().ptr());
PyModule_AddObject(module, "PySideUic", pySide->module().ptr());
ExpressionBindingPy::init_type();
Base::Interpreter().addType(ExpressionBindingPy::type_object(),
module,"ExpressionBinding");
//insert Selection module
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef SelectionModuleDef = {
+3 -1
View File
@@ -1223,6 +1223,7 @@ SET(FreeCADGui_CPP_SRCS
DocumentObserver.cpp
DocumentObserverPython.cpp
ExpressionBinding.cpp
ExpressionBindingPy.cpp
GraphicsViewZoom.cpp
ExpressionCompleter.cpp
GuiApplication.cpp
@@ -1249,7 +1250,8 @@ SET(FreeCADGui_SRCS
DocumentModel.h
DocumentObserver.h
DocumentObserverPython.h
ExpressionBinding.cpp
ExpressionBinding.h
ExpressionBindingPy.h
ExpressionCompleter.h
FreeCADGuiInit.py
GraphicsViewZoom.h
+1 -1
View File
@@ -110,7 +110,7 @@ void ControlSingleton::showDialog(Gui::TaskView::TaskDialog *dlg)
return;
}
// Since the caller sets up a modeless task panel, it indicates intension
// Since the caller sets up a modeless task panel, it indicates intention
// for prolonged editing. So disable auto transaction in the current call
// stack.
// Do this before showing the dialog because its open() function is called
+2 -2
View File
@@ -131,7 +131,7 @@ void DlgGeneralImp::saveSettings()
setRecentFileSize();
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
QString lang = QLocale::languageToString(QLocale::system().language());
QString lang = QLocale::languageToString(QLocale().language());
QByteArray language = hGrp->GetASCII("Language", (const char*)lang.toLatin1()).c_str();
QByteArray current = ui->Languages->itemData(ui->Languages->currentIndex()).toByteArray();
if (current != language) {
@@ -182,7 +182,7 @@ void DlgGeneralImp::loadSettings()
// search for the language files
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
QString langToStr = QLocale::languageToString(QLocale::system().language());
QString langToStr = QLocale::languageToString(QLocale().language());
QByteArray language = hGrp->GetASCII("Language", langToStr.toLatin1()).c_str();
int index = 1;
+6 -6
View File
@@ -136,17 +136,17 @@ bool DlgSettingsColorGradientImp::isOutInvisible() const
void DlgSettingsColorGradientImp::setRange( float fMin, float fMax )
{
ui->floatLineEditMax->blockSignals(true);
ui->floatLineEditMax->setText(QLocale::system().toString(fMax, 'f', numberOfDecimals()));
ui->floatLineEditMax->setText(QLocale().toString(fMax, 'f', numberOfDecimals()));
ui->floatLineEditMax->blockSignals(false);
ui->floatLineEditMin->blockSignals(true);
ui->floatLineEditMin->setText(QLocale::system().toString(fMin, 'f', numberOfDecimals()));
ui->floatLineEditMin->setText(QLocale().toString(fMin, 'f', numberOfDecimals()));
ui->floatLineEditMin->blockSignals(false);
}
void DlgSettingsColorGradientImp::getRange(float& fMin, float& fMax) const
{
fMax = QLocale::system().toFloat(ui->floatLineEditMax->text());
fMin = QLocale::system().toFloat(ui->floatLineEditMin->text());
fMax = QLocale().toFloat(ui->floatLineEditMax->text());
fMin = QLocale().toFloat(ui->floatLineEditMin->text());
}
void DlgSettingsColorGradientImp::setNumberOfLabels(int val)
@@ -171,8 +171,8 @@ int DlgSettingsColorGradientImp::numberOfDecimals() const
void DlgSettingsColorGradientImp::accept()
{
double fMax = QLocale::system().toDouble(ui->floatLineEditMax->text());
double fMin = QLocale::system().toDouble(ui->floatLineEditMin->text());
double fMax = QLocale().toDouble(ui->floatLineEditMax->text());
double fMin = QLocale().toDouble(ui->floatLineEditMin->text());
if (fMax <= fMin) {
QMessageBox::warning(this, tr("Wrong parameter"),
+2 -2
View File
@@ -159,9 +159,9 @@ void DlgUnitsCalculator::valueChanged(const Base::Quantity& quant)
// at first use scientific notation, if there is no "e", we can round it to the user-defined decimals,
// but the user-defined decimals might be too low for cases like "10 um" in "in",
// thus only if value > 0.005 because FC's default are 2 decimals
QString val = QLocale::system().toString(value, 'g');
QString val = QLocale().toString(value, 'g');
if (!val.contains(QChar::fromLatin1('e')) && (value > 0.005))
val = QLocale::system().toString(value, 'f', Base::UnitsApi::getDecimals());
val = QLocale().toString(value, 'f', Base::UnitsApi::getDecimals());
// create the output string
QString out = QString::fromLatin1("%1 %2").arg(val, ui->UnitInput->text());
ui->ValueOutput->setText(out);
+1 -1
View File
@@ -230,7 +230,7 @@ QString DocumentRecovery::createProjectFile(const QString& documentXml)
void DocumentRecovery::closeEvent(QCloseEvent* e)
{
// Do not disable the X button in the title bar
// #0004281: Close Documant Recovery
// #0004281: Close Document Recovery
e->accept();
}
+168
View File
@@ -0,0 +1,168 @@
/***************************************************************************
* Copyright (c) 2020 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include "ExpressionBindingPy.h"
#include "ExpressionBinding.h"
#include "WidgetFactory.h"
#include "QuantitySpinBox.h"
#include "InputField.h"
#include <App/DocumentObjectPy.h>
using namespace Gui;
void ExpressionBindingPy::init_type()
{
behaviors().name("ExpressionBinding");
behaviors().doc("Python interface class for ExpressionBinding");
// you must have overwritten the virtual functions
behaviors().supportRepr();
behaviors().supportGetattr();
behaviors().supportSetattr();
behaviors().set_tp_new(PyMake);
behaviors().readyType();
add_varargs_method("bind",&ExpressionBindingPy::bind,"Bind with an expression");
add_varargs_method("isBound",&ExpressionBindingPy::isBound,"Check if already bound with an expression");
add_varargs_method("apply",&ExpressionBindingPy::apply,"apply");
add_varargs_method("hasExpression",&ExpressionBindingPy::hasExpression,"hasExpression");
add_varargs_method("autoApply",&ExpressionBindingPy::autoApply,"autoApply");
add_varargs_method("setAutoApply",&ExpressionBindingPy::setAutoApply,"setAutoApply");
}
PyObject *ExpressionBindingPy::PyMake(struct _typeobject *, PyObject * args, PyObject *)
{
Py::Tuple tuple(args);
ExpressionBinding* expr = nullptr;
PythonWrapper wrap;
wrap.loadWidgetsModule();
QWidget* obj = dynamic_cast<QWidget*>(wrap.toQObject(tuple.getItem(0)));
if (obj) {
do {
QuantitySpinBox* sb = qobject_cast<QuantitySpinBox*>(obj);
if (sb) {
expr = sb;
break;
}
InputField* le = qobject_cast<InputField*>(obj);
if (le) {
expr = le;
break;
}
}
while(false);
}
if (!expr) {
PyErr_SetString(PyExc_TypeError, "Wrong type");
return nullptr;
}
return new ExpressionBindingPy(expr);
}
ExpressionBindingPy::ExpressionBindingPy(ExpressionBinding* expr)
: expr(expr)
{
}
ExpressionBindingPy::~ExpressionBindingPy()
{
}
Py::Object ExpressionBindingPy::repr()
{
std::stringstream s;
s << "<ExpressionBinding at " << this << ">";
return Py::String(s.str());
}
Py::Object ExpressionBindingPy::bind(const Py::Tuple& args)
{
PyObject* py;
const char* str;
if (!PyArg_ParseTuple(args.ptr(), "O!s", &App::DocumentObjectPy::Type, &py, &str))
throw Py::Exception();
try {
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(py)->getDocumentObjectPtr();
App::ObjectIdentifier id(App::ObjectIdentifier::parse(obj, str));
if (!id.getProperty()) {
throw Base::AttributeError("Wrong property");
}
expr->bind(id);
return Py::None();
}
catch (const Base::Exception& e) {
e.setPyException();
throw Py::Exception();
}
catch (...) {
throw Py::RuntimeError("Cannot bind to object");
}
}
Py::Object ExpressionBindingPy::isBound(const Py::Tuple& args)
{
if (!PyArg_ParseTuple(args.ptr(), ""))
throw Py::Exception();
return Py::Boolean(expr->isBound());
}
Py::Object ExpressionBindingPy::apply(const Py::Tuple& args)
{
const char* str;
if (!PyArg_ParseTuple(args.ptr(), "s", &str))
throw Py::Exception();
return Py::Boolean(expr->apply(str));
}
Py::Object ExpressionBindingPy::hasExpression(const Py::Tuple& args)
{
if (!PyArg_ParseTuple(args.ptr(), ""))
throw Py::Exception();
return Py::Boolean(expr->hasExpression());
}
Py::Object ExpressionBindingPy::autoApply(const Py::Tuple& args)
{
if (!PyArg_ParseTuple(args.ptr(), ""))
throw Py::Exception();
return Py::Boolean(expr->autoApply());
}
Py::Object ExpressionBindingPy::setAutoApply(const Py::Tuple& args)
{
PyObject* b;
if (!PyArg_ParseTuple(args.ptr(), "O!", &PyBool_Type, &b))
throw Py::Exception();
bool value = PyObject_IsTrue(b) ? true : false;
expr->setAutoApply(value);
return Py::None();
}
+57
View File
@@ -0,0 +1,57 @@
/***************************************************************************
* Copyright (c) 2020 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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., 51 Franklin Street, *
* Fifth Floor, Boston, MA 02110-1301, USA *
* *
***************************************************************************/
#ifndef EXPRESSIONBINDINGPY_H
#define EXPRESSIONBINDINGPY_H
#include <CXX/Extensions.hxx>
namespace Gui {
class ExpressionBinding;
class ExpressionBindingPy : public Py::PythonExtension<ExpressionBindingPy>
{
public:
static void init_type(void); // announce properties and methods
ExpressionBindingPy(ExpressionBinding*);
~ExpressionBindingPy();
Py::Object repr();
Py::Object bind(const Py::Tuple&);
Py::Object isBound(const Py::Tuple&);
Py::Object apply(const Py::Tuple&);
Py::Object hasExpression(const Py::Tuple&);
Py::Object autoApply(const Py::Tuple&);
Py::Object setAutoApply(const Py::Tuple&);
private:
static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *);
private:
ExpressionBinding* expr;
};
}
#endif // EXPRESSIONBINDING_H
+95 -58
View File
@@ -245,6 +245,7 @@ public:
int m_CubeWidgetPosY = 0;
int m_PrevWidth = 0;
int m_PrevHeight = 0;
QColor m_TextColor;
QColor m_HiliteColor;
QColor m_ButtonColor;
QColor m_FrontFaceColor;
@@ -275,7 +276,6 @@ public:
NaviCube::NaviCube(Gui::View3DInventorViewer* viewer) {
m_NaviCubeImplementation = new NaviCubeImplementation(viewer);
}
NaviCube::~NaviCube() {
@@ -305,17 +305,37 @@ void NaviCube::setCorner(Corner c) {
}
NaviCubeImplementation::NaviCubeImplementation(
Gui::View3DInventorViewer* viewer) {
Gui::View3DInventorViewer* viewer) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/NaviCube");
m_View3DInventorViewer = viewer;
m_TextColor = QColor(0,0,0,255);
if (hGrp->GetUnsigned("TextColor")) {
m_TextColor.setRgba(hGrp->GetUnsigned("TextColor"));
}
m_FrontFaceColor = QColor(255,255,255,128);
if (hGrp->GetUnsigned("FrontColor")) {
m_FrontFaceColor.setRgba(hGrp->GetUnsigned("FrontColor"));
}
m_BackFaceColor = QColor(226,233,239,128);
m_HiliteColor = QColor(170,226,247);
if (hGrp->GetUnsigned("BackColor")) {
m_BackFaceColor.setRgba(hGrp->GetUnsigned("BackColor"));
}
m_HiliteColor = QColor(170,226,255);
if (hGrp->GetUnsigned("HiliteColor")) {
m_HiliteColor.setRgba(hGrp->GetUnsigned("HiliteColor"));
}
m_ButtonColor = QColor(226,233,239,128);
if (hGrp->GetUnsigned("ButtonColor")) {
m_ButtonColor.setRgba(hGrp->GetUnsigned("ButtonColor"));
}
m_PickingFramebuffer = NULL;
m_CubeWidgetSize = (App::GetApplication().GetUserParameter().
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View")->GetInt("NaviWidgetSize", 132));
m_CubeWidgetSize = (hGrp->GetInt("CubeSize", 132));
m_Menu = createNaviCubeMenu();
}
@@ -372,10 +392,25 @@ GLuint NaviCubeImplementation::createCubeFaceTex(QtGLWidget* gl, float gap, floa
paint.begin(&image);
if (text) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/NaviCube");
paint.setPen(Qt::white);
QFont sansFont(str("Helvetica"), 0.18 * texSize);
sansFont.setStretch(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetInt("NaviFontStretch", 62));
QString fontString = QString::fromUtf8((hGrp->GetASCII("FontString")).c_str());
if (fontString.isEmpty()) {
// Improving readability
sansFont.setWeight(hGrp->GetInt("FontWeight", 87));
sansFont.setStretch(hGrp->GetInt("FontStretch", 62));
}
else {
sansFont.fromString(fontString);
}
// Override fromString
if (hGrp->GetInt("FontWeight") > 0) {
sansFont.setWeight(hGrp->GetInt("FontWeight"));
}
if (hGrp->GetInt("FontStretch") > 0) {
sansFont.setStretch(hGrp->GetInt("FontStretch"));
}
paint.setFont(sansFont);
paint.drawText(QRect(0, 0, texSize, texSize), Qt::AlignCenter,qApp->translate("Gui::NaviCube",text));
}
@@ -616,7 +651,7 @@ void NaviCubeImplementation::addFace(const Vector3f& x, const Vector3f& z, int f
m_Textures[frontTex],
pickId,
m_Textures[pickTex],
Qt::black,
m_TextColor,
2);
m_Faces.push_back(ft);
@@ -680,18 +715,13 @@ void NaviCubeImplementation::initNaviCube(QtGLWidget* gl) {
if (labels.size() != 6) {
labels.clear();
labels.push_back(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetASCII("NaviTextFront", "FRONT"));
labels.push_back(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetASCII("NaviTextRear", "REAR"));
labels.push_back(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetASCII("NaviTextTop", "TOP"));
labels.push_back(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetASCII("NaviTextBottom", "BOTTOM"));
labels.push_back(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetASCII("NaviTextRight", "RIGHT"));
labels.push_back(App::GetApplication().GetUserParameter().GetGroup("BaseApp")
->GetGroup("Preferences")->GetGroup("View")->GetASCII("NaviTextLeft", "LEFT"));
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/NaviCube");
labels.push_back(hGrp->GetASCII("TextFront", "FRONT"));
labels.push_back(hGrp->GetASCII("TextRear", "REAR"));
labels.push_back(hGrp->GetASCII("TextTop", "TOP"));
labels.push_back(hGrp->GetASCII("TextBottom", "BOTTOM"));
labels.push_back(hGrp->GetASCII("TextRight", "RIGHT"));
labels.push_back(hGrp->GetASCII("TextLeft", "LEFT"));
}
float gap = 0.12f;
@@ -801,27 +831,30 @@ void NaviCubeImplementation::handleResize() {
if ((m_PrevWidth > 0) && (m_PrevHeight > 0)) {
// maintain position relative to closest edge
if (m_CubeWidgetPosX > m_PrevWidth / 2)
m_CubeWidgetPosX = view[0] - (m_PrevWidth -m_CubeWidgetPosX);
m_CubeWidgetPosX = view[0] - (m_PrevWidth - m_CubeWidgetPosX);
if (m_CubeWidgetPosY > m_PrevHeight / 2)
m_CubeWidgetPosY = view[1] - (m_PrevHeight - m_CubeWidgetPosY);
}
else { // initial position
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/NaviCube");
int m_CubeWidgetOffsetX = hGrp->GetInt("OffsetX", 0);
int m_CubeWidgetOffsetY = hGrp->GetInt("OffsetY", 0);
switch (m_Corner) {
case NaviCube::TopLeftCorner:
m_CubeWidgetPosX = m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosY = view[1] - m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosX = m_CubeWidgetSize*1.1 / 2 + m_CubeWidgetOffsetX;
m_CubeWidgetPosY = view[1] - m_CubeWidgetSize*1.1 / 2 - m_CubeWidgetOffsetY;
break;
case NaviCube::TopRightCorner:
m_CubeWidgetPosX = view[0] - m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosY = view[1] - m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosX = view[0] - m_CubeWidgetSize*1.1 / 2 - m_CubeWidgetOffsetX;
m_CubeWidgetPosY = view[1] - m_CubeWidgetSize*1.1 / 2 - m_CubeWidgetOffsetY;
break;
case NaviCube::BottomLeftCorner:
m_CubeWidgetPosX = m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosY = m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosX = m_CubeWidgetSize*1.1 / 2 + m_CubeWidgetOffsetX;
m_CubeWidgetPosY = m_CubeWidgetSize*1.1 / 2 + m_CubeWidgetOffsetY;
break;
case NaviCube::BottomRightCorner:
m_CubeWidgetPosX = view[0] - m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosY = m_CubeWidgetSize*1.1 / 2;
m_CubeWidgetPosX = view[0] - m_CubeWidgetSize*1.1 / 2 - m_CubeWidgetOffsetX;
m_CubeWidgetPosY = m_CubeWidgetSize*1.1 / 2 + m_CubeWidgetOffsetY;
break;
}
}
@@ -932,37 +965,41 @@ void NaviCubeImplementation::drawNaviCube(bool pickMode) {
if (!pickMode) {
// Draw the axes
glDisable(GL_TEXTURE_2D);
float a=1.1f;
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/NaviCube");
bool ShowCS = hGrp->GetBool("ShowCS", 1);
if (ShowCS) {
glDisable(GL_TEXTURE_2D);
float a=1.1f;
static GLubyte xbmp[] = { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11 };
glColor3f(1, 0, 0);
glBegin(GL_LINES);
glVertex3f(-1 , -1, -1);
glVertex3f(+1 , -1, -1);
glEnd();
glRasterPos3d(a, -a, -a);
glBitmap(8, 7, 0, 0, 0, 0, xbmp);
static GLubyte xbmp[] = { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11 };
glColor3f(1, 0, 0);
glBegin(GL_LINES);
glVertex3f(-1.1 , -1.1, -1.1);
glVertex3f(+0.5 , -1.1, -1.1);
glEnd();
glRasterPos3d(a, -a, -a);
glBitmap(8, 7, 0, 0, 0, 0, xbmp);
static GLubyte ybmp[] = { 0x04,0x04,0x04,0x04,0x0a,0x11,0x11 };
glColor3f(0, 1, 0);
glBegin(GL_LINES);
glVertex3f(-1 , -1, -1);
glVertex3f(-1 , +1, -1);
glEnd();
glRasterPos3d( -a, a, -a);
glBitmap(8, 7, 0, 0, 0, 0, ybmp);
static GLubyte ybmp[] = { 0x04,0x04,0x04,0x04,0x0a,0x11,0x11 };
glColor3f(0, 1, 0);
glBegin(GL_LINES);
glVertex3f(-1.1 , -1.1, -1.1);
glVertex3f(-1.1 , +0.5, -1.1);
glEnd();
glRasterPos3d( -a, a, -a);
glBitmap(8, 7, 0, 0, 0, 0, ybmp);
static GLubyte zbmp[] = { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f };
glColor3f(0, 0, 1);
glBegin(GL_LINES);
glVertex3f(-1 , -1, -1);
glVertex3f(-1 , -1, +1);
glEnd();
glRasterPos3d( -a, -a, a);
glBitmap(8, 7, 0, 0, 0, 0, zbmp);
static GLubyte zbmp[] = { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f };
glColor3f(0, 0, 1);
glBegin(GL_LINES);
glVertex3f(-1.1 , -1.1, -1.1);
glVertex3f(-1.1 , -1.1, +0.5);
glEnd();
glRasterPos3d( -a, -a, a);
glBitmap(8, 7, 0, 0, 0, 0, zbmp);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
}
}
// Draw the cube faces
+20
View File
@@ -635,6 +635,21 @@ void Placement::on_resetButton_clicked()
onPlacementChanged(0);
}
void Placement::bindObject()
{
if (!selectionObjects.empty()) {
App::DocumentObject* obj = selectionObjects.front().getObject();
ui->xPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.x")));
ui->yPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.y")));
ui->zPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.z")));
ui->yawAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Yaw")));
ui->pitchAngle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Pitch")));
ui->rollAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Roll")));
}
}
void Placement::directionActivated(int index)
{
if (ui->directionActivated(this, index)) {
@@ -840,6 +855,11 @@ TaskPlacement::~TaskPlacement()
// automatically deleted in the sub-class
}
void TaskPlacement::bindObject()
{
widget->bindObject();
}
void TaskPlacement::open()
{
widget->open();
+2
View File
@@ -51,6 +51,7 @@ public:
void accept();
void reject();
void bindObject();
Base::Vector3d getDirection() const;
void setPlacement(const Base::Placement&);
Base::Placement getPlacement() const;
@@ -134,6 +135,7 @@ public:
public:
void setPropertyName(const QString&);
void setPlacement(const Base::Placement&);
void bindObject();
bool accept();
bool reject();
void clicked(int id);
+64 -53
View File
@@ -937,7 +937,7 @@ PropertyFloatItem::PropertyFloatItem()
QVariant PropertyFloatItem::toString(const QVariant& prop) const
{
double value = prop.toDouble();
QString data = QLocale::system().toString(value, 'f', decimals());
QString data = QLocale().toString(value, 'f', decimals());
if (hasExpression())
data += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString()));
@@ -1116,7 +1116,7 @@ PropertyFloatConstraintItem::PropertyFloatConstraintItem()
QVariant PropertyFloatConstraintItem::toString(const QVariant& prop) const
{
double value = prop.toDouble();
QString data = QLocale::system().toString(value, 'f', decimals());
QString data = QLocale().toString(value, 'f', decimals());
return QVariant(data);
}
@@ -1317,11 +1317,12 @@ PropertyVectorItem::PropertyVectorItem()
QVariant PropertyVectorItem::toString(const QVariant& prop) const
{
QLocale loc;
const Base::Vector3d& value = prop.value<Base::Vector3d>();
QString data = QString::fromLatin1("[%1 %2 %3]")
.arg(QLocale::system().toString(value.x, 'f', 2),
QLocale::system().toString(value.y, 'f', 2),
QLocale::system().toString(value.z, 'f', 2));
.arg(loc.toString(value.x, 'f', 2),
loc.toString(value.y, 'f', 2),
loc.toString(value.z, 'f', 2));
if (hasExpression())
data += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString()));
return QVariant(data);
@@ -1363,12 +1364,13 @@ QWidget* PropertyVectorItem::createEditor(QWidget* parent, const QObject* /*rece
void PropertyVectorItem::setEditorData(QWidget *editor, const QVariant& data) const
{
QLocale loc;
QLineEdit* le = qobject_cast<QLineEdit*>(editor);
const Base::Vector3d& value = data.value<Base::Vector3d>();
QString text = QString::fromLatin1("[%1 %2 %3]")
.arg(QLocale::system().toString(value.x, 'f', 2),
QLocale::system().toString(value.y, 'f', 2),
QLocale::system().toString(value.z, 'f', 2));
.arg(loc.toString(value.x, 'f', 2),
loc.toString(value.y, 'f', 2),
loc.toString(value.z, 'f', 2));
le->setProperty("coords", data);
le->setText(text);
}
@@ -1634,24 +1636,25 @@ PropertyMatrixItem::PropertyMatrixItem()
QVariant PropertyMatrixItem::toString(const QVariant& prop) const
{
QLocale loc;
const Base::Matrix4D& value = prop.value<Base::Matrix4D>();
QString text = QString::fromLatin1("[%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14 %15 %16]")
.arg(QLocale::system().toString(value[0][0], 'f', 2), //(unsigned short usNdx)
QLocale::system().toString(value[0][1], 'f', 2),
QLocale::system().toString(value[0][2], 'f', 2),
QLocale::system().toString(value[0][3], 'f', 2),
QLocale::system().toString(value[1][0], 'f', 2),
QLocale::system().toString(value[1][1], 'f', 2),
QLocale::system().toString(value[1][2], 'f', 2),
QLocale::system().toString(value[1][3], 'f', 2),
QLocale::system().toString(value[2][0], 'f', 2))
.arg(QLocale::system().toString(value[2][1], 'f', 2),
QLocale::system().toString(value[2][2], 'f', 2),
QLocale::system().toString(value[2][3], 'f', 2),
QLocale::system().toString(value[3][0], 'f', 2),
QLocale::system().toString(value[3][1], 'f', 2),
QLocale::system().toString(value[3][2], 'f', 2),
QLocale::system().toString(value[3][3], 'f', 2));
.arg(loc.toString(value[0][0], 'f', 2), //(unsigned short usNdx)
loc.toString(value[0][1], 'f', 2),
loc.toString(value[0][2], 'f', 2),
loc.toString(value[0][3], 'f', 2),
loc.toString(value[1][0], 'f', 2),
loc.toString(value[1][1], 'f', 2),
loc.toString(value[1][2], 'f', 2),
loc.toString(value[1][3], 'f', 2),
loc.toString(value[2][0], 'f', 2))
.arg(loc.toString(value[2][1], 'f', 2),
loc.toString(value[2][2], 'f', 2),
loc.toString(value[2][3], 'f', 2),
loc.toString(value[3][0], 'f', 2),
loc.toString(value[3][1], 'f', 2),
loc.toString(value[3][2], 'f', 2),
loc.toString(value[3][3], 'f', 2));
return QVariant(text);
}
@@ -1707,25 +1710,26 @@ QWidget* PropertyMatrixItem::createEditor(QWidget* parent, const QObject* /*rece
void PropertyMatrixItem::setEditorData(QWidget *editor, const QVariant& data) const
{
QLocale loc;
QLineEdit* le = qobject_cast<QLineEdit*>(editor);
const Base::Matrix4D& value = data.value<Base::Matrix4D>();
QString text = QString::fromLatin1("[%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14 %15 %16]")
.arg(QLocale::system().toString(value[0][0], 'f', 2), //(unsigned short usNdx)
QLocale::system().toString(value[0][1], 'f', 2),
QLocale::system().toString(value[0][2], 'f', 2),
QLocale::system().toString(value[0][3], 'f', 2),
QLocale::system().toString(value[1][0], 'f', 2),
QLocale::system().toString(value[1][1], 'f', 2),
QLocale::system().toString(value[1][2], 'f', 2),
QLocale::system().toString(value[1][3], 'f', 2),
QLocale::system().toString(value[2][0], 'f', 2))
.arg(QLocale::system().toString(value[2][1], 'f', 2),
QLocale::system().toString(value[2][2], 'f', 2),
QLocale::system().toString(value[2][3], 'f', 2),
QLocale::system().toString(value[3][0], 'f', 2),
QLocale::system().toString(value[3][1], 'f', 2),
QLocale::system().toString(value[3][2], 'f', 2),
QLocale::system().toString(value[3][3], 'f', 2));
.arg(loc.toString(value[0][0], 'f', 2), //(unsigned short usNdx)
loc.toString(value[0][1], 'f', 2),
loc.toString(value[0][2], 'f', 2),
loc.toString(value[0][3], 'f', 2),
loc.toString(value[1][0], 'f', 2),
loc.toString(value[1][1], 'f', 2),
loc.toString(value[1][2], 'f', 2),
loc.toString(value[1][3], 'f', 2),
loc.toString(value[2][0], 'f', 2))
.arg(loc.toString(value[2][1], 'f', 2),
loc.toString(value[2][2], 'f', 2),
loc.toString(value[2][3], 'f', 2),
loc.toString(value[3][0], 'f', 2),
loc.toString(value[3][1], 'f', 2),
loc.toString(value[3][2], 'f', 2),
loc.toString(value[3][3], 'f', 2));
le->setText(text);
}
@@ -1928,6 +1932,7 @@ void PlacementEditor::browse()
}
task->setPlacement(value().value<Base::Placement>());
task->setPropertyName(propertyname);
task->bindObject();
Gui::Control().showDialog(task);
}
@@ -1939,14 +1944,16 @@ void PlacementEditor::showValue(const QVariant& d)
p.getRotation().getRawValue(dir, angle);
angle = Base::toDegrees<double>(angle);
pos = p.getPosition();
QLocale loc;
QString data = QString::fromUtf8("[(%1 %2 %3);%4 \xc2\xb0;(%5 %6 %7)]")
.arg(QLocale::system().toString(dir.x,'f',2),
QLocale::system().toString(dir.y,'f',2),
QLocale::system().toString(dir.z,'f',2),
QLocale::system().toString(angle,'f',2),
QLocale::system().toString(pos.x,'f',2),
QLocale::system().toString(pos.y,'f',2),
QLocale::system().toString(pos.z,'f',2));
.arg(loc.toString(dir.x,'f',2),
loc.toString(dir.y,'f',2),
loc.toString(dir.z,'f',2),
loc.toString(angle,'f',2),
loc.toString(pos.x,'f',2),
loc.toString(pos.y,'f',2),
loc.toString(pos.z,'f',2));
getLabel()->setText(data);
}
@@ -2138,12 +2145,14 @@ QVariant PropertyPlacementItem::toolTip(const App::Property* prop) const
p.getRotation().getRawValue(dir, angle);
angle = Base::toDegrees<double>(angle);
pos = p.getPosition();
QLocale loc;
QString data = QString::fromUtf8("Axis: (%1 %2 %3)\n"
"Angle: %4\n"
"Position: (%5 %6 %7)")
.arg(QLocale::system().toString(dir.x,'f',decimals()),
QLocale::system().toString(dir.y,'f',decimals()),
QLocale::system().toString(dir.z,'f',decimals()),
.arg(loc.toString(dir.x,'f',decimals()),
loc.toString(dir.y,'f',decimals()),
loc.toString(dir.z,'f',decimals()),
Base::Quantity(angle, Base::Unit::Angle).getUserString(),
Base::Quantity(pos.x, Base::Unit::Length).getUserString(),
Base::Quantity(pos.y, Base::Unit::Length).getUserString(),
@@ -2159,10 +2168,12 @@ QVariant PropertyPlacementItem::toString(const QVariant& prop) const
p.getRotation().getRawValue(dir, angle);
angle = Base::toDegrees<double>(angle);
pos = p.getPosition();
QLocale loc;
QString data = QString::fromUtf8("[(%1 %2 %3); %4; (%5 %6 %7)]")
.arg(QLocale::system().toString(dir.x,'f',2),
QLocale::system().toString(dir.y,'f',2),
QLocale::system().toString(dir.z,'f',2),
.arg(loc.toString(dir.x,'f',2),
loc.toString(dir.y,'f',2),
loc.toString(dir.z,'f',2),
Base::Quantity(angle, Base::Unit::Angle).getUserString(),
Base::Quantity(pos.x, Base::Unit::Length).getUserString(),
Base::Quantity(pos.y, Base::Unit::Length).getUserString(),
+1 -1
View File
@@ -231,7 +231,7 @@ class CheckWBWorker(QtCore.QThread):
class FillMacroListWorker(QtCore.QThread):
"""This worker opulates the list of macros"""
"""This worker populates the list of macros"""
add_macro_signal = QtCore.Signal(Macro)
info_label_signal = QtCore.Signal(str)
+2 -1
View File
@@ -27,7 +27,8 @@ from FreeCAD import Vector
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui,QtCore
from DraftTools import translate, utf8_decode
from draftutils.translate import translate
from draftutils.utils import utf8_decode
else:
# \cond
def translate(ctxt,txt):
+1 -1
View File
@@ -28,7 +28,7 @@ from FreeCAD import Vector
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui,QtCore
from DraftTools import translate
from draftutils.translate import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
+2 -1
View File
@@ -26,6 +26,7 @@ if FreeCAD.GuiUp:
from PySide import QtCore, QtGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftguitools.gui_trackers as DraftTrackers
else:
# \cond
def translate(ctxt,txt):
@@ -180,7 +181,7 @@ class CommandPanel:
# interactive mode
if hasattr(FreeCAD,"DraftWorkingPlane"):
FreeCAD.DraftWorkingPlane.setup()
import DraftTrackers
self.points = []
self.tracker = DraftTrackers.boxTracker()
self.tracker.width(self.Width)
+3 -1
View File
@@ -29,6 +29,8 @@ if FreeCAD.GuiUp:
from PySide import QtCore, QtGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import ArchPrecast
import draftguitools.gui_trackers as DraftTrackers
else:
# \cond
def translate(ctxt,txt):
@@ -247,7 +249,7 @@ class _CommandStructure:
# interactive mode
if hasattr(FreeCAD,"DraftWorkingPlane"):
FreeCAD.DraftWorkingPlane.setup()
import DraftTrackers,ArchPrecast
self.points = []
self.tracker = DraftTrackers.boxTracker()
self.tracker.width(self.Width)
+2 -1
View File
@@ -26,6 +26,7 @@ if FreeCAD.GuiUp:
from PySide import QtCore, QtGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftguitools.gui_trackers as DraftTrackers
else:
# \cond
def translate(ctxt,txt, utf8_decode=False):
@@ -232,7 +233,7 @@ class _CommandWall:
if not done:
# interactive mode
import DraftTrackers
self.points = []
self.tracker = DraftTrackers.boxTracker()
if hasattr(FreeCAD,"DraftWorkingPlane"):
+2 -1
View File
@@ -27,6 +27,7 @@ if FreeCAD.GuiUp:
from PySide import QtCore, QtGui, QtSvg
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftguitools.gui_trackers as DraftTrackers
else:
# \cond
def translate(ctxt,txt):
@@ -653,7 +654,7 @@ class _CommandWindow:
# interactive mode
if hasattr(FreeCAD,"DraftWorkingPlane"):
FreeCAD.DraftWorkingPlane.setup()
import DraftTrackers
self.tracker = DraftTrackers.boxTracker()
self.tracker.length(self.Width)
self.tracker.width(self.Thickness)
+2
View File
@@ -1501,6 +1501,8 @@ def getIfcTypeFromObj(obj):
ifctype = "Group"
if ifctype == "Undefined":
ifctype = "BuildingElementProxy"
if ifctype == "Furniture":
ifctype = "FurnishingElement"
return "Ifc" + ifctype
+2 -2
View File
@@ -10,13 +10,11 @@ SET(Draft_SRCS_base
Draft.py
DraftTools.py
DraftGui.py
DraftTrackers.py
DraftVecUtils.py
DraftGeomUtils.py
DraftLayer.py
DraftEdit.py
DraftFillet.py
DraftSelectPlane.py
WorkingPlane.py
getSVG.py
TestDraft.py
@@ -82,9 +80,11 @@ SET(Draft_GUI_tools
draftguitools/gui_circulararray.py
draftguitools/gui_orthoarray.py
draftguitools/gui_polararray.py
draftguitools/gui_selectplane.py
draftguitools/gui_arrays.py
draftguitools/gui_snaps.py
draftguitools/gui_snapper.py
draftguitools/gui_trackers.py
draftguitools/README.md
)
+21 -12
View File
@@ -2222,38 +2222,47 @@ def getCloneBase(obj,strict=False):
return obj
def mirror(objlist,p1,p2):
"""mirror(objlist,p1,p2,[clone]): creates a mirrored version of the given object(s)
along an axis that passes through the two vectors p1 and p2."""
def mirror(objlist, p1, p2):
"""mirror(objlist, p1, p2)
creates a Part::Mirror of the given object(s), along a plane defined
by the 2 given points and the draft working plane normal.
"""
if not objlist:
FreeCAD.Console.PrintError(translate("draft","No object given")+"\n")
_err = "No object given"
FreeCAD.Console.PrintError(translate("draft", _err) + "\n")
return
if p1 == p2:
FreeCAD.Console.PrintError(translate("draft","The two points are coincident")+"\n")
_err = "The two points are coincident"
FreeCAD.Console.PrintError(translate("draft", _err) + "\n")
return
if not isinstance(objlist,list):
objlist = [objlist]
if hasattr(FreeCAD, "DraftWorkingPlane"):
norm = FreeCAD.DraftWorkingPlane.getNormal()
elif gui:
norm = FreeCADGui.ActiveDocument.ActiveView.getViewDirection().negative()
else:
norm = FreeCAD.Vector(0,0,1)
pnorm = p2.sub(p1).cross(norm).normalize()
result = []
for obj in objlist:
mir = FreeCAD.ActiveDocument.addObject("Part::Mirroring","mirror")
mir.Label = "Mirror of "+obj.Label
mir.Label = "Mirror of " + obj.Label
mir.Source = obj
if gui:
norm = FreeCADGui.ActiveDocument.ActiveView.getViewDirection().negative()
else:
norm = FreeCAD.Vector(0,0,1)
pnorm = p2.sub(p1).cross(norm).normalize()
mir.Base = p1
mir.Normal = pnorm
formatObject(mir,obj)
formatObject(mir, obj)
result.append(mir)
if len(result) == 1:
result = result[0]
select(result)
return result
+1 -1
View File
@@ -35,7 +35,7 @@ if App.GuiUp:
# Do not import GUI-related modules if GUI is not there
import FreeCADGui as Gui
import DraftTools
from DraftTrackers import editTracker, wireTracker, arcTracker, bsplineTracker, bezcurveTracker
from draftguitools.gui_trackers import editTracker, wireTracker, arcTracker, bsplineTracker, bezcurveTracker
from pivy import coin
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
+3 -3
View File
@@ -15,7 +15,7 @@ if FreeCAD.GuiUp:
from PySide.QtCore import QT_TRANSLATE_NOOP
from PySide import QtCore
import DraftTools
import DraftTrackers
import draftguitools.gui_trackers as trackers
from DraftGui import translate
else:
def QT_TRANSLATE_NOOP(context, text):
@@ -221,8 +221,8 @@ class CommandFillet(DraftTools.Creator):
QtCore.QObject.connect(self.ui.check_chamfer,
QtCore.SIGNAL("stateChanged(int)"),
self.set_chamfer)
self.linetrack = DraftTrackers.lineTracker(dotted=True)
self.arctrack = DraftTrackers.arcTracker()
self.linetrack = trackers.lineTracker(dotted=True)
self.arctrack = trackers.arcTracker()
# self.call = self.view.addEventCallback("SoEvent", self.action)
FCC.PrintMessage(translate("draft", "Enter radius") + "\n")
+45 -46
View File
@@ -39,11 +39,11 @@ __url__ = "https://www.freecadweb.org"
import sys, os, FreeCAD, FreeCADGui, WorkingPlane, math, re, Draft, Draft_rc, DraftVecUtils
from FreeCAD import Vector
from PySide import QtCore,QtGui
from DraftGui import todo, translate, utf8_decode
from draftutils.todo import todo
from draftutils.translate import translate
import draftguitools.gui_snapper as gui_snapper
import DraftGui
import DraftTrackers
from DraftTrackers import *
import draftguitools.gui_trackers as trackers
from pivy import coin
if not hasattr(FreeCADGui, "Snapper"):
@@ -58,7 +58,7 @@ if not hasattr(FreeCAD, "DraftWorkingPlane"):
import DraftEdit
# import DraftFillet
import DraftSelectPlane
import draftguitools.gui_selectplane
#---------------------------------------------------------------------------
# Preflight stuff
@@ -271,7 +271,7 @@ class DraftTool:
self.ui.setTitle(name)
self.planetrack = None
if Draft.getParam("showPlaneTracker",False):
self.planetrack = PlaneTracker()
self.planetrack = trackers.PlaneTracker()
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.setTrackers()
@@ -596,7 +596,7 @@ class BSpline(Line):
def Activated(self):
Line.Activated(self,name=translate("draft","BSpline"))
if self.doc:
self.bsplinetrack = bsplineTracker()
self.bsplinetrack = trackers.bsplineTracker()
def action(self,arg):
"""scene event handler"""
@@ -697,7 +697,7 @@ class BezCurve(Line):
def Activated(self):
Line.Activated(self,name=translate("draft","BezCurve"))
if self.doc:
self.bezcurvetrack = bezcurveTracker()
self.bezcurvetrack = trackers.bezcurveTracker()
def action(self,arg):
"""scene event handler"""
@@ -816,7 +816,7 @@ class CubicBezCurve(Line):
def Activated(self):
Line.Activated(self,name=translate("draft","CubicBezCurve"))
if self.doc:
self.bezcurvetrack = bezcurveTracker()
self.bezcurvetrack = trackers.bezcurveTracker()
def action(self,arg):
"""scene event handler"""
@@ -1045,7 +1045,7 @@ class Rectangle(Creator):
self.fillstate = self.ui.hasFill.isChecked()
self.ui.hasFill.setChecked(True)
self.call = self.view.addEventCallback("SoEvent",self.action)
self.rect = rectangleTracker()
self.rect = trackers.rectangleTracker()
FreeCAD.Console.PrintMessage(translate("draft", "Pick first point")+"\n")
def finish(self,closed=False,cont=False):
@@ -1172,8 +1172,8 @@ class Arc(Creator):
else: self.ui.circleUi()
self.altdown = False
self.ui.sourceCmd = self
self.linetrack = lineTracker(dotted=True)
self.arctrack = arcTracker()
self.linetrack = trackers.lineTracker(dotted=True)
self.arctrack = trackers.arcTracker()
self.call = self.view.addEventCallback("SoEvent",self.action)
FreeCAD.Console.PrintMessage(translate("draft", "Pick center point")+"\n")
@@ -1510,7 +1510,7 @@ class Polygon(Creator):
self.ui.numFacesLabel.show()
self.altdown = False
self.ui.sourceCmd = self
self.arctrack = arcTracker()
self.arctrack = trackers.arcTracker()
self.call = self.view.addEventCallback("SoEvent",self.action)
FreeCAD.Console.PrintMessage(translate("draft", "Pick center point")+"\n")
@@ -1688,7 +1688,7 @@ class Ellipse(Creator):
self.ui.pointUi(name)
self.ui.extUi()
self.call = self.view.addEventCallback("SoEvent",self.action)
self.rect = rectangleTracker()
self.rect = trackers.rectangleTracker()
FreeCAD.Console.PrintMessage(translate("draft", "Pick first point")+"\n")
def finish(self,closed=False,cont=False):
@@ -1890,8 +1890,8 @@ class Dimension(Creator):
self.finish()
elif self.hasMeasures():
Creator.Activated(self,name)
self.dimtrack = dimTracker()
self.arctrack = arcTracker()
self.dimtrack = trackers.dimTracker()
self.arctrack = trackers.arcTracker()
self.createOnMeasures()
self.finish()
else:
@@ -1902,8 +1902,8 @@ class Dimension(Creator):
self.ui.selectButton.show()
self.altdown = False
self.call = self.view.addEventCallback("SoEvent",self.action)
self.dimtrack = dimTracker()
self.arctrack = arcTracker()
self.dimtrack = trackers.dimTracker()
self.arctrack = trackers.arcTracker()
self.link = None
self.edges = []
self.pts = []
@@ -2246,7 +2246,7 @@ class ShapeString(Creator):
pass
self.task = DraftGui.ShapeStringTaskPanel()
self.task.sourceCmd = self
DraftGui.todo.delay(FreeCADGui.Control.showDialog,self.task)
todo.delay(FreeCADGui.Control.showDialog,self.task)
else:
self.dialog = None
self.text = ''
@@ -2460,7 +2460,7 @@ class Move(Modifier):
def set_ghosts(self):
if self.ui.isSubelementMode.isChecked():
return self.set_subelement_ghosts()
self.ghosts = [ghostTracker(self.selected_objects)]
self.ghosts = [trackers.ghostTracker(self.selected_objects)]
def set_subelement_ghosts(self):
import Part
@@ -2468,7 +2468,7 @@ class Move(Modifier):
for subelement in object.SubObjects:
if isinstance(subelement, Part.Vertex) \
or isinstance(subelement, Part.Edge):
self.ghosts.append(ghostTracker(subelement))
self.ghosts.append(trackers.ghostTracker(subelement))
def move(self):
if self.ui.isSubelementMode.isChecked():
@@ -2616,7 +2616,7 @@ class Rotate(Modifier):
self.ui.rotateSetCenterUi()
self.ui.modUi()
self.ui.setTitle(translate("draft","Rotate"))
self.arctrack = arcTracker()
self.arctrack = trackers.arcTracker()
self.call = self.view.addEventCallback("SoEvent",self.action)
FreeCAD.Console.PrintMessage(translate("draft", "Pick rotation center")+"\n")
@@ -2730,7 +2730,7 @@ class Rotate(Modifier):
def set_ghosts(self):
if self.ui.isSubelementMode.isChecked():
return self.set_subelement_ghosts()
self.ghosts = [ghostTracker(self.selected_objects)]
self.ghosts = [trackers.ghostTracker(self.selected_objects)]
def set_subelement_ghosts(self):
import Part
@@ -2738,7 +2738,7 @@ class Rotate(Modifier):
for subelement in object.SubObjects:
if isinstance(subelement, Part.Vertex) \
or isinstance(subelement, Part.Edge):
self.ghosts.append(ghostTracker(subelement))
self.ghosts.append(trackers.ghostTracker(subelement))
def finish(self, closed=False, cont=False):
"""finishes the arc"""
@@ -2892,19 +2892,19 @@ class Offset(Modifier):
self.npts = None
self.constrainSeg = None
self.ui.offsetUi()
self.linetrack = lineTracker()
self.linetrack = trackers.lineTracker()
self.faces = False
self.shape = self.sel.Shape
self.mode = None
if Draft.getType(self.sel) in ["Circle","Arc"]:
self.ghost = arcTracker()
self.ghost = trackers.arcTracker()
self.mode = "Circle"
self.center = self.shape.Edges[0].Curve.Center
self.ghost.setCenter(self.center)
self.ghost.setStartAngle(math.radians(self.sel.FirstAngle))
self.ghost.setEndAngle(math.radians(self.sel.LastAngle))
elif Draft.getType(self.sel) == "BSpline":
self.ghost = bsplineTracker(points=self.sel.Points)
self.ghost = trackers.bsplineTracker(points=self.sel.Points)
self.mode = "BSpline"
elif Draft.getType(self.sel) == "BezCurve":
FreeCAD.Console.PrintWarning(translate("draft", "Sorry, offset of Bezier curves is currently still not supported")+"\n")
@@ -2914,7 +2914,7 @@ class Offset(Modifier):
if len(self.sel.Shape.Edges) == 1:
import Part
if isinstance(self.sel.Shape.Edges[0].Curve,Part.Circle):
self.ghost = arcTracker()
self.ghost = trackers.arcTracker()
self.mode = "Circle"
self.center = self.shape.Edges[0].Curve.Center
self.ghost.setCenter(self.center)
@@ -2922,7 +2922,7 @@ class Offset(Modifier):
self.ghost.setStartAngle(self.sel.Shape.Edges[0].FirstParameter)
self.ghost.setEndAngle(self.sel.Shape.Edges[0].LastParameter)
if not self.ghost:
self.ghost = wireTracker(self.shape)
self.ghost = trackers.wireTracker(self.shape)
self.mode = "Wire"
self.call = self.view.addEventCallback("SoEvent",self.action)
FreeCAD.Console.PrintMessage(translate("draft", "Pick distance")+"\n")
@@ -3102,7 +3102,7 @@ class Stretch(Modifier):
self.ui.pointUi("Stretch")
self.ui.extUi()
self.call = self.view.addEventCallback("SoEvent",self.action)
self.rectracker = rectangleTracker(dotted=True,scolor=(0.0,0.0,1.0),swidth=2)
self.rectracker = trackers.rectangleTracker(dotted=True,scolor=(0.0,0.0,1.0),swidth=2)
self.nodetracker = []
self.displacement = None
FreeCAD.Console.PrintMessage(translate("draft", "Pick first point of selection rectangle")+"\n")
@@ -3195,7 +3195,7 @@ class Stretch(Modifier):
self.ops.append([o])
nodes.append(p)
for n in nodes:
nt = editTracker(n,inactive=True)
nt = trackers.editTracker(n,inactive=True)
nt.on()
self.nodetracker.append(nt)
self.step = 3
@@ -3538,7 +3538,7 @@ class Trimex(Modifier):
return
self.obj = sel[0]
self.ui.trimUi()
self.linetrack = lineTracker()
self.linetrack = trackers.lineTracker()
import DraftGeomUtils
@@ -3548,10 +3548,10 @@ class Trimex(Modifier):
if len(self.obj.Shape.Faces) == 1:
# simple extrude mode, the object itself is extruded
self.extrudeMode = True
self.ghost = [ghostTracker([self.obj])]
self.ghost = [trackers.ghostTracker([self.obj])]
self.normal = self.obj.Shape.Faces[0].normalAt(.5,.5)
for v in self.obj.Shape.Vertexes:
self.ghost.append(lineTracker())
self.ghost.append(trackers.lineTracker())
elif len(self.obj.Shape.Faces) > 1:
# face extrude mode, a new object is created
ss = FreeCADGui.Selection.getSelectionEx()[0]
@@ -3560,10 +3560,10 @@ class Trimex(Modifier):
self.obj = self.doc.addObject("Part::Feature","Face")
self.obj.Shape = ss.SubObjects[0]
self.extrudeMode = True
self.ghost = [ghostTracker([self.obj])]
self.ghost = [trackers.ghostTracker([self.obj])]
self.normal = self.obj.Shape.Faces[0].normalAt(.5,.5)
for v in self.obj.Shape.Vertexes:
self.ghost.append(lineTracker())
self.ghost.append(trackers.lineTracker())
else:
# normal wire trimex mode
self.color = self.obj.ViewObject.LineColor
@@ -3583,9 +3583,9 @@ class Trimex(Modifier):
sw = self.width
for e in self.edges:
if DraftGeomUtils.geomType(e) == "Line":
self.ghost.append(lineTracker(scolor=sc,swidth=sw))
self.ghost.append(trackers.lineTracker(scolor=sc,swidth=sw))
else:
self.ghost.append(arcTracker(scolor=sc,swidth=sw))
self.ghost.append(trackers.arcTracker(scolor=sc,swidth=sw))
if not self.ghost: self.finish()
for g in self.ghost: g.on()
self.activePoint = 0
@@ -3967,7 +3967,7 @@ class Scale(Modifier):
def set_ghosts(self):
if self.ui.isSubelementMode.isChecked():
return self.set_subelement_ghosts()
self.ghosts = [ghostTracker(self.selected_objects)]
self.ghosts = [trackers.ghostTracker(self.selected_objects)]
def set_subelement_ghosts(self):
import Part
@@ -3975,7 +3975,7 @@ class Scale(Modifier):
for subelement in object.SubObjects:
if isinstance(subelement, Part.Vertex) \
or isinstance(subelement, Part.Edge):
self.ghosts.append(ghostTracker(subelement))
self.ghosts.append(trackers.ghostTracker(subelement))
def pickRef(self):
self.pickmode = True
@@ -4131,9 +4131,9 @@ class Scale(Modifier):
self.view.removeEventCallback("SoEvent",self.call)
self.task = DraftGui.ScaleTaskPanel()
self.task.sourceCmd = self
DraftGui.todo.delay(FreeCADGui.Control.showDialog,self.task)
DraftGui.todo.delay(self.task.xValue.selectAll,None)
DraftGui.todo.delay(self.task.xValue.setFocus,None)
todo.delay(FreeCADGui.Control.showDialog,self.task)
todo.delay(self.task.xValue.selectAll,None)
todo.delay(self.task.xValue.setFocus,None)
for ghost in self.ghosts:
ghost.on()
elif len(self.node) == 2:
@@ -4972,7 +4972,7 @@ class Mirror(Modifier):
self.ui.modUi()
self.ui.xValue.setFocus()
self.ui.xValue.selectAll()
#self.ghost = ghostTracker(self.sel) TODO: solve this (see below)
# self.ghost = trackers.ghostTracker(self.sel) TODO: solve this (see below)
self.call = self.view.addEventCallback("SoEvent",self.action)
FreeCAD.Console.PrintMessage(translate("draft", "Pick start point of mirror line")+"\n")
self.ui.isCopy.hide()
@@ -5199,7 +5199,7 @@ class Draft_Label(Creator):
self.ui.labelUi(self.name,callback=self.setmode)
self.ui.xValue.setFocus()
self.ui.xValue.selectAll()
self.ghost = DraftTrackers.lineTracker()
self.ghost = trackers.lineTracker()
self.call = self.view.addEventCallback("SoEvent",self.action)
FreeCAD.Console.PrintMessage(translate("draft", "Pick target point")+"\n")
self.ui.isCopy.hide()
@@ -5363,10 +5363,9 @@ class Draft_Arc_3Points:
def Activated(self):
import DraftTrackers
self.points = []
self.normal = None
self.tracker = DraftTrackers.arcTracker()
self.tracker = trackers.arcTracker()
self.tracker.autoinvert = False
if hasattr(FreeCAD,"DraftWorkingPlane"):
FreeCAD.DraftWorkingPlane.setup()
+2 -2
View File
@@ -30,7 +30,7 @@
## Python
### Code formating
### Code formatting
- In general, code should follow [PEP 8](https://www.python.org/dev/peps/pep-0008/)
and [PEP 257](https://www.python.org/dev/peps/pep-0257/) (docstrings).
@@ -72,7 +72,7 @@
and not meant to be part of the public interface should start
with an underscore like `_MyInternalClass` or `_my_small_variable`.
### Python code formating tools
### Python code formatting tools
- Using a code editor that automatically checks compliance with PEP 8
is recommended.
@@ -1,26 +1,26 @@
# -*- coding: utf8 -*-
#***************************************************************************
#* Copyright (c) 2019 Yorik van Havre <[email protected]> *
#* *
#* 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 *
#* *
#***************************************************************************
# ***************************************************************************
# * Copyright (c) 2019 Yorik van Havre <[email protected]> *
# * *
# * 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 *
# * *
# ***************************************************************************
__title__="FreeCAD Draft Workbench GUI Tools - Working plane-related tools"
__title__ = "FreeCAD Draft Workbench GUI Tools - Working plane-related tools"
__author__ = "Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline, Dmitry Chigrin"
__url__ = "https://www.freecadweb.org"
@@ -30,43 +30,43 @@ import FreeCADGui
import math
import Draft
import DraftVecUtils
from DraftGui import todo, translate
from draftutils.todo import todo
from draftutils.translate import translate
def QT_TRANSLATE_NOOP(ctx,txt): return txt
class Draft_SelectPlane:
"""The Draft_SelectPlane FreeCAD command definition"""
"""The Draft_SelectPlane FreeCAD command definition."""
def __init__(self):
self.ac = "FreeCAD.DraftWorkingPlane.alignToPointAndAxis"
self.param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
self.states = []
def GetResources(self):
"""Set icon, menu and tooltip."""
return {'Pixmap' : 'Draft_SelectPlane',
'Accel' : "W, P",
'MenuText': QT_TRANSLATE_NOOP("Draft_SelectPlane", "SelectPlane"),
'ToolTip' : QT_TRANSLATE_NOOP("Draft_SelectPlane", "Select a working plane for geometry creation")}
def IsActive(self):
"""Return True when this command should be available."""
if FreeCADGui.ActiveDocument:
return True
else:
return False
def Activated(self):
"""Execute this when the command is called."""
# reset variables
self.view = Draft.get3DView()
self.wpButton = FreeCADGui.draftToolBar.wplabel
FreeCAD.DraftWorkingPlane.setup()
# write current WP if states are empty
if not self.states:
p = FreeCAD.DraftWorkingPlane
@@ -167,9 +167,7 @@ class Draft_SelectPlane:
self.finish()
def handle(self):
"""tries to build a WP. Returns True if successful"""
"""Build a working plane. Return True if successful."""
sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) == 1:
sel = sel[0]
@@ -276,7 +274,7 @@ class Draft_SelectPlane:
return True
return False
def getCenterPoint(self,x,y,z):
def getCenterPoint(self, x, y, z):
if not self.taskd.form.checkCenter.isChecked():
return FreeCAD.Vector()
@@ -293,19 +291,15 @@ class Draft_SelectPlane:
cp = cam1.add(vcam2)
return cp
def tostr(self,v):
"""makes a string from a vector or tuple"""
def tostr(self, v):
"""Make a string from a vector or tuple."""
return "FreeCAD.Vector("+str(v[0])+","+str(v[1])+","+str(v[2])+")"
def getOffset(self):
"""returns the offset value as a float in mm"""
"""Return the offset value as a float in mm."""
try:
o = float(self.taskd.form.fieldOffset.text())
except:
except Exception:
o = FreeCAD.Units.Quantity(self.taskd.form.fieldOffset.text())
o = o.Value
return o
@@ -377,16 +371,16 @@ class Draft_SelectPlane:
# calculate delta
p = FreeCAD.Vector(c.position.getValue().getValue())
pp = FreeCAD.DraftWorkingPlane.projectPoint(p)
delta = pp.negative() # to bring it above the (0,0) point
delta = pp.negative() # to bring it above the (0,0) point
np = p.add(delta)
c.position.setValue(tuple(np))
self.finish()
def onClickPrevious(self):
p = FreeCAD.DraftWorkingPlane
if len(self.states) > 1:
self.states.pop() # discard the last one
self.states.pop() # discard the last one
s = self.states[-1]
p.u = s[0]
p.v = s[1]
@@ -416,13 +410,11 @@ class Draft_SelectPlane:
def onSetSnapRadius(self,i):
self.param.SetInt("snapRange",i)
if hasattr(FreeCADGui,"Snapper"):
if hasattr(FreeCADGui, "Snapper"):
FreeCADGui.Snapper.showradius()
def display(self,arg):
"""sets the text of the WP button"""
"""Set the text of the working plane button."""
o = self.getOffset()
if o:
if o > 0:
@@ -450,10 +442,8 @@ class Draft_SelectPlane:
FreeCADGui.doCommandGui("FreeCADGui.Snapper.setGrid()")
class SelectPlane_TaskPanel:
'''The editmode TaskPanel for Arch Material objects'''
"""The task panel definition of the Draft_SelectPlane command."""
def __init__(self):
@@ -462,30 +452,28 @@ class SelectPlane_TaskPanel:
def getStandardButtons(self):
return 2097152 #int(QtGui.QDialogButtonBox.Close)
return 2097152 # int(QtGui.QDialogButtonBox.Close)
class Draft_SetWorkingPlaneProxy():
"""The Draft_SetWorkingPlaneProxy FreeCAD command definition"""
def GetResources(self):
return {'Pixmap' : 'Draft_SelectPlane',
"""Set icon, menu and tooltip."""
return {'Pixmap': 'Draft_SelectPlane',
'MenuText': QT_TRANSLATE_NOOP("Draft_SetWorkingPlaneProxy", "Create Working Plane Proxy"),
'ToolTip': QT_TRANSLATE_NOOP("Draft_SetWorkingPlaneProxy", "Creates a proxy object from the current working plane")}
def IsActive(self):
"""Return True when this command should be available."""
if FreeCADGui.ActiveDocument:
return True
else:
return False
def Activated(self):
if hasattr(FreeCAD,"DraftWorkingPlane"):
"""Execute this when the command is called."""
if hasattr(FreeCAD, "DraftWorkingPlane"):
FreeCAD.ActiveDocument.openTransaction("Create WP proxy")
FreeCADGui.addModule("Draft")
FreeCADGui.doCommand("Draft.makeWorkingPlaneProxy(FreeCAD.DraftWorkingPlane.getPlacement())")
@@ -493,6 +481,5 @@ class Draft_SetWorkingPlaneProxy():
FreeCAD.ActiveDocument.commitTransaction()
FreeCADGui.addCommand('Draft_SelectPlane',Draft_SelectPlane())
FreeCADGui.addCommand('Draft_SetWorkingPlaneProxy',Draft_SetWorkingPlaneProxy())
FreeCADGui.addCommand('Draft_SelectPlane', Draft_SelectPlane())
FreeCADGui.addCommand('Draft_SetWorkingPlaneProxy', Draft_SetWorkingPlaneProxy())
+43 -39
View File
@@ -1,25 +1,25 @@
#***************************************************************************
#* Copyright (c) 2011 Yorik van Havre <[email protected]> *
#* *
#* 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 *
#* *
#***************************************************************************
# ***************************************************************************
# * Copyright (c) 2011 Yorik van Havre <[email protected]> *
# * *
# * 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 *
# * *
# ***************************************************************************
__title__="FreeCAD Draft Snap tools"
__title__ = "FreeCAD Draft Snap tools"
__author__ = "Yorik van Havre"
__url__ = "https://www.freecadweb.org"
@@ -31,14 +31,18 @@ __url__ = "https://www.freecadweb.org"
# everything that goes with it (toolbar buttons, cursor icons, etc)
import FreeCAD, FreeCADGui, math, Draft, DraftGui, DraftTrackers, DraftVecUtils, itertools
import FreeCAD, FreeCADGui, math, Draft, DraftVecUtils, itertools
import draftguitools.gui_trackers as trackers
from collections import OrderedDict
from FreeCAD import Vector
from pivy import coin
from PySide import QtCore,QtGui
from PySide import QtCore, QtGui
class Snapper:
"""The Snapper objects contains all the functionality used by draft
"""Classes to manage snapping in Draft and Arch.
The Snapper objects contains all the functionality used by draft
and arch module to manage object snapping. It is responsible for
finding snap points and displaying snap markers. Usually You
only need to invoke it's snap() function, all the rest is taken
@@ -972,9 +976,9 @@ class Snapper:
"show arch dimensions between 2 points"
if self.isEnabled("Dimensions"):
if not self.dim1:
self.dim1 = DraftTrackers.archDimTracker(mode=2)
self.dim1 = trackers.archDimTracker(mode=2)
if not self.dim2:
self.dim2 = DraftTrackers.archDimTracker(mode=3)
self.dim2 = trackers.archDimTracker(mode=3)
self.dim1.p1(p1)
self.dim2.p1(p1)
self.dim1.p2(p2)
@@ -1090,9 +1094,9 @@ class Snapper:
# setup trackers if needed
if not self.constrainLine:
if self.snapStyle:
self.constrainLine = DraftTrackers.lineTracker(scolor=FreeCADGui.draftToolBar.getDefaultColor("snap"))
self.constrainLine = trackers.lineTracker(scolor=FreeCADGui.draftToolBar.getDefaultColor("snap"))
else:
self.constrainLine = DraftTrackers.lineTracker(dotted=True)
self.constrainLine = trackers.lineTracker(dotted=True)
# setting basepoint
if not basepoint:
@@ -1441,23 +1445,23 @@ class Snapper:
self.holdTracker = self.trackers[9][i]
else:
if Draft.getParam("grid",True):
self.grid = DraftTrackers.gridTracker()
self.grid = trackers.gridTracker()
self.grid.on()
else:
self.grid = None
self.tracker = DraftTrackers.snapTracker()
self.trackLine = DraftTrackers.lineTracker()
self.tracker = trackers.snapTracker()
self.trackLine = trackers.lineTracker()
if self.snapStyle:
c = FreeCADGui.draftToolBar.getDefaultColor("snap")
self.extLine = DraftTrackers.lineTracker(scolor=c)
self.extLine2 = DraftTrackers.lineTracker(scolor = c)
self.extLine = trackers.lineTracker(scolor=c)
self.extLine2 = trackers.lineTracker(scolor = c)
else:
self.extLine = DraftTrackers.lineTracker(dotted=True)
self.extLine2 = DraftTrackers.lineTracker(dotted=True)
self.radiusTracker = DraftTrackers.radiusTracker()
self.dim1 = DraftTrackers.archDimTracker(mode=2)
self.dim2 = DraftTrackers.archDimTracker(mode=3)
self.holdTracker = DraftTrackers.snapTracker()
self.extLine = trackers.lineTracker(dotted=True)
self.extLine2 = trackers.lineTracker(dotted=True)
self.radiusTracker = trackers.radiusTracker()
self.dim1 = trackers.archDimTracker(mode=2)
self.dim2 = trackers.archDimTracker(mode=3)
self.holdTracker = trackers.snapTracker()
self.holdTracker.setMarker("cross")
self.holdTracker.clear()
self.trackers[0].append(v)
+1 -1
View File
@@ -68,7 +68,7 @@ class DraftGuiImport(unittest.TestCase):
def test_import_gui_draft_trackers(self):
"""Import Draft tracker utilities."""
module = "DraftTrackers"
module = "draftguitools.gui_trackers"
if not App.GuiUp:
aux._no_gui(module)
self.assertTrue(True)
@@ -68,7 +68,7 @@ class DraftImportTools(unittest.TestCase):
def test_import_gui_draftplane(self):
"""Import Draft SelectPlane."""
module = "DraftSelectPlane"
module = "draftguitools.gui_selectplane"
if not App.GuiUp:
aux._no_gui(module)
self.assertTrue(True)
+15 -8
View File
@@ -59,15 +59,22 @@ def get_draft_array_commands():
def get_draft_modification_commands():
"""Return the modification commands list."""
lst = ["Draft_Move", "Draft_Rotate", "Draft_Offset",
"Draft_Trimex", "Draft_Join", "Draft_Split",
"Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
"Draft_Edit", "Draft_SubelementHighlight",
"Draft_WireToBSpline", "Draft_Draft2Sketch",
"Draft_Shape2DView"]
lst = ["Draft_Move", "Draft_Rotate",
"Draft_Scale", "Draft_Mirror",
"Draft_Offset", "Draft_Trimex",
"Draft_Stretch",
"Separator",
"Draft_Clone"]
lst += get_draft_array_commands()
lst += ["Draft_Clone",
"Draft_Drawing", "Draft_Mirror", "Draft_Stretch"]
lst += ["Separator",
"Draft_Edit", "Draft_SubelementHighlight",
"Separator",
"Draft_Join", "Draft_Split",
"Draft_Upgrade", "Draft_Downgrade",
"Separator",
"Draft_WireToBSpline", "Draft_Draft2Sketch",
"Separator",
"Draft_Shape2DView", "Draft_Drawing"]
return lst
+2 -1
View File
@@ -221,12 +221,13 @@ SET(FemTestsMesh_SRCS
SET(FemTools_SRCS
femtools/__init__.py
femtools/membertools.py
femtools/ccxtools.py
femtools/checksanalysis.py
femtools/constants.py
femtools/errors.py
femtools/femutils.py
femtools/geomtools.py
femtools/membertools.py
femtools/tokrules.py
)
@@ -39,7 +39,7 @@ import FreeCAD
import FreeCADGui
import FreeCADGui as Gui
from femmesh import meshtools
from femtools import geomtools
class _Selector(QtGui.QWidget):
@@ -375,7 +375,7 @@ class GeometryElementsSelection(QtGui.QWidget):
# since only Subelements can be selected
# we're going to select all Faces of said Solids
# the method getElement(element)doesn't return Solid elements
solid = meshtools.get_element(ref[0], ref[1])
solid = geomtools.get_element(ref[0], ref[1])
if not solid:
return
faces = []
@@ -463,50 +463,56 @@ class GeometryElementsSelection(QtGui.QWidget):
self.sel_server = FemSelectionObserver(self.selectionParser, print_message)
def selectionParser(self, selection):
print("selection: {} {} {}".format(
FreeCAD.Console.PrintMessage("Selection: {} {} {}\n".format(
selection[0].Shape.ShapeType,
selection[0].Name,
selection[1]
))
if hasattr(selection[0], "Shape") and selection[1]:
elt = selection[0].Shape.getElement(selection[1])
sobj = selection[0]
elt = sobj.Shape.getElement(selection[1])
ele_ShapeType = elt.ShapeType
if self.selection_mode_solid and "Solid" in self.sel_elem_types:
# in solid selection mode use edges and faces for selection of a solid
# adapt selection variable to hold the Solid
solid_to_add = None
if ele_ShapeType == "Edge":
found_edge = False
for i, s in enumerate(selection[0].Shape.Solids):
found_eltedge_in_other_solid = False
for i, s in enumerate(sobj.Shape.Solids):
for e in s.Edges:
if elt.isSame(e):
if not found_edge:
if found_eltedge_in_other_solid is False:
solid_to_add = str(i + 1)
else:
# could be more than two solids, think of polar pattern
FreeCAD.Console.PrintMessage(
"Edge belongs to more than one solid\n"
" Edge belongs to at least two solids: "
" Solid{}, Solid{}\n"
.format(solid_to_add, str(i + 1))
)
solid_to_add = None
found_edge = True
found_eltedge_in_other_solid = True
elif ele_ShapeType == "Face":
found_face = False
for i, s in enumerate(selection[0].Shape.Solids):
found_eltface_in_other_solid = False
for i, s in enumerate(sobj.Shape.Solids):
for e in s.Faces:
if elt.isSame(e):
if not found_face:
if not found_eltface_in_other_solid:
solid_to_add = str(i + 1)
else:
# AFAIK (bernd) a face can only belong to two solids
FreeCAD.Console.PrintMessage(
"Face belongs to more than one solid\n"
" Face belongs to two solids: Solid{}, Solid{}\n"
.format(solid_to_add, str(i + 1))
)
solid_to_add = None
found_face = True
found_eltface_in_other_solid = True
if solid_to_add:
selection = (selection[0], "Solid" + solid_to_add)
selection = (sobj, "Solid" + solid_to_add)
ele_ShapeType = "Solid"
FreeCAD.Console.PrintMessage(
"selection variable adapted to hold the Solid: {} {} {}\n"
.format(selection[0].Shape.ShapeType, selection[0].Name, selection[1])
" Selection variable adapted to hold the Solid: {} {} {}\n"
.format(sobj.Shape.ShapeType, sobj.Name, selection[1])
)
else:
return
@@ -533,11 +539,15 @@ class GeometryElementsSelection(QtGui.QWidget):
# selected shape will not added to the list
FreeCADGui.Selection.clearSelection()
message = (
"{} is in reference list already!\n"
" Selection {} is in reference list already!\n"
.format(self.get_item_text(selection))
)
FreeCAD.Console.PrintMessage(message)
QtGui.QMessageBox.critical(None, "Geometry already in list", message)
QtGui.QMessageBox.critical(
None,
"Geometry already in list",
message.lstrip(" ")
)
else:
# selected shape will not added to the list
FreeCADGui.Selection.clearSelection()
@@ -548,7 +558,7 @@ class GeometryElementsSelection(QtGui.QWidget):
def has_equal_references_shape_types(self, ref_shty=""):
for ref in self.references:
# the method getElement(element) does not return Solid elements
r = meshtools.get_element(ref[0], ref[1])
r = geomtools.get_element(ref[0], ref[1])
if not r:
FreeCAD.Console.PrintError(
"Problem in retrieving element: {} \n".format(ref[1])
+10 -10
View File
@@ -30,9 +30,12 @@ __url__ = "http://www.freecadweb.org"
# \brief FreeCAD Z88 Mesh reader and writer for FEM workbench
import os
import FreeCAD
from FreeCAD import Console
from femmesh import meshtools
# ************************************************************************************************
# ********* generic FreeCAD import and export methods ********************************************
# names are fix given from FreeCAD, these methods are called from FreeCAD
@@ -86,10 +89,9 @@ def export(
Console.PrintError("No FEM mesh object selected.\n")
return
femnodes_mesh = obj.FemMesh.Nodes
import femmesh.meshtools as FemMeshTools
femelement_table = FemMeshTools.get_femelement_table(obj.FemMesh)
femelement_table = meshtools.get_femelement_table(obj.FemMesh)
z88_element_type = get_z88_element_type(obj.FemMesh, femelement_table)
f = pyopen(filename, "wb")
f = pyopen(filename, "w")
write_z88_mesh_to_file(femnodes_mesh, femelement_table, z88_element_type, f)
f.close()
@@ -429,8 +431,7 @@ def write(
Console.PrintError("Not a FemMesh was given as parameter.\n")
return
femnodes_mesh = fem_mesh.Nodes
import femmesh.meshtools as FemMeshTools
femelement_table = FemMeshTools.get_femelement_table(fem_mesh)
femelement_table = meshtools.get_femelement_table(fem_mesh)
z88_element_type = get_z88_element_type(fem_mesh, femelement_table)
f = pyopen(filename, "w")
write_z88_mesh_to_file(femnodes_mesh, femelement_table, z88_element_type, f)
@@ -554,18 +555,17 @@ def get_z88_element_type(
femmesh,
femelement_table=None
):
import femmesh.meshtools as FemMeshTools
if not femmesh:
Console.PrintError("Error: No femmesh.\n")
if not femelement_table:
Console.PrintError("The femelement_table need to be calculated.\n")
femelement_table = FemMeshTools.get_femelement_table(femmesh)
femelement_table = meshtools.get_femelement_table(femmesh)
# in some cases lowest key in femelement_table is not [1]
for elem in sorted(femelement_table):
elem_length = len(femelement_table[elem])
Console.PrintLog("Node count of first element: {}\n".format(elem_length))
break # break after the first elem
if FemMeshTools.is_solid_femmesh(femmesh):
if meshtools.is_solid_femmesh(femmesh):
if femmesh.TetraCount == femmesh.VolumeCount:
if elem_length == 4:
return 17
@@ -583,7 +583,7 @@ def get_z88_element_type(
return 0
else:
Console.PrintError("no tetra, no hexa or Mixed Volume Elements.\n")
elif FemMeshTools.is_face_femmesh(femmesh):
elif meshtools.is_face_femmesh(femmesh):
if femmesh.TriangleCount == femmesh.FaceCount:
if elem_length == 3:
Console.PrintError("tria3mesh, not supported by Z88.\n")
@@ -605,7 +605,7 @@ def get_z88_element_type(
else:
Console.PrintError("no tria, no quad\n")
return 0
elif FemMeshTools.is_edge_femmesh(femmesh):
elif meshtools.is_edge_femmesh(femmesh):
Console.PrintMessage("Edge femmesh will be exported as 3D truss element nr 4.\n")
return 4
else:
+7 -6
View File
@@ -37,6 +37,7 @@ from FreeCAD import Units
import Fem
from . import meshtools
from femtools import femutils
from femtools import geomtools
class GmshTools():
@@ -413,8 +414,8 @@ class GmshTools():
# Shape to mesh and use the found element as elems
# the method getElement(element)
# does not return Solid elements
ele_shape = meshtools.get_element(sub[0], elems)
found_element = meshtools.find_element_in_shape(
ele_shape = geomtools.get_element(sub[0], elems)
found_element = geomtools.find_element_in_shape(
self.part_obj.Shape, ele_shape
)
if found_element:
@@ -450,8 +451,8 @@ class GmshTools():
)
for eleml in self.ele_length_map:
# the method getElement(element) does not return Solid elements
ele_shape = meshtools.get_element(self.part_obj, eleml)
ele_vertexes = meshtools.get_vertexes_by_element(self.part_obj.Shape, ele_shape)
ele_shape = geomtools.get_element(self.part_obj, eleml)
ele_vertexes = geomtools.get_vertexes_by_element(self.part_obj.Shape, ele_shape)
self.ele_node_map[eleml] = ele_vertexes
Console.PrintMessage(" {}\n".format(self.ele_length_map))
Console.PrintMessage(" {}\n".format(self.ele_node_map))
@@ -501,8 +502,8 @@ class GmshTools():
# we try to find the element it in the Shape to mesh
# and use the found element as elems
# the method getElement(element) does not return Solid elements
ele_shape = meshtools.get_element(sub[0], elems)
found_element = meshtools.find_element_in_shape(
ele_shape = geomtools.get_element(sub[0], elems)
found_element = geomtools.find_element_in_shape(
self.part_obj.Shape,
ele_shape
)
+6 -224
View File
@@ -29,6 +29,8 @@ __url__ = "http://www.freecadweb.org"
import FreeCAD
from femtools import geomtools
# ************************************************************************************************
def get_femnodes_by_femobj_with_references(
@@ -113,7 +115,7 @@ def get_femnodes_by_refshape(
nodes = []
for refelement in ref[1]:
# the following method getElement(element) does not return Solid elements
r = get_element(ref[0], refelement)
r = geomtools.get_element(ref[0], refelement)
FreeCAD.Console.PrintMessage(
" "
"ReferenceShape ... Type: {0}, "
@@ -1934,7 +1936,7 @@ def get_reference_group_elements(
# FreeCAD.Console.PrintMessage("{}\n".format(childs))
for child in childs:
# the method getElement(element) does not return Solid elements
ref_shape = get_element(parent, child)
ref_shape = geomtools.get_element(parent, child)
if not stype:
stype = ref_shape.ShapeType
elif stype != ref_shape.ShapeType:
@@ -1942,7 +1944,7 @@ def get_reference_group_elements(
"Error, two refshapes in References with different ShapeTypes.\n"
)
FreeCAD.Console.PrintLog("\n".format(ref_shape))
found_element = find_element_in_shape(aShape, ref_shape)
found_element = geomtools.find_element_in_shape(aShape, ref_shape)
if found_element is not None:
elements.append(found_element)
else:
@@ -1976,7 +1978,7 @@ def get_reference_group_elements(
else:
FreeCAD.Console.PrintError("This should not happen, please debug!\n")
# in this case we would not have needed to use the
# is_same_geometry() inside find_element_in_shape()
# is_same_geometry() inside geomtools.find_element_in_shape()
# AFAIK we could have used the Part methods isPartner() or even isSame()
# We're going to find out when we need to debug this :-)!
return (key, sorted(elements))
@@ -2056,167 +2058,6 @@ def get_anlysis_empty_references_group_elements(
return group_elements
# ************************************************************************************************
def find_element_in_shape(
aShape,
anElement
):
# import Part
ele_st = anElement.ShapeType
if ele_st == "Solid" or ele_st == "CompSolid":
for index, solid in enumerate(aShape.Solids):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(solid, anElement)))
if is_same_geometry(solid, anElement):
# FreeCAD.Console.PrintMessage("{}\n".format(index))
# Part.show(aShape.Solids[index])
ele = ele_st + str(index + 1)
return ele
FreeCAD.Console.PrintError(
"Solid " + str(anElement) + " not found in: " + str(aShape) + "\n"
)
if ele_st == "Solid" and aShape.ShapeType == "Solid":
message_part = (
"We have been searching for a Solid in a Solid and we have not found it. "
"In most cases this should be searching for a Solid inside a CompSolid. "
"Check the ShapeType of your Part to mesh."
)
FreeCAD.Console.PrintMessage(message_part + "\n")
# Part.show(anElement)
# Part.show(aShape)
elif ele_st == "Face" or ele_st == "Shell":
for index, face in enumerate(aShape.Faces):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(face, anElement)))
if is_same_geometry(face, anElement):
# FreeCAD.Console.PrintMessage("{}\n".format(index))
# Part.show(aShape.Faces[index])
ele = ele_st + str(index + 1)
return ele
elif ele_st == "Edge" or ele_st == "Wire":
for index, edge in enumerate(aShape.Edges):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(edge, anElement)))
if is_same_geometry(edge, anElement):
# FreeCAD.Console.PrintMessage(index, "\n")
# Part.show(aShape.Edges[index])
ele = ele_st + str(index + 1)
return ele
elif ele_st == "Vertex":
for index, vertex in enumerate(aShape.Vertexes):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(vertex, anElement)))
if is_same_geometry(vertex, anElement):
# FreeCAD.Console.PrintMessage("{}\n".format(index))
# Part.show(aShape.Vertexes[index])
ele = ele_st + str(index + 1)
return ele
elif ele_st == "Compound":
FreeCAD.Console.PrintError("Compound is not supported.\n")
# ************************************************************************************************
def get_vertexes_by_element(
aShape,
anElement
):
# we're going to extend the method find_element_in_shape and return the vertexes
# import Part
ele_vertexes = []
ele_st = anElement.ShapeType
if ele_st == "Solid" or ele_st == "CompSolid":
for index, solid in enumerate(aShape.Solids):
if is_same_geometry(solid, anElement):
for vele in aShape.Solids[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)), "\n")
return ele_vertexes
FreeCAD.Console.PrintError(
"Error, Solid " + str(anElement) + " not found in: " + str(aShape) + "\n"
)
elif ele_st == "Face" or ele_st == "Shell":
for index, face in enumerate(aShape.Faces):
if is_same_geometry(face, anElement):
for vele in aShape.Faces[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n")
return ele_vertexes
elif ele_st == "Edge" or ele_st == "Wire":
for index, edge in enumerate(aShape.Edges):
if is_same_geometry(edge, anElement):
for vele in aShape.Edges[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n")
return ele_vertexes
elif ele_st == "Vertex":
for index, vertex in enumerate(aShape.Vertexes):
if is_same_geometry(vertex, anElement):
ele_vertexes.append(index)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n")
return ele_vertexes
elif ele_st == "Compound":
FreeCAD.Console.PrintError("Compound is not supported.\n")
# ************************************************************************************************
def is_same_geometry(
shape1,
shape2
):
# the vertexes and the CenterOfMass are compared
# it is a hack, but I do not know any better !
# check of Volume and Area before starting with the vertices could be added
# BoundBox is possible too, but is BB calculations robust?!
# FreeCAD.Console.PrintMessage("{}\n".format(shape1))
# FreeCAD.Console.PrintMessage("{}\n".format(shape2))
same_Vertexes = 0
if len(shape1.Vertexes) == len(shape2.Vertexes) and len(shape1.Vertexes) > 1:
# compare CenterOfMass
if shape1.CenterOfMass != shape2.CenterOfMass:
return False
else:
# compare the Vertexes
for vs1 in shape1.Vertexes:
for vs2 in shape2.Vertexes:
if vs1.X == vs2.X and vs1.Y == vs2.Y and vs1.Z == vs2.Z:
same_Vertexes += 1
continue
# FreeCAD.Console.PrintMessage("{}\n".(same_Vertexes))
if same_Vertexes == len(shape1.Vertexes):
return True
else:
return False
if len(shape1.Vertexes) == len(shape2.Vertexes) and len(shape1.Vertexes) == 1:
vs1 = shape1.Vertexes[0]
vs2 = shape2.Vertexes[0]
if vs1.X == vs2.X and vs1.Y == vs2.Y and vs1.Z == vs2.Z:
return True
else:
return False
else:
return False
# ************************************************************************************************
def get_element(
part,
element
):
if element.startswith("Solid"):
index = int(element.lstrip("Solid")) - 1
if index >= len(part.Shape.Solids):
FreeCAD.Console.PrintError(
"Index out of range. This Solid does not exist in the Shape!\n"
)
return None
else:
return part.Shape.Solids[index] # Solid
else:
return part.Shape.getElement(element) # Face, Edge, Vertex
# ************************************************************************************************
def femelements_count_ok(
len_femelement_table,
@@ -2374,65 +2215,6 @@ def get_three_non_colinear_nodes(
return [node_1, node_2, node_3]
# ************************************************************************************************
def get_rectangular_coords(
obj
):
from math import cos, sin, radians
A = [1, 0, 0]
B = [0, 1, 0]
a_x = A[0]
a_y = A[1]
a_z = A[2]
b_x = B[0]
b_y = B[1]
b_z = B[2]
x_rot = radians(obj.X_rot)
y_rot = radians(obj.Y_rot)
z_rot = radians(obj.Z_rot)
if obj.X_rot != 0:
a_y = A[1] * cos(x_rot) + A[2] * sin(x_rot)
a_z = A[2] * cos(x_rot) - A[1] * sin(x_rot)
b_y = B[1] * cos(x_rot) + B[2] * sin(x_rot)
b_z = B[2] * cos(x_rot) - B[1] * sin(x_rot)
if obj.Y_rot != 0:
a_x = A[0] * cos(y_rot) - A[2] * sin(y_rot)
a_z = A[2] * cos(y_rot) + A[0] * sin(y_rot)
b_x = B[0] * cos(y_rot) - B[2] * sin(y_rot)
b_z = B[2] * cos(y_rot) + B[0] * sin(z_rot)
if obj.Z_rot != 0:
a_x = A[0] * cos(z_rot) + A[1] * sin(z_rot)
a_y = A[1] * cos(z_rot) - A[0] * sin(z_rot)
b_x = B[0] * cos(z_rot) + B[1] * sin(z_rot)
b_y = B[1] * cos(z_rot) - B[0] * sin(z_rot)
A = [a_x, a_y, a_z]
B = [b_x, b_y, b_z]
A_coords = str(round(A[0], 4)) + "," + str(round(A[1], 4)) + "," + str(round(A[2], 4))
B_coords = str(round(B[0], 4)) + "," + str(round(B[1], 4)) + "," + str(round(B[2], 4))
coords = A_coords + "," + B_coords
return coords
# ************************************************************************************************
def get_cylindrical_coords(
obj
):
vec = obj.Axis
base = obj.BasePoint
Ax = base[0] + 10 * vec[0]
Ay = base[1] + 10 * vec[1]
Az = base[2] + 10 * vec[2]
Bx = base[0] - 10 * vec[0]
By = base[1] - 10 * vec[1]
Bz = base[2] - 10 * vec[2]
A = [Ax, Ay, Az]
B = [Bx, By, Bz]
A_coords = str(A[0]) + "," + str(A[1]) + "," + str(A[2])
B_coords = str(B[0]) + "," + str(B[1]) + "," + str(B[2])
coords = A_coords + "," + B_coords
return coords
# ************************************************************************************************
def write_D_network_element_to_inputfile(
fileName
+3 -2
View File
@@ -39,6 +39,7 @@ import FreeCAD
from .. import writerbase
from femmesh import meshtools
from femtools import geomtools
class FemInputWriterCcx(writerbase.FemInputWriter):
@@ -1084,11 +1085,11 @@ class FemInputWriterCcx(writerbase.FemInputWriter):
f.write("** " + trans_obj.Label + "\n")
if trans_obj.TransformType == "Rectangular":
f.write("*TRANSFORM, NSET=Rect" + trans_obj.Name + ", TYPE=R\n")
coords = meshtools.get_rectangular_coords(trans_obj)
coords = geomtools.get_rectangular_coords(trans_obj)
f.write(coords + "\n")
elif trans_obj.TransformType == "Cylindrical":
f.write("*TRANSFORM, NSET=Cylin" + trans_obj.Name + ", TYPE=C\n")
coords = meshtools.get_cylindrical_coords(trans_obj)
coords = geomtools.get_cylindrical_coords(trans_obj)
f.write(coords + "\n")
def write_constraints_selfweight(self, f):
+8 -8
View File
@@ -32,12 +32,12 @@ import time
import FreeCAD
from .. import writerbase as FemInputWriter
from .. import writerbase
from feminout import importZ88Mesh
from femmesh import meshtools as FemMeshTools
from femmesh import meshtools
class FemInputWriterZ88(FemInputWriter.FemInputWriter):
class FemInputWriterZ88(writerbase.FemInputWriter):
def __init__(
self,
analysis_obj,
@@ -46,7 +46,7 @@ class FemInputWriterZ88(FemInputWriter.FemInputWriter):
member,
dir_name=None
):
FemInputWriter.FemInputWriter.__init__(
writerbase.FemInputWriter.__init__(
self,
analysis_obj,
solver_obj,
@@ -68,7 +68,7 @@ class FemInputWriterZ88(FemInputWriter.FemInputWriter):
if not self.femnodes_mesh:
self.femnodes_mesh = self.femmesh.Nodes
if not self.femelement_table:
self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh)
self.femelement_table = meshtools.get_femelement_table(self.femmesh)
self.element_count = len(self.femelement_table)
self.set_z88_elparam()
self.write_z88_mesh()
@@ -193,7 +193,7 @@ class FemInputWriterZ88(FemInputWriter.FemInputWriter):
def write_z88_elements_properties(self):
element_properties_file_path = self.file_name + "elp.txt"
elements_data = []
if FemMeshTools.is_edge_femmesh(self.femmesh):
if meshtools.is_edge_femmesh(self.femmesh):
if len(self.beamsection_objects) == 1:
beam_obj = self.beamsection_objects[0]["Object"]
width = beam_obj.RectWidth.getValueAs("mm")
@@ -207,7 +207,7 @@ class FemInputWriterZ88(FemInputWriter.FemInputWriter):
)
else:
FreeCAD.Console.PrintError("Multiple beamsections for Z88 not yet supported!\n")
elif FemMeshTools.is_face_femmesh(self.femmesh):
elif meshtools.is_face_femmesh(self.femmesh):
if len(self.shellthickness_objects) == 1:
thick_obj = self.shellthickness_objects[0]["Object"]
thickness = str(thick_obj.Thickness.getValueAs("mm"))
@@ -218,7 +218,7 @@ class FemInputWriterZ88(FemInputWriter.FemInputWriter):
FreeCAD.Console.PrintError(
"Multiple thicknesses for Z88 not yet supported!\n"
)
elif FemMeshTools.is_solid_femmesh(self.femmesh):
elif meshtools.is_solid_femmesh(self.femmesh):
elements_data.append("1 " + str(self.element_count) + " 0 0 0 0 0 0 0")
else:
FreeCAD.Console.PrintError("Error!\n")
+2 -2
View File
@@ -309,10 +309,10 @@ def get_refshape_type(fem_doc_object):
:note:
Undefined behaviour if constraint contains no references (empty list).
"""
import femmesh.meshtools as FemMeshTools
from femtools.geomtools import get_element
if hasattr(fem_doc_object, "References") and fem_doc_object.References:
first_ref_obj = fem_doc_object.References[0]
first_ref_shape = FemMeshTools.get_element(first_ref_obj[0], first_ref_obj[1][0])
first_ref_shape = get_element(first_ref_obj[0], first_ref_obj[1][0])
st = first_ref_shape.ShapeType
FreeCAD.Console.PrintMessage(
"References: {} in {}, {}\n". format(st, fem_doc_object.Name, fem_doc_object.Label)
+248
View File
@@ -0,0 +1,248 @@
# ***************************************************************************
# * Copyright (c) 2020 Bernd Hahnebach <[email protected]> *
# * *
# * 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. *
# * *
# * 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 *
# * *
# ***************************************************************************
__title__ = "FEM geometry tools"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
import FreeCAD
# ************************************************************************************************
def find_element_in_shape(
aShape,
anElement
):
# import Part
ele_st = anElement.ShapeType
if ele_st == "Solid" or ele_st == "CompSolid":
for index, solid in enumerate(aShape.Solids):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(solid, anElement)))
if is_same_geometry(solid, anElement):
# FreeCAD.Console.PrintMessage("{}\n".format(index))
# Part.show(aShape.Solids[index])
ele = ele_st + str(index + 1)
return ele
FreeCAD.Console.PrintError(
"Solid " + str(anElement) + " not found in: " + str(aShape) + "\n"
)
if ele_st == "Solid" and aShape.ShapeType == "Solid":
message_part = (
"We have been searching for a Solid in a Solid and we have not found it. "
"In most cases this should be searching for a Solid inside a CompSolid. "
"Check the ShapeType of your Part to mesh."
)
FreeCAD.Console.PrintMessage(message_part + "\n")
# Part.show(anElement)
# Part.show(aShape)
elif ele_st == "Face" or ele_st == "Shell":
for index, face in enumerate(aShape.Faces):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(face, anElement)))
if is_same_geometry(face, anElement):
# FreeCAD.Console.PrintMessage("{}\n".format(index))
# Part.show(aShape.Faces[index])
ele = ele_st + str(index + 1)
return ele
elif ele_st == "Edge" or ele_st == "Wire":
for index, edge in enumerate(aShape.Edges):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(edge, anElement)))
if is_same_geometry(edge, anElement):
# FreeCAD.Console.PrintMessage(index, "\n")
# Part.show(aShape.Edges[index])
ele = ele_st + str(index + 1)
return ele
elif ele_st == "Vertex":
for index, vertex in enumerate(aShape.Vertexes):
# FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(vertex, anElement)))
if is_same_geometry(vertex, anElement):
# FreeCAD.Console.PrintMessage("{}\n".format(index))
# Part.show(aShape.Vertexes[index])
ele = ele_st + str(index + 1)
return ele
elif ele_st == "Compound":
FreeCAD.Console.PrintError("Compound is not supported.\n")
# ************************************************************************************************
def get_vertexes_by_element(
aShape,
anElement
):
# we're going to extend the method find_element_in_shape and return the vertexes
# import Part
ele_vertexes = []
ele_st = anElement.ShapeType
if ele_st == "Solid" or ele_st == "CompSolid":
for index, solid in enumerate(aShape.Solids):
if is_same_geometry(solid, anElement):
for vele in aShape.Solids[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)), "\n")
return ele_vertexes
FreeCAD.Console.PrintError(
"Error, Solid " + str(anElement) + " not found in: " + str(aShape) + "\n"
)
elif ele_st == "Face" or ele_st == "Shell":
for index, face in enumerate(aShape.Faces):
if is_same_geometry(face, anElement):
for vele in aShape.Faces[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n")
return ele_vertexes
elif ele_st == "Edge" or ele_st == "Wire":
for index, edge in enumerate(aShape.Edges):
if is_same_geometry(edge, anElement):
for vele in aShape.Edges[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n")
return ele_vertexes
elif ele_st == "Vertex":
for index, vertex in enumerate(aShape.Vertexes):
if is_same_geometry(vertex, anElement):
ele_vertexes.append(index)
# FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n")
return ele_vertexes
elif ele_st == "Compound":
FreeCAD.Console.PrintError("Compound is not supported.\n")
# ************************************************************************************************
def is_same_geometry(
shape1,
shape2
):
# the vertexes and the CenterOfMass are compared
# it is a hack, but I do not know any better !
# check of Volume and Area before starting with the vertices could be added
# BoundBox is possible too, but is BB calculations robust?!
# FreeCAD.Console.PrintMessage("{}\n".format(shape1))
# FreeCAD.Console.PrintMessage("{}\n".format(shape2))
same_Vertexes = 0
if len(shape1.Vertexes) == len(shape2.Vertexes) and len(shape1.Vertexes) > 1:
# compare CenterOfMass
if shape1.CenterOfMass != shape2.CenterOfMass:
return False
else:
# compare the Vertexes
for vs1 in shape1.Vertexes:
for vs2 in shape2.Vertexes:
if vs1.X == vs2.X and vs1.Y == vs2.Y and vs1.Z == vs2.Z:
same_Vertexes += 1
continue
# FreeCAD.Console.PrintMessage("{}\n".(same_Vertexes))
if same_Vertexes == len(shape1.Vertexes):
return True
else:
return False
if len(shape1.Vertexes) == len(shape2.Vertexes) and len(shape1.Vertexes) == 1:
vs1 = shape1.Vertexes[0]
vs2 = shape2.Vertexes[0]
if vs1.X == vs2.X and vs1.Y == vs2.Y and vs1.Z == vs2.Z:
return True
else:
return False
else:
return False
# ************************************************************************************************
def get_element(
part,
element
):
if element.startswith("Solid"):
index = int(element.lstrip("Solid")) - 1
if index >= len(part.Shape.Solids):
FreeCAD.Console.PrintError(
"Index out of range. This Solid does not exist in the Shape!\n"
)
return None
else:
return part.Shape.Solids[index] # Solid
else:
return part.Shape.getElement(element) # Face, Edge, Vertex
# ************************************************************************************************
def get_rectangular_coords(
obj
):
from math import cos, sin, radians
A = [1, 0, 0]
B = [0, 1, 0]
a_x = A[0]
a_y = A[1]
a_z = A[2]
b_x = B[0]
b_y = B[1]
b_z = B[2]
x_rot = radians(obj.X_rot)
y_rot = radians(obj.Y_rot)
z_rot = radians(obj.Z_rot)
if obj.X_rot != 0:
a_y = A[1] * cos(x_rot) + A[2] * sin(x_rot)
a_z = A[2] * cos(x_rot) - A[1] * sin(x_rot)
b_y = B[1] * cos(x_rot) + B[2] * sin(x_rot)
b_z = B[2] * cos(x_rot) - B[1] * sin(x_rot)
if obj.Y_rot != 0:
a_x = A[0] * cos(y_rot) - A[2] * sin(y_rot)
a_z = A[2] * cos(y_rot) + A[0] * sin(y_rot)
b_x = B[0] * cos(y_rot) - B[2] * sin(y_rot)
b_z = B[2] * cos(y_rot) + B[0] * sin(z_rot)
if obj.Z_rot != 0:
a_x = A[0] * cos(z_rot) + A[1] * sin(z_rot)
a_y = A[1] * cos(z_rot) - A[0] * sin(z_rot)
b_x = B[0] * cos(z_rot) + B[1] * sin(z_rot)
b_y = B[1] * cos(z_rot) - B[0] * sin(z_rot)
A = [a_x, a_y, a_z]
B = [b_x, b_y, b_z]
A_coords = str(round(A[0], 4)) + "," + str(round(A[1], 4)) + "," + str(round(A[2], 4))
B_coords = str(round(B[0], 4)) + "," + str(round(B[1], 4)) + "," + str(round(B[2], 4))
coords = A_coords + "," + B_coords
return coords
# ************************************************************************************************
def get_cylindrical_coords(
obj
):
vec = obj.Axis
base = obj.BasePoint
Ax = base[0] + 10 * vec[0]
Ay = base[1] + 10 * vec[1]
Az = base[2] + 10 * vec[2]
Bx = base[0] - 10 * vec[0]
By = base[1] - 10 * vec[1]
Bz = base[2] - 10 * vec[2]
A = [Ax, Ay, Az]
B = [Bx, By, Bz]
A_coords = str(A[0]) + "," + str(A[1]) + "," + str(A[2])
B_coords = str(B[0]) + "," + str(B[1]) + "," + str(B[2])
coords = A_coords + "," + B_coords
return coords
@@ -19,27 +19,28 @@
# * USA *
# * *
# ***************************************************************************
"""Provides the Image_Scaling GuiCommand."""
__title__ = "ImageTools._CommandImageScaling"
__author__ = "JAndersM"
__url__ = "http://www.freecadweb.org/index-fr.html"
__author__ = "JAndersM"
__url__ = "http://www.freecadweb.org/index-fr.html"
__version__ = "00.02"
__date__ = "03/05/2019"
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui
from PySide import QtCore
import FreeCADGui, FreeCAD, Part
import math
import pivy.coin as pvy
import DraftTrackers, Draft
__date__ = "03/05/2019"
# translation-related code
#(see forum thread "A new Part tool is being born... JoinFeatures!"
#http://forum.freecadweb.org/viewtopic.php?f=22&t=11112&start=30#p90239 )
import math
import FreeCAD
from PySide import QtCore
if FreeCAD.GuiUp:
from PySide import QtGui
import pivy.coin as pvy
import FreeCADGui
import draftguitools.gui_trackers as trackers
# Translation-related code
# See forum thread "A new Part tool is being born... JoinFeatures!"
# http://forum.freecadweb.org/viewtopic.php?f=22&t=11112&start=30#p90239
try:
_fromUtf8 = QtCore.QString.fromUtf8
except (Exception):
@@ -134,7 +135,7 @@ def cmdCreateImageScaling(name):
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), self.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), self.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.tracker = DraftTrackers.lineTracker(scolor=(1,0,0))
self.tracker = trackers.lineTracker(scolor=(1,0,0))
self.tracker.raiseTracker()
self.tracker.on()
self.dialog.show()
+2 -2
View File
@@ -1,8 +1,8 @@
# FreeCAD init script of the Import module
# (c) 2001 Jürgen Riegel
# (c) 2001 Juergen Riegel
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <[email protected]> *
#* Copyright (c) 2002 Juergen Riegel <[email protected]> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
+3 -2
View File
@@ -1,12 +1,13 @@
# -*- coding: utf8 -*-
# Import gui init module
# (c) 2003 Jürgen Riegel
# (c) 2003 Juergen Riegel
#
# Gathering all the information to start FreeCAD
# This is the second one of three init scripts, the third one
# runs when the gui is up
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <[email protected]> *
#* Copyright (c) 2002 Juergen Riegel <[email protected]> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
+3 -3
View File
@@ -24,18 +24,18 @@ import sys
import FreeCAD
# here the usage description if you use this tool from the command line ("__main__")
# The usage description if you use this tool from the command line ("__main__")
CommandlineUsage = """Material - Tool to work with FreeCAD Material definition cards
Usage:
Material [Options] card-file-name
Options:
-c, --output-csv=file-name write a comma separated grid with the material data
-c, --output-csv=filename write a comma separated grid with the material data
Exit:
0 No Error or Warning found
1 Argument error, wrong or less Arguments given
1 Argument error, wrong or too few Arguments given
Tool to work with FreeCAD Material definition cards
+3 -2
View File
@@ -407,9 +407,10 @@ static TopoShape _getTopoShape(const App::DocumentObject *obj, const char *subna
// not return the linked object when calling getLinkedObject().
// Therefore, it should be handled here.
TopoShape baseShape;
Base::Matrix4D baseMat;
std::string op;
if(link && link->getElementCountValue()) {
linked = link->getTrueLinkedObject(false);
linked = link->getTrueLinkedObject(false,&baseMat);
if(linked && linked!=owner) {
baseShape = Feature::getTopoShape(linked,0,false,0,0,false,false);
// if(!link->getShowElementValue())
@@ -421,7 +422,7 @@ static TopoShape _getTopoShape(const App::DocumentObject *obj, const char *subna
int visible;
std::string childName;
App::DocumentObject *parent=0;
Base::Matrix4D mat;
Base::Matrix4D mat = baseMat;
App::DocumentObject *subObj=0;
if(sub.find('.')==std::string::npos)
visible = 1;
@@ -227,6 +227,17 @@ class AttachmentEditorTaskPanel(FrozenClass):
self.form.setWindowIcon(QtGui.QIcon(':/icons/Part_Attachment.svg'))
self.form.setWindowTitle(_translate('AttachmentEditor',"Attachment",None))
self.form.attachmentOffsetX.setProperty("unit", "mm")
self.form.attachmentOffsetY.setProperty("unit", "mm")
self.form.attachmentOffsetZ.setProperty("unit", "mm")
Gui.ExpressionBinding(self.form.attachmentOffsetX).bind(self.obj,"AttachmentOffset.Base.x")
Gui.ExpressionBinding(self.form.attachmentOffsetY).bind(self.obj,"AttachmentOffset.Base.y")
Gui.ExpressionBinding(self.form.attachmentOffsetZ).bind(self.obj,"AttachmentOffset.Base.z")
Gui.ExpressionBinding(self.form.attachmentOffsetYaw).bind(self.obj,"AttachmentOffset.Rotation.Yaw")
Gui.ExpressionBinding(self.form.attachmentOffsetPitch).bind(self.obj,"AttachmentOffset.Rotation.Pitch")
Gui.ExpressionBinding(self.form.attachmentOffsetRoll).bind(self.obj,"AttachmentOffset.Rotation.Roll")
self.refLines = [self.form.lineRef1,
self.form.lineRef2,
self.form.lineRef3,
@@ -301,6 +312,9 @@ class AttachmentEditorTaskPanel(FrozenClass):
if button == QtGui.QDialogButtonBox.Apply:
if self.obj_is_attachable:
self.writeParameters()
if self.create_transaction:
self.obj.Document.commitTransaction()
self.obj.Document.openTransaction(_translate('AttachmentEditor',"Edit attachment of {feat}",None).format(feat= self.obj.Name))
self.updatePreview()
if self.callback_Apply:
self.callback_Apply()
@@ -434,12 +448,12 @@ class AttachmentEditorTaskPanel(FrozenClass):
try:
old_selfblock = self.block
self.block = True
self.form.attachmentOffsetX.setText ((plm.Base.x * mm).UserString)
self.form.attachmentOffsetY.setText ((plm.Base.y * mm).UserString)
self.form.attachmentOffsetZ.setText ((plm.Base.z * mm).UserString)
self.form.attachmentOffsetYaw.setText ((plm.Rotation.toEuler()[0] * deg).UserString)
self.form.attachmentOffsetPitch.setText((plm.Rotation.toEuler()[1] * deg).UserString)
self.form.attachmentOffsetRoll.setText ((plm.Rotation.toEuler()[2] * deg).UserString)
self.form.attachmentOffsetX.lineEdit().setText ((plm.Base.x * mm).UserString)
self.form.attachmentOffsetY.lineEdit().setText ((plm.Base.y * mm).UserString)
self.form.attachmentOffsetZ.lineEdit().setText ((plm.Base.z * mm).UserString)
self.form.attachmentOffsetYaw.lineEdit().setText ((plm.Rotation.toEuler()[0] * deg).UserString)
self.form.attachmentOffsetPitch.lineEdit().setText((plm.Rotation.toEuler()[1] * deg).UserString)
self.form.attachmentOffsetRoll.lineEdit().setText ((plm.Rotation.toEuler()[2] * deg).UserString)
self.form.checkBoxFlip.setChecked(self.attacher.Reverse)
@@ -154,7 +154,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::InputField" name="attachmentOffsetY">
<widget class="Gui::QuantitySpinBox" name="attachmentOffsetY">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -186,7 +186,7 @@
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::InputField" name="attachmentOffsetZ">
<widget class="Gui::QuantitySpinBox" name="attachmentOffsetZ">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -244,7 +244,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::InputField" name="attachmentOffsetX">
<widget class="Gui::QuantitySpinBox" name="attachmentOffsetX">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -263,7 +263,7 @@
</widget>
</item>
<item row="4" column="1">
<widget class="Gui::InputField" name="attachmentOffsetRoll">
<widget class="Gui::QuantitySpinBox" name="attachmentOffsetRoll">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -295,7 +295,7 @@ Note: The placement is expressed in local space of object being attached.</strin
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::InputField" name="attachmentOffsetPitch">
<widget class="Gui::QuantitySpinBox" name="attachmentOffsetPitch">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -327,7 +327,7 @@ Note: The placement is expressed in local space of object being attached.</strin
</widget>
</item>
<item row="6" column="1">
<widget class="Gui::InputField" name="attachmentOffsetYaw">
<widget class="Gui::QuantitySpinBox" name="attachmentOffsetYaw">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -372,9 +372,9 @@ Note: The placement is expressed in local space of object being attached.</strin
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
<class>Gui::QuantitySpinBox</class>
<extends>QWidget</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<tabstops>
+4 -4
View File
@@ -52,13 +52,13 @@ TextSet getUIStrings(Base::Type attacherType, eMapMode mmode)
return TwoStrings(qApp->translate("Attacher3D", "Translate origin","Attachment3D mode caption"),
qApp->translate("Attacher3D", "Origin is aligned to match Vertex. Orientation is controlled by Placement property.","Attachment3D mode tooltip"));
case mmObjectXY:
return TwoStrings(qApp->translate("Attacher3D", "Object's X Y Z","Attachment3D mode caption"),
return TwoStrings(qApp->translate("Attacher3D", "Object's X Y Z","Attachment3D mode caption"),
qApp->translate("Attacher3D", "Placement is made equal to Placement of linked object.","Attachment3D mode tooltip"));
case mmObjectXZ:
return TwoStrings(qApp->translate("Attacher3D", "Object's X Z-Y","Attachment3D mode caption"),
return TwoStrings(qApp->translate("Attacher3D", "Object's X Z Y","Attachment3D mode caption"),
qApp->translate("Attacher3D", "X', Y', Z' axes are matched with object's local X, Z, -Y, respectively.","Attachment3D mode tooltip"));
case mmObjectYZ:
return TwoStrings(qApp->translate("Attacher3D", "Object's Y Z X","Attachment3D mode caption"),
return TwoStrings(qApp->translate("Attacher3D", "Object's Y Z X","Attachment3D mode caption"),
qApp->translate("Attacher3D", "X', Y', Z' axes are matched with object's local Y, Z, X, respectively.","Attachment3D mode tooltip"));
case mmFlatFace:
return TwoStrings(qApp->translate("Attacher3D", "XY on plane","Attachment3D mode caption"),
@@ -133,7 +133,7 @@ TextSet getUIStrings(Base::Type attacherType, eMapMode mmode)
return TwoStrings(qApp->translate("Attacher2D", "Object's XZ","AttachmentPlane mode caption"),
qApp->translate("Attacher2D", "Plane is aligned to XZ local plane of linked object.","AttachmentPlane mode tooltip"));
case mmObjectYZ:
return TwoStrings(qApp->translate("Attacher2D", "Object's YZ","AttachmentPlane mode caption"),
return TwoStrings(qApp->translate("Attacher2D", "Object's YZ","AttachmentPlane mode caption"),
qApp->translate("Attacher2D", "Plane is aligned to YZ local plane of linked object.","AttachmentPlane mode tooltip"));
case mmFlatFace:
return TwoStrings(qApp->translate("Attacher2D", "Plane face","AttachmentPlane mode caption"),
+5 -5
View File
@@ -110,7 +110,7 @@ void FilletRadiusDelegate::setModelData(QWidget *editor, QAbstractItemModel *mod
spinBox->interpretText();
//double value = spinBox->value();
//QString value = QString::fromLatin1("%1").arg(spinBox->value(),0,'f',2);
//QString value = QLocale::system().toString(spinBox->value().getValue(),'f',Base::UnitsApi::getDecimals());
//QString value = QLocale().toString(spinBox->value().getValue(),'f',Base::UnitsApi::getDecimals());
Base::Quantity value = spinBox->value();
model->setData(index, QVariant::fromValue<Base::Quantity>(value), Qt::EditRole);
@@ -594,8 +594,8 @@ void DlgFilletEdges::setupFillet(const std::vector<App::DocumentObject*>& objs)
if (it != d->edge_ids.end()) {
int index = it - d->edge_ids.begin();
model->setData(model->index(index, 0), Qt::Checked, Qt::CheckStateRole);
//model->setData(model->index(index, 1), QVariant(QLocale::system().toString(et->radius1,'f',Base::UnitsApi::getDecimals())));
//model->setData(model->index(index, 2), QVariant(QLocale::system().toString(et->radius2,'f',Base::UnitsApi::getDecimals())));
//model->setData(model->index(index, 1), QVariant(QLocale().toString(et->radius1,'f',Base::UnitsApi::getDecimals())));
//model->setData(model->index(index, 2), QVariant(QLocale().toString(et->radius2,'f',Base::UnitsApi::getDecimals())));
model->setData(model->index(index, 1), QVariant::fromValue<Base::Quantity>(Base::Quantity(et->radius1, Base::Unit::Length)));
model->setData(model->index(index, 2), QVariant::fromValue<Base::Quantity>(Base::Quantity(et->radius2, Base::Unit::Length)));
@@ -751,8 +751,8 @@ void DlgFilletEdges::on_shapeObject_activated(int index)
for (std::vector<int>::iterator it = d->edge_ids.begin(); it != d->edge_ids.end(); ++it) {
model->setData(model->index(index, 0), QVariant(tr("Edge%1").arg(*it)));
model->setData(model->index(index, 0), QVariant(*it), Qt::UserRole);
//model->setData(model->index(index, 1), QVariant(QLocale::system().toString(1.0,'f',Base::UnitsApi::getDecimals())));
//model->setData(model->index(index, 2), QVariant(QLocale::system().toString(1.0,'f',Base::UnitsApi::getDecimals())));
//model->setData(model->index(index, 1), QVariant(QLocale().toString(1.0,'f',Base::UnitsApi::getDecimals())));
//model->setData(model->index(index, 2), QVariant(QLocale().toString(1.0,'f',Base::UnitsApi::getDecimals())));
model->setData(model->index(index, 1), QVariant::fromValue<Base::Quantity>(Base::Quantity(1.0,Base::Unit::Length)));
model->setData(model->index(index, 2), QVariant::fromValue<Base::Quantity>(Base::Quantity(1.0,Base::Unit::Length)));
std::stringstream element;
+6 -4
View File
@@ -1041,7 +1041,8 @@ TaskDlgAttacher::~TaskDlgAttacher()
void TaskDlgAttacher::open()
{
Gui::Document* document = Gui::Application::Instance->getDocument(ViewProvider->getObject()->getDocument());
document->openCommand("Edit attachment");
}
void TaskDlgAttacher::clicked(int)
@@ -1061,20 +1062,21 @@ bool TaskDlgAttacher::accept()
auto obj = ViewProvider->getObject();
//DeepSOIC: changed this to heavily rely on dialog constantly updating feature properties
if (pcAttach->AttachmentOffset.isTouched()){
//if (pcAttach->AttachmentOffset.isTouched()){
Base::Placement plm = pcAttach->AttachmentOffset.getValue();
double yaw, pitch, roll;
plm.getRotation().getYawPitchRoll(yaw,pitch,roll);
Gui::cmdAppObjectArgs(obj, "AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))",
plm.getPosition().x, plm.getPosition().y, plm.getPosition().z, yaw, pitch, roll);
}
//}
Gui::cmdAppObjectArgs(obj, "MapReversed = %s", pcAttach->MapReversed.getValue() ? "True" : "False");
Gui::cmdAppObjectArgs(obj, "Support = %s", pcAttach->Support.getPyReprString().c_str());
Gui::cmdAppObjectArgs(obj, "MapMode = '%s'", AttachEngine::getModeName(eMapMode(pcAttach->MapMode.getValue())).c_str());
Gui::cmdAppObjectArgs(obj, "MapPathParameter = %f", pcAttach->MapPathParameter.getValue());
Gui::cmdAppObjectArgs(obj, "MapMode = '%s'", AttachEngine::getModeName(eMapMode(pcAttach->MapMode.getValue())).c_str());
Gui::cmdAppObject(obj, "recompute()");
Gui::cmdGuiDocument(obj, "resetEdit()");
+2 -1
View File
@@ -662,7 +662,8 @@ void CmdPartDesignDuplicateSelection::activated(int iMsg)
}
// Adjust visibility of features
FCMD_OBJ_SHOW(newFeatures.back());
if (!newFeatures.empty())
FCMD_OBJ_SHOW(newFeatures.back());
}
updateActive();
@@ -61,54 +61,6 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
proxy = new QWidget(this);
ui->setupUi(proxy);
// box
ui->boxLength->setMaximum(INT_MAX);
ui->boxWidth->setMaximum(INT_MAX);
ui->boxHeight->setMaximum(INT_MAX);
// cylinder
ui->cylinderRadius->setMaximum(INT_MAX);
ui->cylinderHeight->setMaximum(INT_MAX);
// cone
ui->coneRadius1->setMaximum(INT_MAX);
ui->coneRadius2->setMaximum(INT_MAX);
ui->coneHeight->setMaximum(INT_MAX);
// sphere
ui->sphereRadius->setMaximum(INT_MAX);
// ellipsoid
ui->ellipsoidRadius1->setMaximum(INT_MAX);
ui->ellipsoidRadius2->setMaximum(INT_MAX);
ui->ellipsoidRadius3->setMaximum(INT_MAX);
// torus
ui->torusRadius1->setMaximum(INT_MAX);
ui->torusRadius2->setMaximum(INT_MAX);
// wedge
ui->wedgeXmin->setMinimum(INT_MIN);
ui->wedgeXmin->setMaximum(INT_MAX);
ui->wedgeYmin->setMinimum(INT_MIN);
ui->wedgeYmin->setMaximum(INT_MAX);
ui->wedgeZmin->setMinimum(INT_MIN);
ui->wedgeZmin->setMaximum(INT_MAX);
ui->wedgeX2min->setMinimum(INT_MIN);
ui->wedgeX2min->setMaximum(INT_MAX);
ui->wedgeZ2min->setMinimum(INT_MIN);
ui->wedgeZ2min->setMaximum(INT_MAX);
ui->wedgeXmax->setMinimum(INT_MIN);
ui->wedgeXmax->setMaximum(INT_MAX);
ui->wedgeYmax->setMinimum(INT_MIN);
ui->wedgeYmax->setMaximum(INT_MAX);
ui->wedgeZmax->setMinimum(INT_MIN);
ui->wedgeZmax->setMaximum(INT_MAX);
ui->wedgeX2max->setMinimum(INT_MIN);
ui->wedgeX2max->setMaximum(INT_MAX);
ui->wedgeZ2max->setMinimum(INT_MIN);
ui->wedgeZ2max->setMaximum(INT_MAX);
this->groupLayout()->addWidget(proxy);
int index = 0;
@@ -122,6 +74,12 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->boxHeight->bind(static_cast<PartDesign::Box*>(vp->getObject())->Height);
ui->boxWidth->setValue(static_cast<PartDesign::Box*>(vp->getObject())->Width.getValue());
ui->boxWidth->bind(static_cast<PartDesign::Box*>(vp->getObject())->Width);
ui->boxLength->setMinimum(0.0);
ui->boxLength->setMaximum(INT_MAX);
ui->boxWidth->setMinimum(0.0);
ui->boxWidth->setMaximum(INT_MAX);
ui->boxHeight->setMinimum(0.0);
ui->boxHeight->setMaximum(INT_MAX);
break;
case PartDesign::FeaturePrimitive::Cylinder:
index = 2;
@@ -131,6 +89,12 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->cylinderHeight->bind(static_cast<PartDesign::Cylinder*>(vp->getObject())->Height);
ui->cylinderRadius->setValue(static_cast<PartDesign::Cylinder*>(vp->getObject())->Radius.getValue());
ui->cylinderRadius->bind(static_cast<PartDesign::Cylinder*>(vp->getObject())->Radius);
ui->cylinderAngle->setMaximum(360.0);
ui->cylinderAngle->setMinimum(0.0);
ui->cylinderHeight->setMaximum(INT_MAX);
ui->cylinderHeight->setMinimum(0.0);
ui->cylinderRadius->setMaximum(INT_MAX);
ui->cylinderRadius->setMinimum(0.0);
break;
case PartDesign::FeaturePrimitive::Sphere:
index = 4;
@@ -142,6 +106,14 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->sphereAngle3->bind(static_cast<PartDesign::Sphere*>(vp->getObject())->Angle3);
ui->sphereRadius->setValue(static_cast<PartDesign::Sphere*>(vp->getObject())->Radius.getValue());
ui->sphereRadius->bind(static_cast<PartDesign::Sphere*>(vp->getObject())->Radius);
ui->sphereAngle1->setMaximum(ui->sphereAngle2->rawValue()); // must geometrically be <= than sphereAngle2
ui->sphereAngle1->setMinimum(-90.0);
ui->sphereAngle2->setMaximum(90);
ui->sphereAngle2->setMinimum(ui->sphereAngle1->rawValue());
ui->sphereAngle3->setMaximum(360.0);
ui->sphereAngle3->setMinimum(0.0);
ui->sphereRadius->setMaximum(INT_MAX);
ui->sphereRadius->setMinimum(0.0);
break;
case PartDesign::FeaturePrimitive::Cone:
index = 3;
@@ -153,6 +125,14 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->coneRadius1->bind(static_cast<PartDesign::Cone*>(vp->getObject())->Radius1);
ui->coneRadius2->setValue(static_cast<PartDesign::Cone*>(vp->getObject())->Radius2.getValue());
ui->coneRadius2->bind(static_cast<PartDesign::Cone*>(vp->getObject())->Radius2);
ui->coneAngle->setMaximum(360.0);
ui->coneAngle->setMinimum(0.0);
ui->coneHeight->setMaximum(INT_MAX);
ui->coneHeight->setMinimum(0.0);
ui->coneRadius1->setMaximum(INT_MAX);
ui->coneRadius1->setMinimum(0.0);
ui->coneRadius2->setMaximum(INT_MAX);
ui->coneRadius2->setMinimum(0.0);
break;
case PartDesign::FeaturePrimitive::Ellipsoid:
index = 5;
@@ -168,6 +148,18 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->ellipsoidRadius2->bind(static_cast<PartDesign::Ellipsoid*>(vp->getObject())->Radius2);
ui->ellipsoidRadius3->setValue(static_cast<PartDesign::Ellipsoid*>(vp->getObject())->Radius3.getValue());
ui->ellipsoidRadius3->bind(static_cast<PartDesign::Ellipsoid*>(vp->getObject())->Radius3);
ui->ellipsoidAngle1->setMaximum(ui->ellipsoidAngle2->rawValue()); // must geometrically be <= than sphereAngle2
ui->ellipsoidAngle1->setMinimum(-90.0);
ui->ellipsoidAngle2->setMaximum(90);
ui->ellipsoidAngle2->setMinimum(ui->ellipsoidAngle1->rawValue());
ui->ellipsoidAngle3->setMaximum(360.0);
ui->ellipsoidAngle3->setMinimum(0.0);
ui->ellipsoidRadius1->setMinimum(0.0);
ui->ellipsoidRadius1->setMaximum(INT_MAX);
ui->ellipsoidRadius2->setMinimum(0.0);
ui->ellipsoidRadius2->setMaximum(INT_MAX);
ui->ellipsoidRadius3->setMinimum(0.0);
ui->ellipsoidRadius3->setMaximum(INT_MAX);
break;
case PartDesign::FeaturePrimitive::Torus:
index = 6;
@@ -181,6 +173,19 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->torusRadius1->bind(static_cast<PartDesign::Torus*>(vp->getObject())->Radius1);
ui->torusRadius2->setValue(static_cast<PartDesign::Torus*>(vp->getObject())->Radius2.getValue());
ui->torusRadius2->bind(static_cast<PartDesign::Torus*>(vp->getObject())->Radius2);
ui->torusAngle1->setMaximum(ui->torusAngle2->rawValue()); // must geometrically be <= than sphereAngle2
ui->torusAngle1->setMinimum(-180.0);
ui->torusAngle2->setMaximum(180);
ui->torusAngle2->setMinimum(ui->torusAngle1->rawValue());
ui->torusAngle3->setMaximum(360.0);
ui->torusAngle3->setMinimum(0.0);
// this is the outer radius that must not be smaller than the inner one
// otherwise the geometry is impossible and we can even get a crash:
// https://forum.freecadweb.org/viewtopic.php?f=3&t=44467
ui->torusRadius1->setMaximum(INT_MAX);
ui->torusRadius1->setMinimum(ui->torusRadius2->rawValue());
ui->torusRadius2->setMaximum(ui->torusRadius1->rawValue());
ui->torusRadius2->setMinimum(0.0);
break;
case PartDesign::FeaturePrimitive::Prism:
index = 7;
@@ -189,6 +194,10 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->prismCircumradius->bind(static_cast<PartDesign::Prism*>(vp->getObject())->Circumradius);
ui->prismHeight->setValue(static_cast<PartDesign::Prism*>(vp->getObject())->Height.getValue());
ui->prismHeight->bind(static_cast<PartDesign::Prism*>(vp->getObject())->Height);
ui->prismCircumradius->setMaximum(INT_MAX);
ui->prismCircumradius->setMinimum(0.0);
ui->prismHeight->setMaximum(INT_MAX);
ui->prismHeight->setMinimum(0.0);
break;
case PartDesign::FeaturePrimitive::Wedge:
index = 8;
@@ -212,6 +221,26 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
ui->wedgeZ2max->bind(static_cast<PartDesign::Wedge*>(vp->getObject())->Z2max);
ui->wedgeZ2min->setValue(static_cast<PartDesign::Wedge*>(vp->getObject())->Z2min.getValue());
ui->wedgeZ2min->bind(static_cast<PartDesign::Wedge*>(vp->getObject())->Z2min);
ui->wedgeXmin->setMinimum(INT_MIN);
ui->wedgeXmin->setMaximum(ui->wedgeXmax->rawValue()); // must be <= than wedgeXmax
ui->wedgeYmin->setMinimum(INT_MIN);
ui->wedgeYmin->setMaximum(ui->wedgeYmax->rawValue()); // must be <= than wedgeYmax
ui->wedgeZmin->setMinimum(INT_MIN);
ui->wedgeZmin->setMaximum(ui->wedgeZmax->rawValue()); // must be <= than wedgeZmax
ui->wedgeX2min->setMinimum(INT_MIN);
ui->wedgeX2min->setMaximum(ui->wedgeX2max->rawValue()); // must be <= than wedgeXmax
ui->wedgeZ2min->setMinimum(INT_MIN);
ui->wedgeZ2min->setMaximum(ui->wedgeZ2max->rawValue()); // must be <= than wedgeXmax
ui->wedgeXmax->setMinimum(ui->wedgeXmin->rawValue());
ui->wedgeXmax->setMaximum(INT_MAX);
ui->wedgeYmax->setMinimum(ui->wedgeYmin->rawValue());
ui->wedgeYmax->setMaximum(INT_MAX);
ui->wedgeZmax->setMinimum(ui->wedgeZmin->rawValue());
ui->wedgeZmax->setMaximum(INT_MAX);
ui->wedgeX2max->setMinimum(ui->wedgeX2min->rawValue());
ui->wedgeX2max->setMaximum(INT_MAX);
ui->wedgeZ2max->setMinimum(ui->wedgeZ2min->rawValue());
ui->wedgeZ2max->setMaximum(INT_MAX);
break;
}
@@ -283,15 +312,15 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent)
// wedge
connect(ui->wedgeXmax, SIGNAL(valueChanged(double)), this, SLOT(onWedgeXmaxChanged(double)));
connect(ui->wedgeXmin, SIGNAL(valueChanged(double)), this, SLOT(onWedgeXinChanged(double)));
connect(ui->wedgeXmin, SIGNAL(valueChanged(double)), this, SLOT(onWedgeXminChanged(double)));
connect(ui->wedgeYmax, SIGNAL(valueChanged(double)), this, SLOT(onWedgeYmaxChanged(double)));
connect(ui->wedgeYmin, SIGNAL(valueChanged(double)), this, SLOT(onWedgeYinChanged(double)));
connect(ui->wedgeYmin, SIGNAL(valueChanged(double)), this, SLOT(onWedgeYminChanged(double)));
connect(ui->wedgeZmax, SIGNAL(valueChanged(double)), this, SLOT(onWedgeZmaxChanged(double)));
connect(ui->wedgeZmin, SIGNAL(valueChanged(double)), this, SLOT(onWedgeZinChanged(double)));
connect(ui->wedgeZmin, SIGNAL(valueChanged(double)), this, SLOT(onWedgeZminChanged(double)));
connect(ui->wedgeX2max, SIGNAL(valueChanged(double)), this, SLOT(onWedgeX2maxChanged(double)));
connect(ui->wedgeX2min, SIGNAL(valueChanged(double)), this, SLOT(onWedgeX2inChanged(double)));
connect(ui->wedgeX2min, SIGNAL(valueChanged(double)), this, SLOT(onWedgeX2minChanged(double)));
connect(ui->wedgeZ2max, SIGNAL(valueChanged(double)), this, SLOT(onWedgeZ2maxChanged(double)));
connect(ui->wedgeZ2min, SIGNAL(valueChanged(double)), this, SLOT(onWedgeZ2inChanged(double)));
connect(ui->wedgeZ2min, SIGNAL(valueChanged(double)), this, SLOT(onWedgeZ2minChanged(double)));
}
/*
@@ -357,12 +386,14 @@ void TaskBoxPrimitives::onCylinderRadiusChanged(double v) {
void TaskBoxPrimitives::onSphereAngle1Changed(double v) {
PartDesign::Sphere* sph = static_cast<PartDesign::Sphere*>(vp->getObject());
ui->sphereAngle2->setMinimum(v); // Angle1 must geometrically be <= than Angle2
sph->Angle1.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onSphereAngle2Changed(double v) {
PartDesign::Sphere* sph = static_cast<PartDesign::Sphere*>(vp->getObject());
ui->sphereAngle1->setMaximum(v); // Angle1 must geometrically be <= than Angle2
sph->Angle2.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
@@ -406,12 +437,14 @@ void TaskBoxPrimitives::onConeRadius2Changed(double v) {
void TaskBoxPrimitives::onEllipsoidAngle1Changed(double v) {
PartDesign::Ellipsoid* sph = static_cast<PartDesign::Ellipsoid*>(vp->getObject());
ui->ellipsoidAngle2->setMinimum(v); // Angle1 must geometrically be <= than Angle2
sph->Angle1.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onEllipsoidAngle2Changed(double v) {
PartDesign::Ellipsoid* sph = static_cast<PartDesign::Ellipsoid*>(vp->getObject());
ui->ellipsoidAngle1->setMaximum(v); // Angle1 must geometrically be <= than Angle22
sph->Angle2.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
@@ -442,12 +475,14 @@ void TaskBoxPrimitives::onEllipsoidRadius3Changed(double v) {
void TaskBoxPrimitives::onTorusAngle1Changed(double v) {
PartDesign::Torus* sph = static_cast<PartDesign::Torus*>(vp->getObject());
ui->torusAngle2->setMinimum(v); // Angle1 must geometrically be <= than Angle2
sph->Angle1.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onTorusAngle2Changed(double v) {
PartDesign::Torus* sph = static_cast<PartDesign::Torus*>(vp->getObject());
ui->torusAngle1->setMaximum(v); // Angle1 must geometrically be <= than Angle2
sph->Angle2.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
@@ -460,12 +495,17 @@ void TaskBoxPrimitives::onTorusAngle3Changed(double v) {
void TaskBoxPrimitives::onTorusRadius1Changed(double v) {
PartDesign::Torus* sph = static_cast<PartDesign::Torus*>(vp->getObject());
// this is the outer radius that must not be smaller than the inner one
// otherwise the geometry is impossible and we can even get a crash:
// https://forum.freecadweb.org/viewtopic.php?f=3&t=44467
ui->torusRadius2->setMaximum(v);
sph->Radius1.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onTorusRadius2Changed(double v) {
PartDesign::Torus* sph = static_cast<PartDesign::Torus*>(vp->getObject());
ui->torusRadius1->setMinimum(v);
sph->Radius2.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
@@ -489,62 +529,72 @@ void TaskBoxPrimitives::onPrismPolygonChanged(int v) {
}
void TaskBoxPrimitives::onWedgeX2inChanged(double v) {
void TaskBoxPrimitives::onWedgeX2minChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeX2max->setMinimum(v); // wedgeX2min must be <= than wedgeX2max
sph->X2min.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeX2maxChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeX2min->setMaximum(v); // wedgeX2min must be <= than wedgeX2max
sph->X2max.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeXinChanged(double v) {
void TaskBoxPrimitives::onWedgeXminChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeXmax->setMinimum(v);
sph->Xmin.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeXmaxChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeXmin->setMaximum(v); // must be <= than wedgeXmax
sph->Xmax.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeYinChanged(double v) {
void TaskBoxPrimitives::onWedgeYminChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeYmax->setMinimum(v);
sph->Ymin.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeYmaxChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeYmin->setMaximum(v);
sph->Ymax.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeZ2inChanged(double v) {
void TaskBoxPrimitives::onWedgeZ2minChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeZ2max->setMinimum(v);
sph->Z2min.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeZ2maxChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeZ2min->setMaximum(v); // must be <= than wedgeXmax
sph->Z2max.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeZinChanged(double v) {
void TaskBoxPrimitives::onWedgeZminChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeZmax->setMinimum(v);
sph->Zmin.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
void TaskBoxPrimitives::onWedgeZmaxChanged(double v) {
PartDesign::Wedge* sph = static_cast<PartDesign::Wedge*>(vp->getObject());
ui->wedgeZmin->setMaximum(v);
sph->Zmax.setValue(v);
vp->getObject()->getDocument()->recomputeFeature(vp->getObject());
}
@@ -87,15 +87,15 @@ public Q_SLOTS:
void onPrismHeightChanged(double);
void onPrismPolygonChanged(int);
void onWedgeXmaxChanged(double);
void onWedgeXinChanged(double);
void onWedgeXminChanged(double);
void onWedgeYmaxChanged(double);
void onWedgeYinChanged(double);
void onWedgeYminChanged(double);
void onWedgeZmaxChanged(double);
void onWedgeZinChanged(double);
void onWedgeZminChanged(double);
void onWedgeX2maxChanged(double);
void onWedgeX2inChanged(double);
void onWedgeX2minChanged(double);
void onWedgeZ2maxChanged(double);
void onWedgeZ2inChanged(double);
void onWedgeZ2minChanged(double);
private:
/** Notifies when the object is about to be removed. */
+11 -5
View File
@@ -25,6 +25,8 @@ INSTALL(
SET(PathScripts_SRCS
PathCommands.py
PathScripts/PathAdaptive.py
PathScripts/PathAdaptiveGui.py
PathScripts/PathAreaOp.py
PathScripts/PathArray.py
PathScripts/PathCircularHoleBase.py
@@ -46,6 +48,7 @@ SET(PathScripts_SRCS
PathScripts/PathDressupTag.py
PathScripts/PathDressupTagGui.py
PathScripts/PathDressupTagPreferences.py
PathScripts/PathDressupZCorrect.py
PathScripts/PathDrilling.py
PathScripts/PathDrillingGui.py
PathScripts/PathEngrave.py
@@ -82,6 +85,8 @@ SET(PathScripts_SRCS
PathScripts/PathPreferences.py
PathScripts/PathPreferencesPathDressup.py
PathScripts/PathPreferencesPathJob.py
PathScripts/PathProbe.py
PathScripts/PathProbeGui.py
PathScripts/PathProfileBase.py
PathScripts/PathProfileBaseGui.py
PathScripts/PathProfileContour.py
@@ -97,6 +102,7 @@ SET(PathScripts_SRCS
PathScripts/PathSetupSheetOpPrototype.py
PathScripts/PathSetupSheetOpPrototypeGui.py
PathScripts/PathSimpleCopy.py
PathScripts/PathSimulatorGui.py
PathScripts/PathStock.py
PathScripts/PathStop.py
PathScripts/PathSurface.py
@@ -110,15 +116,14 @@ SET(PathScripts_SRCS
PathScripts/PathToolController.py
PathScripts/PathToolControllerGui.py
PathScripts/PathToolEdit.py
PathScripts/PathToolLibraryManager.py
PathScripts/PathToolLibraryEditor.py
PathScripts/PathToolLibraryManager.py
PathScripts/PathUtil.py
PathScripts/PathUtils.py
PathScripts/PathUtilsGui.py
PathScripts/PathSimulatorGui.py
PathScripts/PathWaterline.py
PathScripts/PathWaterlineGui.py
PathScripts/PostUtils.py
PathScripts/PathAdaptiveGui.py
PathScripts/PathAdaptive.py
PathScripts/__init__.py
)
@@ -128,6 +133,7 @@ SET(PathScripts_post_SRCS
PathScripts/post/comparams_post.py
PathScripts/post/dynapath_post.py
PathScripts/post/example_pre.py
PathScripts/post/gcode_pre.py
PathScripts/post/grbl_post.py
PathScripts/post/jtech_post.py
PathScripts/post/linuxcnc_post.py
@@ -188,8 +194,8 @@ SET(PathTests_SRCS
PathTests/boxtest.fcstd
PathTests/test_centroid_00.ngc
PathTests/test_geomop.fcstd
PathTests/test_linuxcnc_00.ngc
PathTests/test_holes00.fcstd
PathTests/test_linuxcnc_00.ngc
)
SET(PathImages_Ops
+35 -30
View File
@@ -1,9 +1,19 @@
<RCC>
<qresource>
<file>icons/Path-Adaptive.svg</file>
<file>icons/Path-ToolDuplicate.svg</file>
<file>icons/Path-3DPocket.svg</file>
<file>icons/Path-3DSurface.svg</file>
<file>icons/Path-Area-View.svg</file>
<file>icons/Path-Area-Workplane.svg</file>
<file>icons/Path-Area.svg</file>
<file>icons/Path-Array.svg</file>
<file>icons/Path-Axis.svg</file>
<file>icons/Path-BFastForward.svg</file>
<file>icons/Path-BPause.svg</file>
<file>icons/Path-BPlay.svg</file>
<file>icons/Path-BStep.svg</file>
<file>icons/Path-BStop.svg</file>
<file>icons/Path-BaseGeometry.svg</file>
<file>icons/Path-Comment.svg</file>
<file>icons/Path-Compound.svg</file>
@@ -17,9 +27,9 @@
<file>icons/Path-Drilling.svg</file>
<file>icons/Path-Engrave.svg</file>
<file>icons/Path-ExportTemplate.svg</file>
<file>icons/Path-Face.svg</file>
<file>icons/Path-FacePocket.svg</file>
<file>icons/Path-FaceProfile.svg</file>
<file>icons/Path-Face.svg</file>
<file>icons/Path-Heights.svg</file>
<file>icons/Path-Helix.svg</file>
<file>icons/Path-Hop.svg</file>
@@ -27,16 +37,17 @@
<file>icons/Path-Job.svg</file>
<file>icons/Path-Kurve.svg</file>
<file>icons/Path-LengthOffset.svg</file>
<file>icons/Path-Machine.svg</file>
<file>icons/Path-MachineLathe.svg</file>
<file>icons/Path-MachineMill.svg</file>
<file>icons/Path-Machine.svg</file>
<file>icons/Path-OpActive.svg</file>
<file>icons/Path-OpCopy.svg</file>
<file>icons/Path-OperationA.svg</file>
<file>icons/Path-OperationB.svg</file>
<file>icons/Path-OpCopy.svg</file>
<file>icons/Path-Plane.svg</file>
<file>icons/Path-Pocket.svg</file>
<file>icons/Path-Post.svg</file>
<file>icons/Path-Probe.svg</file>
<file>icons/Path-Profile-Edges.svg</file>
<file>icons/Path-Profile-Face.svg</file>
<file>icons/Path-Profile.svg</file>
@@ -45,49 +56,40 @@
<file>icons/Path-SetupSheet.svg</file>
<file>icons/Path-Shape.svg</file>
<file>icons/Path-SimpleCopy.svg</file>
<file>icons/Path-Simulator.svg</file>
<file>icons/Path-Speed.svg</file>
<file>icons/Path-Stock.svg</file>
<file>icons/Path-Stop.svg</file>
<file>icons/Path-ToolBit.svg</file>
<file>icons/Path-ToolChange.svg</file>
<file>icons/Path-ToolController.svg</file>
<file>icons/Path-ToolDuplicate.svg</file>
<file>icons/Path-Toolpath.svg</file>
<file>icons/Path-ToolTable.svg</file>
<file>icons/Path-Area.svg</file>
<file>icons/Path-Area-View.svg</file>
<file>icons/Path-Area-Workplane.svg</file>
<file>icons/Path-Simulator.svg</file>
<file>icons/Path-BFastForward.svg</file>
<file>icons/Path-BPause.svg</file>
<file>icons/Path-BPlay.svg</file>
<file>icons/Path-BStep.svg</file>
<file>icons/Path-BStop.svg</file>
<file>icons/Path-Waterline.svg</file>
<file>icons/arrow-ccw.svg</file>
<file>icons/arrow-cw.svg</file>
<file>icons/arrow-down.svg</file>
<file>icons/arrow-left.svg</file>
<file>icons/arrow-left-down.svg</file>
<file>icons/arrow-left-up.svg</file>
<file>icons/arrow-right.svg</file>
<file>icons/arrow-left.svg</file>
<file>icons/arrow-right-down.svg</file>
<file>icons/arrow-right-up.svg</file>
<file>icons/arrow-right.svg</file>
<file>icons/arrow-up.svg</file>
<file>icons/edge-join-miter.svg</file>
<file>icons/edge-join-miter-not.svg</file>
<file>icons/edge-join-round.svg</file>
<file>icons/edge-join-miter.svg</file>
<file>icons/edge-join-round-not.svg</file>
<file>icons/edge-join-round.svg</file>
<file>icons/preferences-path.svg</file>
<file>icons/Path-Adaptive.svg</file>
<file>panels/DlgJobChooser.ui</file>
<file>panels/DlgJobCreate.ui</file>
<file>panels/DlgJobModelSelect.ui</file>
<file>panels/DlgJobTemplateExport.ui</file>
<file>panels/DlgSelectPostProcessor.ui</file>
<file>panels/DlgTCChooser.ui</file>
<file>panels/DlgToolControllerEdit.ui</file>
<file>panels/DlgToolCopy.ui</file>
<file>panels/DlgToolEdit.ui</file>
<file>panels/DlgTCChooser.ui</file>
<file>panels/DogboneEdit.ui</file>
<file>panels/DressupPathBoundary.ui</file>
<file>panels/HoldingTagsEdit.ui</file>
@@ -102,8 +104,10 @@
<file>panels/PageOpHelixEdit.ui</file>
<file>panels/PageOpPocketExtEdit.ui</file>
<file>panels/PageOpPocketFullEdit.ui</file>
<file>panels/PageOpProbeEdit.ui</file>
<file>panels/PageOpProfileFullEdit.ui</file>
<file>panels/PageOpSurfaceEdit.ui</file>
<file>panels/PageOpWaterlineEdit.ui</file>
<file>panels/PathEdit.ui</file>
<file>panels/PointEdit.ui</file>
<file>panels/SetupGlobal.ui</file>
@@ -114,19 +118,29 @@
<file>panels/ToolEditor.ui</file>
<file>panels/ToolLibraryEditor.ui</file>
<file>panels/TaskPathSimulator.ui</file>
<file>panels/ZCorrectEdit.ui</file>
<file>preferences/PathDressupHoldingTags.ui</file>
<file>preferences/PathJob.ui</file>
<file>translations/Path_af.qm</file>
<file>translations/Path_ar.qm</file>
<file>translations/Path_ca.qm</file>
<file>translations/Path_cs.qm</file>
<file>translations/Path_de.qm</file>
<file>translations/Path_el.qm</file>
<file>translations/Path_es-ES.qm</file>
<file>translations/Path_eu.qm</file>
<file>translations/Path_fi.qm</file>
<file>translations/Path_fil.qm</file>
<file>translations/Path_fr.qm</file>
<file>translations/Path_gl.qm</file>
<file>translations/Path_hr.qm</file>
<file>translations/Path_hu.qm</file>
<file>translations/Path_id.qm</file>
<file>translations/Path_it.qm</file>
<file>translations/Path_ja.qm</file>
<file>translations/Path_kab.qm</file>
<file>translations/Path_ko.qm</file>
<file>translations/Path_lt.qm</file>
<file>translations/Path_nl.qm</file>
<file>translations/Path_no.qm</file>
<file>translations/Path_pl.qm</file>
@@ -140,18 +154,9 @@
<file>translations/Path_sv-SE.qm</file>
<file>translations/Path_tr.qm</file>
<file>translations/Path_uk.qm</file>
<file>translations/Path_val-ES.qm</file>
<file>translations/Path_vi.qm</file>
<file>translations/Path_zh-CN.qm</file>
<file>translations/Path_zh-TW.qm</file>
<file>translations/Path_eu.qm</file>
<file>translations/Path_ca.qm</file>
<file>translations/Path_gl.qm</file>
<file>translations/Path_kab.qm</file>
<file>translations/Path_ko.qm</file>
<file>translations/Path_fil.qm</file>
<file>translations/Path_id.qm</file>
<file>translations/Path_lt.qm</file>
<file>translations/Path_val-ES.qm</file>
<file>translations/Path_ar.qm</file>
<file>translations/Path_vi.qm</file>
</qresource>
</RCC>
@@ -0,0 +1,666 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="Path-Probe.svg">
<defs
id="defs2818">
<linearGradient
inkscape:collect="always"
id="linearGradient4030">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop4032" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop4034" />
</linearGradient>
<linearGradient
id="linearGradient4513">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517" />
</linearGradient>
<linearGradient
id="linearGradient3681">
<stop
id="stop3697"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3687"
x1="37.89756"
y1="41.087898"
x2="4.0605712"
y2="40.168594"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3695"
x1="37.894287"
y1="40.484772"
x2="59.811455"
y2="43.558987"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
id="linearGradient3681-3">
<stop
id="stop3697-3"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685-4" />
</linearGradient>
<linearGradient
y2="43.558987"
x2="59.811455"
y1="40.484772"
x1="37.894287"
gradientTransform="translate(-37.00068,-20.487365)"
gradientUnits="userSpaceOnUse"
id="linearGradient3608"
xlink:href="#linearGradient3681-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-2" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-4" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538"
xlink:href="#linearGradient4513-2"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538-6"
xlink:href="#linearGradient4513-1"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-7" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-5" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3069"
xlink:href="#linearGradient4513-1-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-6" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3102"
xlink:href="#linearGradient4513-1-2"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4031"
id="linearGradient4055"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(71.494719,-3.1982556)"
x1="30.000002"
y1="7.9999995"
x2="36"
y2="54.227272" />
<linearGradient
id="linearGradient4031">
<stop
id="stop4033"
offset="0"
style="stop-color:#d3d7cf;stop-opacity:1" />
<stop
id="stop4035"
offset="1"
style="stop-color:#888a85;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4030"
id="linearGradient4036"
x1="35"
y1="60"
x2="24"
y2="24"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0109162,0,0,0.46203172,-0.34931964,34.132636)" />
<linearGradient
inkscape:collect="always"
id="linearGradient3898-8-9">
<stop
style="stop-color:#888a85;stop-opacity:1"
offset="0"
id="stop3900-2-2" />
<stop
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1"
id="stop3902-4-7" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3898-8-9"
id="linearGradient3145"
gradientUnits="userSpaceOnUse"
x1="35.05999"
y1="53.008698"
x2="27.286415"
y2="7.311924"
gradientTransform="matrix(0.98188305,0,0,0.84420947,9.5797362,-5.207764)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="9.57"
inkscape:cx="35.419385"
inkscape:cy="28.985505"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1676"
inkscape:window-height="1011"
inkscape:window-x="1080"
inkscape:window-y="18"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid3206"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title>Path-Drilling</dc:title>
<dc:date>2015-07-04</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Drilling.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g4316">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect3085"
d="m 2.6834291,45.683429 0,16.633142 58.6331419,0 0,-16.633142 c 0,0 -3.143696,0 -58.6331419,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient4036);fill-opacity:1;fill-rule:nonzero;stroke:#0b1521;stroke-width:1.36685824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect3085-7"
d="m 4.0668882,47.121868 0,13.756264 55.8662238,0 0,-13.756264 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#729fcf;stroke-width:1.33377635;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
</g>
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#172a04;stroke-width:5.94895172;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="M 41,39.98955 41,13.945451"
id="path3111"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#ffe300;stroke-width:2.97447586;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="M 41,40.044099 41,14"
id="path3111-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffff00;stroke:#ffff00;stroke-width:1.48723793;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 40.256381,40.022049 0,-26.044099"
id="path3111-3-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<circle
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:1.3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000005;stroke-opacity:1"
id="path4263"
cx="41"
cy="41.044098"
r="4" />
<g
id="g4311">
<g
transform="matrix(1.0125004,0,0,1.0096719,6.4745258,6.0042826)"
id="g4251">
<path
style="fill:none;stroke:#d3d7cf;stroke-width:1.84549868;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 26.982514,0.44587399 27,9 41,9 41.102863,0.44587399 Z"
id="path3906-7-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path3085-5-3"
d="m 31.999995,17 -0.07736,-12.0772507 18.154729,0 L 49.999995,17 Z"
style="fill:url(#linearGradient3145);fill-opacity:1;stroke:#2e3436;stroke-width:1.84549868;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Path-Waterline.svg">
<title
id="title165">Path_Waterline</title>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1304"
inkscape:window-height="745"
id="namedview32"
showgrid="true"
inkscape:snap-bbox="false"
inkscape:snap-nodes="true"
inkscape:snap-global="false"
inkscape:zoom="8.0000004"
inkscape:cx="17.234266"
inkscape:cy="29.588719"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid3009"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs2818">
<linearGradient
id="linearGradient6899"
osb:paint="solid">
<stop
style="stop-color:#074cff;stop-opacity:1;"
offset="0"
id="stop6901" />
</linearGradient>
<linearGradient
id="linearGradient6887"
osb:paint="solid">
<stop
style="stop-color:#074cff;stop-opacity:1;"
offset="0"
id="stop6889" />
</linearGradient>
<linearGradient
id="linearGradient4668"
osb:paint="gradient">
<stop
style="stop-color:#009b00;stop-opacity:1;"
offset="0"
id="stop4670" />
<stop
style="stop-color:#009b00;stop-opacity:0;"
offset="1"
id="stop4672" />
</linearGradient>
<linearGradient
id="linearGradient4662"
osb:paint="solid">
<stop
style="stop-color:#008000;stop-opacity:1;"
offset="0"
id="stop4664" />
</linearGradient>
<linearGradient
id="linearGradient4529"
osb:paint="solid">
<stop
style="stop-color:#0047ff;stop-opacity:1;"
offset="0"
id="stop4531" />
</linearGradient>
<linearGradient
id="linearGradient4513">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient4513"
id="radialGradient3132"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-76.294432,-34.372515)"
cx="32.151962"
cy="27.950663"
fx="32.151962"
fy="27.950663"
r="23.634638" />
<linearGradient
id="linearGradient4031">
<stop
id="stop4033"
offset="0"
style="stop-color:#d3d7cf;stop-opacity:1" />
<stop
id="stop4035"
offset="1"
style="stop-color:#888a85;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient3797">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop3799" />
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="1"
id="stop3801" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient4513"
id="radialGradient3132-4"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-76.294432,-34.372515)"
cx="32.151962"
cy="27.950663"
fx="32.151962"
fy="27.950663"
r="23.634638" />
<radialGradient
xlink:href="#linearGradient3797"
id="radialGradient3805"
cx="16.46319"
cy="23.895996"
fx="16.46319"
fy="23.895996"
r="18.501005"
gradientTransform="matrix(0.80330389,1.0328193,-1.4593803,1.1350735,42.108301,-16.627212)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient4031"
id="linearGradient4055"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(71.494719,-3.1982556)"
x1="30.000002"
y1="10"
x2="36"
y2="54.227272" />
</defs>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Path_Waterline</dc:title>
<dc:title>Path-Waterline</dc:title>
<dc:date>2019-05-19</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Waterline.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[russ4262] Russell Johnson</dc:title>
</cc:Agent>
</dc:contributor>
<dc:creator>
<cc:Agent>
<dc:title>[russ4262] Russell Johnson</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
style="display:inline;opacity:1">
<g
style="display:inline;opacity:1"
id="g4358"
transform="matrix(0.58483815,0,0,0.51339436,78.144089,14.290628)" />
<circle
r="17.5"
cy="37.5"
cx="29.4599"
id="path4493"
style="display:inline;opacity:0.97000002;fill:url(#radialGradient3805);fill-opacity:1;stroke:#0b1521;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="15.5"
cy="37.5"
cx="29.4599"
id="path4493-1"
style="display:inline;opacity:0.97000002;fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
transform="matrix(0.58483815,0,0,0.51339436,71.618929,23.569677)"
id="g4358-4"
style="display:inline;opacity:1" />
<circle
style="display:inline;opacity:0.97000002;fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle4822"
cx="29.4599"
cy="37.5"
r="15.5" />
<path
inkscape:connector-curvature="0"
id="circle4734"
d="m 43.012901,20.993607 a 17.5,17.5 0 0 1 -13.197266,6.03125 17.5,17.5 0 0 1 -13.048828,-5.900391 21.189749,21.189749 0 0 0 -4.238281,4.552735 23.697439,23.697439 0 0 0 17.46289,7.720703 23.697439,23.697439 0 0 0 17.435547,-7.720703 21.189749,21.189749 0 0 0 -4.414062,-4.683594 z"
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:1;stroke:#1a1a1a;stroke-width:2.70827866;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4737"
d="m 8.731651,34.735794 a 21.189749,21.189749 0 0 0 -0.283203,3.294922 21.189749,21.189749 0 0 0 0.628906,5.039063 25.091324,11.279887 0 0 0 20.228515,4.626953 25.091324,11.279887 0 0 0 21.011719,-5.123047 21.189749,21.189749 0 0 0 0.509766,-4.542969 21.189749,21.189749 0 0 0 -0.191406,-2.634765 24.954107,14.861135 0 0 1 -20.623047,6.498046 24.954107,14.861135 0 0 1 -21.28125,-7.158203 z"
style="opacity:1;fill:#1a1a1a;fill-opacity:1;stroke:#1a1a1a;stroke-width:1.89686728;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="circle4734-3"
d="M 14.698516,22.688476 A 21.189749,21.189749 0 0 0 12.72,25.067383 24.982393,24.982393 0 0 0 29.452422,31.606445 24.982393,24.982393 0 0 0 46.862578,24.489258 21.189749,21.189749 0 0 0 45.337188,22.696289 21.8059,21.8059 0 0 1 30.026641,29.05957 21.8059,21.8059 0 0 1 14.698516,22.688476 Z"
style="display:inline;opacity:1;fill:#73d216;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4835"
d="m 8.1302074,37.508398 a 21.189749,21.189749 0 0 0 -0.035156,0.962891 21.189749,21.189749 0 0 0 0.109374,2.058594 28.746413,17.1196 0 0 0 21.7109376,5.976562 28.746413,17.1196 0 0 0 20.310547,-5.021484 21.189749,21.189749 0 0 0 0.248047,-3.001953 28.904483,18.541831 0 0 1 -20.488282,5.484375 28.904483,18.541831 0 0 1 -21.8554676,-6.458985 z"
style="opacity:1;fill:#73d216;fill-opacity:1;stroke:#00ff00;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="circle4734-3-9"
d="m 15.509519,21.31383 a 21.189749,21.189749 0 0 0 -1.978516,2.378907 24.982393,24.982393 0 0 0 16.732422,6.539062 24.982393,24.982393 0 0 0 17.410156,-7.117187 21.189749,21.189749 0 0 0 -1.52539,-1.792969 21.8059,21.8059 0 0 1 -15.310547,6.363281 21.8059,21.8059 0 0 1 -15.328125,-6.371094 z"
style="display:inline;opacity:1;fill:#8ae234;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4835-0"
d="m 8.1959236,36.208398 a 21.189749,21.189749 0 0 0 -0.03516,0.962891 21.189749,21.189749 0 0 0 0.109374,2.058594 28.746413,17.1196 0 0 0 21.7109374,5.976562 28.746413,17.1196 0 0 0 20.310548,-5.021484 21.189749,21.189749 0 0 0 0.248047,-3.001953 28.904483,18.541831 0 0 1 -20.488283,5.484375 28.904483,18.541831 0 0 1 -21.8554674,-6.458985 z"
style="display:inline;opacity:1;fill:#8ae234;fill-opacity:1;stroke:#00ff00;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
transform="matrix(0.66585367,0,0,0.65271967,-19.339925,4.6218574)"
id="g4051">
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient4055);fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:3.03373241;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 94.494721,6.8017444 -0.01099,27.5961536 18.021979,-7.660256 -0.011,-19.9358976 z M 112.49472,29.801744 94.483731,37.462 l 0.01099,8.339744 17.999999,-8 z m 0.011,10.724359 -16.520148,7.660256 7.509158,4.615385 9,-5 z"
id="rect4417-3" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#d3d7cf;stroke-width:3.03373241;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="M 97.487394,9.8850776 V 29.753867 l 11.991186,-5.098858 0.0235,-14.7699314 z m 12.014656,24.5647544 -11.989201,5.01406 v 1.711385 l 11.963741,-5.40113 z m 0.0452,10.829611 -7.19513,3.327881 1.20492,0.808046 5.9895,-3.408611 z"
id="rect4417-1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>572</width>
<height>419</height>
<width>454</width>
<height>386</height>
</rect>
</property>
<property name="windowTitle">
@@ -154,6 +154,37 @@
</item>
</widget>
</item>
<item row="8" column="6">
<widget class="QComboBox" name="enableRotation">
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>A(x)</string>
</property>
</item>
<item>
<property name="text">
<string>B(y)</string>
</property>
</item>
<item>
<property name="text">
<string>A &amp; B</string>
</property>
</item>
</widget>
</item>
<item row="8" column="4">
<widget class="QLabel" name="label">
<property name="text">
<string>Enable Rotation</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>376</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>ToolController</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="toolController">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The tool and its settings to be used for this operation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Probe Grid Points</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="1">
<widget class="QSpinBox" name="PointCountX">
<property name="minimum">
<number>3</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QSpinBox" name="PointCountY">
<property name="minimum">
<number>3</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>X:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Y:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Probe</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Y Offset</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::InputField" name="Yoffset">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>X Offset</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::InputField" name="Xoffset">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Output</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>File Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="OutputFileName">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the filename where the probe points should be written.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>ProbePoints.txt</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="SetOutputFileName">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>357</width>
<height>427</height>
<width>350</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
@@ -24,7 +24,7 @@
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="toolController_label">
<property name="text">
<string>ToolController</string>
</property>
@@ -38,7 +38,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<widget class="QLabel" name="coolantController_label">
<property name="text">
<string>Coolant Mode</string>
</property>
@@ -57,126 +57,35 @@
<item row="1" column="0">
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Algorithm</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="algorithmSelect">
<item>
<property name="text">
<string>OCL Dropcutter</string>
</property>
</item>
<item>
<property name="text">
<string>OCL Waterline</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>BoundBox</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="boundBoxSelect">
<widget class="QComboBox" name="scanType">
<item>
<property name="text">
<string>Stock</string>
<string>Planar</string>
</property>
</item>
<item>
<property name="text">
<string>BaseBoundBox</string>
<string>Rotational</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>BoundBox extra offset X, Y</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::InputField" name="boundBoxExtraOffsetX" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="Gui::InputField" name="boundBoxExtraOffsetY" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Drop Cutter Direction</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="dropCutterDirSelect">
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="layerMode">
<item>
<property name="text">
<string>X</string>
<string>Single-pass</string>
</property>
</item>
<item>
<property name="text">
<string>Y</string>
<string>Multi-pass</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Depth offset</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="Gui::InputField" name="depthOffset" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Sample interval</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="Gui::InputField" name="sampleInterval" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Step over</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<item row="8" column="1" colspan="2">
<widget class="QSpinBox" name="stepOver">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The amount by which the tool is laterally displaced on each cycle of the pattern, specified in percent of the tool diameter.&lt;/p&gt;&lt;p&gt;A step over of 100% results in no overlap between two different cycles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -195,20 +104,180 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<item row="8" column="0">
<widget class="QLabel" name="stepOver_label">
<property name="text">
<string>Optimize output</string>
<string>Step over</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="sampleInterval_label">
<property name="text">
<string>Sample interval</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="layerMode_label">
<property name="text">
<string>Layer Mode</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QCheckBox" name="optimizeEnabled">
<property name="text">
<string>Optimize Linear Paths</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="dropCutterDirSelect_label">
<property name="text">
<string>Drop Cutter Direction</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="boundBoxExtraOffset_label">
<property name="text">
<string>BoundBox extra offset X, Y</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QCheckBox" name="useStartPoint">
<property name="text">
<string>Use Start Point</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="scanType_label">
<property name="text">
<string>Scan Type</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="boundBoxSelect_label">
<property name="text">
<string>BoundBox</string>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<widget class="QCheckBox" name="optimizeEnabled">
<property name="text">
<string>Enabled</string>
<widget class="Gui::InputField" name="depthOffset" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Gui::InputField" name="boundBoxExtraOffsetX" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::InputField" name="boundBoxExtraOffsetY" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="1" colspan="2">
<widget class="Gui::InputField" name="sampleInterval" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="depthOffset_label">
<property name="text">
<string>Depth offset</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<widget class="QComboBox" name="dropCutterDirSelect">
<item>
<property name="text">
<string>X</string>
</property>
</item>
<item>
<property name="text">
<string>Y</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="boundBoxSelect">
<item>
<property name="text">
<string>Stock</string>
</property>
</item>
<item>
<property name="text">
<string>BaseBoundBox</string>
</property>
</item>
</widget>
</item>
<item row="13" column="1">
<widget class="QCheckBox" name="optimizeStepOverTransitions">
<property name="text">
<string>Optimize StepOver Transitions</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="cutPattern_label">
<property name="text">
<string>Cut Pattern</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="cutPattern">
<item>
<property name="text">
<string>Line</string>
</property>
</item>
<item>
<property name="text">
<string>ZigZag</string>
</property>
</item>
<item>
<property name="text">
<string>Circular</string>
</property>
</item>
<item>
<property name="text">
<string>CircularZigZag</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
@@ -0,0 +1,269 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>ToolController</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="toolController">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The tool and its settings to be used for this operation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="3">
<widget class="QComboBox" name="boundBoxSelect">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<item>
<property name="text">
<string>Stock</string>
</property>
</item>
<item>
<property name="text">
<string>BaseBoundBox</string>
</property>
</item>
</widget>
</item>
<item row="4" column="3">
<widget class="QLineEdit" name="boundaryAdjustment">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="layerMode">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<item>
<property name="text">
<string>Single-pass</string>
</property>
</item>
<item>
<property name="text">
<string>Multi-pass</string>
</property>
</item>
</widget>
</item>
<item row="0" column="3">
<widget class="QComboBox" name="algorithmSelect">
<item>
<property name="text">
<string>OCL Dropcutter</string>
</property>
</item>
<item>
<property name="text">
<string>Experimental</string>
</property>
</item>
</widget>
</item>
<item row="11" column="3">
<widget class="QCheckBox" name="optimizeEnabled">
<property name="text">
<string>Optimize Linear Paths</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="boundaryAdjustment_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Boundary Adjustment</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="cutPattern">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Line</string>
</property>
</item>
<item>
<property name="text">
<string>ZigZag</string>
</property>
</item>
<item>
<property name="text">
<string>Circular</string>
</property>
</item>
<item>
<property name="text">
<string>CircularZigZag</string>
</property>
</item>
</widget>
</item>
<item row="8" column="3">
<widget class="QSpinBox" name="stepOver">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The amount by which the tool is laterally displaced on each cycle of the pattern, specified in percent of the tool diameter.&lt;/p&gt;&lt;p&gt;A step over of 100% results in no overlap between two different cycles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="layerMode_label">
<property name="text">
<string>Layer Mode</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="boundBoxSelect_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>BoundBox</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="stepOver_label">
<property name="text">
<string>Step over</string>
</property>
</widget>
</item>
<item row="9" column="3">
<widget class="Gui::InputField" name="sampleInterval" native="true">
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="cutPattern_label">
<property name="text">
<string>Cut Pattern</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="sampleInterval_label">
<property name="text">
<string>Sample interval</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="algorithmSelect_label">
<property name="text">
<string>Algorithm</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QWidget</extends>
<header>gui::inputfield.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TaskPanel</class>
<widget class="QWidget" name="TaskPanel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>376</width>
<height>387</height>
</rect>
</property>
<property name="windowTitle">
<string>Z Depth Correction</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolBox" name="toolBox">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="Dressup">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>340</height>
</rect>
</property>
<attribute name="label">
<string>Dressup</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Probe Points File</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QToolButton" name="SetProbePointFileName">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>File Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="ProbePointFileName">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the filename containing the probe data&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+21 -16
View File
@@ -21,8 +21,9 @@
# * *
# ***************************************************************************/
class PathCommandGroup:
def __init__(self, cmdlist, menu, tooltip = None):
def __init__(self, cmdlist, menu, tooltip=None):
self.cmdlist = cmdlist
self.menu = menu
if tooltip is None:
@@ -34,7 +35,7 @@ class PathCommandGroup:
return tuple(self.cmdlist)
def GetResources(self):
return { 'MenuText': self.menu, 'ToolTip': self.tooltip }
return {'MenuText': self.menu, 'ToolTip': self.tooltip}
def IsActive(self):
if FreeCAD.ActiveDocument is not None:
@@ -43,6 +44,7 @@ class PathCommandGroup:
return True
return False
class PathWorkbench (Workbench):
"Path workbench"
@@ -87,15 +89,15 @@ class PathWorkbench (Workbench):
# build commands list
projcmdlist = ["Path_Job", "Path_Post"]
toolcmdlist = ["Path_Inspect", "Path_Simulator", "Path_ToolLibraryEdit", "Path_SelectLoop", "Path_OpActiveToggle"]
prepcmdlist = ["Path_Fixture", "Path_Comment", "Path_Stop", "Path_Custom"]
twodopcmdlist = ["Path_Contour", "Path_Profile_Faces", "Path_Profile_Edges", "Path_Pocket_Shape", "Path_Drilling", "Path_MillFace", "Path_Helix", "Path_Adaptive" ]
prepcmdlist = ["Path_Fixture", "Path_Comment", "Path_Stop", "Path_Custom", "Path_Probe"]
twodopcmdlist = ["Path_Contour", "Path_Profile_Faces", "Path_Profile_Edges", "Path_Pocket_Shape", "Path_Drilling", "Path_MillFace", "Path_Helix", "Path_Adaptive"]
threedopcmdlist = ["Path_Pocket_3D"]
engravecmdlist = ["Path_Engrave", "Path_Deburr"]
modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy" ]
dressupcmdlist = ["Path_DressupAxisMap", "Path_DressupPathBoundary", "Path_DressupDogbone", "Path_DressupDragKnife", "Path_DressupLeadInOut", "Path_DressupRampEntry", "Path_DressupTag"]
modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy"]
dressupcmdlist = ["Path_DressupAxisMap", "Path_DressupPathBoundary", "Path_DressupDogbone", "Path_DressupDragKnife", "Path_DressupLeadInOut", "Path_DressupRampEntry", "Path_DressupTag", "Path_DressupZCorrect"]
extracmdlist = []
#modcmdmore = ["Path_Hop",]
#remotecmdlist = ["Path_Remote"]
# modcmdmore = ["Path_Hop",]
# remotecmdlist = ["Path_Remote"]
engravecmdgroup = ['Path_EngraveTools']
FreeCADGui.addCommand('Path_EngraveTools', PathCommandGroup(engravecmdlist, QtCore.QT_TRANSLATE_NOOP("Path", 'Engraving Operations')))
@@ -107,11 +109,12 @@ class PathWorkbench (Workbench):
extracmdlist.extend(["Path_Area", "Path_Area_Workplane"])
try:
import ocl # pylint: disable=unused-variable
import ocl # pylint: disable=unused-variable
from PathScripts import PathSurfaceGui
threedopcmdlist.append("Path_Surface")
from PathScripts import PathWaterlineGui
threedopcmdlist.extend(["Path_Surface", "Path_Waterline"])
threedcmdgroup = ['Path_3dTools']
FreeCADGui.addCommand('Path_3dTools', PathCommandGroup(threedopcmdlist, QtCore.QT_TRANSLATE_NOOP("Path",'3D Operations')))
FreeCADGui.addCommand('Path_3dTools', PathCommandGroup(threedopcmdlist, QtCore.QT_TRANSLATE_NOOP("Path", '3D Operations')))
except ImportError:
FreeCAD.Console.PrintError("OpenCamLib is not working!\n")
@@ -122,7 +125,9 @@ class PathWorkbench (Workbench):
if extracmdlist:
self.appendToolbar(QtCore.QT_TRANSLATE_NOOP("Path", "Helpful Tools"), extracmdlist)
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path")], projcmdlist +["Path_ExportTemplate", "Separator"] + toolbitcmdlist + toolcmdlist +["Separator"] + twodopcmdlist + engravecmdlist +["Separator"] +threedopcmdlist +["Separator"])
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path")], projcmdlist + ["Path_ExportTemplate", "Separator"] +
toolbitcmdlist + toolcmdlist + ["Separator"] + twodopcmdlist + engravecmdlist + ["Separator"] +
threedopcmdlist + ["Separator"])
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path"), QtCore.QT_TRANSLATE_NOOP(
"Path", "Path Dressup")], dressupcmdlist)
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path"), QtCore.QT_TRANSLATE_NOOP(
@@ -136,7 +141,7 @@ class PathWorkbench (Workbench):
curveAccuracy = PathPreferences.defaultLibAreaCurveAccuracy()
if curveAccuracy:
Path.Area.setDefaultParams(Accuracy = curveAccuracy)
Path.Area.setDefaultParams(Accuracy=curveAccuracy)
Log('Loading Path workbench... done\n')
@@ -171,8 +176,8 @@ class PathWorkbench (Workbench):
if obj.isDerivedFrom("Path::Feature"):
if "Profile" in selectedName or "Contour" in selectedName or "Dressup" in selectedName:
self.appendContextMenu("", "Separator")
#self.appendContextMenu("", ["Set_StartPoint"])
#self.appendContextMenu("", ["Set_EndPoint"])
# self.appendContextMenu("", ["Set_StartPoint"])
# self.appendContextMenu("", ["Set_EndPoint"])
for cmd in self.dressupcmds:
self.appendContextMenu("", [cmd])
menuAppended = True
@@ -182,10 +187,10 @@ class PathWorkbench (Workbench):
if menuAppended:
self.appendContextMenu("", "Separator")
Gui.addWorkbench(PathWorkbench())
FreeCAD.addImportType(
"GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap *.gcode)", "PathGui")
# FreeCAD.addExportType(
# "GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap *.gcode)", "PathGui")
+45 -18
View File
@@ -3,7 +3,6 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 sliptonic <[email protected]> *
# * Copyright (c) 2020 russ4262 (Russell Johnson) *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -49,6 +48,7 @@ PathLog.setLevel(LOGLEVEL, PathLog.thisModule())
if LOGLEVEL is PathLog.Level.DEBUG:
PathLog.trackModule()
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
@@ -66,7 +66,6 @@ class ObjectOp(PathOp.ObjectOp):
'''opFeatures(obj) ... returns the base features supported by all Path.Area based operations.
The standard feature list is OR'ed with the return value of areaOpFeatures().
Do not overwrite, implement areaOpFeatures(obj) instead.'''
# return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureStepDown | PathOp.FeatureHeights | PathOp.FeatureStartPoint | self.areaOpFeatures(obj) | PathOp.FeatureRotation
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureStepDown | PathOp.FeatureHeights | PathOp.FeatureStartPoint | self.areaOpFeatures(obj) | PathOp.FeatureCoolant
def areaOpFeatures(self, obj):
@@ -304,8 +303,6 @@ class ObjectOp(PathOp.ObjectOp):
pathParams['return_end'] = True
# Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
pathParams['preamble'] = False
#if not self.areaOpRetractTool(obj):
# pathParams['threshold'] = 2.001 * self.radius
if self.endVector is None:
V = hWire.Wires[0].Vertexes
@@ -374,12 +371,6 @@ class ObjectOp(PathOp.ObjectOp):
obj.ClearanceHeight.Value = strDep + self.clrOfset
obj.SafeHeight.Value = strDep + self.safOfst
#if self.initWithRotation is False:
# if obj.FinalDepth.Value == obj.OpFinalDepth.Value:
# obj.FinalDepth.Value = finDep
# if obj.StartDepth.Value == obj.OpStartDepth.Value:
# obj.StartDepth.Value = strDep
# Create visual axes when debugging.
if PathLog.getLevel(PathLog.thisModule()) == 4:
self.visualAxis()
@@ -467,10 +458,14 @@ class ObjectOp(PathOp.ObjectOp):
# Rotate Model to correct angle
ppCmds.insert(0, Path.Command('G0', {axisOfRot: angle, 'F': self.axialRapid}))
# Raise cutter to safe depth and return index to starting position
ppCmds.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
if axis != nextAxis:
ppCmds.append(Path.Command('G0', {axisOfRot: 0.0, 'F': self.axialRapid}))
# Raise cutter to safe height
ppCmds.insert(0, Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
# Return index to starting position if axis of rotation changes.
if numShapes > 1:
if ns != numShapes - 1:
if axis != nextAxis:
ppCmds.append(Path.Command('G0', {axisOfRot: 0.0, 'F': self.axialRapid}))
# Eif
# Save gcode commands to object command list
@@ -483,9 +478,43 @@ class ObjectOp(PathOp.ObjectOp):
# Raise cutter to safe height and rotate back to original orientation
if self.rotateFlag is True:
resetAxis = False
lastJobOp = None
nextJobOp = None
opIdx = 0
JOB = PathUtils.findParentJob(obj)
jobOps = JOB.Operations.Group
numJobOps = len(jobOps)
for joi in range(0, numJobOps):
jo = jobOps[joi]
if jo.Name == obj.Name:
opIdx = joi
lastOpIdx = opIdx - 1
nextOpIdx = opIdx + 1
if lastOpIdx > -1:
lastJobOp = jobOps[lastOpIdx]
if nextOpIdx < numJobOps:
nextJobOp = jobOps[nextOpIdx]
if lastJobOp is not None:
if hasattr(lastJobOp, 'EnableRotation'):
PathLog.debug('Last Op, {}, has `EnableRotation` set to {}'.format(lastJobOp.Label, lastJobOp.EnableRotation))
if lastJobOp.EnableRotation != obj.EnableRotation:
resetAxis = True
if ns == numShapes - 1: # If last shape, check next op EnableRotation setting
if nextJobOp is not None:
if hasattr(nextJobOp, 'EnableRotation'):
PathLog.debug('Next Op, {}, has `EnableRotation` set to {}'.format(nextJobOp.Label, nextJobOp.EnableRotation))
if nextJobOp.EnableRotation != obj.EnableRotation:
resetAxis = True
# Raise to safe height if rotation activated
self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
self.commandlist.append(Path.Command('G0', {'A': 0.0, 'F': self.axialRapid}))
self.commandlist.append(Path.Command('G0', {'B': 0.0, 'F': self.axialRapid}))
# reset rotational axises if necessary
if resetAxis is True:
self.commandlist.append(Path.Command('G0', {'A': 0.0, 'F': self.axialRapid}))
self.commandlist.append(Path.Command('G0', {'B': 0.0, 'F': self.axialRapid}))
self.useTempJobClones('Delete') # Delete temp job clone group and contents
self.guiMessage('title', None, show=True) # Process GUI messages to user
@@ -531,10 +560,8 @@ class ObjectOp(PathOp.ObjectOp):
Determine rotational radii for 4th-axis rotations, for clearance/safe heights '''
parentJob = PathUtils.findParentJob(obj)
# bb = parentJob.Stock.Shape.BoundBox
xlim = 0.0
ylim = 0.0
# zlim = 0.0
# Determine boundbox radius based upon xzy limits data
if math.fabs(self.stockBB.ZMin) > math.fabs(self.stockBB.ZMax):
+26 -10
View File
@@ -25,9 +25,12 @@ import FreeCAD
import FreeCADGui
import Path
from PySide import QtCore
from copy import copy
__doc__ = """Path Custom object and FreeCAD command"""
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
# Qt translation handling
def translate(context, text, disambig=None):
@@ -35,10 +38,14 @@ def translate(context, text, disambig=None):
class ObjectCustom:
def __init__(self, obj):
obj.addProperty("App::PropertyStringList", "Gcode", "Path",
QtCore.QT_TRANSLATE_NOOP("PathCustom", "The gcode to be inserted"))
obj.addProperty("App::PropertyLink", "ToolController", "Path",
QtCore.QT_TRANSLATE_NOOP("PathCustom", "The tool controller that will be used to calculate the path"))
obj.addProperty("App::PropertyPlacement", "Offset", "Path",
"Placement Offset")
def __init__(self,obj):
obj.addProperty("App::PropertyStringList", "Gcode", "Path", QtCore.QT_TRANSLATE_NOOP("PathCustom", "The gcode to be inserted"))
obj.addProperty("App::PropertyLink", "ToolController", "Path", QtCore.QT_TRANSLATE_NOOP("PathCustom", "The tool controller that will be used to calculate the path"))
obj.Proxy = self
def __getstate__(self):
@@ -48,13 +55,22 @@ class ObjectCustom:
return None
def execute(self, obj):
newpath = Path.Path()
if obj.Gcode:
s = ""
for l in obj.Gcode:
s += str(l)
if s:
path = Path.Path(s)
obj.Path = path
newcommand = Path.Command(str(l))
if newcommand.Name in movecommands:
if 'X' in newcommand.Parameters:
newcommand.x += obj.Offset.Base.x
if 'Y' in newcommand.Parameters:
newcommand.y += obj.Offset.Base.y
if 'Z' in newcommand.Parameters:
newcommand.z += obj.Offset.Base.z
newpath.insertCommand(newcommand)
obj.Path=newpath
class CommandPathCustom:
@@ -75,7 +91,7 @@ class CommandPathCustom:
FreeCAD.ActiveDocument.openTransaction("Create Custom Path")
FreeCADGui.addModule("PathScripts.PathCustom")
FreeCADGui.addModule("PathScripts.PathUtils")
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Custom")')
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Custom")')
FreeCADGui.doCommand('PathScripts.PathCustom.ObjectCustom(obj)')
FreeCADGui.doCommand('obj.ViewObject.Proxy = 0')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
@@ -86,4 +102,4 @@ class CommandPathCustom:
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Custom', CommandPathCustom())
FreeCADGui.addCommand('Path_Custom', CommandPathCustom())
@@ -0,0 +1,343 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2018 sliptonic <[email protected]> *
# * *
# * 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 *
# * *
# * Bilinear interpolation code modified heavily from the interpolation *
# * library https://github.com/pmav99/interpolation *
# * Copyright (c) 2013 by Panagiotis Mavrogiorgos *
# * *
# ***************************************************************************
import FreeCAD
import FreeCADGui
import Part
import Path
import PathScripts.PathGeom as PathGeom
import PathScripts.PathLog as PathLog
import PathScripts.PathUtils as PathUtils
from PySide import QtCore, QtGui
"""Z Depth Correction Dressup. This dressup takes a probe file as input and does bilinear interpolation of the Zdepths to correct for a surface which is not parallel to the milling table/bed. The probe file should conform to the format specified by the linuxcnc G38 probe logging: 9-number coordinate consisting of XYZABCUVW http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g38
"""
LOG_MODULE = PathLog.thisModule()
if False:
PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
else:
PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE)
# Qt tanslation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
movecommands = ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']
rapidcommands = ['G0', 'G00']
arccommands = ['G2', 'G3', 'G02', 'G03']
class ObjectDressup:
def __init__(self, obj):
obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The base path to modify"))
obj.addProperty("App::PropertyFile", "probefile", "ProbeData", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "The point file from the surface probing."))
obj.Proxy = self
obj.addProperty("Part::PropertyPartShape", "interpSurface", "Path")
obj.setEditorMode('interpSurface', 2) # hide
obj.addProperty("App::PropertyDistance", "ArcInterpolate", "Interpolate", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "Deflection distance for arc interpolation"))
obj.addProperty("App::PropertyDistance", "SegInterpolate", "Interpolate", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrectp", "break segments into smaller segments of this length."))
obj.ArcInterpolate = 0.1
obj.SegInterpolate = 1.0
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def onChanged(self, fp, prop):
if str(prop) == "probefile":
self._loadFile(fp, fp.probefile)
def _bilinearInterpolate(self, surface, x, y):
p1 = FreeCAD.Vector(x, y, 100.0)
p2 = FreeCAD.Vector(x, y, -100.0)
vertical_line = Part.Line(p1, p2)
points, curves = vertical_line.intersectCS(surface)
return points[0].Z
def _loadFile(self, obj, filename):
if filename == "":
return
f1 = open(filename, 'r')
try:
pointlist = []
for line in f1.readlines():
w = line.split()
xval = round(float(w[0]), 2)
yval = round(float(w[1]), 2)
zval = round(float(w[2]), 2)
pointlist.append([xval, yval, zval])
PathLog.debug(pointlist)
cols = list(zip(*pointlist))
PathLog.debug("cols: {}".format(cols))
yindex = list(sorted(set(cols[1])))
PathLog.debug("yindex: {}".format(yindex))
array = []
for y in yindex:
points = sorted([p for p in pointlist if p[1] == y])
inner = []
for p in points:
inner.append(FreeCAD.Vector(p[0], p[1], p[2]))
array.append(inner)
intSurf = Part.BSplineSurface()
intSurf.interpolate(array)
obj.interpSurface = intSurf.toShape()
except Exception:
raise ValueError("File does not contain appropriate point data")
def execute(self, obj):
sampleD = obj.SegInterpolate.Value
curveD = obj.ArcInterpolate.Value
if obj.interpSurface.isNull(): # No valid probe data. return unchanged path
obj.Path = obj.Base.Path
return
surface = obj.interpSurface.toNurbs().Faces[0].Surface
if obj.Base:
if obj.Base.isDerivedFrom("Path::Feature"):
if obj.Base.Path:
if obj.Base.Path.Commands:
pathlist = obj.Base.Path.Commands
newcommandlist = []
currLocation = {'X': 0, 'Y': 0, 'Z': 0, 'F': 0}
for c in pathlist:
PathLog.debug(c)
PathLog.debug(" curLoc:{}".format(currLocation))
newparams = dict(c.Parameters)
zval = newparams.get("Z", currLocation['Z'])
if c.Name in movecommands:
curVec = FreeCAD.Vector(currLocation['X'], currLocation['Y'], currLocation['Z'])
arcwire = PathGeom.edgeForCmd(c, curVec)
if arcwire is None:
continue
if c.Name in arccommands:
pointlist = arcwire.discretize(Deflection=curveD)
else:
disc_number = int(arcwire.Length / sampleD)
if disc_number > 1:
pointlist = arcwire.discretize(Number=int(arcwire.Length / sampleD))
else:
pointlist = [v.Point for v in arcwire.Vertexes]
for point in pointlist:
offset = self._bilinearInterpolate(surface, point.x, point.y)
newcommand = Path.Command("G1", {'X': point.x, 'Y': point.y, 'Z': point.z + offset})
newcommandlist.append(newcommand)
currLocation.update(newcommand.Parameters)
currLocation['Z'] = zval
else:
# Non Feed Command
newcommandlist.append(c)
currLocation.update(c.Parameters)
path = Path.Path(newcommandlist)
obj.Path = path
class TaskPanel:
def __init__(self, obj):
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(":/panels/ZCorrectEdit.ui")
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupZCorrect", "Edit Z Correction Dress-up"))
self.interpshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "InterpolationSurface")
self.interpshape.Shape = obj.interpSurface
self.interpshape.ViewObject.Transparency = 60
self.interpshape.ViewObject.ShapeColor = (1.00000, 1.00000, 0.01961)
self.interpshape.ViewObject.Selectable = False
stock = PathUtils.findParentJob(obj).Stock
self.interpshape.Placement.Base.z = stock.Shape.BoundBox.ZMax
def reject(self):
FreeCAD.ActiveDocument.abortTransaction()
FreeCADGui.Control.closeDialog()
FreeCAD.ActiveDocument.recompute()
def accept(self):
self.getFields()
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.removeObject(self.interpshape.Name)
FreeCADGui.ActiveDocument.resetEdit()
FreeCADGui.Control.closeDialog()
FreeCAD.ActiveDocument.recompute()
FreeCAD.ActiveDocument.recompute()
def getFields(self):
self.obj.Proxy.execute(self.obj)
def updateUI(self):
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
for obj in FreeCAD.ActiveDocument.Objects:
if obj.Name.startswith('Shape'):
FreeCAD.ActiveDocument.removeObject(obj.Name)
print('object name %s' % self.obj.Name)
if hasattr(self.obj.Proxy, "shapes"):
PathLog.info("showing shapes attribute")
for shapes in self.obj.Proxy.shapes.itervalues():
for shape in shapes:
Part.show(shape)
else:
PathLog.info("no shapes attribute found")
def updateModel(self):
self.getFields()
self.updateUI()
FreeCAD.ActiveDocument.recompute()
def setFields(self):
self.form.ProbePointFileName.setText(self.obj.probefile)
self.updateUI()
def open(self):
pass
def setupUi(self):
self.setFields()
# now that the form is filled, setup the signal handlers
self.form.ProbePointFileName.editingFinished.connect(self.updateModel)
self.form.SetProbePointFileName.clicked.connect(self.SetProbePointFileName)
def SetProbePointFileName(self):
filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Probe", "Select Probe Point File"), None, translate("Path_Probe", "All Files (*.*)"))
if filename and filename[0]:
self.obj.probefile = str(filename[0])
self.setFields()
class ViewProviderDressup:
def __init__(self, vobj):
vobj.Proxy = self
def attach(self, vobj):
self.obj = vobj.Object
if self.obj and self.obj.Base:
for i in self.obj.Base.InList:
if hasattr(i, "Group"):
group = i.Group
for g in group:
if g.Name == self.obj.Base.Name:
group.remove(g)
i.Group = group
return
def claimChildren(self):
return [self.obj.Base]
def setEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
panel = TaskPanel(vobj.Object)
FreeCADGui.Control.showDialog(panel)
panel.setupUi()
return True
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def onDelete(self, arg1=None, arg2=None):
'''this makes sure that the base operation is added back to the project and visible'''
FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True
job = PathUtils.findParentJob(arg1.Object)
job.Proxy.addOperation(arg1.Object.Base)
arg1.Object.Base = None
return True
class CommandPathDressup:
def GetResources(self):
return {'Pixmap': 'Path-Dressup',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "Z Depth Correction Dress-up"),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "Use Probe Map to correct Z depth")}
def IsActive(self):
if FreeCAD.ActiveDocument is not None:
for o in FreeCAD.ActiveDocument.Objects:
if o.Name[:3] == "Job":
return True
return False
def Activated(self):
# check that the selection contains exactly what we want
selection = FreeCADGui.Selection.getSelection()
if len(selection) != 1:
FreeCAD.Console.PrintError(translate("Path_Dressup", "Please select one path object\n"))
return
if not selection[0].isDerivedFrom("Path::Feature"):
FreeCAD.Console.PrintError(translate("Path_Dressup", "The selected object is not a path\n"))
return
if selection[0].isDerivedFrom("Path::FeatureCompoundPython"):
FreeCAD.Console.PrintError(translate("Path_Dressup", "Please select a Path object"))
return
# everything ok!
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupZCorrect", "Create Dress-up"))
FreeCADGui.addModule("PathScripts.PathDressupZCorrect")
FreeCADGui.addModule("PathScripts.PathUtils")
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "ZCorrectDressup")')
FreeCADGui.doCommand('PathScripts.PathDressupZCorrect.ObjectDressup(obj)')
FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' + selection[0].Name)
FreeCADGui.doCommand('PathScripts.PathDressupZCorrect.ViewProviderDressup(obj.ViewObject)')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
FreeCADGui.doCommand('Gui.ActiveDocument.getObject(obj.Base.Name).Visibility = False')
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_DressupZCorrect', CommandPathDressup())
FreeCAD.Console.PrintLog("Loading PathDressup... done\n")
@@ -99,6 +99,8 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
obj.PeckEnabled = self.form.peckEnabled.isChecked()
if obj.ExtraOffset != str(self.form.ExtraOffset.currentText()):
obj.ExtraOffset = str(self.form.ExtraOffset.currentText())
if obj.EnableRotation != str(self.form.enableRotation.currentText()):
obj.EnableRotation = str(self.form.enableRotation.currentText())
self.updateToolController(obj, self.form.toolController)
self.updateCoolant(obj, self.form.coolantController)
@@ -122,6 +124,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
self.setupToolController(obj, self.form.toolController)
self.setupCoolant(obj, self.form.coolantController)
self.selectInComboBox(obj.EnableRotation, self.form.enableRotation)
def getSignalsForUpdate(self, obj):
@@ -137,6 +140,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
signals.append(self.form.coolantController.currentIndexChanged)
signals.append(self.form.coolantController.currentIndexChanged)
signals.append(self.form.ExtraOffset.currentIndexChanged)
signals.append(self.form.enableRotation.currentIndexChanged)
return signals
+7
View File
@@ -313,6 +313,9 @@ def edgeForCmd(cmd, startPoint):
"""edgeForCmd(cmd, startPoint).
Returns an Edge representing the given command, assuming a given startPoint."""
PathLog.debug("cmd: {}".format(cmd))
PathLog.debug("startpoint {}".format(startPoint))
endPoint = commandEndPoint(cmd, startPoint)
if (cmd.Name in CmdMoveStraight) or (cmd.Name in CmdMoveRapid):
if pointsCoincide(startPoint, endPoint):
@@ -343,6 +346,10 @@ def edgeForCmd(cmd, startPoint):
if isRoughly(startPoint.z, endPoint.z):
midPoint = center + Vector(math.cos(angle), math.sin(angle), 0) * R
PathLog.debug("arc: (%.2f, %.2f) -> (%.2f, %.2f) -> (%.2f, %.2f)" % (startPoint.x, startPoint.y, midPoint.x, midPoint.y, endPoint.x, endPoint.y))
PathLog.debug("StartPoint:{}".format(startPoint))
PathLog.debug("MidPoint:{}".format(midPoint))
PathLog.debug("EndPoint:{}".format(endPoint))
return Part.Edge(Part.Arc(startPoint, midPoint, endPoint))
# It's a Helix
+5 -1
View File
@@ -35,6 +35,7 @@ else:
Processed = False
def Startup():
global Processed # pylint: disable=global-statement
if not Processed:
@@ -51,6 +52,7 @@ def Startup():
from PathScripts import PathDressupPathBoundaryGui
from PathScripts import PathDressupTagGui
from PathScripts import PathDressupLeadInOut
from PathScripts import PathDressupZCorrect
from PathScripts import PathDrillingGui
from PathScripts import PathEngraveGui
from PathScripts import PathFixture
@@ -61,6 +63,7 @@ def Startup():
from PathScripts import PathPocketGui
from PathScripts import PathPocketShapeGui
from PathScripts import PathPost
from PathScripts import PathProbeGui
from PathScripts import PathProfileContourGui
from PathScripts import PathProfileEdgesGui
from PathScripts import PathProfileFacesGui
@@ -69,12 +72,13 @@ def Startup():
from PathScripts import PathSimpleCopy
from PathScripts import PathSimulatorGui
from PathScripts import PathStop
# from PathScripts import PathSurfaceGui # Added in initGui.py due to OCL dependency
from PathScripts import PathToolController
from PathScripts import PathToolControllerGui
from PathScripts import PathToolLibraryManager
from PathScripts import PathToolLibraryEditor
from PathScripts import PathUtilsGui
# from PathScripts import PathWaterlineGui # Added in initGui.py due to OCL dependency
Processed = True
else:
PathLog.debug('Skipping PathGui initialisation')
+5 -7
View File
@@ -99,7 +99,7 @@ class ObjectFace(PathPocketBase.ObjectPocket):
'''areaOpShapes(obj) ... return top face'''
# Facing is done either against base objects
holeShape = None
if obj.Base:
PathLog.debug("obj.Base: {}".format(obj.Base))
faces = []
@@ -147,17 +147,17 @@ class ObjectFace(PathPocketBase.ObjectPocket):
# Find the correct shape depending on Boundary shape.
PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape))
bb = planeshape.BoundBox
# Apply offset for clearing edges
offset = 0;
if obj.ClearEdges == True:
offset = self.radius + 0.1
bb.XMin = bb.XMin - offset
bb.YMin = bb.YMin - offset
bb.XMax = bb.XMax + offset
bb.YMax = bb.YMax + offset
if obj.BoundaryShape == 'Boundbox':
bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1))
env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams)
@@ -170,7 +170,7 @@ class ObjectFace(PathPocketBase.ObjectPocket):
elif obj.BoundaryShape == 'Stock':
stock = PathUtils.findParentJob(obj).Stock.Shape
env = stock
if obj.ExcludeRaisedAreas is True and oneBase[1] is True:
includedFaces = self.getAllIncludedFaces(oneBase[0], stock, faceZ=minHeight)
if len(includedFaces) > 0:
@@ -269,7 +269,6 @@ def SetupProperties():
setup.append("BoundaryShape")
setup.append("ExcludeRaisedAreas")
setup.append("ClearEdges")
return setup
@@ -278,5 +277,4 @@ def Create(name, obj=None):
if obj is None:
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
obj.Proxy = ObjectFace(obj, name)
return obj
+28 -14
View File
@@ -3,7 +3,6 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 sliptonic <[email protected]> *
# * Copyright (c) 2020 russ4262 (Russell Johnson) *
# * Copyright (c) 2020 Schildkroet *
# * *
# * This program is free software; you can redistribute it and/or modify *
@@ -42,7 +41,7 @@ __author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Class and implementation of shape based Pocket operation."
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
@@ -435,7 +434,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
if obj.Base:
PathLog.debug('Processing... obj.Base')
self.removalshapes = [] # pylint: disable=attribute-defined-outside-init
# ----------------------------------------------------------------------
if obj.EnableRotation == 'Off':
stock = PathUtils.findParentJob(obj).Stock
for (base, subList) in obj.Base:
@@ -450,11 +449,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
(isLoop, norm, surf) = self.checkForFacesLoop(base, subsList)
if isLoop is True:
PathLog.info("Common Surface.Axis or normalAt() value found for loop faces.")
PathLog.debug("Common Surface.Axis or normalAt() value found for loop faces.")
rtn = False
subCount += 1
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.info("angle: {}; axis: {}".format(angle, axis))
PathLog.debug("angle: {}; axis: {}".format(angle, axis))
if rtn is True:
faceNums = ""
@@ -471,15 +470,17 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
rtn = False
PathLog.warning(translate("PathPocketShape", "Face appears to NOT be horizontal AFTER rotation applied."))
break
if rtn is False:
PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
if obj.InverseAngle is False:
if obj.AttemptInverseAngle is True:
PathLog.debug("Applying the inverse angle.")
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
else:
PathLog.warning(translate("Path", "Consider toggling the InverseAngle property and recomputing the operation."))
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.warning(msg)
if angle < -180.0:
if angle < 0.0:
angle += 360.0
tup = clnBase, subsList, angle, axis, clnStock
@@ -518,6 +519,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
(norm, surf) = self.getFaceNormAndSurf(face)
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("initial {}".format(praInfo))
if rtn is True:
faceNum = sub.replace('Face', '')
@@ -525,20 +527,31 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
# Verify faces are correctly oriented - InverseAngle might be necessary
faceIA = clnBase.Shape.getElement(sub)
(norm, surf) = self.getFaceNormAndSurf(faceIA)
(rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
(rtn, praAngle, praAxis, praInfo2) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("follow-up {}".format(praInfo2))
if abs(praAngle) == 180.0:
rtn = False
if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp is False')
angle -= 180.0
if rtn is True:
PathLog.debug("Face not aligned after initial rotation.")
PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
if obj.InverseAngle is False:
if obj.AttemptInverseAngle is True:
PathLog.debug("Applying the inverse angle.")
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
else:
PathLog.warning(translate("Path", "Consider toggling the InverseAngle property and recomputing the operation."))
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.warning(msg)
if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp is False')
angle += 180.0
else:
PathLog.debug("Face appears to be oriented correctly.")
if angle < -180.0:
if angle < 0.0:
angle += 360.0
tup = clnBase, [sub], angle, axis, clnStock
@@ -650,8 +663,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
if shpZMin > obj.FinalDepth.Value:
afD = shpZMin
if sD <= afD:
PathLog.error('Start Depth is lower than face depth.')
sD = afD + 1.0
msg = translate('PathPocketShape', 'Start Depth is lower than face depth. Setting to ')
PathLog.warning(msg + ' {} mm.'.format(sD))
else:
face.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - shpZMin))
+107
View File
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2018 sliptonic <[email protected]> *
# * *
# * 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 *
# * *
# ***************************************************************************
from __future__ import print_function
import FreeCAD
import Path
import PathScripts.PathLog as PathLog
import PathScripts.PathOp as PathOp
import PathScripts.PathUtils as PathUtils
from PySide import QtCore
__title__ = "Path Probing Operation"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Path Probing operation."
if False:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# Qt tanslation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
class ObjectProbing(PathOp.ObjectOp):
'''Proxy object for Probing operation.'''
def opFeatures(self, obj):
'''opFeatures(obj) ... Probing works on the stock object.'''
return PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureTool
def initOperation(self, obj):
obj.addProperty("App::PropertyLength", "Xoffset", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "X offset between tool and probe"))
obj.addProperty("App::PropertyLength", "Yoffset", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Y offset between tool and probe"))
obj.addProperty("App::PropertyInteger", "PointCountX", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in X direction"))
obj.addProperty("App::PropertyInteger", "PointCountY", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in Y direction"))
obj.addProperty("App::PropertyFile", "OutputFileName", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The output location for the probe data to be written"))
def nextpoint(self, startpoint=0.0, endpoint=0.0, count=3):
curstep = 0
dist = (endpoint - startpoint) / (count - 1)
while curstep <= count-1:
yield startpoint + (curstep * dist)
curstep += 1
def opExecute(self, obj):
'''opExecute(obj) ... generate probe locations.'''
PathLog.track()
self.commandlist.append(Path.Command("(Begin Probing)"))
stock = PathUtils.findParentJob(obj).Stock
bb = stock.Shape.BoundBox
openstring = '(PROBEOPEN {})'.format(obj.OutputFileName)
self.commandlist.append(Path.Command(openstring))
self.commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
for y in self.nextpoint(bb.YMin, bb.YMax, obj.PointCountY):
for x in self.nextpoint(bb.XMin, bb.XMax, obj.PointCountX):
self.commandlist.append(Path.Command("G0", {"X": x + obj.Xoffset.Value, "Y": y + obj.Yoffset.Value, "Z": obj.SafeHeight.Value}))
self.commandlist.append(Path.Command("G38.2", {"Z": obj.FinalDepth.Value, "F": obj.ToolController.VertFeed.Value}))
self.commandlist.append(Path.Command("G0", {"Z": obj.SafeHeight.Value}))
self.commandlist.append(Path.Command("(PROBECLOSE)"))
def opSetDefaultValues(self, obj, job):
'''opSetDefaultValues(obj, job) ... set default value for RetractHeight'''
def SetupProperties():
setup = ['Xoffset', 'Yoffset', 'PointCountX', 'PointCountY', 'OutputFileName']
return setup
def Create(name, obj=None):
'''Create(name) ... Creates and returns a Probing operation.'''
if obj is None:
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
proxy = ObjectProbing(obj, name)
return obj
+94
View File
@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2017 sliptonic <[email protected]> *
# * *
# * 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
import FreeCADGui
import PathScripts.PathProbe as PathProbe
import PathScripts.PathOpGui as PathOpGui
import PathScripts.PathGui as PathGui
from PySide import QtCore, QtGui
__title__ = "Path Probing Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Probing operation page controller and command implementation."
# Qt tanslation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Page controller class for the Probing operation.'''
def getForm(self):
'''getForm() ... returns UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpProbeEdit.ui")
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
self.updateToolController(obj, self.form.toolController)
PathGui.updateInputField(obj, 'Xoffset', self.form.Xoffset)
PathGui.updateInputField(obj, 'Yoffset', self.form.Yoffset)
obj.PointCountX = self.form.PointCountX.value()
obj.PointCountY = self.form.PointCountY.value()
obj.OutputFileName = str(self.form.OutputFileName.text())
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
self.setupToolController(obj, self.form.toolController)
self.form.Xoffset.setText(FreeCAD.Units.Quantity(obj.Xoffset.Value, FreeCAD.Units.Length).UserString)
self.form.Yoffset.setText(FreeCAD.Units.Quantity(obj.Yoffset.Value, FreeCAD.Units.Length).UserString)
self.form.OutputFileName.setText(obj.OutputFileName)
self.form.PointCountX.setValue(obj.PointCountX)
self.form.PointCountY.setValue(obj.PointCountY)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.toolController.currentIndexChanged)
signals.append(self.form.PointCountX.valueChanged)
signals.append(self.form.PointCountY.valueChanged)
signals.append(self.form.OutputFileName.editingFinished)
signals.append(self.form.Xoffset.valueChanged)
signals.append(self.form.Yoffset.valueChanged)
self.form.SetOutputFileName.clicked.connect(self.SetOutputFileName)
return signals
def SetOutputFileName(self):
filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Probe", "Select Output File"), None, translate("Path_Probe", "All Files (*.*)"))
if filename and filename[0]:
self.obj.OutputFileName = str(filename[0])
self.setFields(self.obj)
Command = PathOpGui.SetupOperation('Probe', PathProbe.Create, TaskPanelOpPage,
'Path-Probe',
QtCore.QT_TRANSLATE_NOOP("Probe", "Probe"),
QtCore.QT_TRANSLATE_NOOP("Probe", "Create a Probing Grid from a job stock"),
PathProbe.SetupProperties)
FreeCAD.Console.PrintLog("Loading PathProbeGui... done\n")
+30 -26
View File
@@ -112,18 +112,23 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
else:
PathLog.error(translate('PathProfileEdges', 'The selected edge(s) are inaccessible.'))
else:
cutWireObjs = False
(origWire, flatWire) = self._flattenWire(obj, wire, obj.FinalDepth.Value)
cutShp = self._getCutAreaCrossSection(obj, base, origWire, flatWire)
if cutShp is not False:
cutWireObjs = self._extractPathWire(obj, base, flatWire, cutShp)
if cutWireObjs is not False:
for cW in cutWireObjs:
shapes.append((cW, False))
self.profileEdgesIsOpen = True
if self.JOB.GeometryTolerance.Value == 0.0:
msg = self.JOB.Label + '.GeometryTolerance = 0.0.'
msg += translate('PathProfileEdges', 'Please set to an acceptable value greater than zero.')
PathLog.error(msg)
else:
PathLog.error(translate('PathProfileEdges', 'The selected edge(s) are inaccessible.'))
cutWireObjs = False
(origWire, flatWire) = self._flattenWire(obj, wire, obj.FinalDepth.Value)
cutShp = self._getCutAreaCrossSection(obj, base, origWire, flatWire)
if cutShp is not False:
cutWireObjs = self._extractPathWire(obj, base, flatWire, cutShp)
if cutWireObjs is not False:
for cW in cutWireObjs:
shapes.append((cW, False))
self.profileEdgesIsOpen = True
else:
PathLog.error(translate('PathProfileEdges', 'The selected edge(s) are inaccessible.'))
# Delete the temporary objects
if PathLog.getLevel(PathLog.thisModule()) != 4:
@@ -179,13 +184,13 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
return (OW, FW)
# Open-edges methods
def _getCutAreaCrossSection(self, obj, base, origWire, flatWireObj):
PathLog.debug('_getCutAreaCrossSection()')
tmpGrp = self.tmpGrp
FCAD = FreeCAD.ActiveDocument
tolerance = self.JOB.GeometryTolerance.Value
# toolDiam = float(obj.ToolController.Tool.Diameter)
toolDiam = 2 * self.radius # self.radius defined in PathAreaOp or PathprofileBase modules
toolDiam = 2 * self.radius # self.radius defined in PathAreaOp or PathProfileBase modules
minBfr = toolDiam * 1.25
bbBfr = (self.ofstRadius * 2) * 1.25
if bbBfr < minBfr:
@@ -243,34 +248,33 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
# Cut model(selected edges) from extended edges boundbox
cutArea = extBndboxEXT.Shape.cut(base.Shape)
CA = FCAD.addObject('Part::Feature', 'tmpBndboxCutByBase')
CA.Shape = cutArea
CA.purgeTouched()
tmpGrp.addObject(CA)
# Get top and bottom faces of cut area (CA), and combine faces when necessary
topFc = list()
botFc = list()
bbZMax = CA.Shape.BoundBox.ZMax
bbZMin = CA.Shape.BoundBox.ZMin
for f in range(0, len(CA.Shape.Faces)):
Fc = CA.Shape.Faces[f]
if abs(Fc.BoundBox.ZMax - bbZMax) < tolerance and abs(Fc.BoundBox.ZMin - bbZMax) < tolerance:
bbZMax = cutArea.BoundBox.ZMax
bbZMin = cutArea.BoundBox.ZMin
for f in range(0, len(cutArea.Faces)):
FcBB = cutArea.Faces[f].BoundBox
if abs(FcBB.ZMax - bbZMax) < tolerance and abs(FcBB.ZMin - bbZMax) < tolerance:
topFc.append(f)
if abs(Fc.BoundBox.ZMax - bbZMin) < tolerance and abs(Fc.BoundBox.ZMin - bbZMin) < tolerance:
if abs(FcBB.ZMax - bbZMin) < tolerance and abs(FcBB.ZMin - bbZMin) < tolerance:
botFc.append(f)
topComp = Part.makeCompound([CA.Shape.Faces[f] for f in topFc])
if len(topFc) == 0:
PathLog.error('Failed to identify top faces of cut area.')
return False
topComp = Part.makeCompound([cutArea.Faces[f] for f in topFc])
topComp.translate(FreeCAD.Vector(0, 0, fdv - topComp.BoundBox.ZMin)) # Translate face to final depth
if len(botFc) > 1:
PathLog.debug('len(botFc) > 1')
bndboxFace = Part.Face(extBndbox.Shape.Wires[0])
tmpFace = Part.Face(extBndbox.Shape.Wires[0])
for f in botFc:
Q = tmpFace.cut(CA.Shape.Faces[f])
Q = tmpFace.cut(cutArea.Faces[f])
tmpFace = Q
botComp = bndboxFace.cut(tmpFace)
else:
botComp = Part.makeCompound([CA.Shape.Faces[f] for f in botFc])
botComp = Part.makeCompound([cutArea.Faces[f] for f in botFc]) # Part.makeCompound([CA.Shape.Faces[f] for f in botFc])
botComp.translate(FreeCAD.Vector(0, 0, fdv - botComp.BoundBox.ZMin)) # Translate face to final depth
# Convert compound shapes to FC objects for use in multicommon operation
+46 -25
View File
@@ -3,7 +3,6 @@
# ***************************************************************************
# * *
# * Copyright (c) 2014 Yorik van Havre <[email protected]> *
# * Copyright (c) 2020 russ4262 (Russell Johnson) *
# * Copyright (c) 2020 Schildkroet *
# * *
# * This program is free software; you can redistribute it and/or modify *
@@ -43,6 +42,7 @@ __doc__ = "Path Profile operation based on faces."
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
@@ -132,22 +132,42 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
rtn = False
(norm, surf) = self.getFaceNormAndSurf(shape)
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("initial faceRotationAnalysis: {}".format(praInfo))
if rtn is True:
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, subCount)
# Verify faces are correctly oriented - InverseAngle might be necessary
faceIA = getattr(clnBase.Shape, sub)
(norm, surf) = self.getFaceNormAndSurf(faceIA)
(rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
(rtn, praAngle, praAxis, praInfo2) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("follow-up faceRotationAnalysis: {}".format(praInfo2))
if abs(praAngle) == 180.0:
rtn = False
if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp 1 is False')
angle -= 180.0
if rtn is True:
PathLog.error(translate("Path", "Face appears misaligned after initial rotation."))
if obj.AttemptInverseAngle is True and obj.InverseAngle is False:
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
if obj.InverseAngle is False:
if obj.AttemptInverseAngle is True:
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
else:
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.warning(msg)
if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp 2 is False')
angle += 180.0
else:
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.error(msg)
PathLog.debug(' isFaceUp')
else:
PathLog.debug("Face appears to be oriented correctly.")
if angle < 0.0:
angle += 360.0
tup = clnBase, sub, tag, angle, axis, clnStock
else:
if self.warnDisabledAxis(obj, axis) is False:
@@ -157,21 +177,21 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
tag = base.Name + '_' + axis + str(angle).replace('.', '_')
stock = PathUtils.findParentJob(obj).Stock
tup = base, sub, tag, angle, axis, stock
allTuples.append(tup)
if subCount > 1:
msg = translate('Path', "Multiple faces in Base Geometry.") + " "
msg += translate('Path', "Depth settings will be applied to all faces.")
PathLog.warning(msg)
(Tags, Grps) = self.sortTuplesByIndex(allTuples, 2) # return (TagList, GroupList)
subList = []
for o in range(0, len(Tags)):
subList = []
for (base, sub, tag, angle, axis, stock) in Grps[o]:
subList.append(sub)
pair = base, subList, angle, axis, stock
baseSubsTuples.append(pair)
# Efor
@@ -196,7 +216,7 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face
for wire in shape.Wires[1:]:
holes.append((base.Shape, wire))
# Add face depth to list
faceDepths.append(shape.BoundBox.ZMin)
else:
@@ -205,13 +225,12 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
PathLog.error(msg)
FreeCAD.Console.PrintWarning(msg)
# Set initial Start and Final Depths and recalculate depthparams
finDep = obj.FinalDepth.Value
strDep = obj.StartDepth.Value
if strDep > stock.Shape.BoundBox.ZMax:
strDep = stock.Shape.BoundBox.ZMax
startDepths.append(strDep)
self.depthparams = self._customDepthParams(obj, strDep, finDep)
@@ -230,31 +249,34 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
if obj.processPerimeter:
if obj.HandleMultipleFeatures == 'Collectively':
custDepthparams = self.depthparams
if obj.LimitDepthToFace is True and obj.EnableRotation != 'Off':
if profileshape.BoundBox.ZMin > obj.FinalDepth.Value:
finDep = profileshape.BoundBox.ZMin
custDepthparams = self._customDepthParams(obj, strDep, finDep - 0.5) # only an envelope
envDepthparams = self._customDepthParams(obj, strDep + 0.5, finDep) # only an envelope
try:
env = PathUtils.getEnvelope(base.Shape, subshape=profileshape, depthparams=custDepthparams)
# env = PathUtils.getEnvelope(base.Shape, subshape=profileshape, depthparams=envDepthparams)
env = PathUtils.getEnvelope(profileshape, depthparams=envDepthparams)
except Exception: # pylint: disable=broad-except
# PathUtils.getEnvelope() failed to return an object.
PathLog.error(translate('Path', 'Unable to create path for face(s).'))
else:
tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep
shapes.append(tup)
elif obj.HandleMultipleFeatures == 'Individually':
for shape in faces:
profShape = Part.makeCompound([shape])
# profShape = Part.makeCompound([shape])
finalDep = obj.FinalDepth.Value
custDepthparams = self.depthparams
if obj.Side == 'Inside':
if finalDep < shape.BoundBox.ZMin:
# Recalculate depthparams
finalDep = shape.BoundBox.ZMin
custDepthparams = self._customDepthParams(obj, strDep, finalDep - 0.5)
env = PathUtils.getEnvelope(base.Shape, subshape=profShape, depthparams=custDepthparams)
custDepthparams = self._customDepthParams(obj, strDep + 0.5, finalDep)
# env = PathUtils.getEnvelope(base.Shape, subshape=profShape, depthparams=custDepthparams)
env = PathUtils.getEnvelope(shape, depthparams=custDepthparams)
tup = env, False, 'pathProfileFaces', angle, axis, strDep, finalDep
shapes.append(tup)
@@ -262,11 +284,11 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
startDepth = max(startDepths)
if obj.StartDepth.Value > startDepth:
obj.StartDepth.Value = startDepth
else: # Try to build targets from the job base
if 1 == len(self.model):
if hasattr(self.model[0], "Proxy"):
PathLog.info("hasattr() Proxy")
PathLog.debug("hasattr() Proxy")
if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet
if obj.processCircles or obj.processHoles:
for shape in self.model[0].Proxy.getHoles(self.model[0], transform=True):
@@ -302,7 +324,7 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
obj.InverseAngle = False
obj.AttemptInverseAngle = True
obj.LimitDepthToFace = True
obj.HandleMultipleFeatures = 'Collectively'
obj.HandleMultipleFeatures = 'Individually'
def SetupProperties():
@@ -321,6 +343,5 @@ def Create(name, obj=None):
'''Create(name) ... Creates and returns a Profile based on faces operation.'''
if obj is None:
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
obj.Proxy = ObjectProfile(obj, name)
return obj
+27 -3
View File
@@ -30,12 +30,14 @@ import PathScripts.PathUtils as PathUtils
import math
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
class PathBaseGate(object):
# pylint: disable=no-init
pass
class EGate(PathBaseGate):
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
return sub and sub[0:4] == 'Edge'
@@ -66,6 +68,7 @@ class ENGRAVEGate(PathBaseGate):
return False
class CHAMFERGate(PathBaseGate):
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
try:
@@ -94,7 +97,7 @@ class DRILLGate(PathBaseGate):
if hasattr(obj, "Shape") and sub:
shape = obj.Shape
subobj = shape.getElement(sub)
return PathUtils.isDrillable(shape, subobj, includePartials = True)
return PathUtils.isDrillable(shape, subobj, includePartials=True)
else:
return False
@@ -159,6 +162,7 @@ class POCKETGate(PathBaseGate):
return pocketable
class ADAPTIVEGate(PathBaseGate):
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
@@ -167,45 +171,58 @@ class ADAPTIVEGate(PathBaseGate):
obj = obj.Shape
except Exception: # pylint: disable=broad-except
return False
return adaptive
class CONTOURGate(PathBaseGate):
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
pass
class PROBEGate:
def allow(self, doc, obj, sub):
pass
def contourselect():
FreeCADGui.Selection.addSelectionGate(CONTOURGate())
FreeCAD.Console.PrintWarning("Contour Select Mode\n")
def eselect():
FreeCADGui.Selection.addSelectionGate(EGate())
FreeCAD.Console.PrintWarning("Edge Select Mode\n")
def drillselect():
FreeCADGui.Selection.addSelectionGate(DRILLGate())
FreeCAD.Console.PrintWarning("Drilling Select Mode\n")
def engraveselect():
FreeCADGui.Selection.addSelectionGate(ENGRAVEGate())
FreeCAD.Console.PrintWarning("Engraving Select Mode\n")
def chamferselect():
FreeCADGui.Selection.addSelectionGate(CHAMFERGate())
FreeCAD.Console.PrintWarning("Deburr Select Mode\n")
def profileselect():
FreeCADGui.Selection.addSelectionGate(PROFILEGate())
FreeCAD.Console.PrintWarning("Profiling Select Mode\n")
def pocketselect():
FreeCADGui.Selection.addSelectionGate(POCKETGate())
FreeCAD.Console.PrintWarning("Pocketing Select Mode\n")
def adaptiveselect():
FreeCADGui.Selection.addSelectionGate(ADAPTIVEGate())
FreeCAD.Console.PrintWarning("Adaptive Select Mode\n")
def surfaceselect():
if(MESHGate() is True or PROFILEGate() is True):
FreeCADGui.Selection.addSelectionGate(True)
@@ -215,6 +232,10 @@ def surfaceselect():
# FreeCADGui.Selection.addSelectionGate(PROFILEGate()) # Added for face selection
FreeCAD.Console.PrintWarning("Surfacing Select Mode\n")
def probeselect():
FreeCADGui.Selection.addSelectionGate(PROBEGate())
FreeCAD.Console.PrintWarning("Probe Select Mode\n")
def select(op):
opsel = {}
opsel['Contour'] = contourselect
@@ -229,9 +250,12 @@ def select(op):
opsel['Profile Edges'] = eselect
opsel['Profile Faces'] = profileselect
opsel['Surface'] = surfaceselect
opsel['Waterline'] = surfaceselect
opsel['Adaptive'] = adaptiveselect
opsel['Probe'] = probeselect
return opsel[op]
def clear():
FreeCADGui.Selection.removeSelectionGate()
FreeCAD.Console.PrintWarning("Free Select\n")
@@ -149,6 +149,7 @@ class OpPrototype(object):
'App::PropertyBool': PropertyBool,
'App::PropertyDistance': PropertyDistance,
'App::PropertyEnumeration': PropertyEnumeration,
'App::PropertyFile': PropertyString,
'App::PropertyFloat': PropertyFloat,
'App::PropertyFloatConstraint': Property,
'App::PropertyFloatList': Property,
@@ -188,6 +188,18 @@ class _PropertyFloatEditor(_PropertyEditor):
def setModelData(self, widget):
self.prop.setValue(widget.value())
class _PropertyFileEditor(_PropertyEditor):
def widget(self, parent):
return QtGui.QLineEdit(parent)
def setEditorData(self, widget):
text = '' if self.prop.getValue() is None else self.prop.getValue()
widget.setText(text)
def setModelData(self, widget):
self.prop.setValue(widget.text())
_EditorFactory = {
PathSetupSheetOpPrototype.Property: None,
PathSetupSheetOpPrototype.PropertyAngle: _PropertyAngleEditor,
File diff suppressed because it is too large Load Diff
+63 -32
View File
@@ -39,89 +39,120 @@ __doc__ = "Surface operation page controller and command implementation."
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Page controller class for the Surface operation.'''
def initPage(self, obj):
self.setTitle("3D Surface")
self.updateVisibility()
def getForm(self):
'''getForm() ... returns UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpSurfaceEdit.ui")
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
self.updateToolController(obj, self.form.toolController)
self.updateCoolant(obj, self.form.coolantController)
PathGui.updateInputField(obj, 'DepthOffset', self.form.depthOffset)
PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
if obj.StepOver != self.form.stepOver.value():
obj.StepOver = self.form.stepOver.value()
if obj.Algorithm != str(self.form.algorithmSelect.currentText()):
obj.Algorithm = str(self.form.algorithmSelect.currentText())
if obj.BoundBox != str(self.form.boundBoxSelect.currentText()):
obj.BoundBox = str(self.form.boundBoxSelect.currentText())
if obj.DropCutterDir != str(self.form.dropCutterDirSelect.currentText()):
obj.DropCutterDir = str(self.form.dropCutterDirSelect.currentText())
if obj.ScanType != str(self.form.scanType.currentText()):
obj.ScanType = str(self.form.scanType.currentText())
if obj.StepOver != self.form.stepOver.value():
obj.StepOver = self.form.stepOver.value()
if obj.LayerMode != str(self.form.layerMode.currentText()):
obj.LayerMode = str(self.form.layerMode.currentText())
if obj.CutPattern != str(self.form.cutPattern.currentText()):
obj.CutPattern = str(self.form.cutPattern.currentText())
obj.DropCutterExtraOffset.x = FreeCAD.Units.Quantity(self.form.boundBoxExtraOffsetX.text()).Value
obj.DropCutterExtraOffset.y = FreeCAD.Units.Quantity(self.form.boundBoxExtraOffsetY.text()).Value
if obj.DropCutterDir != str(self.form.dropCutterDirSelect.currentText()):
obj.DropCutterDir = str(self.form.dropCutterDirSelect.currentText())
PathGui.updateInputField(obj, 'DepthOffset', self.form.depthOffset)
PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
if obj.UseStartPoint != self.form.useStartPoint.isChecked():
obj.UseStartPoint = self.form.useStartPoint.isChecked()
if obj.OptimizeLinearPaths != self.form.optimizeEnabled.isChecked():
obj.OptimizeLinearPaths = self.form.optimizeEnabled.isChecked()
self.updateToolController(obj, self.form.toolController)
self.updateCoolant(obj, self.form.coolantController)
if obj.OptimizeStepOverTransitions != self.form.optimizeStepOverTransitions.isChecked():
obj.OptimizeStepOverTransitions = self.form.optimizeStepOverTransitions.isChecked()
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
self.selectInComboBox(obj.Algorithm, self.form.algorithmSelect)
self.setupToolController(obj, self.form.toolController)
self.setupCoolant(obj, self.form.coolantController)
self.selectInComboBox(obj.BoundBox, self.form.boundBoxSelect)
self.selectInComboBox(obj.ScanType, self.form.scanType)
self.selectInComboBox(obj.LayerMode, self.form.layerMode)
self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
self.form.boundBoxExtraOffsetX.setText(FreeCAD.Units.Quantity(obj.DropCutterExtraOffset.x, FreeCAD.Units.Length).UserString)
self.form.boundBoxExtraOffsetY.setText(FreeCAD.Units.Quantity(obj.DropCutterExtraOffset.y, FreeCAD.Units.Length).UserString)
self.selectInComboBox(obj.DropCutterDir, self.form.dropCutterDirSelect)
self.form.boundBoxExtraOffsetX.setText(str(obj.DropCutterExtraOffset.x))
self.form.boundBoxExtraOffsetY.setText(str(obj.DropCutterExtraOffset.y))
self.form.depthOffset.setText(FreeCAD.Units.Quantity(obj.DepthOffset.Value, FreeCAD.Units.Length).UserString)
self.form.sampleInterval.setText(str(obj.SampleInterval))
self.form.stepOver.setValue(obj.StepOver)
self.form.sampleInterval.setText(FreeCAD.Units.Quantity(obj.SampleInterval.Value, FreeCAD.Units.Length).UserString)
if obj.UseStartPoint:
self.form.useStartPoint.setCheckState(QtCore.Qt.Checked)
else:
self.form.useStartPoint.setCheckState(QtCore.Qt.Unchecked)
if obj.OptimizeLinearPaths:
self.form.optimizeEnabled.setCheckState(QtCore.Qt.Checked)
else:
self.form.optimizeEnabled.setCheckState(QtCore.Qt.Unchecked)
self.setupToolController(obj, self.form.toolController)
self.setupCoolant(obj, self.form.coolantController)
if obj.OptimizeStepOverTransitions:
self.form.optimizeStepOverTransitions.setCheckState(QtCore.Qt.Checked)
else:
self.form.optimizeStepOverTransitions.setCheckState(QtCore.Qt.Unchecked)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.toolController.currentIndexChanged)
signals.append(self.form.algorithmSelect.currentIndexChanged)
signals.append(self.form.coolantController.currentIndexChanged)
signals.append(self.form.boundBoxSelect.currentIndexChanged)
signals.append(self.form.dropCutterDirSelect.currentIndexChanged)
signals.append(self.form.scanType.currentIndexChanged)
signals.append(self.form.layerMode.currentIndexChanged)
signals.append(self.form.cutPattern.currentIndexChanged)
signals.append(self.form.boundBoxExtraOffsetX.editingFinished)
signals.append(self.form.boundBoxExtraOffsetY.editingFinished)
signals.append(self.form.sampleInterval.editingFinished)
signals.append(self.form.stepOver.editingFinished)
signals.append(self.form.dropCutterDirSelect.currentIndexChanged)
signals.append(self.form.depthOffset.editingFinished)
signals.append(self.form.stepOver.editingFinished)
signals.append(self.form.sampleInterval.editingFinished)
signals.append(self.form.useStartPoint.stateChanged)
signals.append(self.form.optimizeEnabled.stateChanged)
signals.append(self.form.coolantController.currentIndexChanged)
signals.append(self.form.optimizeStepOverTransitions.stateChanged)
return signals
def updateVisibility(self):
if self.form.algorithmSelect.currentText() == "OCL Dropcutter":
self.form.boundBoxExtraOffsetX.setEnabled(True)
self.form.boundBoxExtraOffsetY.setEnabled(True)
self.form.boundBoxSelect.setEnabled(True)
self.form.dropCutterDirSelect.setEnabled(True)
self.form.stepOver.setEnabled(True)
else:
if self.form.scanType.currentText() == "Planar":
self.form.cutPattern.setEnabled(True)
self.form.boundBoxExtraOffsetX.setEnabled(False)
self.form.boundBoxExtraOffsetY.setEnabled(False)
self.form.boundBoxSelect.setEnabled(False)
self.form.dropCutterDirSelect.setEnabled(False)
self.form.stepOver.setEnabled(False)
else:
self.form.cutPattern.setEnabled(False)
self.form.boundBoxExtraOffsetX.setEnabled(True)
self.form.boundBoxExtraOffsetY.setEnabled(True)
self.form.dropCutterDirSelect.setEnabled(True)
def registerSignalHandlers(self, obj):
self.form.algorithmSelect.currentIndexChanged.connect(self.updateVisibility)
self.form.scanType.currentIndexChanged.connect(self.updateVisibility)
Command = PathOpGui.SetupOperation('Surface',
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2020 sliptonic <[email protected]> *
# * Copyright (c) 2020 russ4262 <[email protected]> *
# * *
# * 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
import FreeCADGui
import PathScripts.PathWaterline as PathWaterline
import PathScripts.PathGui as PathGui
import PathScripts.PathOpGui as PathOpGui
from PySide import QtCore
__title__ = "Path Waterline Operation UI"
__author__ = "sliptonic (Brad Collette), russ4262 (Russell Johnson)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Waterline operation page controller and command implementation."
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Page controller class for the Waterline operation.'''
def initPage(self, obj):
# self.setTitle("Waterline")
self.updateVisibility()
def getForm(self):
'''getForm() ... returns UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpWaterlineEdit.ui")
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
self.updateToolController(obj, self.form.toolController)
if obj.Algorithm != str(self.form.algorithmSelect.currentText()):
obj.Algorithm = str(self.form.algorithmSelect.currentText())
if obj.BoundBox != str(self.form.boundBoxSelect.currentText()):
obj.BoundBox = str(self.form.boundBoxSelect.currentText())
if obj.LayerMode != str(self.form.layerMode.currentText()):
obj.LayerMode = str(self.form.layerMode.currentText())
if obj.CutPattern != str(self.form.cutPattern.currentText()):
obj.CutPattern = str(self.form.cutPattern.currentText())
PathGui.updateInputField(obj, 'BoundaryAdjustment', self.form.boundaryAdjustment)
if obj.StepOver != self.form.stepOver.value():
obj.StepOver = self.form.stepOver.value()
PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
if obj.OptimizeLinearPaths != self.form.optimizeEnabled.isChecked():
obj.OptimizeLinearPaths = self.form.optimizeEnabled.isChecked()
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
self.setupToolController(obj, self.form.toolController)
self.selectInComboBox(obj.Algorithm, self.form.algorithmSelect)
self.selectInComboBox(obj.BoundBox, self.form.boundBoxSelect)
self.selectInComboBox(obj.LayerMode, self.form.layerMode)
self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
self.form.boundaryAdjustment.setText(FreeCAD.Units.Quantity(obj.BoundaryAdjustment.Value, FreeCAD.Units.Length).UserString)
self.form.stepOver.setValue(obj.StepOver)
self.form.sampleInterval.setText(FreeCAD.Units.Quantity(obj.SampleInterval.Value, FreeCAD.Units.Length).UserString)
if obj.OptimizeLinearPaths:
self.form.optimizeEnabled.setCheckState(QtCore.Qt.Checked)
else:
self.form.optimizeEnabled.setCheckState(QtCore.Qt.Unchecked)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.toolController.currentIndexChanged)
signals.append(self.form.algorithmSelect.currentIndexChanged)
signals.append(self.form.boundBoxSelect.currentIndexChanged)
signals.append(self.form.layerMode.currentIndexChanged)
signals.append(self.form.cutPattern.currentIndexChanged)
signals.append(self.form.boundaryAdjustment.editingFinished)
signals.append(self.form.stepOver.editingFinished)
signals.append(self.form.sampleInterval.editingFinished)
signals.append(self.form.optimizeEnabled.stateChanged)
return signals
def updateVisibility(self):
if self.form.algorithmSelect.currentText() == 'OCL Dropcutter':
self.form.cutPattern.setEnabled(False)
self.form.boundaryAdjustment.setEnabled(False)
self.form.stepOver.setEnabled(False)
self.form.sampleInterval.setEnabled(True)
self.form.optimizeEnabled.setEnabled(True)
else:
self.form.cutPattern.setEnabled(True)
self.form.boundaryAdjustment.setEnabled(True)
if self.form.cutPattern.currentText() == 'None':
self.form.stepOver.setEnabled(False)
else:
self.form.stepOver.setEnabled(True)
self.form.sampleInterval.setEnabled(False)
self.form.optimizeEnabled.setEnabled(False)
def registerSignalHandlers(self, obj):
self.form.algorithmSelect.currentIndexChanged.connect(self.updateVisibility)
self.form.cutPattern.currentIndexChanged.connect(self.updateVisibility)
Command = PathOpGui.SetupOperation('Waterline',
PathWaterline.Create,
TaskPanelOpPage,
'Path-Waterline',
QtCore.QT_TRANSLATE_NOOP("Waterline", "Waterline"),
QtCore.QT_TRANSLATE_NOOP("Waterline", "Create a Waterline Operation from a model"),
PathWaterline.SetupProperties)
FreeCAD.Console.PrintLog("Loading PathWaterlineGui... done\n")
+6 -7
View File
@@ -73,13 +73,11 @@ def insert(filename, docname):
def parse(inputstring):
"parse(inputstring): returns a parsed output string"
print("preprocessing...")
print(inputstring)
PathLog.track(inputstring)
# split the input by line
lines = inputstring.split("\n")
output = ""
lastcommand = None
print(lines)
output = [] #""
lastcommand = None
for lin in lines:
# remove any leftover trailing and preceding spaces
@@ -91,7 +89,7 @@ def parse(inputstring):
# remove line numbers
lin = lin.split(" ", 1)
if len(lin) >= 1:
lin = lin[1]
lin = lin[1].strip()
else:
continue
@@ -100,7 +98,8 @@ def parse(inputstring):
continue
if lin[0].upper() in ["G", "M"]:
# found a G or M command: we store it
output += lin + "\n"
#output += lin + "\n"
output.append(Path.Command(str(lin))) # + "\n"
last = lin[0].upper()
for c in lin[1:]:
if not c.isdigit():
@@ -110,7 +109,7 @@ def parse(inputstring):
lastcommand = last
elif lastcommand:
# no G or M command: we repeat the last one
output += lastcommand + " " + lin + "\n"
output.append(Path.Command(str(lastcommand + " " + lin))) # + "\n"
print("done preprocessing.")
return output
+129
View File
@@ -0,0 +1,129 @@
# ***************************************************************************
# * (c) Yorik van Havre ([email protected]) 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
import Path
import FreeCAD
import PathScripts.PathUtils
import PathScripts.PathLog as PathLog
import re
# LEVEL = PathLog.Level.DEBUG
LEVEL = PathLog.Level.INFO
PathLog.setLevel(LEVEL, PathLog.thisModule())
if LEVEL == PathLog.Level.DEBUG:
PathLog.trackModule(PathLog.thisModule())
# to distinguish python built-in open function from the one declared below
if open.__module__ in ['__builtin__', 'io']:
pythonopen = open
def open(filename):
"called when freecad opens a file."
PathLog.track(filename)
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"
PathLog.track(filename)
gfile = pythonopen(filename)
gcode = gfile.read()
gfile.close()
# split on tool changes
paths = re.split('(?=[mM]+\s?0?6)', gcode)
# if there are any tool changes combine the preamble with the default tool
if len(paths) > 1:
paths = ["\n".join(paths[0:2])] + paths[2:]
for path in paths:
gcode = parse(path)
doc = FreeCAD.getDocument(docname)
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Custom")
PathScripts.PathCustom.ObjectCustom(obj)
obj.ViewObject.Proxy = 0
obj.Gcode = gcode
PathScripts.PathUtils.addToJob(obj)
obj.ToolController = PathScripts.PathUtils.findToolController(obj)
FreeCAD.ActiveDocument.recompute()
def parse(inputstring):
"parse(inputstring): returns a parsed output string"
print("preprocessing...")
PathLog.track(inputstring)
# split the input by line
lines = inputstring.split("\n")
output = [] #""
lastcommand = None
for lin in lines:
# remove any leftover trailing and preceding spaces
lin = lin.strip()
if not lin:
# discard empty lines
continue
if lin[0].upper() in ["N"]:
# remove line numbers
lin = lin.split(" ", 1)
if len(lin) >= 1:
lin = lin[1].strip()
else:
continue
if lin[0] in ["(", "%", "#", ";"]:
# discard comment and other non strictly gcode lines
continue
if lin[0].upper() in ["G", "M"]:
# found a G or M command: we store it
#output += lin + "\n"
output.append(lin) # + "\n"
last = lin[0].upper()
for c in lin[1:]:
if not c.isdigit():
break
else:
last += c
lastcommand = last
elif lastcommand:
# no G or M command: we repeat the last one
output.append(lastcommand + " " + lin) # + "\n"
print("done preprocessing.")
return output
print(__name__ + " gcode preprocessor loaded.")
+1 -1
View File
@@ -261,7 +261,7 @@ def export(objectslist, filename, argstring):
return
# Skip inactive operations
if not PathUtil.opProperty(obj, 'Active'):
if PathUtil.opProperty(obj, 'Active') is False:
continue
# do the pre_op
+3 -2
View File
@@ -1,8 +1,9 @@
# -*- coding: utf8 -*-
# FreeCAD init script of the Raytracing module
# (c) 2001 Jürgen Riegel
# (c) 2001 Juergen Riegel
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <[email protected]> *
#* Copyright (c) 2002 Juergen Riegel <[email protected]> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
+2 -2
View File
@@ -1,12 +1,12 @@
# Raytracing gui init module
# (c) 2003 Jürgen Riegel
# (c) 2003 Juergen Riegel
#
# Gathering all the information to start FreeCAD
# This is the second one of three init scripts, the third one
# runs when the gui is up
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <[email protected]> *
#* Copyright (c) 2002 Juergen Riegel <[email protected]> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
+3 -2
View File
@@ -1,8 +1,9 @@
# -*- coding: utf8 -*-
# FreeCAD init script of the ReverseEngineering module
# (c) 2001 Jürgen Riegel
# (c) 2001 Juergen Riegel
# ***************************************************************************
# * Copyright (c) 2002 Jürgen Riegel <[email protected]> *
# * Copyright (c) 2002 Juergen Riegel <[email protected]> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
+2 -2
View File
@@ -1,12 +1,12 @@
# ReverseEngineering gui init module
# (c) 2003 Jürgen Riegel
# (c) 2003 Juergen Riegel
#
# Gathering all the information to start FreeCAD
# This is the second one of three init scripts, the third one
# runs when the gui is up
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <[email protected]> *
#* Copyright (c) 2002 Juergen Riegel <[email protected]> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
+2 -2
View File
@@ -1,8 +1,8 @@
# FreeCAD init script of the Robot module
# (c) 2001 Jürgen Riegel
# (c) 2001 Juergen Riegel
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <[email protected]> *
#* Copyright (c) 2002 Juergen Riegel <[email protected]> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *

Some files were not shown because too many files have changed in this diff Show More