Merge remote-tracking branch 'origin/master' into step

This commit is contained in:
Jean-Marie Verdun 2017-01-08 21:55:46 +01:00
commit 358238f745
48 changed files with 2715 additions and 598 deletions

View File

@ -1036,7 +1036,7 @@ bool Application::activateWorkbench(const char* name)
} }
Base::Console().Error("%s\n", (const char*)msg.toLatin1()); Base::Console().Error("%s\n", (const char*)msg.toLatin1());
Base::Console().Log("%s\n", e.getStackTrace().c_str()); Base::Console().Error("%s\n", e.getStackTrace().c_str());
if (!d->startingUp) { if (!d->startingUp) {
wc.restoreCursor(); wc.restoreCursor();
QMessageBox::critical(getMainWindow(), QObject::tr("Workbench failure"), QMessageBox::critical(getMainWindow(), QObject::tr("Workbench failure"),
@ -1279,10 +1279,9 @@ typedef void (*_qt_msg_handler_old)(QtMsgType type, const char *msg);
_qt_msg_handler_old old_qtmsg_handler = 0; _qt_msg_handler_old old_qtmsg_handler = 0;
#if QT_VERSION >= 0x050000 #if QT_VERSION >= 0x050000
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &qmsg) void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{ {
Q_UNUSED(context); Q_UNUSED(context);
const QChar *msg = qmsg.unicode();
#ifdef FC_DEBUG #ifdef FC_DEBUG
switch (type) switch (type)
{ {
@ -1290,26 +1289,26 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
case QtInfoMsg: case QtInfoMsg:
#endif #endif
case QtDebugMsg: case QtDebugMsg:
Base::Console().Message("%s\n", msg); Base::Console().Message("%s\n", msg.toUtf8().constData());
break; break;
case QtWarningMsg: case QtWarningMsg:
Base::Console().Warning("%s\n", msg); Base::Console().Warning("%s\n", msg.toUtf8().constData());
break; break;
case QtCriticalMsg: case QtCriticalMsg:
Base::Console().Error("%s\n", msg); Base::Console().Error("%s\n", msg.toUtf8().constData());
break; break;
case QtFatalMsg: case QtFatalMsg:
Base::Console().Error("%s\n", msg); Base::Console().Error("%s\n", msg.toUtf8().constData());
abort(); // deliberately core dump abort(); // deliberately core dump
} }
#ifdef FC_OS_WIN32 #ifdef FC_OS_WIN32
if (old_qtmsg_handler) if (old_qtmsg_handler)
(*old_qtmsg_handler)(type, context, qmsg); (*old_qtmsg_handler)(type, context, msg);
#endif #endif
#else #else
// do not stress user with Qt internals but write to log file if enabled // do not stress user with Qt internals but write to log file if enabled
Q_UNUSED(type); Q_UNUSED(type);
Base::Console().Log("%s\n", msg); Base::Console().Log("%s\n", msg.toUtf8().constData());
#endif #endif
} }
#else #else

View File

@ -69,7 +69,7 @@ public:
void pause(); void pause();
void resume(); void resume();
private slots: private Q_SLOTS:
void readClient(); void readClient();
void discardClient(); void discardClient();

View File

@ -195,7 +195,7 @@ void TaskHeader::leaveEvent ( QEvent * /*event*/ )
void TaskHeader::fold() void TaskHeader::fold()
{ {
if (myExpandable) { if (myExpandable) {
emit activated(); Q_EMIT activated();
// Toggling the 'm_fold' member here may lead to inconsistencies with its ActionGroup. // Toggling the 'm_fold' member here may lead to inconsistencies with its ActionGroup.
// Thus, the method setFold() was added and called from ActionGroup when required. // Thus, the method setFold() was added and called from ActionGroup when required.
#if 0 #if 0
@ -254,7 +254,7 @@ void TaskHeader::changeIcons()
void TaskHeader::mouseReleaseEvent ( QMouseEvent * event ) void TaskHeader::mouseReleaseEvent ( QMouseEvent * event )
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
emit activated(); Q_EMIT activated();
} }
} }

View File

@ -50,7 +50,7 @@ public:
QMenu * getMenu(void) const; QMenu * getMenu(void) const;
public slots: public Q_SLOTS:
void changeRenderMode(QAction * action); void changeRenderMode(QAction * action);
void changeStereoMode(QAction * action); void changeStereoMode(QAction * action);
void changeTransparencyType(QAction * action); void changeTransparencyType(QAction * action);

View File

@ -48,7 +48,7 @@ public:
SensorManager(void); SensorManager(void);
~SensorManager(); ~SensorManager();
public slots: public Q_SLOTS:
void idleTimeout(void); void idleTimeout(void);
void delayTimeout(void); void delayTimeout(void);
void timerQueueTimeout(void); void timerQueueTimeout(void);

View File

@ -70,7 +70,7 @@ SignalThread::run(void)
// just wait, and trigger every time we receive a signal // just wait, and trigger every time we receive a signal
this->waitcond.wait(&this->mutex); this->waitcond.wait(&this->mutex);
if (!this->isstopped) { if (!this->isstopped) {
emit triggerSignal(); Q_EMIT triggerSignal();
} }
} }
} }

View File

@ -162,10 +162,10 @@ void iisIconLabel::mousePressEvent ( QMouseEvent * event )
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
m_pressed = true; m_pressed = true;
emit pressed(); Q_EMIT pressed();
} else } else
if (event->button() == Qt::RightButton) if (event->button() == Qt::RightButton)
emit contextMenu(); Q_EMIT contextMenu();
update(); update();
} }
@ -174,11 +174,11 @@ void iisIconLabel::mouseReleaseEvent ( QMouseEvent * event )
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
m_pressed = false; m_pressed = false;
emit released(); Q_EMIT released();
if (rect().contains( event->pos() )) { if (rect().contains( event->pos() )) {
emit clicked(); Q_EMIT clicked();
emit activated(); Q_EMIT activated();
} }
} }
@ -190,7 +190,7 @@ void iisIconLabel::keyPressEvent ( QKeyEvent * event )
switch (event->key()) { switch (event->key()) {
case Qt::Key_Space: case Qt::Key_Space:
case Qt::Key_Return: case Qt::Key_Return:
emit activated(); Q_EMIT activated();
break; break;
default:; default:;

View File

@ -165,7 +165,7 @@ void iisTaskHeader::leaveEvent ( QEvent * /*event*/ )
void iisTaskHeader::fold() void iisTaskHeader::fold()
{ {
if (myExpandable) { if (myExpandable) {
emit activated(); Q_EMIT activated();
m_fold = !m_fold; m_fold = !m_fold;
changeIcons(); changeIcons();
@ -195,7 +195,7 @@ void iisTaskHeader::changeIcons()
void iisTaskHeader::mouseReleaseEvent ( QMouseEvent * event ) void iisTaskHeader::mouseReleaseEvent ( QMouseEvent * event )
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
emit activated(); Q_EMIT activated();
} }
} }

View File

@ -222,46 +222,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Dimensions precision level</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Gui::PrefSpinBox" name="gui::prefspinbox_5">
<property name="maximum">
<number>8</number>
</property>
<property name="value">
<number>2</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>dimPrecision</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_15"> <layout class="QHBoxLayout" name="horizontalLayout_15">
<item> <item>

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>522</width> <width>522</width>
<height>462</height> <height>473</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -153,6 +153,46 @@ such as &quot;Arial:Bold&quot;</string>
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_19">
<property name="text">
<string>Number of decimals</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Gui::PrefSpinBox" name="gui::prefspinbox_5">
<property name="maximum">
<number>8</number>
</property>
<property name="value">
<number>2</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>dimPrecision</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
@ -364,7 +404,7 @@ such as &quot;Arial:Bold&quot;</string>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="Gui::PrefFileChooser" name="gui::preffilechooser" native="true"> <widget class="Gui::PrefFileChooser" name="gui::preffilechooser">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>300</width> <width>300</width>
@ -405,6 +445,21 @@ such as &quot;Arial:Bold&quot;</string>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction> <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<customwidgets> <customwidgets>
<customwidget>
<class>Gui::FileChooser</class>
<extends>QWidget</extends>
<header>Gui/FileDialog.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFileChooser</class>
<extends>Gui::FileChooser</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefSpinBox</class>
<extends>QSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget> <customwidget>
<class>Gui::PrefCheckBox</class> <class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends> <extends>QCheckBox</extends>
@ -425,16 +480,6 @@ such as &quot;Arial:Bold&quot;</string>
<extends>QDoubleSpinBox</extends> <extends>QDoubleSpinBox</extends>
<header>Gui/PrefWidgets.h</header> <header>Gui/PrefWidgets.h</header>
</customwidget> </customwidget>
<customwidget>
<class>Gui::FileChooser</class>
<extends>QWidget</extends>
<header>Gui/FileDialog.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFileChooser</class>
<extends>Gui::FileChooser</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -776,6 +776,8 @@ non-parametric curve"""
warn('polygon fallback on %s' %spline) warn('polygon fallback on %s' %spline)
return drawSplineIterpolation(controlpoints,closed=closed,\ return drawSplineIterpolation(controlpoints,closed=closed,\
forceShape=forceShape,alwaysDiscretize=True) forceShape=forceShape,alwaysDiscretize=True)
if fitpoints and not(controlpoints):
return drawSplineIterpolation(fitpoints,closed=closed,forceShape=forceShape)
try: try:
bspline=Part.BSplineCurve() bspline=Part.BSplineCurve()
bspline.buildFromPolesMultsKnots(poles=controlpoints,mults=multvector,\ bspline.buildFromPolesMultsKnots(poles=controlpoints,mults=multvector,\

View File

@ -71,6 +71,7 @@ SET(FemScripts_SRCS
_CommandMaterial.py _CommandMaterial.py
_CommandMeshGmshFromShape.py _CommandMeshGmshFromShape.py
_CommandMeshNetgenFromShape.py _CommandMeshNetgenFromShape.py
_CommandMeshGroup.py
_CommandMeshRegion.py _CommandMeshRegion.py
_CommandPrintMeshInfo.py _CommandPrintMeshInfo.py
_CommandPurgeResults.py _CommandPurgeResults.py
@ -83,6 +84,7 @@ SET(FemScripts_SRCS
_FemConstraintSelfWeight.py _FemConstraintSelfWeight.py
_FemMaterialMechanicalNonlinear.py _FemMaterialMechanicalNonlinear.py
_FemMeshGmsh.py _FemMeshGmsh.py
_FemMeshGroup.py
_FemMeshRegion.py _FemMeshRegion.py
_FemShellThickness.py _FemShellThickness.py
_FemSolverCalculix.py _FemSolverCalculix.py
@ -90,6 +92,7 @@ SET(FemScripts_SRCS
_FemMaterial.py _FemMaterial.py
_TaskPanelFemBeamSection.py _TaskPanelFemBeamSection.py
_TaskPanelFemMeshGmsh.py _TaskPanelFemMeshGmsh.py
_TaskPanelFemMeshGroup.py
_TaskPanelFemMeshRegion.py _TaskPanelFemMeshRegion.py
_TaskPanelFemShellThickness.py _TaskPanelFemShellThickness.py
_TaskPanelFemSolverCalculix.py _TaskPanelFemSolverCalculix.py
@ -99,6 +102,7 @@ SET(FemScripts_SRCS
_ViewProviderFemConstraintSelfWeight.py _ViewProviderFemConstraintSelfWeight.py
_ViewProviderFemMaterialMechanicalNonlinear.py _ViewProviderFemMaterialMechanicalNonlinear.py
_ViewProviderFemMeshGmsh.py _ViewProviderFemMeshGmsh.py
_ViewProviderFemMeshGroup.py
_ViewProviderFemMeshRegion.py _ViewProviderFemMeshRegion.py
_ViewProviderFemShellThickness.py _ViewProviderFemShellThickness.py
_ViewProviderFemSolverCalculix.py _ViewProviderFemSolverCalculix.py
@ -122,6 +126,7 @@ SET(FemScripts_SRCS
FemMaterialMechanicalNonlinear.py FemMaterialMechanicalNonlinear.py
FemMesh2Mesh.py FemMesh2Mesh.py
FemMeshGmsh.py FemMeshGmsh.py
FemMeshGroup.py
FemMeshRegion.py FemMeshRegion.py
FemMeshTools.py FemMeshTools.py
FemShellThickness.py FemShellThickness.py
@ -136,6 +141,7 @@ SET(FemScripts_SRCS
z88DispReader.py z88DispReader.py
TaskPanelFemBeamSection.ui TaskPanelFemBeamSection.ui
TaskPanelFemMeshGmsh.ui TaskPanelFemMeshGmsh.ui
TaskPanelFemMeshGroup.ui
TaskPanelFemMeshRegion.ui TaskPanelFemMeshRegion.ui
TaskPanelFemShellThickness.ui TaskPanelFemShellThickness.ui
TaskPanelFemSolverCalculix.ui TaskPanelFemSolverCalculix.ui

View File

@ -42,6 +42,8 @@ FemResultObject::FemResultObject()
ADD_PROPERTY_TYPE(Stats,(0), "Fem",Prop_None,"Statistics of the results"); ADD_PROPERTY_TYPE(Stats,(0), "Fem",Prop_None,"Statistics of the results");
ADD_PROPERTY_TYPE(DisplacementVectors,(), "Fem",Prop_None,"List of displacement vectors"); ADD_PROPERTY_TYPE(DisplacementVectors,(), "Fem",Prop_None,"List of displacement vectors");
ADD_PROPERTY_TYPE(DisplacementLengths,(0), "Fem",Prop_None,"List of displacement lengths"); ADD_PROPERTY_TYPE(DisplacementLengths,(0), "Fem",Prop_None,"List of displacement lengths");
ADD_PROPERTY_TYPE(StressVectors,(), "Fem",Prop_None,"List of Stress vectors");
ADD_PROPERTY_TYPE(StrainVectors,(), "Fem",Prop_None,"List of Strain vectors");
ADD_PROPERTY_TYPE(StressValues,(0), "Fem",Prop_None,"List of Von Misses stress values"); ADD_PROPERTY_TYPE(StressValues,(0), "Fem",Prop_None,"List of Von Misses stress values");
ADD_PROPERTY_TYPE(PrincipalMax,(0), "Fem",Prop_None,"List of First Principal (Max) stress values"); ADD_PROPERTY_TYPE(PrincipalMax,(0), "Fem",Prop_None,"List of First Principal (Max) stress values");
ADD_PROPERTY_TYPE(PrincipalMed,(0), "Fem",Prop_None,"List of Second Principal (Med) stress values"); ADD_PROPERTY_TYPE(PrincipalMed,(0), "Fem",Prop_None,"List of Second Principal (Med) stress values");
@ -59,6 +61,8 @@ FemResultObject::FemResultObject()
Stats.setStatus(App::Property::ReadOnly, true); Stats.setStatus(App::Property::ReadOnly, true);
DisplacementVectors.setStatus(App::Property::ReadOnly, true); DisplacementVectors.setStatus(App::Property::ReadOnly, true);
DisplacementLengths.setStatus(App::Property::ReadOnly, true); DisplacementLengths.setStatus(App::Property::ReadOnly, true);
StressVectors.setStatus(App::Property::ReadOnly, true);
StrainVectors.setStatus(App::Property::ReadOnly, true);
StressValues.setStatus(App::Property::ReadOnly, true); StressValues.setStatus(App::Property::ReadOnly, true);
PrincipalMax.setStatus(App::Property::ReadOnly, true); PrincipalMax.setStatus(App::Property::ReadOnly, true);
PrincipalMed.setStatus(App::Property::ReadOnly, true); PrincipalMed.setStatus(App::Property::ReadOnly, true);

View File

@ -49,8 +49,12 @@ public:
App::PropertyFloatList Stats; App::PropertyFloatList Stats;
/// Displacement vectors of analysis /// Displacement vectors of analysis
App::PropertyVectorList DisplacementVectors; App::PropertyVectorList DisplacementVectors;
/// Lengths of displacement vestors of analysis /// Lengths of displacement vectors of analysis
App::PropertyFloatList DisplacementLengths; App::PropertyFloatList DisplacementLengths;
/// Stress vectors of analysis
App::PropertyVectorList StressVectors;
/// Strain vectors of analysis
App::PropertyVectorList StrainVectors;
/// Von Mises Stress values of analysis /// Von Mises Stress values of analysis
App::PropertyFloatList StressValues; App::PropertyFloatList StressValues;
/// First principal Stress values of analysis /// First principal Stress values of analysis

View File

@ -760,6 +760,7 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
const FemResultObject* res = static_cast<const FemResultObject*>(obj); const FemResultObject* res = static_cast<const FemResultObject*>(obj);
if(!res->StressValues.getValues().empty()) { if(!res->StressValues.getValues().empty()) {
const std::vector<double>& vec = res->StressValues.getValues(); const std::vector<double>& vec = res->StressValues.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfValues(vec.size()); data->SetNumberOfValues(vec.size());
data->SetName("Von Mises stress"); data->SetName("Von Mises stress");
@ -768,10 +769,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
data->SetValue(i, vec[i]); data->SetValue(i, vec[i]);
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
} }}
if(!res->StressValues.getValues().empty()) { if(!res->MaxShear.getValues().empty()) {
const std::vector<double>& vec = res->MaxShear.getValues(); const std::vector<double>& vec = res->MaxShear.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfValues(vec.size()); data->SetNumberOfValues(vec.size());
data->SetName("Max shear stress (Tresca)"); data->SetName("Max shear stress (Tresca)");
@ -780,10 +782,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
data->SetValue(i, vec[i]); data->SetValue(i, vec[i]);
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
} }}
if(!res->StressValues.getValues().empty()) { if(!res->PrincipalMax.getValues().empty()) {
const std::vector<double>& vec = res->PrincipalMax.getValues(); const std::vector<double>& vec = res->PrincipalMax.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfValues(vec.size()); data->SetNumberOfValues(vec.size());
data->SetName("Maximum Principal stress"); data->SetName("Maximum Principal stress");
@ -792,10 +795,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
data->SetValue(i, vec[i]); data->SetValue(i, vec[i]);
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
} }}
if(!res->StressValues.getValues().empty()) { if(!res->PrincipalMax.getValues().empty()) {
const std::vector<double>& vec = res->PrincipalMin.getValues(); const std::vector<double>& vec = res->PrincipalMin.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfValues(vec.size()); data->SetNumberOfValues(vec.size());
data->SetName("Minimum Principal stress"); data->SetName("Minimum Principal stress");
@ -804,10 +808,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
data->SetValue(i, vec[i]); data->SetValue(i, vec[i]);
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
} }}
if(!res->StressValues.getValues().empty()) { if (!res->Temperature.getValues().empty()) {
const std::vector<double>& vec = res->Temperature.getValues(); const std::vector<double>& vec = res->Temperature.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfValues(vec.size()); data->SetNumberOfValues(vec.size());
data->SetName("Temperature"); data->SetName("Temperature");
@ -816,10 +821,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
data->SetValue(i, vec[i]); data->SetValue(i, vec[i]);
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
} }}
if(!res->StressValues.getValues().empty()) { if (!res->UserDefined.getValues().empty()) {
const std::vector<double>& vec = res->UserDefined.getValues(); const std::vector<double>& vec = res->UserDefined.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfValues(vec.size()); data->SetNumberOfValues(vec.size());
data->SetName("User Defined Results"); data->SetName("User Defined Results");
@ -828,11 +834,12 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
data->SetValue(i, vec[i]); data->SetValue(i, vec[i]);
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
} }}
if(!res->StressValues.getValues().empty()) { if(!res->DisplacementVectors.getValues().empty()) {
const std::vector<Base::Vector3d>& vec = res->DisplacementVectors.getValues(); const std::vector<Base::Vector3d>& vec = res->DisplacementVectors.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New(); vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfComponents(3); data->SetNumberOfComponents(3);
data->SetName("Displacement"); data->SetName("Displacement");
@ -843,7 +850,38 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar
} }
grid->GetPointData()->AddArray(data); grid->GetPointData()->AddArray(data);
}}
if(!res->StressVectors.getValues().empty()) {
const std::vector<Base::Vector3d>& vec = res->StressVectors.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfComponents(3);
data->SetName("Stress Vectors");
for(std::vector<Base::Vector3d>::const_iterator it=vec.begin(); it!=vec.end(); ++it) {
double tuple[] = {it->x, it->y , it->z};
data->InsertNextTuple(tuple);
} }
grid->GetPointData()->AddArray(data);
}}
if(!res->StrainVectors.getValues().empty()) {
const std::vector<Base::Vector3d>& vec = res->StrainVectors.getValues();
if (vec.size()>1) {
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfComponents(3);
data->SetName("Strain Vectors");
for(std::vector<Base::Vector3d>::const_iterator it=vec.begin(); it!=vec.end(); ++it) {
double tuple[] = {it->x, it->y, it->z};
data->InsertNextTuple(tuple);
}
grid->GetPointData()->AddArray(data);
}}
} }
} // namespace } // namespace

View File

@ -55,6 +55,13 @@ INSTALL(
FemMesh2Mesh.py FemMesh2Mesh.py
_CommandFEMMesh2Mesh.py _CommandFEMMesh2Mesh.py
FemMeshGroup.py
_FemMeshGroup.py
_ViewProviderFemMeshGroup.py
_CommandMeshGroup.py
_TaskPanelFemMeshGroup.py
TaskPanelFemMeshGroup.ui
FemMeshRegion.py FemMeshRegion.py
_FemMeshRegion.py _FemMeshRegion.py
_ViewProviderFemMeshRegion.py _ViewProviderFemMeshRegion.py

View File

@ -215,17 +215,39 @@ class FemGmshTools():
print(' ' + self.gmsh_bin) print(' ' + self.gmsh_bin)
def get_group_data(self): def get_group_data(self):
self.group_elements = {}
# TODO solid, face, edge seam not work together, some print or make it work together
# TODO handle groups for Edges and Vertexes
# mesh groups and groups of analysis member
if not self.mesh_obj.MeshGroupList:
print (' No mesh group objects.')
else:
print (' Mesh group objects, we need to get the elements.')
for mg in self.mesh_obj.MeshGroupList:
new_group_elements = FemMeshTools.get_mesh_group_elements(mg, self.part_obj)
for ge in new_group_elements:
if ge not in self.group_elements:
self.group_elements[ge] = new_group_elements[ge]
else:
FreeCAD.Console.PrintError(" A group with this name exists already.\n")
if self.analysis: if self.analysis:
print(' Group meshing.') print(' Group meshing.')
self.group_elements = FemMeshTools.get_analysis_group_elements(self.analysis, self.part_obj) new_group_elements = FemMeshTools.get_analysis_group_elements(self.analysis, self.part_obj)
print(' {}'.format(self.group_elements)) for ge in new_group_elements:
if ge not in self.group_elements:
self.group_elements[ge] = new_group_elements[ge]
else: else:
print(' NO group meshing.') FreeCAD.Console.PrintError(" A group with this name exists already.\n")
else:
print(' No anlysis members for group meshing.')
print(' {}'.format(self.group_elements))
# mesh regions
self.ele_length_map = {} # { 'ElementString' : element length } self.ele_length_map = {} # { 'ElementString' : element length }
self.ele_node_map = {} # { 'ElementString' : [element nodes] } self.ele_node_map = {} # { 'ElementString' : [element nodes] }
if not self.mesh_obj.MeshRegionList: if not self.mesh_obj.MeshRegionList:
print (' No Mesh regions.') print (' No mesh regions.')
else: else:
print (' Mesh regions, we need to get the elements.') print (' Mesh regions, we need to get the elements.')
if self.part_obj.Shape.ShapeType == 'Compound': if self.part_obj.Shape.ShapeType == 'Compound':
@ -279,7 +301,7 @@ class FemGmshTools():
geo = open(self.temp_file_geo, "w") geo = open(self.temp_file_geo, "w")
geo.write('Merge "' + self.temp_file_geometry + '";\n') geo.write('Merge "' + self.temp_file_geometry + '";\n')
geo.write("\n") geo.write("\n")
if self.analysis and self.group_elements: if self.group_elements:
# print(' We gone have found elements to make mesh groups for.') # print(' We gone have found elements to make mesh groups for.')
geo.write("// group data\n") geo.write("// group data\n")
# we use the element name of FreeCAD which starts with 1 (example: 'Face1'), same as GMSH # we use the element name of FreeCAD which starts with 1 (example: 'Face1'), same as GMSH

View File

@ -0,0 +1,49 @@
# ***************************************************************************
# * *
# * Copyright (c) 2016 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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__ = "FemMeshGroup"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
## \addtogroup FEM
# @{
import FreeCAD
import _FemMeshGroup
def makeFemMeshGroup(base_mesh, use_label=False, name="FEMMeshGroup"):
'''makeFemMeshGroup([length], [name]): creates a FEM mesh region object to define properties for a regon of a FEM mesh'''
obj = FreeCAD.ActiveDocument.addObject("Fem::FeaturePython", name)
_FemMeshGroup._FemMeshGroup(obj)
obj.UseLabel = use_label
# obj.BaseMesh = base_mesh
# App::PropertyLinkList does not support append, we will use a temporary list to append the mesh group obj. to the list
tmplist = base_mesh.MeshGroupList
tmplist.append(obj)
base_mesh.MeshGroupList = tmplist
if FreeCAD.GuiUp:
import _ViewProviderFemMeshGroup
_ViewProviderFemMeshGroup._ViewProviderFemMeshGroup(obj.ViewObject)
return obj
# @}

View File

@ -995,21 +995,60 @@ def get_ref_shape_node_sum_geom_table(node_geom_table):
return node_sum_geom_table return node_sum_geom_table
def get_mesh_group_elements(mesh_group_obj, aPart):
'''the Reference shapes of the mesh_group_object are searched in the Shape of aPart. If found in shape they are added to a dict
{MeshGroupIdentifier : ['ShapeType of the Elements'], [ElementID, ElementID, ...], ...}
'''
group_elements = {} # { name : [element, element, ... , element]}
if mesh_group_obj.References:
grp_ele = get_reference_group_elements(mesh_group_obj, aPart)
group_elements[grp_ele[0]] = grp_ele[1]
else:
FreeCAD.Console.PrintError(' Empty reference in mesh group object: ' + mesh_group_obj.Name + ' ' + mesh_group_obj.Label)
return group_elements
def get_analysis_group_elements(aAnalysis, aPart): def get_analysis_group_elements(aAnalysis, aPart):
''' all Reference shapes of all Analysis member are searched in the Shape of aPart. If found in shape they are added to a dict ''' all Reference shapes of all Analysis member are searched in the Shape of aPart. If found in shape they are added to a dict
{ConstraintName : ['ShapeType of the Elements'], [ElementID, ElementID, ...], ...} {ConstraintName : ['ShapeType of the Elements'], [ElementID, ElementID, ...], ...}
''' '''
aShape = aPart.Shape
group_elements = {} # { name : [element, element, ... , element]} group_elements = {} # { name : [element, element, ... , element]}
empty_references = [] empty_references = []
for m in aAnalysis.Member: for m in aAnalysis.Member:
if hasattr(m, "References"): if hasattr(m, "References"):
# print(m.Name) if m.References:
key = m.Name grp_ele = get_reference_group_elements(m, aPart)
group_elements[grp_ele[0]] = grp_ele[1]
else:
print(' Empty reference: ' + m.Name)
empty_references.append(m)
if empty_references:
if len(empty_references) == 1:
group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aPart.Shape)
else:
FreeCAD.Console.PrintError('Problem: more than one object with empty references.\n')
print('We gone try to get the empty material references anyway.\n')
# ShellThickness and BeamSection could have empty references, but on solid meshes only materials should have empty references
for er in empty_references:
print(er.Name)
group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aPart.Shape)
# check if all groups have elements:
for g in group_elements:
# print(group_elements[g])
if len(group_elements[g]) == 0:
FreeCAD.Console.PrintError('Error: shapes for: ' + g + 'not found!\n')
return group_elements
def get_reference_group_elements(obj, aPart):
aShape = aPart.Shape
if hasattr(obj, "UseLabel") and obj.UseLabel:
key = obj.Label # TODO check the character of the Label, only allow underline and standard english character
else:
key = obj.Name
elements = [] elements = []
stype = None stype = None
if m.References: for r in obj.References:
for r in m.References:
parent = r[0] parent = r[0]
childs = r[1] childs = r[1]
# print(parent) # print(parent)
@ -1026,29 +1065,10 @@ def get_analysis_group_elements(aAnalysis, aPart):
elements.append(found_element) elements.append(found_element)
else: else:
FreeCAD.Console.PrintError('Problem: No element found for: ' + str(ref_shape) + '\n') FreeCAD.Console.PrintError('Problem: No element found for: ' + str(ref_shape) + '\n')
print(' ' + m.Name) print(' ' + obj.Name)
print(' ' + str(m.References)) print(' ' + str(obj.References))
print(' ' + r[0].Name) print(' ' + r[0].Name)
group_elements[key] = sorted(elements) return (key, sorted(elements))
else:
print(' Empty reference: ' + m.Name)
empty_references.append(m)
if empty_references:
if len(empty_references) == 1:
group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape)
else:
FreeCAD.Console.PrintError('Problem: more than one object with empty references.\n')
print('We gone try to get the empty material references anyway.\n')
# ShellThickness and BeamSection could have empty references, but on solid meshes only materials should have empty references
for er in empty_references:
print(er.Name)
group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape)
# check if all groups have elements:
for g in group_elements:
# print(group_elements[g])
if len(group_elements[g]) == 0:
FreeCAD.Console.PrintError('Error: shapes for: ' + g + 'not found!\n')
return group_elements
def get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape): def get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape):

View File

@ -378,9 +378,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject):
if self.analysis_type == "static": if self.analysis_type == "static":
if not (self.fixed_constraints or self.displacement_constraints): if not (self.fixed_constraints or self.displacement_constraints):
message += "Static analysis: Neither constraint fixed nor constraint displacement defined.\n" message += "Static analysis: Neither constraint fixed nor constraint displacement defined.\n"
if self.analysis_type == "static": # no check in the regard of loads (constraint force, pressure, self weight) is done because an analysis without loads at all is an valid analysis too
if not (self.force_constraints or self.pressure_constraints or self.selfweight_constraints):
message += "Static analysis: Neither constraint force nor constraint pressure or a constraint selfweight defined.\n"
if self.analysis_type == "thermomech": if self.analysis_type == "thermomech":
if not self.initialtemperature_constraints: if not self.initialtemperature_constraints:
message += "Thermomechanical analysis: No initial temperature defined.\n" message += "Thermomechanical analysis: No initial temperature defined.\n"

View File

@ -71,6 +71,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Fem_MeshNetgenFromShape" << "Fem_MeshNetgenFromShape"
<< "Fem_MeshGmshFromShape" << "Fem_MeshGmshFromShape"
<< "Fem_MeshRegion" << "Fem_MeshRegion"
<< "Fem_MeshGroup"
//<< "Fem_CreateNodesSet" //<< "Fem_CreateNodesSet"
<< "Separator" << "Separator"
<< "Fem_Material" << "Fem_Material"
@ -143,6 +144,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
<< "Fem_MeshNetgenFromShape" << "Fem_MeshNetgenFromShape"
<< "Fem_MeshGmshFromShape" << "Fem_MeshGmshFromShape"
<< "Fem_MeshRegion" << "Fem_MeshRegion"
<< "Fem_MeshGroup"
<< "Fem_CreateNodesSet" << "Fem_CreateNodesSet"
<< "Separator" << "Separator"
<< "Fem_Material" << "Fem_Material"

View File

@ -54,6 +54,7 @@ class FemWorkbench (Workbench):
import _CommandFEMMesh2Mesh import _CommandFEMMesh2Mesh
import _CommandMeshGmshFromShape import _CommandMeshGmshFromShape
import _CommandMeshNetgenFromShape import _CommandMeshNetgenFromShape
import _CommandMeshGroup
import _CommandMeshRegion import _CommandMeshRegion
import _CommandAnalysis import _CommandAnalysis
import _CommandShellThickness import _CommandShellThickness

View File

@ -0,0 +1,120 @@
<?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>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Mesh group</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1677215</height>
</size>
</property>
<property name="title">
<string>Identifier used for mesh export</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QFormLayout" name="formLayout_1">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QRadioButton" name="rb_name">
<property name="text">
<string>Name</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="rb_label">
<property name="text">
<string>Label</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_1">
<property name="title">
<string>References</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_Reference">
<property name="text">
<string>Add reference</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="list_References"/>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QRadioButton" name="rb_solid">
<property name="text">
<string>Solid</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="rb_standard">
<property name="text">
<string>Face, Edge, Vertex</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="l_label_text_5">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selection&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<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>
<resources/>
<connections/>
</ui>

View File

@ -270,8 +270,38 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="user_def_text"> <widget class="QLabel" name="user_def_text">
<property name="minimumSize">
<size>
<width>0</width>
<height>17</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="text"> <property name="text">
<string>Available: Disp(x,y,z) Principal stresses (P1,P2,P3)</string> <string>Available: Disp(x,y,z) Principal stresses(P1,P2,P3) Stress(sx,sy,sz) Strain (ex,ey,ez)</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="indent">
<number>-1</number>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -0,0 +1,57 @@
# ***************************************************************************
# * *
# * Copyright (c) 2016 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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__ = "_CommandMeshGroup"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
## @package CommandMeshGroup
# \ingroup FEM
import FreeCAD
from FemCommands import FemCommands
import FreeCADGui
from PySide import QtCore
class _CommandMeshGroup(FemCommands):
"The Fem_MeshGroup command definition"
def __init__(self):
super(_CommandMeshGroup, self).__init__()
self.resources = {'Pixmap': 'fem-femmesh-from-shape',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_MeshGroup", "FEM mesh group"),
'Accel': "M, G",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_MeshGroup", "Creates a FEM mesh group")}
self.is_active = 'with_gmsh_femmesh'
def Activated(self):
FreeCAD.ActiveDocument.openTransaction("Create FemMeshGroup")
FreeCADGui.addModule("FemMeshGroup")
sel = FreeCADGui.Selection.getSelection()
if (len(sel) == 1):
sobj = sel[0]
if len(sel) == 1 and hasattr(sobj, "Proxy") and sobj.Proxy.Type == "FemMeshGmsh":
FreeCADGui.doCommand("FemMeshGroup.makeFemMeshGroup(App.ActiveDocument." + sobj.Name + ")")
FreeCADGui.Selection.clearSelection()
FreeCADGui.addCommand('Fem_MeshGroup', _CommandMeshGroup())

View File

@ -46,6 +46,9 @@ class _FemMeshGmsh():
obj.addProperty("App::PropertyLinkList", "MeshRegionList", "Base", "Mesh regions of the mesh") obj.addProperty("App::PropertyLinkList", "MeshRegionList", "Base", "Mesh regions of the mesh")
obj.MeshRegionList = [] obj.MeshRegionList = []
obj.addProperty("App::PropertyLinkList", "MeshGroupList", "Base", "Mesh groups of the mesh")
obj.MeshRegionList = []
obj.addProperty("App::PropertyLink", "Part", "FEM Mesh", "Part object to mesh") obj.addProperty("App::PropertyLink", "Part", "FEM Mesh", "Part object to mesh")
obj.Part = None obj.Part = None

View File

@ -0,0 +1,40 @@
# ***************************************************************************
# * *
# * Copyright (c) 2016 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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__ = "_FemMeshGroup"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
## @package FemMeshGroup
# \ingroup FEM
class _FemMeshGroup:
"The FemMeshGroup object"
def __init__(self, obj):
obj.addProperty("App::PropertyBool", "UseLabel", "MeshGroupProperties", "The identifier used for export (True: Label, False: Name)")
obj.addProperty("App::PropertyLinkSubList", "References", "MeshGroupShapes", "List of FEM mesh group shapes")
obj.Proxy = self
self.Type = "FemMeshGroup"
def execute(self, obj):
return

View File

@ -0,0 +1,190 @@
# ***************************************************************************
# * *
# * Copyright (c) 2016 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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__ = "_TaskPanelFemMeshGroup"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
## @package TaskPanelFemMeshGroup
# \ingroup FEM
import FreeCAD
import FreeCADGui
from PySide import QtGui
from PySide import QtCore
class _TaskPanelFemMeshGroup:
'''The TaskPanel for editing References property of FemMeshGroup objects'''
def __init__(self, obj):
FreeCADGui.Selection.clearSelection()
self.sel_server = None
self.obj = obj
self.selection_mode_solid = False
self.selection_mode_std_print_message = "Select Faces, Edges and Vertices by single click on them to add them to the list."
self.selection_mode_solid_print_message = "Select Solids by single click on a Face or Edge which belongs to the Solid, to add the Solid to the list."
self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/TaskPanelFemMeshGroup.ui")
QtCore.QObject.connect(self.form.rb_name, QtCore.SIGNAL("toggled(bool)"), self.choose_exportidentifier_name)
QtCore.QObject.connect(self.form.rb_label, QtCore.SIGNAL("toggled(bool)"), self.choose_exportidentifier_label)
QtCore.QObject.connect(self.form.rb_standard, QtCore.SIGNAL("toggled(bool)"), self.choose_selection_mode_standard)
QtCore.QObject.connect(self.form.rb_solid, QtCore.SIGNAL("toggled(bool)"), self.choose_selection_mode_solid)
QtCore.QObject.connect(self.form.pushButton_Reference, QtCore.SIGNAL("clicked()"), self.add_references)
self.form.list_References.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.form.list_References.connect(self.form.list_References, QtCore.SIGNAL("customContextMenuRequested(QPoint)"), self.references_list_right_clicked)
self.get_meshgroup_props()
self.update()
def accept(self):
self.set_meshgroup_props()
if self.sel_server:
FreeCADGui.Selection.removeObserver(self.sel_server)
FreeCADGui.ActiveDocument.resetEdit()
FreeCAD.ActiveDocument.recompute()
return True
def reject(self):
if self.sel_server:
FreeCADGui.Selection.removeObserver(self.sel_server)
FreeCADGui.ActiveDocument.resetEdit()
return True
def get_meshgroup_props(self):
self.use_label = self.obj.UseLabel
self.references = []
if self.obj.References:
self.tuplereferences = self.obj.References
self.get_references()
def set_meshgroup_props(self):
self.obj.References = self.references
self.obj.UseLabel = self.use_label
def update(self):
'fills the widgets'
self.form.rb_name.setChecked(not self.use_label)
self.form.rb_label.setChecked(self.use_label)
self.rebuild_list_References()
def choose_exportidentifier_name(self, state):
self.use_label = not state
def choose_exportidentifier_label(self, state):
self.use_label = state
def choose_selection_mode_standard(self, state):
self.selection_mode_solid = not state
if self.sel_server and not self.selection_mode_solid:
print(self.selection_mode_std_print_message)
def choose_selection_mode_solid(self, state):
self.selection_mode_solid = state
if self.sel_server and self.selection_mode_solid:
print(self.selection_mode_solid_print_message)
def get_references(self):
for ref in self.tuplereferences:
for elem in ref[1]:
self.references.append((ref[0], elem))
def references_list_right_clicked(self, QPos):
self.form.contextMenu = QtGui.QMenu()
menu_item = self.form.contextMenu.addAction("Remove Reference")
if not self.references:
menu_item.setDisabled(True)
self.form.connect(menu_item, QtCore.SIGNAL("triggered()"), self.remove_reference)
parentPosition = self.form.list_References.mapToGlobal(QtCore.QPoint(0, 0))
self.form.contextMenu.move(parentPosition + QPos)
self.form.contextMenu.show()
def remove_reference(self):
if not self.references:
return
currentItemName = str(self.form.list_References.currentItem().text())
for ref in self.references:
refname_to_compare_listentry = ref[0].Name + ':' + ref[1]
if refname_to_compare_listentry == currentItemName:
self.references.remove(ref)
self.rebuild_list_References()
def add_references(self):
'''Called if Button add_reference is triggered'''
# in constraints EditTaskPanel the selection is active as soon as the taskpanel is open
# here the addReference button EditTaskPanel has to be triggered to start selection mode
FreeCADGui.Selection.clearSelection()
# start SelectionObserver and parse the function to add the References to the widget
if self.selection_mode_solid: # print message on button click
print_message = self.selection_mode_solid_print_message
else:
print_message = self.selection_mode_std_print_message
import FemSelectionObserver
self.sel_server = FemSelectionObserver.FemSelectionObserver(self.selectionParser, print_message)
def selectionParser(self, selection):
print('selection: ', selection[0].Shape.ShapeType, ' ', selection[0].Name, ' ', selection[1])
if hasattr(selection[0], "Shape") and selection[1]:
elt = selection[0].Shape.getElement(selection[1])
if self.selection_mode_solid:
# in solid selection mode use edges and faces for selection of a solid
solid_to_add = None
if elt.ShapeType == 'Edge':
found_edge = False
for i, s in enumerate(selection[0].Shape.Solids):
for e in s.Edges:
if elt.isSame(e):
if not found_edge:
solid_to_add = str(i + 1)
else:
FreeCAD.Console.PrintMessage('Edge belongs to more than one solid\n')
solid_to_add = None
found_edge = True
elif elt.ShapeType == 'Face':
found_face = False
for i, s in enumerate(selection[0].Shape.Solids):
for e in s.Faces:
if elt.isSame(e):
if not found_face:
solid_to_add = str(i + 1)
else:
FreeCAD.Console.PrintMessage('Face belongs to more than one solid\n')
solid_to_add = None
found_edge = True
if solid_to_add:
selection = (selection[0], 'Solid' + solid_to_add)
print('selection element changed to Solid: ', selection[0].Shape.ShapeType, ' ', selection[0].Name, ' ', selection[1])
else:
return
if selection not in self.references:
self.references.append(selection)
self.rebuild_list_References()
else:
FreeCAD.Console.PrintMessage(selection[0].Name + ' --> ' + selection[1] + ' is in reference list already!\n')
def rebuild_list_References(self):
self.form.list_References.clear()
items = []
for ref in self.references:
item_name = ref[0].Name + ':' + ref[1]
items.append(item_name)
for listItemName in sorted(items):
self.form.list_References.addItem(listItemName)

View File

@ -21,7 +21,7 @@
# *************************************************************************** # ***************************************************************************
__title__ = "Result Control Task Panel" __title__ = "Result Control Task Panel"
__author__ = "Juergen Riegel" __author__ = "Juergen Riegel, Michael Hindley"
__url__ = "http://www.freecadweb.org" __url__ = "http://www.freecadweb.org"
## @package TaskPanelShowResult ## @package TaskPanelShowResult
@ -231,7 +231,15 @@ class _TaskPanelShowResult:
x = np.array(dispvectors[:, 0]) x = np.array(dispvectors[:, 0])
y = np.array(dispvectors[:, 1]) y = np.array(dispvectors[:, 1])
z = np.array(dispvectors[:, 2]) z = np.array(dispvectors[:, 2])
userdefined_eq = x + y + z + T + Von + P1 + P2 + P3 # Dummy equation to get around flake8, varibles not being used stressvectors = np.array(self.result_object.StressVectors)
sx = np.array(stressvectors[:, 0])
sy = np.array(stressvectors[:, 1])
sz = np.array(stressvectors[:, 2])
strainvectors = np.array(self.result_object.StrainVectors)
ex = np.array(strainvectors[:, 0])
ey = np.array(strainvectors[:, 1])
ez = np.array(strainvectors[:, 2])
userdefined_eq = x + y + z + T + Von + P1 + P2 + P3 + sx + sy + sz + ex + ey + ez # Dummy equation to get around flake8, varibles not being used
userdefined_eq = self.form.user_def_eq.toPlainText() # Get equation to be used userdefined_eq = self.form.user_def_eq.toPlainText() # Get equation to be used
UserDefinedFormula = eval(userdefined_eq).tolist() UserDefinedFormula = eval(userdefined_eq).tolist()
self.result_object.UserDefined = UserDefinedFormula self.result_object.UserDefined = UserDefinedFormula

View File

@ -112,7 +112,7 @@ class _ViewProviderFemMeshGmsh:
return None return None
def claimChildren(self): def claimChildren(self):
return self.Object.MeshRegionList return (self.Object.MeshRegionList + self.Object.MeshGroupList)
def onDelete(self, feature, subelements): def onDelete(self, feature, subelements):
try: try:

View File

@ -0,0 +1,89 @@
# ***************************************************************************
# * *
# * Copyright (c) 2016 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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__ = "_ViewProviderFemMeshGroup"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
## @package ViewProviderFemMeshGroup
# \ingroup FEM
import FreeCAD
import FreeCADGui
from pivy import coin
class _ViewProviderFemMeshGroup:
"A View Provider for the FemMeshGroup object"
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/fem-femmesh-from-shape.svg"
def attach(self, vobj):
self.ViewObject = vobj
self.Object = vobj.Object
self.standard = coin.SoGroup()
vobj.addDisplayMode(self.standard, "Standard")
def getDisplayModes(self, obj):
return ["Standard"]
def getDefaultDisplayMode(self):
return "Standard"
def updateData(self, obj, prop):
return
def onChanged(self, vobj, prop):
return
def setEdit(self, vobj, mode=0):
# hide all meshes
for o in FreeCAD.ActiveDocument.Objects:
if o.isDerivedFrom("Fem::FemMeshObject"):
o.ViewObject.hide()
# show task panel
import _TaskPanelFemMeshGroup
taskd = _TaskPanelFemMeshGroup._TaskPanelFemMeshGroup(self.Object)
taskd.obj = vobj.Object
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
return
def doubleClicked(self, vobj):
doc = FreeCADGui.getDocument(vobj.Object.Document)
if not doc.getInEdit():
doc.setEdit(vobj.Object.Name)
else:
FreeCAD.Console.PrintError('Active Task Dialog found! Please close this one first!\n')
return True
def __getstate__(self):
return None
def __setstate__(self, state):
return None

View File

@ -54,15 +54,19 @@ def readResult(frd_input):
elements_quad4 = {} elements_quad4 = {}
elements_quad8 = {} elements_quad8 = {}
elements_seg2 = {} elements_seg2 = {}
elements_seg3 = {}
results = [] results = []
mode_results = {} mode_results = {}
mode_disp = {} mode_disp = {}
mode_stress = {} mode_stress = {}
mode_stressv = {}
mode_strain = {}
mode_temp = {} mode_temp = {}
mode_disp_found = False mode_disp_found = False
nodes_found = False nodes_found = False
mode_stress_found = False mode_stress_found = False
mode_strain_found = False
mode_temp_found = False mode_temp_found = False
mode_time_found = False mode_time_found = False
elements_found = False elements_found = False
@ -250,6 +254,14 @@ def readResult(frd_input):
nd1 = int(line[3:13]) nd1 = int(line[3:13])
nd2 = int(line[13:23]) nd2 = int(line[13:23])
elements_seg2[elem] = (nd1, nd2) elements_seg2[elem] = (nd1, nd2)
elif elemType == 12:
# B32 CalculiX --> seg3 FreeCAD
# Also D element element number
# N1, N3 ,N2 Order in outpufile is 1,3,2
nd1 = int(line[3:13])
nd3 = int(line[13:23])
nd2 = int(line[23:33])
elements_seg3[elem] = (nd1, nd2, nd3)
# Check if we found new eigenmode # Check if we found new eigenmode
if line[5:10] == "PMODE": if line[5:10] == "PMODE":
@ -266,7 +278,7 @@ def readResult(frd_input):
mode_disp[elem] = FreeCAD.Vector(mode_disp_x, mode_disp_y, mode_disp_z) mode_disp[elem] = FreeCAD.Vector(mode_disp_x, mode_disp_y, mode_disp_z)
if line[5:11] == "STRESS": if line[5:11] == "STRESS":
mode_stress_found = True mode_stress_found = True
# we found a displacement line in the frd file # we found a stress line in the frd file
if mode_stress_found and (line[1:3] == "-1"): if mode_stress_found and (line[1:3] == "-1"):
elem = int(line[4:13]) elem = int(line[4:13])
stress_1 = float(line[13:25]) stress_1 = float(line[13:25])
@ -276,6 +288,19 @@ def readResult(frd_input):
stress_5 = float(line[61:73]) stress_5 = float(line[61:73])
stress_6 = float(line[73:85]) stress_6 = float(line[73:85])
mode_stress[elem] = (stress_1, stress_2, stress_3, stress_4, stress_5, stress_6) mode_stress[elem] = (stress_1, stress_2, stress_3, stress_4, stress_5, stress_6)
mode_stressv[elem] = FreeCAD.Vector(stress_1, stress_2, stress_3)
if line[5:13] == "TOSTRAIN":
mode_strain_found = True
# we found a strain line in the frd file
if mode_strain_found and (line[1:3] == "-1"):
elem = int(line[4:13])
strain_1 = float(line[13:25])
strain_2 = float(line[25:37])
strain_3 = float(line[37:49])
# strain_4 = float(line[49:61]) #Not used in vector
# strain_5 = float(line[61:73])
# strain_6 = float(line[73:85])
mode_strain[elem] = FreeCAD.Vector(strain_1, strain_2, strain_3)
# Check if we found a time step # Check if we found a time step
if line[4:10] == "1PSTEP": if line[4:10] == "1PSTEP":
mode_time_found = True mode_time_found = True
@ -309,6 +334,8 @@ def readResult(frd_input):
mode_results['number'] = eigenmode mode_results['number'] = eigenmode
mode_results['disp'] = mode_disp mode_results['disp'] = mode_disp
mode_results['stress'] = mode_stress mode_results['stress'] = mode_stress
mode_results['stressv'] = mode_stressv
mode_results['strainv'] = mode_strain
mode_results['temp'] = mode_temp mode_results['temp'] = mode_temp
mode_results['time'] = timestep mode_results['time'] = timestep
results.append(mode_results) results.append(mode_results)
@ -322,6 +349,8 @@ def readResult(frd_input):
mode_results['number'] = eigenmode mode_results['number'] = eigenmode
mode_results['disp'] = mode_disp mode_results['disp'] = mode_disp
mode_results['stress'] = mode_stress mode_results['stress'] = mode_stress
mode_results['stressv'] = mode_stressv
mode_results['strainv'] = mode_strain
mode_results['time'] = 0 # Dont return time if static mode_results['time'] = 0 # Dont return time if static
results.append(mode_results) results.append(mode_results)
mode_disp = {} mode_disp = {}
@ -336,7 +365,7 @@ def readResult(frd_input):
return {'Nodes': nodes, return {'Nodes': nodes,
'Hexa8Elem': elements_hexa8, 'Penta6Elem': elements_penta6, 'Tetra4Elem': elements_tetra4, 'Tetra10Elem': elements_tetra10, 'Hexa8Elem': elements_hexa8, 'Penta6Elem': elements_penta6, 'Tetra4Elem': elements_tetra4, 'Tetra10Elem': elements_tetra10,
'Penta15Elem': elements_penta15, 'Hexa20Elem': elements_hexa20, 'Tria3Elem': elements_tria3, 'Tria6Elem': elements_tria6, 'Penta15Elem': elements_penta15, 'Hexa20Elem': elements_hexa20, 'Tria3Elem': elements_tria3, 'Tria6Elem': elements_tria6,
'Quad4Elem': elements_quad4, 'Quad8Elem': elements_quad8, 'Seg2Elem': elements_seg2, 'Quad4Elem': elements_quad4, 'Quad8Elem': elements_quad8, 'Seg2Elem': elements_seg2, 'Seg3Elem': elements_seg3,
'Results': results} 'Results': results}
@ -421,6 +450,8 @@ def importFrd(filename, analysis=None, result_name_prefix=None):
break break
disp = result_set['disp'] disp = result_set['disp']
stressv = result_set['stressv']
strainv = result_set['strainv']
no_of_values = len(disp) no_of_values = len(disp)
displacement = [] displacement = []
for k, v in disp.iteritems(): for k, v in disp.iteritems():
@ -438,6 +469,8 @@ def importFrd(filename, analysis=None, result_name_prefix=None):
if len(disp) > 0: if len(disp) > 0:
results.DisplacementVectors = map((lambda x: x * scale), disp.values()) results.DisplacementVectors = map((lambda x: x * scale), disp.values())
results.StressVectors = map((lambda x: x * scale), stressv.values())
results.StrainVectors = map((lambda x: x * scale), strainv.values())
results.NodeNumbers = disp.keys() results.NodeNumbers = disp.keys()
if(mesh_object): if(mesh_object):
results.Mesh = mesh_object results.Mesh = mesh_object

View File

@ -30,10 +30,10 @@ the module
''' '''
try: try:
from PySide import QtGui
_encoding = QtGui.QApplication.UnicodeUTF8 _encoding = QtGui.QApplication.UnicodeUTF8
def translate(context, text): def translate(context, text):
"convenience function for Qt translator" "convenience function for Qt translator"
from PySide import QtGui
return QtGui.QApplication.translate(context, text, None, _encoding) return QtGui.QApplication.translate(context, text, None, _encoding)
except AttributeError: except AttributeError:
def translate(context, text): def translate(context, text):

View File

@ -186,7 +186,7 @@ Intersection of this and a given list of topo shapes.
Supports: Supports:
- Fuzzy Boolean operations (global tolerance for a Boolean operation) - Fuzzy Boolean operations (global tolerance for a Boolean operation)
- Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s2)) - Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s3))
- Parallelization of Boolean Operations algorithm - Parallelization of Boolean Operations algorithm
OCC 6.9.0 or later is required.</UserDocu> OCC 6.9.0 or later is required.</UserDocu>
@ -203,7 +203,7 @@ Section of this and a given list of topo shapes.
Supports: Supports:
- Fuzzy Boolean operations (global tolerance for a Boolean operation) - Fuzzy Boolean operations (global tolerance for a Boolean operation)
- Support of multiple arguments for a single Boolean operation - Support of multiple arguments for a single Boolean operation (s1 AND (s2 OR s3))
- Parallelization of Boolean Operations algorithm - Parallelization of Boolean Operations algorithm
OCC 6.9.0 or later is required.</UserDocu> OCC 6.9.0 or later is required.</UserDocu>

View File

@ -37,16 +37,16 @@ if FreeCAD.GuiUp:
#-------------------------- translation-related code ---------------------------------------- #-------------------------- translation-related code ----------------------------------------
#(see forum thread "A new Part tool is being born... JoinFeatures!" #(see forum thread "A new Part tool is being born... JoinFeatures!"
#http://forum.freecadweb.org/viewtopic.php?f=22&t=11112&start=30#p90239 ) #http://forum.freecadweb.org/viewtopic.php?f=22&t=11112&start=30#p90239 )
try: try:
_fromUtf8 = QtCore.QString.fromUtf8 _fromUtf8 = QtCore.QString.fromUtf8
except Exception: except Exception:
def _fromUtf8(s): def _fromUtf8(s):
return s return s
try: try:
_encoding = QtGui.QApplication.UnicodeUTF8 _encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig): def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding) return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError: except AttributeError:
def _translate(context, text, disambig): def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig) return QtGui.QApplication.translate(context, text, disambig)
#--------------------------/translation-related code ---------------------------------------- #--------------------------/translation-related code ----------------------------------------

View File

@ -99,7 +99,7 @@ public:
~TaskCheckGeometryResults(); ~TaskCheckGeometryResults();
QString getShapeContentString(); QString getShapeContentString();
private slots: private Q_SLOTS:
void currentRowChanged (const QModelIndex &current, const QModelIndex &previous); void currentRowChanged (const QModelIndex &current, const QModelIndex &previous);
private: private:

View File

@ -198,7 +198,7 @@ protected:
QPixmap *stepActive; QPixmap *stepActive;
QPixmap *stepDone; QPixmap *stepDone;
private slots: private Q_SLOTS:
void selectionSlot(bool checked); void selectionSlot(bool checked);
void buildPixmaps(); void buildPixmaps();
@ -229,7 +229,7 @@ class DimensionControl : public QWidget
public: public:
explicit DimensionControl(QWidget* parent); explicit DimensionControl(QWidget* parent);
QPushButton *resetButton; QPushButton *resetButton;
public slots: public Q_SLOTS:
void toggle3dSlot(bool); void toggle3dSlot(bool);
void toggleDeltaSlot(bool); void toggleDeltaSlot(bool);
void clearAllSlot(bool); void clearAllSlot(bool);
@ -250,7 +250,7 @@ public:
protected: protected:
virtual void onSelectionChanged(const Gui::SelectionChanges& msg); virtual void onSelectionChanged(const Gui::SelectionChanges& msg);
protected slots: protected Q_SLOTS:
void selection1Slot(bool checked); void selection1Slot(bool checked);
void selection2Slot(bool checked); void selection2Slot(bool checked);
void resetDialogSlot(bool); void resetDialogSlot(bool);
@ -326,7 +326,7 @@ public:
protected: protected:
virtual void onSelectionChanged(const Gui::SelectionChanges& msg); virtual void onSelectionChanged(const Gui::SelectionChanges& msg);
protected slots: protected Q_SLOTS:
void selection1Slot(bool checked); void selection1Slot(bool checked);
void selection2Slot(bool checked); void selection2Slot(bool checked);
void resetDialogSlot(bool); void resetDialogSlot(bool);

View File

@ -121,12 +121,12 @@ int ToolPy::PyInit(PyObject* args, PyObject* kwd)
else else
getToolPtr()->Material = Tool::MATUNDEFINED; getToolPtr()->Material = Tool::MATUNDEFINED;
getToolPtr()->Diameter = PyFloat_AsDouble(dia); getToolPtr()->Diameter = dia ? PyFloat_AsDouble(dia) : 0.0;
getToolPtr()->LengthOffset = PyFloat_AsDouble(len); getToolPtr()->LengthOffset = len ? PyFloat_AsDouble(len) : 0.0;
getToolPtr()->FlatRadius = PyFloat_AsDouble(fla); getToolPtr()->FlatRadius = fla ? PyFloat_AsDouble(fla) : 0.0;
getToolPtr()->CornerRadius = PyFloat_AsDouble(cor); getToolPtr()->CornerRadius = cor ? PyFloat_AsDouble(cor) : 0.0;
getToolPtr()->CuttingEdgeAngle = PyFloat_AsDouble(ang); getToolPtr()->CuttingEdgeAngle = ang ? PyFloat_AsDouble(ang) : 0.0;
getToolPtr()->CuttingEdgeHeight = PyFloat_AsDouble(hei); getToolPtr()->CuttingEdgeHeight = hei ? PyFloat_AsDouble(hei) : 0.0;
return 0; return 0;
} }

View File

@ -53,6 +53,7 @@ SET(PathScripts_SRCS
PathScripts/PathSimpleCopy.py PathScripts/PathSimpleCopy.py
PathScripts/PathStock.py PathScripts/PathStock.py
PathScripts/PathStop.py PathScripts/PathStop.py
PathScripts/PathHelix.py
PathScripts/PathSurface.py PathScripts/PathSurface.py
PathScripts/PathToolLenOffset.py PathScripts/PathToolLenOffset.py
PathScripts/PathToolLibraryManager.py PathScripts/PathToolLibraryManager.py

View File

@ -18,6 +18,7 @@
<file>icons/Path-FaceProfile.svg</file> <file>icons/Path-FaceProfile.svg</file>
<file>icons/Path-Face.svg</file> <file>icons/Path-Face.svg</file>
<file>icons/Path-Heights.svg</file> <file>icons/Path-Heights.svg</file>
<file>icons/Path-Helix.svg</file>
<file>icons/Path-Hop.svg</file> <file>icons/Path-Hop.svg</file>
<file>icons/Path-Inspect.svg</file> <file>icons/Path-Inspect.svg</file>
<file>icons/Path-Job.svg</file> <file>icons/Path-Job.svg</file>

View File

@ -0,0 +1,548 @@
<?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-Helix.svg">
<defs
id="defs2818">
<linearGradient
id="linearGradient4513">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517" />
</linearGradient>
<linearGradient
id="linearGradient3681">
<stop
id="stop3697"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3687"
x1="37.89756"
y1="41.087898"
x2="4.0605712"
y2="40.168594"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3695"
x1="37.894287"
y1="40.484772"
x2="59.811455"
y2="43.558987"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(127.27273,-51.272729)" />
<linearGradient
id="linearGradient3681-3">
<stop
id="stop3697-3"
offset="0"
style="stop-color:#fff110;stop-opacity:1;" />
<stop
style="stop-color:#cf7008;stop-opacity:1;"
offset="1"
id="stop3685-4" />
</linearGradient>
<linearGradient
y2="43.558987"
x2="59.811455"
y1="40.484772"
x1="37.894287"
gradientTransform="translate(-37.00068,-20.487365)"
gradientUnits="userSpaceOnUse"
id="linearGradient3608"
xlink:href="#linearGradient3681-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-2" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-4" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538"
xlink:href="#linearGradient4513-2"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="7.9319997"
fx="32.151962"
cy="7.9319997"
cx="32.151962"
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
gradientUnits="userSpaceOnUse"
id="radialGradient4538-6"
xlink:href="#linearGradient4513-1"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-7" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-5" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3069"
xlink:href="#linearGradient4513-1-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient4513-1-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4515-8-6" />
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="1"
id="stop4517-6-6" />
</linearGradient>
<radialGradient
r="23.634638"
fy="35.869175"
fx="32.151962"
cy="35.869175"
cx="32.151962"
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
gradientUnits="userSpaceOnUse"
id="radialGradient3102"
xlink:href="#linearGradient4513-1-2"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4513-1"
id="radialGradient3132"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.39497909,0,0,1.1841158,29.624484,3.2399607)"
cx="32.151962"
cy="27.950663"
fx="32.151962"
fy="27.950663"
r="23.634638" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.7781744"
inkscape:cx="-38.302485"
inkscape:cy="32.387915"
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="2560"
inkscape:window-height="1335"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="false">
<inkscape:grid
type="xygrid"
id="grid4207"
dotted="true"
spacingx="0.5"
spacingy="0.5" />
</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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:none;fill-rule:evenodd;stroke:#008b00;stroke-width:9;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 42,57 32,57 C 2,57 2,54.5 32,44.5 62,34.5 62,32 32,32 2,32 2,29.5 32,19.5 62,9.4999998 62,6.9999998 32,6.9999998 l -10,0"
id="path4379"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:transform-center-x="0.38569462"
inkscape:transform-center-y="-1.2856487" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#radialGradient3132);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.97430003;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 33.328125,14.73446 0,17.46591 c 0,0 0,1.310757 0,8.59375 l 17.34375,-8.59375 0,-1.90625 0,-15.55966 z m 17.34375,22.55966 -17.34375,8.5625 0,7.03125 17.34375,-8.59375 z m 0,12.09375 -14.5625,7.21875 5.9375,3.90625 8.625,-5.71875 z"
id="rect4417"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -65,6 +65,7 @@ class PathWorkbench (Workbench):
from PathScripts import PathCustom from PathScripts import PathCustom
from PathScripts import PathInspect from PathScripts import PathInspect
from PathScripts import PathSimpleCopy from PathScripts import PathSimpleCopy
from PathScripts import PathHelix
from PathScripts import PathEngrave from PathScripts import PathEngrave
from PathScripts import PathSurface from PathScripts import PathSurface
from PathScripts import PathSanity from PathScripts import PathSanity
@ -80,7 +81,7 @@ class PathWorkbench (Workbench):
projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"] projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"]
toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"] toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"]
prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_ToolLenOffset", "Path_Comment", "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_FromShape"] prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_ToolLenOffset", "Path_Comment", "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_FromShape"]
twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace"] twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace", "Path_Helix"]
threedopcmdlist = ["Path_Surfacing"] threedopcmdlist = ["Path_Surfacing"]
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ] modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"] dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"]

View File

@ -275,7 +275,7 @@ class Chord (object):
return dir == 'Back' or dir == side return dir == 'Back' or dir == side
def connectsTo(self, chord): def connectsTo(self, chord):
return PathGeom.isRoughly(self.End, chord.Start) return PathGeom.pointsCoincide(self.End, chord.Start)
class Bone: class Bone:
def __init__(self, boneId, obj, lastCommand, inChord, outChord, smooth): def __init__(self, boneId, obj, lastCommand, inChord, outChord, smooth):

View File

@ -0,0 +1,818 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* *
#* Copyright (c) 2016 Lorenz Hüdepohl <dev@stellardeath.org> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD, Path
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from DraftTools import translate
from . import PathUtils
from .PathUtils import fmt
"""Helix Drill object and FreeCAD command"""
if FreeCAD.GuiUp:
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def translate(context, text, disambig=None):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def translate(context, text, disambig=None):
return QtGui.QApplication.translate(context, text, disambig)
else:
def translate(context, text, disambig=None):
return text
def z_cylinder(cyl):
""" Test if cylinder is aligned to z-Axis"""
if cyl.Surface.Axis.x != 0.0:
return False
if cyl.Surface.Axis.y != 0.0:
return False
return True
def connected(edge, face):
for otheredge in face.Edges:
if edge.isSame(otheredge):
return True
return False
def cylinders_in_selection():
from Part import Cylinder
selections = FreeCADGui.Selection.getSelectionEx()
cylinders = []
for selection in selections:
base = selection.Object
cylinders.append((base, []))
for feature in selection.SubElementNames:
subobj = getattr(base.Shape, feature)
if subobj.ShapeType =='Face':
if isinstance(subobj.Surface, Cylinder):
if z_cylinder(subobj):
cylinders[-1][1].append(feature)
return cylinders
def helix_cut(center, r_out, r_in, dr, zmax, zmin, dz, safe_z, tool_diameter, vfeed, hfeed, direction, startside):
"""
center: 2-tuple
(x0,y0) coordinates of center
r_out, r_in: floats
radial range, cut from outer radius r_out in layers of dr to inner radius r_in
zmax, zmin: floats
z-range, cut from zmax in layers of dz down to zmin
safe_z: float
safety layer height
tool_diameter: float
Width of tool
"""
from numpy import ceil, allclose, linspace
if (zmax <= zmin):
return
out = "(helix_cut <{0}, {1}>, {2})".format(center[0], center[1],
", ".join(map(str, (r_out, r_in, dr, zmax, zmin, dz, safe_z, tool_diameter, vfeed, hfeed, direction, startside))))
x0, y0 = center
nz = max(int(ceil((zmax - zmin)/dz)), 2)
zi = linspace(zmax, zmin, 2 * nz + 1)
if dr > tool_diameter:
FreeCAD.Console.PrintWarning("PathHelix: Warning, shortening dr to tool diameter!\n")
dr = tool_diameter
def xyz(x=None, y=None, z=None):
out = ""
if x is not None:
out += " X" + fmt(x)
if y is not None:
out += " Y" + fmt(y)
if z is not None:
out += " Z" + fmt(z)
return out
def rapid(x=None, y=None, z=None):
return "G0" + xyz(x,y,z) + "\n"
def F(f=None):
return (" F" + fmt(f) if f else "")
def feed(x=None, y=None, z=None, f=None):
return "G1" + xyz(x,y,z) + F(f) + "\n"
def arc(x,y,i,j,z,f):
if direction == "CW":
code = "G2"
elif direction == "CCW":
code = "G3"
return code + " I" + fmt(i) + " J" + fmt(j) + " X" + fmt(x) + " Y" + fmt(y) + " Z" + fmt(z) + F(f) + "\n"
def helix_cut_r(r):
out = ""
out += rapid(x=x0+r,y=y0)
out += rapid(z=zmax + tool_diameter)
out += feed(z=zmax,f=vfeed)
z=zmin
for i in range(1,nz+1):
out += arc(x0-r, y0, i=-r, j=0.0, z = zi[2*i-1], f=hfeed)
out += arc(x0+r, y0, i= r, j=0.0, z = zi[2*i], f=hfeed)
out += arc(x0-r, y0, i=-r, j=0.0, z = zmin, f=hfeed)
out += arc(x0+r, y0, i=r, j=0.0, z = zmin, f=hfeed)
out += feed(z=zmax + tool_diameter, f=vfeed)
out += rapid(z=safe_z)
return out
assert(r_out > 0.0)
assert(r_in >= 0.0)
msg = None
if r_out < 0.0:
msg = "r_out < 0"
elif r_in > 0 and r_out - r_in < tool_diameter:
msg = "r_out - r_in = {0} is < tool diameter of {1}".format(r_out - r_in, tool_diamater)
elif r_in == 0.0 and not r_out > tool_diameter/2.:
msg = "Cannot drill a hole of diameter {0} with a tool of diameter {1}".format(2 * r_out, tool_diameter)
elif not startside in ["inside", "outside"]:
msg = "Invalid value for parameter 'startside'"
if msg:
out += "(ERROR: Hole at {0}:".format((x0, y0, zmax)) + msg + ")\n"
FreeCAD.Console.PrintError("PathHelix: Hole at {0}:".format((x0, y0, zmax)) + msg + "\n")
return out
if r_in > 0:
out += "(annulus mode)\n"
r_out = r_out - tool_diameter/2
r_in = r_in + tool_diameter/2
if abs((r_out - r_in) / dr) < 1e-5:
radii = [(r_out + r_in)/2]
else:
nr = max(int(ceil((r_out - r_in)/dr)), 2)
radii = linspace(r_out, r_in, nr)
elif r_out <= 2 * dr:
out += "(single helix mode)\n"
radii = [r_out - tool_diameter/2]
assert(radii[0] > 0)
else:
out += "(full hole mode)\n"
r_out = r_out - tool_diameter/2
r_in = dr/2
nr = max(1 + int(ceil((r_out - r_in)/dr)), 2)
radii = linspace(r_out, r_in, nr)
assert(all(radii > 0))
if startside == "inside":
radii = radii[::-1]
for r in radii:
out += "(radius {0})\n".format(r)
out += helix_cut_r(r)
return out
def features_by_centers(base, features):
import scipy.spatial
features = sorted(features,
key = lambda feature : getattr(base.Shape, feature).Surface.Radius,
reverse = True)
coordinates = [(cylinder.Surface.Center.x, cylinder.Surface.Center.y) for cylinder in
[getattr(base.Shape, feature) for feature in features]]
tree = scipy.spatial.KDTree(coordinates)
seen = {}
by_centers = {}
for n, feature in enumerate(features):
if n in seen:
continue
seen[n] = True
cylinder = getattr(base.Shape, feature)
xc, yc, _ = cylinder.Surface.Center
by_centers[xc, yc] = {cylinder.Surface.Radius : feature}
for coord in tree.query_ball_point((xc, yc), cylinder.Surface.Radius):
seen[coord] = True
cylinder = getattr(base.Shape, features[coord])
by_centers[xc, yc][cylinder.Surface.Radius] = features[coord]
return by_centers
class ObjectPathHelix(object):
def __init__(self,obj):
# Basic
obj.addProperty("App::PropertyLinkSubList","Features","Path",translate("Features","Selected features for the drill operation"))
obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Set to False to disable code generation"))
obj.addProperty("App::PropertyString","Comment","Path",translate("Comment","An optional comment for this profile, will appear in G-Code"))
# Helix specific
obj.addProperty("App::PropertyEnumeration", "Direction", "Helix Drill",
translate("Direction", "The direction of the circular cuts, clockwise (CW), or counter clockwise (CCW)"))
obj.Direction = ['CW','CCW']
obj.addProperty("App::PropertyEnumeration", "StartSide", "Helix Drill",
translate("Direction", "Start cutting from the inside or outside"))
obj.StartSide = ['inside','outside']
obj.addProperty("App::PropertyLength", "DeltaR", "Helix Drill",
translate("DeltaR", "Radius increment (must be smaller than tool diameter)"))
# Depth Properties
obj.addProperty("App::PropertyDistance", "Clearance", "Depths",
translate("Clearance","Safe distance above the top of the hole to which to retract the tool"))
obj.addProperty("App::PropertyLength", "StepDown", "Depths",
translate("StepDown","Incremental Step Down of Tool"))
obj.addProperty("App::PropertyBool","UseStartDepth","Depths",
translate("Use Start Depth","Set to True to manually specify a start depth"))
obj.addProperty("App::PropertyDistance", "StartDepth", "Depths",
translate("Start Depth","Starting Depth of Tool - first cut depth in Z"))
obj.addProperty("App::PropertyBool","UseFinalDepth","Depths",
translate("Use Final Depth","Set to True to manually specify a final depth"))
obj.addProperty("App::PropertyDistance", "FinalDepth", "Depths",
translate("Final Depth","Final Depth of Tool - lowest value in Z"))
obj.addProperty("App::PropertyDistance", "ThroughDepth", "Depths",
translate("Through Depth","Add this amount of additional cutting depth to open-ended holes. Only used if UseFinalDepth is False"))
# The current tool number, read-only
# this is apparently used internally, to keep track of tool chagnes
obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The current tool in use"))
obj.ToolNumber = (0,0,1000,1)
obj.setEditorMode('ToolNumber',1) #make this read only
obj.Proxy = self
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def execute(self,obj):
from Part import Circle, Cylinder, Plane
from math import sqrt
output = '(helix cut operation'
if obj.Comment:
output += ', '+ str(obj.Comment)+')\n'
else:
output += ')\n'
if obj.Features:
if not obj.Active:
obj.Path = Path.Path("(helix cut operation inactive)")
if obj.ViewObject:
obj.ViewObject.Visibility = False
return
toolload = PathUtils.getLastToolLoad(obj)
if toolload is None or toolload.ToolNumber == 0:
FreeCAD.Console.PrintError("PathHelix: No tool selected for helix cut operation, insert a tool change operation first\n")
obj.Path = Path.Path("(ERROR: no tool selected for helix cut operation)")
return
tool = PathUtils.getTool(obj, toolload.ToolNumber)
zsafe = max(baseobj.Shape.BoundBox.ZMax for baseobj, features in obj.Features) + obj.Clearance.Value
output += "G0 Z" + fmt(zsafe)
drill_jobs = []
for base, features in obj.Features:
for center, by_radius in features_by_centers(base, features).items():
radii = sorted(by_radius.keys(), reverse=True)
cylinders = map(lambda radius: getattr(base.Shape, by_radius[radius]), radii)
zsafe = max(cyl.BoundBox.ZMax for cyl in cylinders) + obj.Clearance.Value
cur_z = cylinders[0].BoundBox.ZMax
jobs = []
for cylinder in cylinders:
# Find other edge of current cylinder
other_edge = None
for edge in cylinder.Edges:
if isinstance(edge.Curve, Circle) and edge.Curve.Center.z != cur_z:
other_edge = edge
break
next_z = other_edge.Curve.Center.z
dz = next_z - cur_z
r = cylinder.Surface.Radius
if dz < 0:
# This is a closed hole if the face connected to the current cylinder at next_z has
# the cylinder's edge as its OuterWire
closed = None
for face in base.Shape.Faces:
if connected(other_edge, face) and not face.isSame(cylinder.Faces[0]):
wire = face.OuterWire
if len(wire.Edges) == 1 and wire.Edges[0].isSame(other_edge):
closed = True
else:
closed = False
if closed is None:
raise Exception("Cannot determine if this cylinder is closed on the z = {0} side".format(next_z))
xc, yc, _ = cylinder.Surface.Center
jobs.append(dict(xc=xc, yc=yc, zmin=next_z, zmax=cur_z, r_out=r, r_in=0.0, closed=closed, zsafe=zsafe))
elif dz > 0:
new_jobs = []
for job in jobs:
if job["zmin"] < next_z < job["zmax"]:
# split this job
job1 = dict(job)
job2 = dict(job)
job1["zmin"] = next_z
job2["zmax"] = next_z
job2["r_in"] = r
new_jobs.append(job1)
new_jobs.append(job2)
else:
new_jobs.append(job)
jobs = new_jobs
else:
FreeCAD.Console.PrintError("PathHelix: Encountered cylinder with zero height\n")
break
cur_z = next_z
if obj.UseStartDepth:
jobs = [job for job in jobs if job["zmin"] < obj.StartDepth.Value]
if jobs:
jobs[0]["zmax"] = obj.StartDepth.Value
if obj.UseFinalDepth:
jobs = [job for job in jobs if job["zmax"] > obj.FinalDepth.Value]
if jobs:
jobs[-1]["zmin"] = obj.FinalDepth.Value
else:
if not jobs[-1]["closed"]:
jobs[-1]["zmin"] -= obj.ThroughDepth.Value
drill_jobs.extend(jobs)
for job in drill_jobs:
output += helix_cut((job["xc"], job["yc"]), job["r_out"], job["r_in"], obj.DeltaR.Value,
job["zmax"], job["zmin"], obj.StepDown.Value,
job["zsafe"], tool.Diameter,
toolload.VertFeed.Value, toolload.HorizFeed.Value, obj.Direction, obj.StartSide)
output += '\n'
obj.Path = Path.Path(output)
if obj.ViewObject:
obj.ViewObject.Visibility = True
taskpanels = {}
class ViewProviderPathHelix(object):
def __init__(self,vobj):
vobj.Proxy = self
def attach(self,vobj):
self.Object = vobj.Object
return
def getIcon(self):
return ":/icons/Path-Helix.svg"
def setEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
taskpanel = TaskPanel(vobj.Object)
FreeCADGui.Control.showDialog(taskpanel)
taskpanels[0] = taskpanel
return True
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class CommandPathHelix(object):
def GetResources(self):
return {'Pixmap' : 'Path-Helix',
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathHelix","PathHelix"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathHelix","Creates a helix cut from selected circles")}
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):
import FreeCADGui
import Path
from PathScripts import PathUtils
FreeCAD.ActiveDocument.openTransaction(translate("PathHelix","Create a helix cut"))
FreeCADGui.addModule("PathScripts.PathHelix")
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","PathHelix")
ObjectPathHelix(obj)
ViewProviderPathHelix(obj.ViewObject)
obj.Features = cylinders_in_selection()
obj.DeltaR = 1.0
toolLoad = PathUtils.getLastToolLoad(obj)
if toolLoad is not None:
obj.ToolNumber = toolLoad.ToolNumber
tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
if tool:
# start with 25% overlap
obj.DeltaR = tool.Diameter * 0.75
obj.Active = True
obj.Comment = ""
obj.Direction = "CW"
obj.StartSide = "inside"
obj.Clearance = 10.0
obj.StepDown = 1.0
obj.UseStartDepth = False
obj.StartDepth = 1.0
obj.UseFinalDepth = False
obj.FinalDepth = 0.0
obj.ThroughDepth = 0.0
PathUtils.addToJob(obj)
obj.ViewObject.startEditing()
FreeCAD.ActiveDocument.recompute()
def print_exceptions(func):
from functools import wraps
import traceback
import sys
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
ex_type, ex, tb = sys.exc_info()
FreeCAD.Console.PrintError("".join(traceback.format_exception(ex_type, ex, tb)) + "\n")
raise
return wrapper
def print_all_exceptions(cls):
for entry in dir(cls):
obj = getattr(cls, entry)
if not entry.startswith("__") and hasattr(obj, "__call__"):
setattr(cls, entry, print_exceptions(obj))
return cls
@print_all_exceptions
class TaskPanel(object):
def __init__(self, obj):
from Units import Quantity
self.obj = obj
ui = FreeCADGui.UiLoader()
layout = QtGui.QGridLayout()
headerStyle = "QLabel { font-weight: bold; font-size: large; }"
grayed_out = "background-color: #d0d0d0;"
self.previous_value = {}
def addWidget(widget):
row = layout.rowCount()
layout.addWidget(widget, row, 0, 1, 2)
def addWidgets(widget1, widget2):
row = layout.rowCount()
layout.addWidget(widget1, row, 0)
layout.addWidget(widget2, row, 1)
def heading(label):
heading = QtGui.QLabel(label)
heading.setStyleSheet(headerStyle)
addWidget(heading)
def addQuantity(property, labelstring, activator=None, max=None):
self.previous_value[property] = getattr(self.obj, property)
widget = ui.createWidget("Gui::InputField")
if activator:
self.previous_value[activator] = getattr(self.obj, activator)
currently_active = getattr(self.obj, activator)
label = QtGui.QCheckBox(labelstring)
def change(state):
setattr(self.obj, activator, label.isChecked())
if label.isChecked():
widget.setStyleSheet("")
else:
widget.setStyleSheet(grayed_out)
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
label.stateChanged.connect(change)
label.setChecked(currently_active)
if not currently_active:
widget.setStyleSheet(grayed_out)
label.setToolTip(self.obj.getDocumentationOfProperty(activator))
else:
label = QtGui.QLabel(labelstring)
label.setToolTip(self.obj.getDocumentationOfProperty(property))
widget.setText(str(getattr(self.obj, property)))
widget.setToolTip(self.obj.getDocumentationOfProperty(property))
if max:
# cannot use widget.setMaximum() as apparently ui.createWidget()
# returns the object up-casted to QWidget.
widget.setProperty("maximum", max)
def change(quantity):
setattr(self.obj, property, quantity)
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
QtCore.QObject.connect(widget, QtCore.SIGNAL("valueChanged(const Base::Quantity &)"), change)
addWidgets(label, widget)
return label, widget
def addCheckBox(property, label):
self.previous_value[property] = getattr(self.obj, property)
widget = QtGui.QCheckBox(label)
widget.setToolTip(self.obj.getDocumentationOfProperty(property))
def change(state):
setattr(self.obj, property, widget.isChecked())
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
widget.stateChanged.connect(change)
widget.setChecked(getattr(self.obj, property))
addWidget(widget)
def addEnumeration(property, label, options):
self.previous_value[property] = getattr(self.obj, property)
label = QtGui.QLabel(label)
label.setToolTip(self.obj.getDocumentationOfProperty(property))
widget = QtGui.QComboBox()
widget.setToolTip(self.obj.getDocumentationOfProperty(property))
for option_label, option_value in options:
widget.addItem(option_label)
def change(index):
setattr(self.obj, property, options[index][1])
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
widget.currentIndexChanged.connect(change)
addWidgets(label, widget)
self.featureTree = QtGui.QTreeWidget()
self.featureTree.setMinimumHeight(200)
self.featureTree.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
#self.featureTree.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
#self.featureTree.setDefaultDropAction(QtCore.Qt.MoveAction)
self.fillFeatureTree()
sm = self.featureTree.selectionModel()
sm.selectionChanged.connect(self.selectFeatures)
addWidget(self.featureTree)
self.featureTree.expandAll()
self.addButton = QtGui.QPushButton("Add holes")
self.addButton.clicked.connect(self.addCylinders)
self.delButton = QtGui.QPushButton("Delete")
self.delButton.clicked.connect(self.delCylinders)
addWidgets(self.addButton, self.delButton)
heading("Drill parameters")
addCheckBox("Active", "Operation is active")
tool = PathUtils.getTool(self.obj,self.obj.ToolNumber)
if not tool:
drmax = None
else:
drmax = tool.Diameter
addQuantity("DeltaR", "Step in Radius", max=drmax)
addQuantity("StepDown", "Step in Z")
addEnumeration("Direction", "Cut direction", [("Clockwise", "CW"), ("Counter-Clockwise", "CCW")])
addEnumeration("StartSide", "Start Side", [("Start from inside", "inside"), ("Start from outside", "outside")])
heading("Cutting Depths")
addQuantity("Clearance", "Clearance Distance")
addQuantity("StartDepth", "Absolute start height", "UseStartDepth")
fdcheckbox, fdinput = addQuantity("FinalDepth", "Absolute final height", "UseFinalDepth")
tdlabel, tdinput = addQuantity("ThroughDepth", "Extra drill depth\nfor open holes")
# make ThroughDepth and FinalDepth mutually exclusive
def fd_change(state):
if fdcheckbox.isChecked():
tdinput.setStyleSheet(grayed_out)
else:
tdinput.setStyleSheet("")
fdcheckbox.stateChanged.connect(fd_change)
def td_change(quantity):
fdcheckbox.setChecked(False)
QtCore.QObject.connect(tdinput, QtCore.SIGNAL("valueChanged(const Base::Quantity &)"), td_change)
if obj.UseFinalDepth:
tdinput.setStyleSheet(grayed_out)
# add
widget = QtGui.QWidget()
widget.setLayout(layout)
self.form = widget
def addCylinders(self):
features_per_base = {}
for base, features in self.obj.Features:
features_per_base[base] = list(set(features))
for base, features in cylinders_in_selection():
for feature in features:
if base in features_per_base:
if not feature in features_per_base[base]:
features_per_base[base].append(feature)
else:
features_per_base[base] = [feature]
self.obj.Features = list(features_per_base.items())
self.featureTree.clear()
self.fillFeatureTree()
self.featureTree.expandAll()
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
def delCylinders(self):
del_features = []
def delete_feature(item, base=None):
kind, feature = item.data(0, QtCore.Qt.UserRole)
assert(kind == "feature")
if base is None:
base_item = item.parent().parent()
_, base = base_item.data(0, QtCore.Qt.UserRole)
del_features.append((base, feature))
item.parent().takeChild(item.parent().indexOfChild(item))
def delete_hole(item, base=None):
kind, center = item.data(0, QtCore.Qt.UserRole)
assert(kind == "hole")
if base is None:
base_item = item.parent()
_, base = base_item.data(0, QtCore.Qt.UserRole)
for i in reversed(range(item.childCount())):
delete_feature(item.child(i), base=base)
item.parent().takeChild(item.parent().indexOfChild(item))
def delete_base(item):
kind, base = item.data(0, QtCore.Qt.UserRole)
assert(kind == "base")
for i in reversed(range(item.childCount())):
delete_hole(item.child(i), base=base)
self.featureTree.takeTopLevelItem(self.featureTree.indexOfTopLevelItem(item))
for item in self.featureTree.selectedItems():
kind, info = item.data(0, QtCore.Qt.UserRole)
if kind == "base":
delete_base(item)
elif kind == "hole":
parent = item.parent()
delete_hole(item)
if parent.childCount() == 0:
self.featureTree.takeTopLevelItem(self.featureTree.indexOfTopLevelItem(parent))
elif kind =="feature":
parent = item.parent()
delete_feature(item)
if parent.childCount() == 0:
parent.parent().takeChild(parent.parent().indexOfChild(parent))
else:
raise Exception("No such item kind: {0}".format(kind))
for base, features in cylinders_in_selection():
for feature in features:
del_features.append((base, feature))
new_features = []
for obj, features in self.obj.Features:
for feature in features:
if (obj, feature) not in del_features:
new_features.append((obj, feature))
self.obj.Features = new_features
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
def fillFeatureTree(self):
for base, features in self.obj.Features:
base_item = QtGui.QTreeWidgetItem()
base_item.setText(0, base.Name)
base_item.setData(0, QtCore.Qt.UserRole, ("base", base))
self.featureTree.addTopLevelItem(base_item)
for center, by_radius in features_by_centers(base, features).items():
hole_item = QtGui.QTreeWidgetItem()
hole_item.setText(0, "Hole at ({0[0]:.2f}, {0[1]:.2f})".format(center))
hole_item.setData(0, QtCore.Qt.UserRole, ("hole", center))
base_item.addChild(hole_item)
for radius in sorted(by_radius.keys(), reverse=True):
feature = by_radius[radius]
cylinder = getattr(base.Shape, feature)
cyl_item = QtGui.QTreeWidgetItem()
cyl_item.setText(0, "Diameter {0:.2f}, {1}".format(2 * cylinder.Surface.Radius, feature))
cyl_item.setData(0, QtCore.Qt.UserRole, ("feature", feature))
hole_item.addChild(cyl_item)
def selectFeatures(self, selected, deselected):
FreeCADGui.Selection.clearSelection()
def select_feature(item, base=None):
kind, feature = item.data(0, QtCore.Qt.UserRole)
assert(kind == "feature")
if base is None:
base_item = item.parent().parent()
_, base = base_item.data(0, QtCore.Qt.UserRole)
FreeCADGui.Selection.addSelection(base, feature)
def select_hole(item, base=None):
kind, center = item.data(0, QtCore.Qt.UserRole)
assert(kind == "hole")
if base is None:
base_item = item.parent()
_, base = base_item.data(0, QtCore.Qt.UserRole)
for i in range(item.childCount()):
select_feature(item.child(i), base=base)
def select_base(item):
kind, base = item.data(0, QtCore.Qt.UserRole)
assert(kind == "base")
for i in range(item.childCount()):
select_hole(item.child(i), base=base)
for item in self.featureTree.selectedItems():
kind, info = item.data(0, QtCore.Qt.UserRole)
if kind == "base":
select_base(item)
elif kind == "hole":
select_hole(item)
elif kind == "feature":
select_feature(item)
def needsFullSpace(self):
return True
def accept(self):
FreeCADGui.ActiveDocument.resetEdit()
FreeCADGui.Control.closeDialog()
def reject(self):
for property in self.previous_value:
setattr(self.obj, property, self.previous_value[property])
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.resetEdit()
FreeCADGui.Control.closeDialog()
if FreeCAD.GuiUp:
import FreeCADGui
FreeCADGui.addCommand('Path_Helix',CommandPathHelix())

View File

@ -29,6 +29,7 @@
#endif #endif
#include <Base/Tools.h> #include <Base/Tools.h>
#include <Base/Tools2D.h>
#include <App/Application.h> #include <App/Application.h>
#include <Gui/Application.h> #include <Gui/Application.h>
#include <Gui/Document.h> #include <Gui/Document.h>
@ -3252,10 +3253,29 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
Base::Vector3d p1b = lineSeg1->getEndPoint(); Base::Vector3d p1b = lineSeg1->getEndPoint();
Base::Vector3d p2a = lineSeg2->getStartPoint(); Base::Vector3d p2a = lineSeg2->getStartPoint();
Base::Vector3d p2b = lineSeg2->getEndPoint(); Base::Vector3d p2b = lineSeg2->getEndPoint();
// Get the intersection point in 2d of the two lines if possible
Base::Line2d line1(Base::Vector2d(p1a.x, p1a.y), Base::Vector2d(p1b.x, p1b.y));
Base::Line2d line2(Base::Vector2d(p2a.x, p2a.y), Base::Vector2d(p2b.x, p2b.y));
Base::Vector2d s;
if (line1.Intersect(line2, s)) {
// get the end points of the line segments that are closest to the intersection point
Base::Vector3d s3d(s.x, s.y, p1a.z);
if (Base::DistanceP2(s3d, p1a) < Base::DistanceP2(s3d, p1b))
PosId1 = Sketcher::start;
else
PosId1 = Sketcher::end;
if (Base::DistanceP2(s3d, p2a) < Base::DistanceP2(s3d, p2b))
PosId2 = Sketcher::start;
else
PosId2 = Sketcher::end;
}
else {
// if all points are collinear
double length = DBL_MAX; double length = DBL_MAX;
for (int i=0; i <= 1; i++) { for (int i=0; i <= 1; i++) {
for (int j=0; j <= 1; j++) { for (int j=0; j <= 1; j++) {
double tmp = ((j?p2a:p2b)-(i?p1a:p1b)).Length(); double tmp = Base::DistanceP2((j?p2a:p2b), (i?p1a:p1b));
if (tmp < length) { if (tmp < length) {
length = tmp; length = tmp;
PosId1 = i ? Sketcher::start : Sketcher::end; PosId1 = i ? Sketcher::start : Sketcher::end;
@ -3263,6 +3283,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
} }
} }
} }
}
Base::Vector3d dir1 = ((PosId1 == Sketcher::start) ? 1. : -1.) * Base::Vector3d dir1 = ((PosId1 == Sketcher::start) ? 1. : -1.) *
(lineSeg1->getEndPoint()-lineSeg1->getStartPoint()); (lineSeg1->getEndPoint()-lineSeg1->getStartPoint());
@ -3280,8 +3301,8 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
} }
} }
double ActAngle = atan2(-dir1.y*dir2.x+dir1.x*dir2.y, double ActAngle = atan2(dir1.x*dir2.y-dir1.y*dir2.x,
dir1.x*dir2.x+dir1.y*dir2.y); dir1.y*dir2.y+dir1.x*dir2.x);
if (ActAngle < 0) { if (ActAngle < 0) {
ActAngle *= -1; ActAngle *= -1;
std::swap(GeoId1,GeoId2); std::swap(GeoId1,GeoId2);

View File

@ -181,7 +181,7 @@ signals:
void clicked(); void clicked();
void selected(); void selected();
public slots: public Q_SLOTS:
void setColor(const QColor &color, const QString &text = QString()); void setColor(const QColor &color, const QString &text = QString());
protected: protected:
@ -224,10 +224,10 @@ signals:
void selected(const QColor &); void selected(const QColor &);
void hid(); void hid();
public slots: public Q_SLOTS:
void getColorFromDialog(); void getColorFromDialog();
protected slots: protected Q_SLOTS:
void updateSelected(); void updateSelected();
protected: protected:
@ -434,7 +434,7 @@ void QtColorPicker::setStandardColors()
void QtColorPicker::setCurrentColor(const QColor &color) void QtColorPicker::setCurrentColor(const QColor &color)
{ {
if (color.isValid() && col == color) { if (color.isValid() && col == color) {
emit colorSet(color); Q_EMIT colorSet(color);
return; return;
} }
if (col == color || !color.isValid()) if (col == color || !color.isValid())
@ -457,8 +457,8 @@ void QtColorPicker::setCurrentColor(const QColor &color)
repaint(); repaint();
item->setSelected(true); item->setSelected(true);
emit colorChanged(color); Q_EMIT colorChanged(color);
emit colorSet(color); Q_EMIT colorSet(color);
} }
/*! /*!
@ -679,7 +679,7 @@ void ColorPickerPopup::updateSelected()
if (sender() && sender()->inherits("ColorPickerItem")) { if (sender() && sender()->inherits("ColorPickerItem")) {
ColorPickerItem *item = (ColorPickerItem *)sender(); ColorPickerItem *item = (ColorPickerItem *)sender();
lastSel = item->color(); lastSel = item->color();
emit selected(item->color()); Q_EMIT selected(item->color());
} }
hide(); hide();
@ -764,7 +764,7 @@ void ColorPickerPopup::keyPressEvent(QKeyEvent *e)
} }
lastSel = wi->color(); lastSel = wi->color();
emit selected(wi->color()); Q_EMIT selected(wi->color());
hide(); hide();
} else if (w && w->inherits("QPushButton")) { } else if (w && w->inherits("QPushButton")) {
ColorPickerItem *wi = reinterpret_cast<ColorPickerItem *>(w); ColorPickerItem *wi = reinterpret_cast<ColorPickerItem *>(w);
@ -784,7 +784,7 @@ void ColorPickerPopup::keyPressEvent(QKeyEvent *e)
} }
lastSel = wi->color(); lastSel = wi->color();
emit selected(wi->color()); Q_EMIT selected(wi->color());
hide(); hide();
} }
} }
@ -811,7 +811,7 @@ void ColorPickerPopup::hideEvent(QHideEvent *e)
setFocus(); setFocus();
emit hid(); Q_EMIT hid();
QFrame::hideEvent(e); QFrame::hideEvent(e);
} }
@ -906,7 +906,7 @@ void ColorPickerPopup::getColorFromDialog()
//QColor col = QColor::fromRgba(rgb); //QColor col = QColor::fromRgba(rgb);
insertColor(col, tr("Custom"), -1); insertColor(col, tr("Custom"), -1);
lastSel = col; lastSel = col;
emit selected(col); Q_EMIT selected(col);
} }
void ColorPickerPopup::setLastSel(const QColor & col) { lastSel = col; } void ColorPickerPopup::setLastSel(const QColor & col) { lastSel = col; }
@ -994,7 +994,7 @@ void ColorPickerItem::mouseMoveEvent(QMouseEvent *)
void ColorPickerItem::mouseReleaseEvent(QMouseEvent *) void ColorPickerItem::mouseReleaseEvent(QMouseEvent *)
{ {
sel = true; sel = true;
emit selected(); Q_EMIT selected();
} }
/*! /*!
@ -1062,7 +1062,7 @@ void ColorPickerButton::mouseReleaseEvent(QMouseEvent *)
{ {
setFrameShadow(Raised); setFrameShadow(Raised);
repaint(); repaint();
emit clicked(); Q_EMIT clicked();
} }
/*! /*!
@ -1096,7 +1096,7 @@ void ColorPickerButton::keyReleaseEvent(QKeyEvent *e)
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) { } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) {
setFrameShadow(Raised); setFrameShadow(Raised);
repaint(); repaint();
emit clicked(); Q_EMIT clicked();
} else { } else {
QFrame::keyReleaseEvent(e); QFrame::keyReleaseEvent(e);
} }

View File

@ -182,7 +182,7 @@ void FileChooser::chooseFile()
if (!fn.isEmpty()) { if (!fn.isEmpty()) {
lineEdit->setText(fn); lineEdit->setText(fn);
emit fileNameSelected(fn); Q_EMIT fileNameSelected(fn);
} }
} }
@ -1032,7 +1032,7 @@ void ColorButton::onChooseColor()
if ( c.isValid() ) if ( c.isValid() )
{ {
setColor( c ); setColor( c );
emit changed(); Q_EMIT changed();
} }
} }

View File

@ -153,7 +153,7 @@ void Wizard::setCurrentIndex(int index)
textLabel->setText(stackWidget->currentWidget()->windowTitle()); textLabel->setText(stackWidget->currentWidget()->windowTitle());
_backButton->setEnabled(index > 0); _backButton->setEnabled(index > 0);
_nextButton->setEnabled(index < count()-1); _nextButton->setEnabled(index < count()-1);
emit currentIndexChanged(index); Q_EMIT currentIndexChanged(index);
} }
} }
@ -171,7 +171,7 @@ void Wizard::setPageTitle(QString const &newTitle)
{ {
stackWidget->currentWidget()->setWindowTitle(newTitle); stackWidget->currentWidget()->setWindowTitle(newTitle);
textLabel->setText(newTitle); textLabel->setText(newTitle);
emit pageTitleChanged(newTitle); Q_EMIT pageTitleChanged(newTitle);
} }
WizardExtension::WizardExtension(Wizard *widget, QObject *parent) WizardExtension::WizardExtension(Wizard *widget, QObject *parent)